X-Git-Url: https://git.giacomolli.uk/onechat/blobdiff_plain/723f6e18adc6482ed913e1618c8a76e981720c2b..b1be0ee8530df5db8973bfe355f2f6d3149383a0:/tools.go?ds=sidebyside diff --git a/tools.go b/tools.go index a75514f..621cd84 100644 --- a/tools.go +++ b/tools.go @@ -27,9 +27,9 @@ const ( // output, enough for toolClipCharacters/2 characters of 4 bytes. toolClipBytes = 4 * toolClipCharacters / 2 - // toolShellWaitDelay is how long a command can keep its output open + // toolShellOutputTimeout is how long a command can keep its output open // after it ends or after its kill. - toolShellWaitDelay = 2 * time.Second + toolShellOutputTimeout = 2 * time.Second ) // ToolLog is the log that the tools write their calls and results to. @@ -54,8 +54,8 @@ type ToolChat interface { // spawn and tell, the WhatsApp link for the WhatsApp tools, and the schedules // for the schedule tools. Each permanent agent has the parent-child queue. Only // the root has child creation and source routes. A child has neither of them -// nor a WhatsApp link. The toolbox of a subagent has none of them, so these are -// unknown tools there. +// nor a WhatsApp link. Only the root can show images to the user. The toolbox +// of a subagent has none of them, so these are unknown tools there. type Toolbox struct { log ToolLog chat ToolChat @@ -68,13 +68,16 @@ type Toolbox struct { messages *DaemonMessages children *DaemonChildren routes *Routes + images *ImageStore + imageShow *ImageShow } // Call logs the call of the tool name with its JSON arguments, makes the log -// durable, runs the tool, and logs its clipped output, which it returns. A -// failed tool gives its error as the output. An error of Call itself is an -// error of the log. When ctx ends before the flush, the tool does not run. -func (b *Toolbox) Call(ctx context.Context, name, arguments string) (string, error) { +// durable, runs the tool, and logs its clipped text. It returns that text or +// the text and image parts of a single-message zoom. Pixels never enter the +// log. A failed tool gives its error as the output. An error of Call itself is +// an error of the log. When ctx ends before the flush, the tool does not run. +func (b *Toolbox) Call(ctx context.Context, name, arguments string) (any, error) { _, err := b.log.Append("tool", toolCallText(name, arguments)) if err != nil { return "", fmt.Errorf("append tool call: %w", err) @@ -90,12 +93,26 @@ func (b *Toolbox) Call(ctx context.Context, name, arguments string) (string, err output = toolClip(b.run(ctx, name, arguments)) } + var ( + result any = output + resultErr error + ) + + if err == nil { + result, resultErr = imageToolOutput(b, name, arguments, output) + } + + if resultErr != nil { + output = "error: " + resultErr.Error() + result = output + } + _, err = b.log.Append("echo", output) if err != nil { return "", fmt.Errorf("append tool result: %w", err) } - return output, nil + return result, nil } // run runs one tool and returns its output, or its error as text. @@ -215,6 +232,16 @@ func (b *Toolbox) run(ctx context.Context, name, arguments string) string { case err == nil: output, err = b.runs.Tell(ctx, args.Name, args.Message) } + case "image_show": + var args ToolImageShowArguments + + err = toolDecodeArguments(arguments, &args) + switch { + case b.imageShow == nil: + err = fmt.Errorf("unknown tool %q", name) + case err == nil: + output, err = b.imageShow.Show(ctx, args.Image, args.Caption) + } case "whatsapp_find": var args ToolWhatsAppFindArguments @@ -233,7 +260,7 @@ func (b *Toolbox) run(ctx context.Context, name, arguments string) string { case b.whatsapp == nil: err = fmt.Errorf("unknown tool %q", name) case err == nil: - output, err = b.whatsapp.Send(ctx, args.ID, args.Chat, args.Text) + output, err = b.whatsapp.Send(ctx, args.ID, args.Chat, args.Text, args.Image) } case "schedule_set": var args ToolScheduleSetArguments @@ -363,7 +390,7 @@ func (b *Toolbox) shell(ctx context.Context, command string) string { cmd.Cancel = func() error { return syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL) } - cmd.WaitDelay = toolShellWaitDelay + cmd.WaitDelay = toolShellOutputTimeout output := &ToolHeadTail{} cmd.Stdout = output @@ -533,6 +560,12 @@ type ToolTellArguments struct { Message string `json:"message"` } +// ToolImageShowArguments are the arguments of image_show. +type ToolImageShowArguments struct { + Image string `json:"image"` + Caption string `json:"caption"` +} + // ToolWhatsAppFindArguments are the arguments of whatsapp_find. type ToolWhatsAppFindArguments struct { Name string `json:"name"` @@ -540,9 +573,10 @@ type ToolWhatsAppFindArguments struct { // ToolWhatsAppSendArguments are the arguments of whatsapp_send. type ToolWhatsAppSendArguments struct { - ID string `json:"id"` - Chat string `json:"chat"` - Text string `json:"text"` + ID string `json:"id"` + Chat string `json:"chat"` + Text string `json:"text"` + Image string `json:"image"` } // ToolScheduleSetArguments are the arguments of schedule_set. Exactly one of At