fix: strip empty Read pages argument in OpenAI-to-Claude translator (#1354)

Some OpenAI-compatible providers emit optional string tool parameters
as empty strings (e.g. pages: "") instead of omitting them. Claude
Code rejects pages: "" as invalid, breaking the Read tool for
non-PDF files routed through 9router.

Add sanitizeToolArguments() that parses tool-call arguments and
removes known optional empty-string fields before emitting
input_json_delta back to Claude format. Currently handles the
Read tool pages field specifically.

Includes regression test.

Fixes #1278

Co-authored-by: JoJo <noreply@github.com>
This commit is contained in:
Hamsa_M
2026-05-23 09:27:13 +07:00
committed by GitHub
co-authored by JoJo
parent 2604b46847
commit 315dcaa37a
2 changed files with 58 additions and 2 deletions
+39 -1
View File
@@ -8,6 +8,7 @@
import { describe, it, expect } from "vitest";
import { openaiToClaudeRequest } from "../../open-sse/translator/request/openai-to-claude.js";
import { openaiToClaudeResponse } from "../../open-sse/translator/response/openai-to-claude.js";
describe("openaiToClaudeRequest", () => {
describe("response_format handling", () => {
@@ -121,4 +122,41 @@ describe("openaiToClaudeRequest", () => {
expect(systemText).toContain("You must respond with valid JSON");
});
});
});
});
describe("openaiToClaudeResponse", () => {
it("omits empty Read pages tool argument before emitting Claude input deltas", () => {
const state = { toolCalls: new Map() };
const chunk = {
id: "chatcmpl-test",
model: "gpt-test",
choices: [{
delta: {
tool_calls: [{
index: 0,
id: "call_read",
function: {
name: "Read",
arguments: JSON.stringify({
file_path: "/tmp/example.txt",
offset: 0,
limit: 120,
pages: ""
})
}
}]
}
}]
};
const result = openaiToClaudeResponse(chunk, state);
const inputDelta = result.find(event => event.delta?.type === "input_json_delta");
expect(inputDelta).toBeDefined();
expect(JSON.parse(inputDelta.delta.partial_json)).toEqual({
file_path: "/tmp/example.txt",
offset: 0,
limit: 120
});
});
});