返回 DeepSeek-Reasonix
cache_diagnostics_test.go
根目录 / internal / agent / cache_diagnostics_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 type cacheDiagProvider struct {
13 chunks [][]provider.Chunk
14 calls int
15 }
16
17 func (p *cacheDiagProvider) Name() string { return "cache-diag" }
18
19 func (p *cacheDiagProvider) Stream(_ context.Context, _ provider.Request) (<-chan provider.Chunk, error) {
20 chunks := p.chunks[p.calls]
21 p.calls++
22 ch := make(chan provider.Chunk, len(chunks))
23 for _, chunk := range chunks {
24 ch <- chunk
25 }
26 close(ch)
27 return ch, nil
28 }
29
30 func TestRunPopulatesCacheDiagnosticsOnUsageEvents(t *testing.T) {
31 prov := &cacheDiagProvider{chunks: [][]provider.Chunk{
32 {
33 {Type: provider.ChunkText, Text: "first"},
34 {Type: provider.ChunkUsage, Usage: &provider.Usage{
35 PromptTokens: 100, CompletionTokens: 10, TotalTokens: 110,
36 CacheHitTokens: 0, CacheMissTokens: 100,
37 }},
38 },
39 {
40 {Type: provider.ChunkText, Text: "second"},
41 {Type: provider.ChunkUsage, Usage: &provider.Usage{
42 PromptTokens: 100, CompletionTokens: 10, TotalTokens: 110,
43 CacheHitTokens: 80, CacheMissTokens: 20,
44 }},
45 },
46 }}
47 reg := tool.NewRegistry()
48 var diagnostics []*event.CacheDiagnostics
49 sink := event.FuncSink(func(e event.Event) {
50 if e.Kind == event.Usage {
51 diagnostics = append(diagnostics, e.CacheDiagnostics)
52 }
53 })
54 session := NewSession("stable system")
55 session.IncrementRewrite()
56 a := New(prov, reg, session, Options{}, sink)
57
58 if err := a.Run(context.Background(), "one"); err != nil {
59 t.Fatalf("first Run: %v", err)
60 }
61 reg.Add(fakeTool{name: "read_file", readOnly: true})
62 if err := a.Run(context.Background(), "two"); err != nil {
63 t.Fatalf("second Run: %v", err)
64 }
65
66 if len(diagnostics) != 2 {
67 t.Fatalf("got %d usage diagnostics, want 2", len(diagnostics))
68 }
69 first, second := diagnostics[0], diagnostics[1]
70 if first == nil || second == nil {
71 t.Fatalf("diagnostics should be populated on every usage event: first=%v second=%v", first, second)
72 }
73 if first.PrefixChanged {
74 t.Fatalf("first usage should not report a changed prefix: %+v", first)
75 }
76 if first.CacheMissTokens != 100 || first.CacheHitTokens != 0 {
77 t.Fatalf("first cache tokens = hit %d miss %d, want hit 0 miss 100", first.CacheHitTokens, first.CacheMissTokens)
78 }
79 if !second.PrefixChanged {
80 t.Fatalf("second usage should report the tool prefix change: %+v", second)
81 }
82 if len(second.PrefixChangeReasons) != 1 || second.PrefixChangeReasons[0] != "tools" {
83 t.Fatalf("second change reasons = %v, want [tools]", second.PrefixChangeReasons)
84 }
85 if second.CacheHitTokens != 80 || second.CacheMissTokens != 20 {
86 t.Fatalf("second cache tokens = hit %d miss %d, want hit 80 miss 20", second.CacheHitTokens, second.CacheMissTokens)
87 }
88 if first.ToolsHash == second.ToolsHash {
89 t.Fatalf("tool hash should change after registering a tool: %q", first.ToolsHash)
90 }
91 }
92
92 lines GO