返回 DeepSeek-Reasonix
skill_disclosure_test.go
根目录 / internal / boot / skill_disclosure_test.go
1 package boot
2
3 import (
4 "context"
5 "fmt"
6 "reflect"
7 "strings"
8 "testing"
9
10 "reasonix/internal/agent/testutil"
11 "reasonix/internal/event"
12 "reasonix/internal/provider"
13 )
14
15 func TestSkillDiscoveryAndEmbeddedReferencesThroughBoot(t *testing.T) {
16 isolateConfigHome(t)
17 dir := robustTempDir(t)
18 t.Chdir(dir)
19 writeFile(t, dir, "reasonix.toml", `
20 default_model = "test-model"
21 [agent]
22 system_prompt = "BASE"
23 [[providers]]
24 name = "test-model"
25 kind = "boot-token-profile-test"
26 model = "x"
27 `)
28 for i := range 100 {
29 name := fmt.Sprintf("a-%03d-%s", i, strings.Repeat("x", 45))
30 writeFile(t, dir, ".agents/skills/"+name+"/SKILL.md",
31 "---\nname: "+name+"\ndescription: Common fixture operation\n---\nFixture body")
32 }
33 const target = "zzz-rare-diagnostic"
34 writeFile(t, dir, ".agents/skills/"+target+"/SKILL.md",
35 "---\nname: "+target+"\ndescription: Diagnose rare service faults\n---\nRARE_BODY_ON_DEMAND")
36 registerBootTokenProfileTestProvider()
37 prov := testutil.NewMock("skill-disclosure",
38 testutil.Turn{ToolCalls: []provider.ToolCall{{ID: "find", Name: "use_capability",
39 Arguments: `{"action":"search","query":"zzz-rare-diagnostic"}`}}},
40 testutil.Turn{ToolCalls: []provider.ToolCall{{ID: "load", Name: "use_capability",
41 Arguments: `{"action":"call","capability_id":"skill:zzz-rare-diagnostic","arguments":{}}`}}},
42 testutil.Turn{ToolCalls: []provider.ToolCall{{ID: "guide", Name: "use_capability",
43 Arguments: `{"action":"call","capability_id":"tool:read_skill","arguments":{"name":"reasonix-guide"}}`}}},
44 testutil.Turn{ToolCalls: []provider.ToolCall{{ID: "page", Name: "use_capability",
45 Arguments: `{"action":"call","capability_id":"tool:read_skill","arguments":{"name":"reasonix-guide","reference":"references/hooks.md"}}`}}},
46 testutil.Turn{Text: "done"},
47 )
48 setBootTokenProfileTestProvider(t, prov)
49 ctrl, err := Build(context.Background(), Options{Sink: event.Discard})
50 if err != nil {
51 t.Fatal(err)
52 }
53 defer ctrl.Close()
54 if err := ctrl.Run(context.Background(), "Find the rare diagnostic and consult hook matching guidance."); err != nil {
55 t.Fatal(err)
56 }
57 reqs := mainConversationRequests(prov.Requests())
58 if len(reqs) < 5 {
59 t.Fatalf("request count = %d, want full discovery/read sequence", len(reqs))
60 }
61 initial := sessionContextMessage(reqs[0].Messages)
62 if strings.Contains(initial, target) || !strings.Contains(initial, "more skills") {
63 t.Fatalf("fixture did not exercise an omitted catalog entry: %s", initial)
64 }
65 for _, req := range reqs {
66 if systemMessage(req.Messages) != systemMessage(reqs[0].Messages) ||
67 !reflect.DeepEqual(req.Tools, reqs[0].Tools) {
68 t.Fatal("skill discovery/read changed the provider prefix or schemas")
69 }
70 }
71 var outputs []string
72 for _, msg := range ctrl.History() {
73 if msg.Role == provider.RoleTool {
74 outputs = append(outputs, msg.Content)
75 }
76 }
77 if len(outputs) != 4 {
78 t.Fatalf("tool outputs = %d, want 4", len(outputs))
79 }
80 if !strings.Contains(outputs[0], "skill:"+target) ||
81 !strings.Contains(outputs[1], "RARE_BODY_ON_DEMAND") {
82 t.Fatalf("omitted skill not discoverable/invocable: %v", outputs[:2])
83 }
84 if strings.Contains(outputs[2], "hook.invalid_matcher") ||
85 !strings.Contains(outputs[3], "hook.invalid_matcher") {
86 t.Fatalf("reference page not loaded only on demand: %v", outputs[2:])
87 }
88 }
89
89 lines GO