mirror of
https://github.com/Nezumi-2711/9router.git
synced 2026-09-22 13:38:31 +00:00
fix: sanitize Read tool args to prevent retry loops from non-Anthropic models (#1144)
* fix: sanitize Read tool args to prevent retry loops from non-Anthropic models * fix: sanitize invalid Read pages from tool args Non-Anthropic models sometimes emit optional Read args like pages: "" for non-PDF files, which Claude Code rejects before the tool runs. Drop invalid pages values, keep valid PDF page ranges, and coerce numeric string bounds before clamping limit/offset. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
dcc46f2eee
commit
96a9a2b36d
@@ -0,0 +1,61 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { openaiToClaudeResponse } from "../../open-sse/translator/response/openai-to-claude.js";
|
||||
|
||||
function createState() {
|
||||
return { toolCalls: new Map(), nextBlockIndex: 0 };
|
||||
}
|
||||
|
||||
function getInputJsonDelta(events) {
|
||||
return events.find((event) => event.type === "content_block_delta" && event.delta?.type === "input_json_delta")?.delta.partial_json;
|
||||
}
|
||||
|
||||
describe("openaiToClaudeResponse tool argument sanitization", () => {
|
||||
it("drops invalid Read pages and clamps numeric bounds", () => {
|
||||
const state = createState();
|
||||
|
||||
openaiToClaudeResponse({
|
||||
id: "chatcmpl-test-read",
|
||||
model: "test-model",
|
||||
choices: [{ delta: { tool_calls: [{ index: 0, id: "toolu_read", function: { name: "Read" } }] } }],
|
||||
}, state);
|
||||
|
||||
const events = openaiToClaudeResponse({
|
||||
id: "chatcmpl-test-read",
|
||||
model: "test-model",
|
||||
choices: [{
|
||||
delta: { tool_calls: [{ index: 0, function: { arguments: JSON.stringify({ file_path: "F:/repo/file.js", offset: -5, limit: 999999999, pages: "" }) } }] },
|
||||
finish_reason: "tool_calls",
|
||||
}],
|
||||
}, state);
|
||||
|
||||
expect(JSON.parse(getInputJsonDelta(events))).toEqual({
|
||||
file_path: "F:/repo/file.js",
|
||||
offset: 0,
|
||||
limit: 2000,
|
||||
});
|
||||
});
|
||||
|
||||
it("keeps valid PDF pages", () => {
|
||||
const state = createState();
|
||||
|
||||
openaiToClaudeResponse({
|
||||
id: "chatcmpl-test-pdf",
|
||||
model: "test-model",
|
||||
choices: [{ delta: { tool_calls: [{ index: 0, id: "toolu_pdf", function: { name: "proxy_Read" } }] } }],
|
||||
}, state);
|
||||
|
||||
const events = openaiToClaudeResponse({
|
||||
id: "chatcmpl-test-pdf",
|
||||
model: "test-model",
|
||||
choices: [{
|
||||
delta: { tool_calls: [{ index: 0, function: { arguments: JSON.stringify({ file_path: "F:/repo/doc.pdf", pages: "1-3" }) } }] },
|
||||
finish_reason: "tool_calls",
|
||||
}],
|
||||
}, state);
|
||||
|
||||
expect(JSON.parse(getInputJsonDelta(events))).toEqual({
|
||||
file_path: "F:/repo/doc.pdf",
|
||||
pages: "1-3",
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user