]> git.giacomolli.uk - onechat/blobdiff - tools.go
Log daemon events at their slog levels
[onechat] / tools.go
index 009e3312404c646f53a8fea4284371264b420a70..4c0035085b1f1dfcc6ab75309031a96ae1b7066d 100644 (file)
--- a/tools.go
+++ b/tools.go
@@ -71,12 +71,12 @@ type Toolbox struct {
 func (b *Toolbox) Call(ctx context.Context, name, arguments string) (string, error) {
        _, err := b.log.Append("tool", toolCallText(name, arguments))
        if err != nil {
-               return "", err
+               return "", fmt.Errorf("append tool call: %w", err)
        }
 
        err = b.log.Flush(ctx)
        if err != nil && ctx.Err() == nil {
-               return "", err
+               return "", fmt.Errorf("flush tool call: %w", err)
        }
 
        output := "not run: the turn was interrupted"
@@ -86,7 +86,7 @@ func (b *Toolbox) Call(ctx context.Context, name, arguments string) (string, err
 
        _, err = b.log.Append("echo", output)
        if err != nil {
-               return "", err
+               return "", fmt.Errorf("append tool result: %w", err)
        }
 
        return output, nil
@@ -94,11 +94,15 @@ func (b *Toolbox) Call(ctx context.Context, name, arguments string) (string, err
 
 // run runs one tool and returns its output, or its error as text.
 func (b *Toolbox) run(ctx context.Context, name, arguments string) string {
-       var output string
-       var err error
+       var (
+               output string
+               err    error
+       )
+
        switch name {
        case "zoom":
                var args ToolZoomArguments
+
                err = toolDecodeArguments(arguments, &args)
                switch {
                case err != nil:
@@ -109,36 +113,42 @@ func (b *Toolbox) run(ctx context.Context, name, arguments string) string {
                }
        case "date":
                var args ToolDateArguments
+
                err = toolDecodeArguments(arguments, &args)
                if err == nil {
                        output, err = b.date(args.ID)
                }
        case "shell":
                var args ToolShellArguments
+
                err = toolDecodeArguments(arguments, &args)
                if err == nil {
                        output = b.shell(ctx, args.Command)
                }
        case "read":
                var args ToolPathArguments
+
                err = toolDecodeArguments(arguments, &args)
                if err == nil {
                        output, err = b.read(args.Path)
                }
        case "write":
                var args ToolWriteArguments
+
                err = toolDecodeArguments(arguments, &args)
                if err == nil {
                        output, err = b.write(args.Path, args.Text)
                }
        case "edit":
                var args ToolEditArguments
+
                err = toolDecodeArguments(arguments, &args)
                if err == nil {
                        output, err = b.edit(args.Path, args.Old, args.New)
                }
        case "spawn":
                var args ToolSpawnArguments
+
                err = toolDecodeArguments(arguments, &args)
                switch {
                case b.runs == nil:
@@ -148,6 +158,7 @@ func (b *Toolbox) run(ctx context.Context, name, arguments string) string {
                }
        case "tell":
                var args ToolTellArguments
+
                err = toolDecodeArguments(arguments, &args)
                switch {
                case b.runs == nil:
@@ -157,6 +168,7 @@ func (b *Toolbox) run(ctx context.Context, name, arguments string) string {
                }
        case "whatsapp_find":
                var args ToolWhatsAppFindArguments
+
                err = toolDecodeArguments(arguments, &args)
                switch {
                case b.whatsapp == nil:
@@ -166,6 +178,7 @@ func (b *Toolbox) run(ctx context.Context, name, arguments string) string {
                }
        case "whatsapp_send":
                var args ToolWhatsAppSendArguments
+
                err = toolDecodeArguments(arguments, &args)
                switch {
                case b.whatsapp == nil:
@@ -175,6 +188,7 @@ func (b *Toolbox) run(ctx context.Context, name, arguments string) string {
                }
        case "schedule_set":
                var args ToolScheduleSetArguments
+
                err = toolDecodeArguments(arguments, &args)
                switch {
                case b.schedules == nil:
@@ -192,6 +206,7 @@ func (b *Toolbox) run(ctx context.Context, name, arguments string) string {
                }
        case "schedule_remove":
                var args ToolScheduleRemoveArguments
+
                err = toolDecodeArguments(arguments, &args)
                switch {
                case b.schedules == nil:
@@ -234,18 +249,20 @@ func (b *Toolbox) zoom(id, n int) (string, error) {
        if n == 1 {
                message, err := b.chat.Message(id)
                if err != nil {
-                       return "", err
+                       return "", fmt.Errorf("read zoom message: %w", err)
                }
 
                return leafSource(message), nil
        }
 
        level := bits.TrailingZeros(uint(n)) - 1
+
        var lines []string
+
        for _, index := range []int{2 * id / n, 2*id/n + 1} {
                node, built, err := b.chat.Node(level, index)
                if err != nil {
-                       return "", err
+                       return "", fmt.Errorf("read zoom node: %w", err)
                }
 
                text := viewPending
@@ -267,12 +284,12 @@ func (b *Toolbox) date(id int) (string, error) {
 
        message, err := b.chat.Message(id)
        if err != nil {
-               return "", err
+               return "", fmt.Errorf("read dated message: %w", err)
        }
 
        date, err := time.Parse(time.RFC3339Nano, message.Date)
        if err != nil {
-               return "", err
+               return "", fmt.Errorf("parse message date: %w", err)
        }
 
        return date.Local().Format("Monday 2006-01-02 15:04:05 -07:00"), nil
@@ -298,7 +315,9 @@ func (b *Toolbox) shell(ctx context.Context, command string) string {
        err := cmd.Run()
 
        text := output.String()
+
        var exit *exec.ExitError
+
        switch {
        case ctx.Err() != nil:
                text += "\n[interrupted]"
@@ -317,7 +336,7 @@ func (b *Toolbox) shell(ctx context.Context, command string) string {
 func (b *Toolbox) read(path string) (text string, err error) {
        file, err := os.Open(b.path(path))
        if err != nil {
-               return "", err
+               return "", fmt.Errorf("open tool file: %w", err)
        }
 
        defer func() {
@@ -327,7 +346,7 @@ func (b *Toolbox) read(path string) (text string, err error) {
        output := &ToolHeadTail{}
        _, err = io.Copy(output, file)
        if err != nil {
-               return "", err
+               return "", fmt.Errorf("read tool file: %w", err)
        }
 
        return output.String(), nil
@@ -339,12 +358,12 @@ func (b *Toolbox) write(path, text string) (string, error) {
        path = b.path(path)
        err := os.MkdirAll(filepath.Dir(path), 0o755)
        if err != nil {
-               return "", err
+               return "", fmt.Errorf("create tool file directory: %w", err)
        }
 
        err = os.WriteFile(path, []byte(text), 0o644)
        if err != nil {
-               return "", err
+               return "", fmt.Errorf("write tool file: %w", err)
        }
 
        return fmt.Sprintf("wrote %d bytes to %s", len(text), path), nil
@@ -356,7 +375,7 @@ func (b *Toolbox) edit(path, old, replacement string) (string, error) {
        path = b.path(path)
        data, err := os.ReadFile(path)
        if err != nil {
-               return "", err
+               return "", fmt.Errorf("read file to edit: %w", err)
        }
 
        if old == "" {
@@ -370,12 +389,12 @@ func (b *Toolbox) edit(path, old, replacement string) (string, error) {
 
        info, err := os.Stat(path)
        if err != nil {
-               return "", err
+               return "", fmt.Errorf("read file mode: %w", err)
        }
 
        err = os.WriteFile(path, []byte(strings.Replace(string(data), old, replacement, 1)), info.Mode().Perm())
        if err != nil {
-               return "", err
+               return "", fmt.Errorf("write edited file: %w", err)
        }
 
        return "edited " + path, nil
@@ -530,6 +549,7 @@ func toolClip(text string) string {
 func toolDecodeArguments(arguments string, args any) error {
        decoder := json.NewDecoder(strings.NewReader(arguments))
        decoder.DisallowUnknownFields()
+
        err := decoder.Decode(args)
        if err != nil {
                return fmt.Errorf("arguments: %w", err)