fix: resolve 400 error with native tools on OpenRouter (#9238)

This commit is contained in:
Daniel 2025-11-13 15:10:38 -05:00 committed by GitHub
parent d270ea1983
commit 237c324fcd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 18 additions and 14 deletions

View file

@ -81,7 +81,7 @@ export class NativeToolCallParser {
switch (toolCall.name) {
case "read_file":
if (args.files && Array.isArray(args.files)) {
nativeArgs = args.files as NativeArgsFor<TName>
nativeArgs = { files: args.files } as NativeArgsFor<TName>
}
break

View file

@ -179,8 +179,8 @@ export async function presentAssistantMessage(cline: Task) {
return getSimpleReadFileToolDescription(block.name, block.params)
} else {
// Prefer native typed args when available; fall back to legacy params
// Check if nativeArgs exists and is an array (native protocol)
if (Array.isArray(block.nativeArgs)) {
// Check if nativeArgs exists (native protocol)
if (block.nativeArgs) {
return readFileTool.getReadFileToolDescription(block.name, block.nativeArgs)
}
return readFileTool.getReadFileToolDescription(block.name, block.params)

View file

@ -2451,11 +2451,14 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
// Get the tool call ID that was stored during parsing
const toolCallId = (toolUse as any).id
if (toolCallId) {
// nativeArgs is already in the correct API format for all tools
const input = toolUse.nativeArgs || toolUse.params
assistantContent.push({
type: "tool_use" as const,
id: toolCallId,
name: toolUse.name,
input: toolUse.nativeArgs || toolUse.params,
input,
})
}
}

View file

@ -43,7 +43,7 @@ interface FileResult {
export class ReadFileTool extends BaseTool<"read_file"> {
readonly name = "read_file" as const
parseLegacy(params: Partial<Record<string, string>>): FileEntry[] {
parseLegacy(params: Partial<Record<string, string>>): { files: FileEntry[] } {
const argsXmlTag = params.args
const legacyPath = params.path
const legacyStartLineStr = params.start_line
@ -79,7 +79,7 @@ export class ReadFileTool extends BaseTool<"read_file"> {
fileEntries.push(fileEntry)
}
return fileEntries
return { files: fileEntries }
}
// Legacy single file path
@ -99,13 +99,14 @@ export class ReadFileTool extends BaseTool<"read_file"> {
fileEntries.push(fileEntry)
}
return fileEntries
return { files: fileEntries }
}
async execute(fileEntries: FileEntry[], task: Task, callbacks: ToolCallbacks): Promise<void> {
async execute(params: { files: FileEntry[] }, task: Task, callbacks: ToolCallbacks): Promise<void> {
const { handleError, pushToolResult } = callbacks
const fileEntries = params.files
if (fileEntries.length === 0) {
if (!fileEntries || fileEntries.length === 0) {
task.consecutiveMistakeCount++
task.recordToolError("read_file")
const errorMsg = await task.sayAndCreateMissingParamError("read_file", "args (containing valid file paths)")
@ -578,11 +579,11 @@ export class ReadFileTool extends BaseTool<"read_file"> {
}
getReadFileToolDescription(blockName: string, blockParams: any): string
getReadFileToolDescription(blockName: string, nativeArgs: FileEntry[]): string
getReadFileToolDescription(blockName: string, nativeArgs: { files: FileEntry[] }): string
getReadFileToolDescription(blockName: string, second: any): string {
// If native typed args (FileEntry[]) were provided
if (Array.isArray(second)) {
const paths = (second as FileEntry[]).map((f) => f?.path).filter(Boolean) as string[]
// If native typed args ({ files: FileEntry[] }) were provided
if (second && typeof second === "object" && "files" in second && Array.isArray(second.files)) {
const paths = (second.files as FileEntry[]).map((f) => f?.path).filter(Boolean) as string[]
if (paths.length === 0) {
return `[${blockName} with no valid paths]`
} else if (paths.length === 1) {

View file

@ -82,7 +82,7 @@ export type ToolProtocol = "xml" | "native"
* Tools not listed here will fall back to `any` for backward compatibility.
*/
export type NativeToolArgs = {
read_file: FileEntry[]
read_file: { files: FileEntry[] }
attempt_completion: { result: string }
execute_command: { command: string; cwd?: string }
insert_content: { path: string; line: number; content: string }