返回 DeepSeek-Reasonix
dispatch_test.go
根目录 / internal / agent / dispatch_test.go
1 package agent
2
3 import (
4 "context"
5 "testing"
6
7 "reasonix/internal/event"
8 "reasonix/internal/provider"
9 "reasonix/internal/tool"
10 )
11
12 // TestEarlyToolDispatch proves a ChunkToolCallStart surfaces a ToolDispatch
13 // immediately (Partial, name only) so the card shows while the arguments are
14 // still streaming, and that a second, full dispatch (with args) follows once the
15 // call completes — the fix for "the edit_file card only appears after everything
16 // is written".
17 func TestEarlyToolDispatch(t *testing.T) {
18 prov := &mockProvider{name: "p", chunks: []provider.Chunk{
19 {Type: provider.ChunkToolCallStart, ToolCall: &provider.ToolCall{ID: "c1", Name: "read_file"}},
20 {Type: provider.ChunkToolCall, ToolCall: &provider.ToolCall{ID: "c1", Name: "read_file", Arguments: `{"path":"/x"}`}},
21 {Type: provider.ChunkDone},
22 }}
23 reg := tool.NewRegistry()
24 reg.Add(fakeTool{name: "read_file", readOnly: true})
25
26 var got []event.Event
27 sink := event.FuncSink(func(e event.Event) { got = append(got, e) })
28 a := New(prov, reg, NewSession(""), Options{MaxSteps: 1}, sink)
29 _ = a.Run(context.Background(), "go") // errors at the 1-step cap; we only want the events
30
31 var partial, full int
32 for _, e := range got {
33 if e.Kind != event.ToolDispatch {
34 continue
35 }
36 if e.Tool.Partial {
37 partial++
38 if e.Tool.Name != "read_file" {
39 t.Errorf("partial dispatch name = %q, want read_file", e.Tool.Name)
40 }
41 } else {
42 full++
43 if e.Tool.Args == "" {
44 t.Errorf("full dispatch should carry args, got none")
45 }
46 }
47 }
48 if partial != 2 {
49 t.Errorf("want 2 partial (early) dispatches (original + grace-round replay), got %d", partial)
50 }
51 if full != 1 {
52 t.Errorf("want 1 full dispatch, got %d", full)
53 }
54 }
55
55 lines GO