// input of turnInput. The loop runs the tool calls of each response and sends
// their results back, until a response has no tool call: the final answer.
//
-// The view goes out as the messages of a viewLayout. After the turns, when no
-// message waits, a warm-up request sends only the prompt, the tools, and the
-// view messages, and stops at its first output item. It writes a cache entry
-// at the end of the view, where the next turn has a message end, so the first
-// request of that turn reads the view from the cache. The warm-up is not
-// logged, and its output is not used.
+// The view goes out as the messages of a viewLayout, so the first request of
+// a turn reads the view of the turn before from the prompt cache.
//
// Each later request of a turn begins with the whole input of the request
// before it, then its output items, then the tool results. The output items
client modelClient
tools *toolbox
layout *viewLayout
- warmUp bool
clock func() time.Time
notify func(Event)
wake chan struct{}
Output string `json:"output"`
}
-// newLoop returns the loop of agent with its memory, with the warm-up on. Its
-// calls go through client, and notify gets each event. The clock gives the
-// date of a turn.
+// newLoop returns the loop of agent with its memory. Its calls go through
+// client, and notify gets each event. The clock gives the date of a turn.
func newLoop(agent *Agent, memory *Memory, client modelClient, clock func() time.Time, notify func(Event)) (*loop, error) {
layout, err := loadLayout(filepath.Join(agent.dir, layoutFileName))
if err != nil {
memory: memory,
client: client,
layout: layout,
- warmUp: true,
clock: clock,
notify: notify,
wake: make(chan struct{}, 1),
return l.memory.Flush(ctx)
}
-// Run runs a turn each time user messages wait, and a warm-up after the turns,
-// until ctx ends. It returns the error of a failed log write, which stops the
-// agent. A failed warm-up only costs the cache of the next turn, so its error
-// stays in the usage file.
+// Run runs a turn each time user messages wait, until ctx ends. It returns the
+// error of a failed log write, which stops the agent.
func (l *loop) Run(ctx context.Context) error {
for {
- turned := false
for l.waiting() {
err := l.turn(ctx)
if err != nil {
return err
}
-
- turned = true
- }
-
- if turned && l.warmUp {
- _ = l.warm(ctx)
}
select {
return input, nil
}
-// warm sends the warm-up request, when the builds are done and no message
-// waits.
-func (l *loop) warm(ctx context.Context) error {
- err := l.memory.Wait(ctx)
- if err != nil || l.waiting() {
- return err
- }
-
- instructions, err := l.agent.Prompt()
- if err != nil {
- return err
- }
-
- view, err := l.layout.Split(l.memory.ViewLines(nil))
- if err != nil {
- return err
- }
-
- _, err = l.client.Create(ctx, Request{
- Model: l.agent.config.Model,
- Effort: l.agent.config.Effort,
- Instructions: instructions,
- Tools: agentTools(),
- Input: warmUpInput(view),
- Turn: "warmup",
- StopAtOutput: true,
- })
-
- return err
-}
-
// state returns the per-turn state: the end of the view, and the date and time
// of the turn.
func (l *loop) state() string {
// and one for each new message, whole. The view leaves out the leaf lines of
// the new messages.
func turnInput(view []string, state string, messages []Message) []any {
- input := warmUpInput(view)
+ input := make([]any, 0, len(view)+1+len(messages))
+ for _, text := range view {
+ input = append(input, userMessage(text))
+ }
+
input = append(input, userMessage(state))
for _, message := range messages {
input = append(input, userMessage(message.Text))
return input
}
-// warmUpInput returns a user message for each view message.
-func warmUpInput(view []string) []any {
- input := make([]any, len(view))
- for n, text := range view {
- input[n] = userMessage(text)
- }
-
- return input
-}
-
// inputItem returns an output item as an input item for the next request. The
// calls do not store their items, so the item goes back whole, without its id.
func inputItem(raw json.RawMessage) (map[string]any, error) {