use options

This commit is contained in:
mudler
2024-04-01 20:02:25 +02:00
parent b45490e84d
commit 7c679ead94
3 changed files with 61 additions and 34 deletions

View File

@@ -63,12 +63,11 @@ func New(opts ...Option) (*Agent, error) {
// Ask is a pre-emptive, blocking call that returns the response as soon as it's ready. // Ask is a pre-emptive, blocking call that returns the response as soon as it's ready.
// It discards any other computation. // It discards any other computation.
func (a *Agent) Ask(text, image string) []string { func (a *Agent) Ask(opts ...JobOption) []string {
//a.StopAction() //a.StopAction()
j := NewJob(text, image) j := NewJob(opts...)
fmt.Println("Job created", text) // fmt.Println("Job created", text)
a.jobQueue <- j a.jobQueue <- j
fmt.Println("Waiting for result")
return j.Result.WaitResult() return j.Result.WaitResult()
} }
@@ -94,26 +93,18 @@ func (a *Agent) Run() error {
// Expose a REST API to interact with the agent to ask it things // Expose a REST API to interact with the agent to ask it things
fmt.Println("Agent is running")
clearConvTimer := time.NewTicker(1 * time.Minute) clearConvTimer := time.NewTicker(1 * time.Minute)
for { for {
fmt.Println("Agent loop")
select { select {
case job := <-a.jobQueue: case job := <-a.jobQueue:
fmt.Println("job from the queue")
// Consume the job and generate a response // Consume the job and generate a response
// TODO: Give a short-term memory to the agent // TODO: Give a short-term memory to the agent
a.consumeJob(job) a.consumeJob(job)
case <-a.context.Done(): case <-a.context.Done():
fmt.Println("Context canceled, agent is stopping...")
// Agent has been canceled, return error // Agent has been canceled, return error
return ErrContextCanceled return ErrContextCanceled
case <-clearConvTimer.C: case <-clearConvTimer.C:
fmt.Println("Removing chat history...")
// TODO: decide to do something on its own with the conversation result // TODO: decide to do something on its own with the conversation result
// before clearing it out // before clearing it out

View File

@@ -61,7 +61,12 @@ var _ = Describe("Agent test", func() {
Expect(err).ToNot(HaveOccurred()) Expect(err).ToNot(HaveOccurred())
go agent.Run() go agent.Run()
defer agent.Stop() defer agent.Stop()
res := agent.Ask("can you get the weather in boston, and afterward of Milano, Italy?", "") res := agent.Ask(
WithReasoningCallback(func(a Action, ap action.ActionParams, s string) {
fmt.Println("Reasoning", s)
}),
WithText("can you get the weather in boston, and afterward of Milano, Italy?"),
)
Expect(res).To(ContainElement(testActionResult), fmt.Sprint(res)) Expect(res).To(ContainElement(testActionResult), fmt.Sprint(res))
Expect(res).To(ContainElement(testActionResult2), fmt.Sprint(res)) Expect(res).To(ContainElement(testActionResult2), fmt.Sprint(res))
}) })
@@ -75,7 +80,9 @@ var _ = Describe("Agent test", func() {
Expect(err).ToNot(HaveOccurred()) Expect(err).ToNot(HaveOccurred())
go agent.Run() go agent.Run()
defer agent.Stop() defer agent.Stop()
res := agent.Ask("can you get the weather in boston?", "") res := agent.Ask(
WithText("can you get the weather in boston?"),
)
Expect(res).To(ContainElement(testActionResult), fmt.Sprint(res)) Expect(res).To(ContainElement(testActionResult), fmt.Sprint(res))
}) })
}) })

View File

@@ -14,52 +14,82 @@ type Job struct {
// The job is a request to the agent to do something // The job is a request to the agent to do something
// It can be a question, a command, or a request to do something // It can be a question, a command, or a request to do something
// The agent will try to do it, and return a response // The agent will try to do it, and return a response
Text string Text string
Image string // base64 encoded image Image string // base64 encoded image
Result *JobResult Result *JobResult
reasoningCallback func(Action, action.ActionParams, string)
resultCallback func(Action, action.ActionParams, string, string)
} }
// JobResult is the result of a job // JobResult is the result of a job
type JobResult struct { type JobResult struct {
sync.Mutex sync.Mutex
// The result of a job // The result of a job
Data []string Data []string
reasoningCallback func(Action, action.ActionParams, string) Error error
resultCallback func(Action, action.ActionParams, string, string) ready chan bool
ready chan bool }
type JobOption func(*Job)
func WithReasoningCallback(f func(Action, action.ActionParams, string)) JobOption {
return func(r *Job) {
r.reasoningCallback = f
}
}
func WithResultCallback(f func(Action, action.ActionParams, string, string)) JobOption {
return func(r *Job) {
r.resultCallback = f
}
} }
// NewJobResult creates a new job result // NewJobResult creates a new job result
func NewJobResult() *JobResult { func NewJobResult() *JobResult {
return &JobResult{ r := &JobResult{
ready: make(chan bool), ready: make(chan bool),
} }
return r
} }
func (j *JobResult) Callback(a Action, p action.ActionParams, s string) { func (j *Job) Callback(a Action, p action.ActionParams, s string) {
if j.reasoningCallback == nil { if j.reasoningCallback == nil {
return return
} }
j.reasoningCallback(a, p, s) j.reasoningCallback(a, p, s)
} }
func (j *JobResult) CallbackWithResult(a Action, p action.ActionParams, s, r string) { func (j *Job) CallbackWithResult(a Action, p action.ActionParams, s, r string) {
if j.resultCallback == nil { if j.resultCallback == nil {
return return
} }
j.resultCallback(a, p, s, r) j.resultCallback(a, p, s, r)
} }
func WithImage(image string) JobOption {
return func(j *Job) {
j.Image = image
}
}
func WithText(text string) JobOption {
return func(j *Job) {
j.Text = text
}
}
// NewJob creates a new job // NewJob creates a new job
// It is a request to the agent to do something // It is a request to the agent to do something
// It has a JobResult to get the result asynchronously // It has a JobResult to get the result asynchronously
// To wait for a Job result, use JobResult.WaitResult() // To wait for a Job result, use JobResult.WaitResult()
func NewJob(text, image string) *Job { func NewJob(opts ...JobOption) *Job {
return &Job{ j := &Job{
Text: text,
Image: image,
Result: NewJobResult(), Result: NewJobResult(),
} }
for _, o := range opts {
o(j)
}
return j
} }
// SetResult sets the result of a job // SetResult sets the result of a job
@@ -71,10 +101,11 @@ func (j *JobResult) SetResult(text string) {
} }
// SetResult sets the result of a job // SetResult sets the result of a job
func (j *JobResult) Finish() { func (j *JobResult) Finish(e error) {
j.Lock() j.Lock()
defer j.Unlock() defer j.Unlock()
j.Error = e
close(j.ready) close(j.ready)
} }
@@ -165,7 +196,7 @@ func (a *Agent) consumeJob(job *Job) {
return return
} }
job.Result.Callback(chosenAction, params.actionParams, reasoning) job.Callback(chosenAction, params.actionParams, reasoning)
if params.actionParams == nil { if params.actionParams == nil {
fmt.Println("no parameters") fmt.Println("no parameters")
@@ -186,7 +217,7 @@ func (a *Agent) consumeJob(job *Job) {
} }
fmt.Printf("Action run result: %v\n", result) fmt.Printf("Action run result: %v\n", result)
job.Result.SetResult(result) job.Result.SetResult(result)
job.Result.CallbackWithResult(chosenAction, params.actionParams, reasoning, result) job.CallbackWithResult(chosenAction, params.actionParams, reasoning, result)
// calling the function // calling the function
messages = append(messages, openai.ChatCompletionMessage{ messages = append(messages, openai.ChatCompletionMessage{
@@ -220,8 +251,6 @@ func (a *Agent) consumeJob(job *Job) {
} else if !chosenAction.Definition().Name.Is(action.ReplyActionName) { } else if !chosenAction.Definition().Name.Is(action.ReplyActionName) {
// We need to do another action (?) // We need to do another action (?)
// The agent decided to do another action // The agent decided to do another action
fmt.Println("Another action to do: ", followingAction.Definition().Name)
fmt.Println("Reasoning: ", reasoning)
// call ourselves again // call ourselves again
a.currentReasoning = reasoning a.currentReasoning = reasoning
a.nextAction = followingAction a.nextAction = followingAction
@@ -249,5 +278,5 @@ func (a *Agent) consumeJob(job *Job) {
msg.Content) msg.Content)
a.currentConversation = append(a.currentConversation, msg) a.currentConversation = append(a.currentConversation, msg)
job.Result.Finish() job.Result.Finish(nil)
} }