返回 DeepSeek-Reasonix
tool.go
根目录 / internal / lsp / tool.go
1 package lsp
2
3 import (
4 "context"
5 "encoding/json"
6 "fmt"
7
8 "reasonix/internal/tool"
9 )
10
11 // Tools adapts the manager's read-only queries to the tool.Tool interface. They
12 // report ReadOnly=true, so a batch of them rides the agent's parallel dispatch
13 // and shares one server per language.
14 func Tools(m *Manager) []tool.Tool {
15 return []tool.Tool{
16 posTool{m, "lsp_definition", "Jump to where a symbol is defined. Give the file, the 1-based line the symbol appears on, and the symbol text itself.", m.Definition},
17 posTool{m, "lsp_references", "List every reference to a symbol across the workspace. Give the file, the 1-based line, and the symbol text.", m.References},
18 posTool{m, "lsp_hover", "Show the type signature and documentation for a symbol. Give the file, the 1-based line, and the symbol text.", m.Hover},
19 diagTool{m},
20 }
21 }
22
23 type posArgs struct {
24 File string `json:"file"`
25 Line int `json:"line"`
26 Symbol string `json:"symbol"`
27 }
28
29 type posTool struct {
30 m *Manager
31 name string
32 desc string
33 fn func(context.Context, string, int, string) (string, error)
34 }
35
36 func (t posTool) Name() string { return t.name }
37 func (t posTool) Description() string { return t.desc }
38 func (t posTool) ReadOnly() bool { return true }
39
40 // SnipHint keeps a long head of definitions/references and a short tail: the
41 // first results are the ones the model acts on.
42 func (posTool) SnipHint() tool.SnipHint {
43 return tool.SnipHint{Head: 60, Tail: 10, HeadChars: 10000, TailChars: 1500}
44 }
45 func (t posTool) Schema() json.RawMessage {
46 return json.RawMessage(`{
47 "type":"object",
48 "properties":{
49 "file":{"type":"string","description":"Path to the source file, relative to the workspace root or absolute."},
50 "line":{"type":"integer","description":"1-based line number the symbol appears on."},
51 "symbol":{"type":"string","description":"The exact symbol text on that line, e.g. \"executeBatch\". Used to locate the column."}
52 },
53 "required":["file","line","symbol"]
54 }`)
55 }
56
57 func (t posTool) Execute(ctx context.Context, args json.RawMessage) (string, error) {
58 var p posArgs
59 if err := json.Unmarshal(args, &p); err != nil {
60 return "", fmt.Errorf("invalid args: %w", err)
61 }
62 if p.File == "" || p.Symbol == "" || p.Line < 1 {
63 return "", fmt.Errorf("file, line (>=1) and symbol are required")
64 }
65 return t.fn(ctx, p.File, p.Line, p.Symbol)
66 }
67
68 type diagTool struct{ m *Manager }
69
70 func (diagTool) Name() string { return "lsp_diagnostics" }
71 func (diagTool) ReadOnly() bool { return true }
72
73 // SnipHint keeps a long head of diagnostics and a short tail.
74 func (diagTool) SnipHint() tool.SnipHint {
75 return tool.SnipHint{Head: 60, Tail: 10, HeadChars: 10000, TailChars: 1500}
76 }
77 func (diagTool) Description() string {
78 return "Report compiler/linter diagnostics (errors, warnings) for a file from its language server. Use after editing to check the change compiles."
79 }
80 func (diagTool) Schema() json.RawMessage {
81 return json.RawMessage(`{"type":"object","properties":{"file":{"type":"string","description":"Path to the source file, relative to the workspace root or absolute."}},"required":["file"]}`)
82 }
83
84 func (t diagTool) Execute(ctx context.Context, args json.RawMessage) (string, error) {
85 var p struct {
86 File string `json:"file"`
87 }
88 if err := json.Unmarshal(args, &p); err != nil {
89 return "", fmt.Errorf("invalid args: %w", err)
90 }
91 if p.File == "" {
92 return "", fmt.Errorf("file is required")
93 }
94 return t.m.Diagnostics(ctx, p.File)
95 }
96
96 lines GO