返回 DeepSeek-Reasonix
tools.go
根目录 / internal / browser / tools.go
1 package browser
2
3 import (
4 "context"
5 "encoding/json"
6
7 "reasonix/internal/tool"
8 )
9
10 // Names lists the tool names in the order Tools returns them.
11 func Names() []string {
12 return []string{
13 "browser_tabs", "browser_open", "browser_navigate", "browser_snapshot", "browser_screenshot",
14 "browser_click", "browser_type", "browser_press", "browser_scroll", "browser_select", "browser_upload",
15 "browser_download", "browser_close",
16 }
17 }
18
19 // Tools builds the thirteen browser tools over exec. A nil exec still yields
20 // every tool, each reporting ProviderVisible false so it fails closed.
21 func Tools(exec Executor) []tool.Tool {
22 return []tool.Tool{
23 tabsTool(exec), openTool(exec), navigateTool(exec), snapshotTool(exec), screenshotTool(exec),
24 clickTool(exec), typeTool(exec), pressTool(exec), scrollTool(exec), selectTool(exec), uploadTool(exec),
25 downloadTool(exec), closeTool(exec),
26 }
27 }
28
29 type runFunc func(ctx context.Context, exec Executor, args json.RawMessage) (string, error)
30
31 // base carries one tool's identity and its availability check.
32 type base struct {
33 exec Executor
34 name string
35 description string
36 schema json.RawMessage
37 snip tool.SnipHint
38 }
39
40 func (b base) Name() string { return b.name }
41 func (b base) Description() string { return b.description }
42 func (b base) Schema() json.RawMessage { return b.schema }
43 func (b base) SnipHint() tool.SnipHint { return b.snip }
44 func (b base) ProviderVisible(ctx context.Context) bool {
45 if b.exec == nil {
46 return false
47 }
48 if a, ok := b.exec.(Availability); ok {
49 return a.Available(ctx)
50 }
51 return true
52 }
53
54 func (b base) ready(ctx context.Context) error {
55 if b.exec == nil {
56 return tool.Blocked(noBrowserText)
57 }
58 if !b.ProviderVisible(ctx) {
59 return tool.Blocked(noGrantText)
60 }
61 return ctx.Err()
62 }
63
64 type readTool struct {
65 base
66 run runFunc
67 }
68
69 func (readTool) ReadOnly() bool { return true }
70 func (readTool) PlanModeSafe() bool { return true }
71 func (t readTool) Execute(ctx context.Context, args json.RawMessage) (string, error) {
72 if err := t.ready(ctx); err != nil {
73 return "", err
74 }
75 return t.run(ctx, t.exec, args)
76 }
77
78 type writeTool struct {
79 base
80 run runFunc
81 }
82
83 func (writeTool) ReadOnly() bool { return false }
84 func (writeTool) PlanModeSafe() bool { return false }
85 func (t writeTool) Execute(ctx context.Context, args json.RawMessage) (string, error) {
86 if err := t.ready(ctx); err != nil {
87 return "", err
88 }
89 return t.run(ctx, t.exec, args)
90 }
91
92 // EffectHint names the tab a write touches, or the URL when no tab exists
93 // yet (browser_open); every write reaches the network and none is destructive.
94 func (writeTool) EffectHint(args json.RawMessage) tool.EffectHint {
95 var p struct {
96 TabID string `json:"tabId"`
97 URL string `json:"url"`
98 }
99 _ = json.Unmarshal(args, &p)
100 hint := tool.EffectHint{Known: true, UsesNetwork: true}
101 switch {
102 case p.TabID != "":
103 hint.Targets = []string{p.TabID}
104 case p.URL != "":
105 hint.Targets = []string{p.URL}
106 }
107 return hint
108 }
109
110 var (
111 shortSnip = tool.SnipHint{Head: 8, Tail: 4, HeadChars: 800, TailChars: 400}
112 listSnip = tool.SnipHint{Head: 40, Tail: 8, HeadChars: 4000, TailChars: 800}
113 treeSnip = tool.SnipHint{Head: 200, Tail: 20, HeadChars: 16000, TailChars: 2000}
114 )
115
115 lines GO