Skip to content

Commit b442b91

Browse files
authored
better tool input descriptions (#2507)
better tool input descriptions, also fixes a bug with utf-8/base64 in the diff viewer.
1 parent a7d76d3 commit b442b91

File tree

14 files changed

+70
-34
lines changed

14 files changed

+70
-34
lines changed

.roo/rules/rules.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ It has a TypeScript/React frontend and a Go backend. They talk together over `ws
4141
- NEVER use cursor-help (it looks terrible)
4242
- useAtom() and useAtomValue() are react HOOKS, so they must be called at the component level not inline in JSX
4343
- If you use React.memo(), make sure to add a displayName for the component
44+
- Other
45+
- never use atob() or btoa() (not UTF-8 safe). use functions in frontend/util/util.ts for base64 decoding and encoding
4446
- In general, when writing functions, we prefer _early returns_ rather than putting the majority of a function inside of an if block.
4547

4648
### Styling

frontend/app/view/aifilediff/aifilediff.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
import { RpcApi } from "@/app/store/wshclientapi";
55
import { TabRpcClient } from "@/app/store/wshrpcutil";
6+
import { base64ToString } from "@/util/util";
67
import { DiffViewer } from "@/app/view/codeeditor/diffviewer";
78
import { globalStore, WOS } from "@/store/global";
89
import * as jotai from "jotai";
@@ -80,8 +81,8 @@ const AiFileDiffView: React.FC<ViewComponentProps<AiFileDiffViewModel>> = ({ blo
8081
return;
8182
}
8283

83-
const originalContent = atob(result.originalcontents64);
84-
const modifiedContent = atob(result.modifiedcontents64);
84+
const originalContent = base64ToString(result.originalcontents64);
85+
const modifiedContent = base64ToString(result.modifiedcontents64);
8586

8687
globalStore.set(model.diffDataAtom, {
8788
original: originalContent,

pkg/aiusechat/openai/openai-backend.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -939,8 +939,8 @@ func createToolUseData(toolCallID, toolName string, toolDef *uctypes.ToolDefinit
939939
return toolUseData
940940
}
941941

942-
if toolDef.ToolInputDesc != nil {
943-
toolUseData.ToolDesc = toolDef.ToolInputDesc(parsedArgs)
942+
if toolDef.ToolCallDesc != nil {
943+
toolUseData.ToolDesc = toolDef.ToolCallDesc(parsedArgs, nil, nil)
944944
}
945945

946946
if toolDef.ToolApproval != nil {

pkg/aiusechat/tools_builder.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ func GetBuilderWriteAppFileToolDefinition(appId string) uctypes.ToolDefinition {
5555
"required": []string{"contents"},
5656
"additionalProperties": false,
5757
},
58-
ToolInputDesc: func(input any) string {
58+
ToolCallDesc: func(input any, output any, toolUseData *uctypes.UIMessageDataToolUse) string {
5959
return fmt.Sprintf("writing app.go for %s", appId)
6060
},
6161
ToolAnyCallback: func(input any, toolUseData *uctypes.UIMessageDataToolUse) (any, error) {
@@ -142,7 +142,7 @@ func GetBuilderEditAppFileToolDefinition(appId string) uctypes.ToolDefinition {
142142
"required": []string{"edits"},
143143
"additionalProperties": false,
144144
},
145-
ToolInputDesc: func(input any) string {
145+
ToolCallDesc: func(input any, output any, toolUseData *uctypes.UIMessageDataToolUse) string {
146146
params, err := parseBuilderEditAppFileInput(input)
147147
if err != nil {
148148
return fmt.Sprintf("error parsing input: %v", err)
@@ -185,7 +185,7 @@ func GetBuilderListFilesToolDefinition(appId string) uctypes.ToolDefinition {
185185
"properties": map[string]any{},
186186
"additionalProperties": false,
187187
},
188-
ToolInputDesc: func(input any) string {
188+
ToolCallDesc: func(input any, output any, toolUseData *uctypes.UIMessageDataToolUse) string {
189189
return fmt.Sprintf("listing files for %s", appId)
190190
},
191191
ToolAnyCallback: func(input any, toolUseData *uctypes.UIMessageDataToolUse) (any, error) {

pkg/aiusechat/tools_readdir.go

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,11 +106,23 @@ func GetReadDirToolDefinition() uctypes.ToolDefinition {
106106
"required": []string{"path"},
107107
"additionalProperties": false,
108108
},
109-
ToolInputDesc: func(input any) string {
109+
ToolCallDesc: func(input any, output any, toolUseData *uctypes.UIMessageDataToolUse) string {
110110
parsed, err := parseReadDirInput(input)
111111
if err != nil {
112112
return fmt.Sprintf("error parsing input: %v", err)
113113
}
114+
115+
readFullDir := false
116+
if output != nil {
117+
if outputMap, ok := output.(map[string]any); ok {
118+
_, wasTruncated := outputMap["truncated"]
119+
readFullDir = !wasTruncated
120+
}
121+
}
122+
123+
if readFullDir {
124+
return fmt.Sprintf("reading directory %q (entire directory)", parsed.Path)
125+
}
114126
return fmt.Sprintf("reading directory %q (max_entries: %d)", parsed.Path, *parsed.MaxEntries)
115127
},
116128
ToolAnyCallback: readDirCallback,

pkg/aiusechat/tools_readdir_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -287,7 +287,7 @@ func TestGetReadDirToolDefinition(t *testing.T) {
287287
t.Error("ToolApproval should not be nil")
288288
}
289289

290-
if toolDef.ToolInputDesc == nil {
291-
t.Error("ToolInputDesc should not be nil")
290+
if toolDef.ToolCallDesc == nil {
291+
t.Error("ToolCallDesc should not be nil")
292292
}
293293
}

pkg/aiusechat/tools_readfile.go

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,6 @@ func isBlockedFile(expandedPath string) (bool, string) {
197197
return false, ""
198198
}
199199

200-
201200
func readTextFileCallback(input any, toolUseData *uctypes.UIMessageDataToolUse) (any, error) {
202201
const ReadLimit = 1024 * 1024 * 1024
203202

@@ -283,7 +282,7 @@ func readTextFileCallback(input any, toolUseData *uctypes.UIMessageDataToolUse)
283282
"modified_time": modTime.UTC().Format(time.RFC3339),
284283
"mode": fileInfo.Mode().String(),
285284
}
286-
if stopReason != "" {
285+
if stopReason == "read_limit" || stopReason == StopReasonMaxBytes {
287286
result["truncated"] = stopReason
288287
}
289288

@@ -319,20 +318,20 @@ func GetReadTextFileToolDefinition() uctypes.ToolDefinition {
319318
"count": map[string]any{
320319
"type": "integer",
321320
"minimum": 1,
322-
"default": 100,
321+
"default": ReadFileDefaultLineCount,
323322
"description": "Number of lines to return",
324323
},
325324
"max_bytes": map[string]any{
326325
"type": "integer",
327326
"minimum": 1,
328-
"default": 51200,
327+
"default": ReadFileDefaultMaxBytes,
329328
"description": "Maximum bytes to return. If the result exceeds this, it will be truncated at line boundaries",
330329
},
331330
},
332331
"required": []string{"filename"},
333332
"additionalProperties": false,
334333
},
335-
ToolInputDesc: func(input any) string {
334+
ToolCallDesc: func(input any, output any, toolUseData *uctypes.UIMessageDataToolUse) string {
336335
parsed, err := parseReadTextFileInput(input)
337336
if err != nil {
338337
return fmt.Sprintf("error parsing input: %v", err)
@@ -342,10 +341,24 @@ func GetReadTextFileToolDefinition() uctypes.ToolDefinition {
342341
offset := *parsed.Offset
343342
count := *parsed.Count
344343

344+
readFullFile := false
345+
if output != nil {
346+
if outputMap, ok := output.(map[string]any); ok {
347+
_, wasTruncated := outputMap["truncated"]
348+
readFullFile = !wasTruncated
349+
}
350+
}
351+
345352
if origin == "start" && offset == 0 {
353+
if readFullFile {
354+
return fmt.Sprintf("reading %q (entire file)", parsed.Filename)
355+
}
346356
return fmt.Sprintf("reading %q (first %d lines)", parsed.Filename, count)
347357
}
348358
if origin == "end" && offset == 0 {
359+
if readFullFile {
360+
return fmt.Sprintf("reading %q (entire file)", parsed.Filename)
361+
}
349362
return fmt.Sprintf("reading %q (last %d lines)", parsed.Filename, count)
350363
}
351364
if origin == "end" {

pkg/aiusechat/tools_screenshot.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ func GetCaptureScreenshotToolDefinition(tabId string) uctypes.ToolDefinition {
6767
"required": []string{"widget_id"},
6868
"additionalProperties": false,
6969
},
70-
ToolInputDesc: func(input any) string {
70+
ToolCallDesc: func(input any, output any, toolUseData *uctypes.UIMessageDataToolUse) string {
7171
inputMap, ok := input.(map[string]any)
7272
if !ok {
7373
return "error parsing input: invalid format"

pkg/aiusechat/tools_term.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ func GetTermGetScrollbackToolDefinition(tabId string) uctypes.ToolDefinition {
178178
"required": []string{"widget_id"},
179179
"additionalProperties": false,
180180
},
181-
ToolInputDesc: func(input any) string {
181+
ToolCallDesc: func(input any, output any, toolUseData *uctypes.UIMessageDataToolUse) string {
182182
parsed, err := parseTermGetScrollbackInput(input)
183183
if err != nil {
184184
return fmt.Sprintf("error parsing input: %v", err)
@@ -214,7 +214,6 @@ func GetTermGetScrollbackToolDefinition(tabId string) uctypes.ToolDefinition {
214214
}
215215
}
216216

217-
218217
type TermCommandOutputToolInput struct {
219218
WidgetId string `json:"widget_id"`
220219
}
@@ -259,7 +258,7 @@ func GetTermCommandOutputToolDefinition(tabId string) uctypes.ToolDefinition {
259258
"required": []string{"widget_id"},
260259
"additionalProperties": false,
261260
},
262-
ToolInputDesc: func(input any) string {
261+
ToolCallDesc: func(input any, output any, toolUseData *uctypes.UIMessageDataToolUse) string {
263262
parsed, err := parseTermCommandOutputInput(input)
264263
if err != nil {
265264
return fmt.Sprintf("error parsing input: %v", err)

pkg/aiusechat/tools_tsunami.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ func GetTsunamiGetDataToolDefinition(block *waveobj.Block, rtInfo *waveobj.ObjRT
124124
"properties": map[string]any{},
125125
"additionalProperties": false,
126126
},
127-
ToolInputDesc: func(input any) string {
127+
ToolCallDesc: func(input any, output any, toolUseData *uctypes.UIMessageDataToolUse) string {
128128
return fmt.Sprintf("getting data from %s (%s)", desc, blockIdPrefix)
129129
},
130130
ToolAnyCallback: makeTsunamiGetCallback(status, "/api/data"),
@@ -149,7 +149,7 @@ func GetTsunamiGetConfigToolDefinition(block *waveobj.Block, rtInfo *waveobj.Obj
149149
"properties": map[string]any{},
150150
"additionalProperties": false,
151151
},
152-
ToolInputDesc: func(input any) string {
152+
ToolCallDesc: func(input any, output any, toolUseData *uctypes.UIMessageDataToolUse) string {
153153
return fmt.Sprintf("getting config from %s (%s)", desc, blockIdPrefix)
154154
},
155155
ToolAnyCallback: makeTsunamiGetCallback(status, "/api/config"),
@@ -182,7 +182,7 @@ func GetTsunamiSetConfigToolDefinition(block *waveobj.Block, rtInfo *waveobj.Obj
182182
Name: toolName,
183183
ToolLogName: "tsunami:setconfig",
184184
InputSchema: inputSchema,
185-
ToolInputDesc: func(input any) string {
185+
ToolCallDesc: func(input any, output any, toolUseData *uctypes.UIMessageDataToolUse) string {
186186
return fmt.Sprintf("updating config for %s (%s)", desc, blockIdPrefix)
187187
},
188188
ToolAnyCallback: makeTsunamiPostCallback(status, "/api/config"),

0 commit comments

Comments
 (0)