返回 DeepSeek-Reasonix
usecapability_image_test.go
根目录 / internal / agent / usecapability_image_test.go
1 package agent
2
3 import (
4 "context"
5 "encoding/base64"
6 "strings"
7 "sync/atomic"
8 "testing"
9 "time"
10
11 "reasonix/internal/capability"
12 "reasonix/internal/event"
13 "reasonix/internal/imageinput"
14 "reasonix/internal/plugin"
15 "reasonix/internal/provider"
16 "reasonix/internal/tool"
17 )
18
19 func TestPlannerFirstOnDemandMCPCallPreservesImages(t *testing.T) {
20 t.Setenv("REASONIX_CACHE_HOME", t.TempDir())
21 payload := base64.StdEncoding.EncodeToString([]byte("png-bytes"))
22 var toolCalls atomic.Int32
23 server := imageMCPServer(t, &toolCalls, payload)
24 defer server.Close()
25
26 ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
27 defer cancel()
28 host := plugin.NewHost()
29 defer host.Close()
30 spec := plugin.Spec{Name: "image", Type: "http", URL: server.URL, Authorized: true}
31 runtime := NewMCPCapabilityRuntime(ctx, host, []plugin.Spec{spec}, tool.NewRegistry(), nil)
32 proxy := runtime.NewFrontend(capability.NewLedger(), nil)
33 reg := tool.NewRegistry()
34 reg.Add(proxy)
35 prov := &scriptedProvider{name: "p", turns: [][]provider.Chunk{
36 {toolCallChunk("image-call", "use_capability", `{"action":"call","capability_id":"mcp-tool:image/screenshot","arguments":{}}`), {Type: provider.ChunkDone}},
37 {{Type: provider.ChunkText, Text: "done"}, {Type: provider.ChunkDone}},
38 }}
39 session := NewSession("sys")
40 vision := &summaryProvider{}
41 planner := NewPlannerAgent(prov, reg, session, Options{ImageInput: &imageinput.Config{Model: "vision/model", Resolve: func(string) (provider.Provider, error) { return vision, nil }}}, event.Discard)
42 if host.HasClient("image") {
43 t.Fatal("test requires the MCP server to start on first tool dispatch")
44 }
45 if err := planner.Run(withNoClosedLoop(ctx), "take a screenshot"); err != nil {
46 t.Fatalf("Run: %v", err)
47 }
48 if got := toolCalls.Load(); got != 1 {
49 t.Fatalf("image tools/call count = %d, want 1", got)
50 }
51 wantImage := "data:image/png;base64," + payload
52 for _, message := range session.Messages {
53 if message.Role != provider.RoleTool || message.ToolCallID != "image-call" {
54 continue
55 }
56 if len(message.Images) != 1 || message.Images[0] != wantImage {
57 t.Fatalf("first on-demand MCP images = %v, want %q", message.Images, wantImage)
58 }
59 if !strings.Contains(message.Content, "captured [image: image/png]") {
60 t.Fatalf("first on-demand MCP text = %q, want image placeholder", message.Content)
61 }
62 if message.VisionSummary == nil || !strings.Contains(message.Content, "OCR: Z7") || vision.calls.Load() != 1 {
63 t.Fatal("on-demand image did not use summary service")
64 }
65 return
66 }
67 t.Fatal("no tool message recorded for first on-demand MCP call")
68 }
69
69 lines GO