mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-09-05 08:10:14 +00:00
fix: allow partial args through parseToolCall finalization for write_to_file and other tools
When the model response gets truncated (e.g., due to output token limits), required parameters like `content` for write_to_file arrive as `undefined`. The finalization path in parseToolCall() required ALL params via AND conditions, causing nativeArgs to stay undefined and the tool call to be silently dropped or throw a generic error -- bypassing the tool's own user-friendly retry logic. The streaming/partial path already uses OR conditions to allow partial args through. This change aligns the finalization path with the same pattern, so that partially-constructed args flow through to the tool's execute() method, which can produce specific "missing parameter X, retrying..." messages that get sent back to the model. Fixes #12079
This commit is contained in:
parent
eafed9705c
commit
cb70c75b5b
2 changed files with 100 additions and 15 deletions
|
|
@ -794,7 +794,7 @@ export class NativeToolCallParser {
|
|||
break
|
||||
|
||||
case "apply_diff":
|
||||
if (args.path !== undefined && args.diff !== undefined) {
|
||||
if (args.path !== undefined || args.diff !== undefined) {
|
||||
nativeArgs = {
|
||||
path: args.path,
|
||||
diff: args.diff,
|
||||
|
|
@ -805,8 +805,8 @@ export class NativeToolCallParser {
|
|||
case "edit":
|
||||
case "search_and_replace":
|
||||
if (
|
||||
args.file_path !== undefined &&
|
||||
args.old_string !== undefined &&
|
||||
args.file_path !== undefined ||
|
||||
args.old_string !== undefined ||
|
||||
args.new_string !== undefined
|
||||
) {
|
||||
nativeArgs = {
|
||||
|
|
@ -819,7 +819,7 @@ export class NativeToolCallParser {
|
|||
break
|
||||
|
||||
case "ask_followup_question":
|
||||
if (args.question !== undefined && args.follow_up !== undefined) {
|
||||
if (args.question !== undefined || args.follow_up !== undefined) {
|
||||
nativeArgs = {
|
||||
question: args.question,
|
||||
follow_up: args.follow_up,
|
||||
|
|
@ -837,7 +837,7 @@ export class NativeToolCallParser {
|
|||
break
|
||||
|
||||
case "generate_image":
|
||||
if (args.prompt !== undefined && args.path !== undefined) {
|
||||
if (args.prompt !== undefined || args.path !== undefined) {
|
||||
nativeArgs = {
|
||||
prompt: args.prompt,
|
||||
path: args.path,
|
||||
|
|
@ -865,7 +865,7 @@ export class NativeToolCallParser {
|
|||
break
|
||||
|
||||
case "search_files":
|
||||
if (args.path !== undefined && args.regex !== undefined) {
|
||||
if (args.path !== undefined || args.regex !== undefined) {
|
||||
nativeArgs = {
|
||||
path: args.path,
|
||||
regex: args.regex,
|
||||
|
|
@ -875,7 +875,7 @@ export class NativeToolCallParser {
|
|||
break
|
||||
|
||||
case "switch_mode":
|
||||
if (args.mode_slug !== undefined && args.reason !== undefined) {
|
||||
if (args.mode_slug !== undefined || args.reason !== undefined) {
|
||||
nativeArgs = {
|
||||
mode_slug: args.mode_slug,
|
||||
reason: args.reason,
|
||||
|
|
@ -903,7 +903,7 @@ export class NativeToolCallParser {
|
|||
break
|
||||
|
||||
case "write_to_file":
|
||||
if (args.path !== undefined && args.content !== undefined) {
|
||||
if (args.path !== undefined || args.content !== undefined) {
|
||||
nativeArgs = {
|
||||
path: args.path,
|
||||
content: args.content,
|
||||
|
|
@ -912,7 +912,7 @@ export class NativeToolCallParser {
|
|||
break
|
||||
|
||||
case "use_mcp_tool":
|
||||
if (args.server_name !== undefined && args.tool_name !== undefined) {
|
||||
if (args.server_name !== undefined || args.tool_name !== undefined) {
|
||||
nativeArgs = {
|
||||
server_name: args.server_name,
|
||||
tool_name: args.tool_name,
|
||||
|
|
@ -922,7 +922,7 @@ export class NativeToolCallParser {
|
|||
break
|
||||
|
||||
case "access_mcp_resource":
|
||||
if (args.server_name !== undefined && args.uri !== undefined) {
|
||||
if (args.server_name !== undefined || args.uri !== undefined) {
|
||||
nativeArgs = {
|
||||
server_name: args.server_name,
|
||||
uri: args.uri,
|
||||
|
|
@ -940,8 +940,8 @@ export class NativeToolCallParser {
|
|||
|
||||
case "search_replace":
|
||||
if (
|
||||
args.file_path !== undefined &&
|
||||
args.old_string !== undefined &&
|
||||
args.file_path !== undefined ||
|
||||
args.old_string !== undefined ||
|
||||
args.new_string !== undefined
|
||||
) {
|
||||
nativeArgs = {
|
||||
|
|
@ -954,8 +954,8 @@ export class NativeToolCallParser {
|
|||
|
||||
case "edit_file":
|
||||
if (
|
||||
args.file_path !== undefined &&
|
||||
args.old_string !== undefined &&
|
||||
args.file_path !== undefined ||
|
||||
args.old_string !== undefined ||
|
||||
args.new_string !== undefined
|
||||
) {
|
||||
nativeArgs = {
|
||||
|
|
@ -977,7 +977,7 @@ export class NativeToolCallParser {
|
|||
break
|
||||
|
||||
case "new_task":
|
||||
if (args.mode !== undefined && args.message !== undefined) {
|
||||
if (args.mode !== undefined || args.message !== undefined) {
|
||||
nativeArgs = {
|
||||
mode: args.mode,
|
||||
message: args.message,
|
||||
|
|
|
|||
|
|
@ -313,6 +313,91 @@ describe("NativeToolCallParser", () => {
|
|||
})
|
||||
})
|
||||
|
||||
describe("write_to_file tool", () => {
|
||||
it("should parse write_to_file with both path and content", () => {
|
||||
const toolCall = {
|
||||
id: "toolu_wtf_1",
|
||||
name: "write_to_file" as const,
|
||||
arguments: JSON.stringify({
|
||||
path: "src/test.ts",
|
||||
content: "console.log('hello')",
|
||||
}),
|
||||
}
|
||||
|
||||
const result = NativeToolCallParser.parseToolCall(toolCall)
|
||||
|
||||
expect(result).not.toBeNull()
|
||||
expect(result?.type).toBe("tool_use")
|
||||
if (result?.type === "tool_use") {
|
||||
const nativeArgs = result.nativeArgs as { path: string; content: string }
|
||||
expect(nativeArgs.path).toBe("src/test.ts")
|
||||
expect(nativeArgs.content).toBe("console.log('hello')")
|
||||
}
|
||||
})
|
||||
|
||||
it("should parse write_to_file with missing content (truncated response)", () => {
|
||||
const toolCall = {
|
||||
id: "toolu_wtf_2",
|
||||
name: "write_to_file" as const,
|
||||
arguments: JSON.stringify({
|
||||
path: "src/test.ts",
|
||||
}),
|
||||
}
|
||||
|
||||
const result = NativeToolCallParser.parseToolCall(toolCall)
|
||||
|
||||
expect(result).not.toBeNull()
|
||||
expect(result?.type).toBe("tool_use")
|
||||
if (result?.type === "tool_use") {
|
||||
const nativeArgs = result.nativeArgs as { path: string; content?: string }
|
||||
expect(nativeArgs.path).toBe("src/test.ts")
|
||||
expect(nativeArgs.content).toBeUndefined()
|
||||
}
|
||||
})
|
||||
|
||||
it("should parse write_to_file with missing path", () => {
|
||||
const toolCall = {
|
||||
id: "toolu_wtf_3",
|
||||
name: "write_to_file" as const,
|
||||
arguments: JSON.stringify({
|
||||
content: "console.log('hello')",
|
||||
}),
|
||||
}
|
||||
|
||||
const result = NativeToolCallParser.parseToolCall(toolCall)
|
||||
|
||||
expect(result).not.toBeNull()
|
||||
expect(result?.type).toBe("tool_use")
|
||||
if (result?.type === "tool_use") {
|
||||
const nativeArgs = result.nativeArgs as { path?: string; content: string }
|
||||
expect(nativeArgs.path).toBeUndefined()
|
||||
expect(nativeArgs.content).toBe("console.log('hello')")
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe("apply_diff tool with missing params", () => {
|
||||
it("should parse apply_diff with missing diff (truncated response)", () => {
|
||||
const toolCall = {
|
||||
id: "toolu_ad_1",
|
||||
name: "apply_diff" as const,
|
||||
arguments: JSON.stringify({
|
||||
path: "src/test.ts",
|
||||
}),
|
||||
}
|
||||
|
||||
const result = NativeToolCallParser.parseToolCall(toolCall)
|
||||
|
||||
expect(result).not.toBeNull()
|
||||
expect(result?.type).toBe("tool_use")
|
||||
if (result?.type === "tool_use") {
|
||||
const nativeArgs = result.nativeArgs as { path: string; diff?: string }
|
||||
expect(nativeArgs.path).toBe("src/test.ts")
|
||||
expect(nativeArgs.diff).toBeUndefined()
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe("finalizeStreamingToolCall", () => {
|
||||
describe("read_file tool", () => {
|
||||
it("should parse read_file args on finalize", () => {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue