65 lines
1.8 KiB
Go
65 lines
1.8 KiB
Go
package agents
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/base64"
|
|
"encoding/json"
|
|
"fmt"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"strings"
|
|
"time"
|
|
|
|
"g.fc.run/ai/byre/internal/mcp"
|
|
)
|
|
|
|
func (m *Manager) renderDiagramTool() mcp.Tool {
|
|
return mcp.Tool{
|
|
Name: "render_diagram",
|
|
Description: "See a diagram as byre's app draws it in the transcript: pass the body of a ```diagram block and get back a PNG of the result, drawn by the app's own renderer in light mode. " +
|
|
"A source the app can't draw comes back as the reason instead.",
|
|
InputSchema: json.RawMessage(`{
|
|
"type": "object",
|
|
"properties": {
|
|
"source": {"type": "string", "description": "The block's body, without the fence lines."}
|
|
},
|
|
"required": ["source"]
|
|
}`),
|
|
Rich: func(ctx context.Context, raw json.RawMessage) mcp.Result {
|
|
args := struct {
|
|
Source string `json:"source"`
|
|
}{}
|
|
err := json.Unmarshal(raw, &args)
|
|
if err != nil {
|
|
return mcp.TextResult(fmt.Sprintf("malformed render_diagram call: %v", err), true)
|
|
}
|
|
png, err := renderDiagram(ctx, args.Source)
|
|
if err != nil {
|
|
return mcp.TextResult(err.Error(), true)
|
|
}
|
|
return mcp.Result{Content: []mcp.Content{{Type: "image", Data: base64.StdEncoding.EncodeToString(png), MimeType: "image/png"}}}
|
|
},
|
|
}
|
|
}
|
|
|
|
func renderDiagram(ctx context.Context, source string) ([]byte, error) {
|
|
exe, err := os.Executable()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
app := filepath.Join(filepath.Dir(exe), "..", "MacOS", "Byre")
|
|
ctx, cancel := context.WithTimeout(ctx, 30*time.Second)
|
|
defer cancel()
|
|
cmd := exec.CommandContext(ctx, app, "render-diagram")
|
|
cmd.Stdin = strings.NewReader(source)
|
|
stderr := bytes.Buffer{}
|
|
cmd.Stderr = &stderr
|
|
out, err := cmd.Output()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("render diagram: %w: %s", err, strings.TrimSpace(stderr.String()))
|
|
}
|
|
return out, nil
|
|
}
|