mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-02 21:32:10 +03:00
Merge pull request #1313 from Gi99lin/fix/chatcore-max-output-tokens-responses-passthrough
fix: preserve max_output_tokens for Responses API targets in chatCore sanitization
This commit is contained in:
@@ -910,8 +910,21 @@ export async function handleChatCore({
|
||||
}
|
||||
|
||||
// ── Common input sanitization (runs for ALL paths including passthrough) ──
|
||||
// #994: Normalize max_output_tokens to max_tokens for universal compatibility
|
||||
if (body.max_output_tokens !== undefined) {
|
||||
// #994: Normalize between max_output_tokens and max_tokens for universal compatibility.
|
||||
// For Responses API targets, max_output_tokens is the canonical field. For others,
|
||||
// max_tokens is preferred. We handle normalization here to support passthrough
|
||||
// paths where the translator is skipped.
|
||||
if (targetFormat === FORMATS.OPENAI_RESPONSES) {
|
||||
if (body.max_output_tokens === undefined) {
|
||||
if (body.max_completion_tokens !== undefined) {
|
||||
body.max_output_tokens = body.max_completion_tokens;
|
||||
delete body.max_completion_tokens;
|
||||
} else if (body.max_tokens !== undefined) {
|
||||
body.max_output_tokens = body.max_tokens;
|
||||
delete body.max_tokens;
|
||||
}
|
||||
}
|
||||
} else if (body.max_output_tokens !== undefined) {
|
||||
if (body.max_tokens === undefined) {
|
||||
body.max_tokens = body.max_output_tokens;
|
||||
}
|
||||
|
||||
@@ -179,6 +179,90 @@ test("chatCore sanitization normalizes max_output_tokens into max_tokens", async
|
||||
assert.equal("max_tokens" in untouched.call.body, false);
|
||||
});
|
||||
|
||||
test("chatCore sanitization preserves max_output_tokens for openai-responses targets", async () => {
|
||||
// When the target provider uses openai-responses format (e.g. Codex),
|
||||
// max_output_tokens is the canonical field and must NOT be normalized to
|
||||
// max_tokens. Normalizing it breaks Responses→Responses passthrough because
|
||||
// the translator (which converts max_tokens back) is skipped for same-format.
|
||||
const { call } = await invokeChatCore({
|
||||
endpoint: "/v1/responses",
|
||||
body: {
|
||||
model: "gpt-5.4",
|
||||
max_output_tokens: 4096,
|
||||
input: [{ role: "user", content: "hello" }],
|
||||
},
|
||||
responseFactory: () =>
|
||||
new Response(
|
||||
JSON.stringify({
|
||||
id: "resp_test",
|
||||
object: "response",
|
||||
status: "completed",
|
||||
model: "gpt-5.4",
|
||||
output: [
|
||||
{ type: "message", role: "assistant", content: [{ type: "output_text", text: "ok" }] },
|
||||
],
|
||||
usage: { input_tokens: 1, output_tokens: 1, total_tokens: 2 },
|
||||
}),
|
||||
{ status: 200, headers: { "Content-Type": "application/json" } }
|
||||
),
|
||||
});
|
||||
|
||||
// max_output_tokens should survive sanitization for Responses targets
|
||||
assert.equal("max_tokens" in call.body, false, "max_tokens must not be injected for Responses targets");
|
||||
|
||||
// Reverse normalization: max_tokens → max_output_tokens for Responses targets
|
||||
const fromMaxTokens = await invokeChatCore({
|
||||
endpoint: "/v1/responses",
|
||||
body: {
|
||||
model: "gpt-5.4",
|
||||
max_tokens: 2048,
|
||||
input: [{ role: "user", content: "hello" }],
|
||||
},
|
||||
responseFactory: () =>
|
||||
new Response(
|
||||
JSON.stringify({
|
||||
id: "resp_test2",
|
||||
object: "response",
|
||||
status: "completed",
|
||||
model: "gpt-5.4",
|
||||
output: [
|
||||
{ type: "message", role: "assistant", content: [{ type: "output_text", text: "ok" }] },
|
||||
],
|
||||
usage: { input_tokens: 1, output_tokens: 1, total_tokens: 2 },
|
||||
}),
|
||||
{ status: 200, headers: { "Content-Type": "application/json" } }
|
||||
),
|
||||
});
|
||||
|
||||
assert.equal("max_tokens" in fromMaxTokens.call.body, false, "max_tokens should be converted to max_output_tokens");
|
||||
|
||||
// Reverse normalization: max_completion_tokens → max_output_tokens for Responses targets
|
||||
const fromMaxCompletion = await invokeChatCore({
|
||||
endpoint: "/v1/responses",
|
||||
body: {
|
||||
model: "gpt-5.4",
|
||||
max_completion_tokens: 8192,
|
||||
input: [{ role: "user", content: "hello" }],
|
||||
},
|
||||
responseFactory: () =>
|
||||
new Response(
|
||||
JSON.stringify({
|
||||
id: "resp_test3",
|
||||
object: "response",
|
||||
status: "completed",
|
||||
model: "gpt-5.4",
|
||||
output: [
|
||||
{ type: "message", role: "assistant", content: [{ type: "output_text", text: "ok" }] },
|
||||
],
|
||||
usage: { input_tokens: 1, output_tokens: 1, total_tokens: 2 },
|
||||
}),
|
||||
{ status: 200, headers: { "Content-Type": "application/json" } }
|
||||
),
|
||||
});
|
||||
|
||||
assert.equal("max_completion_tokens" in fromMaxCompletion.call.body, false, "max_completion_tokens should be converted to max_output_tokens");
|
||||
});
|
||||
|
||||
test("chatCore sanitization strips empty message names and filters empty tool names", async () => {
|
||||
// Note: `input` field is tested separately because its presence triggers
|
||||
// Responses format detection (PR #1002), which changes message handling.
|
||||
|
||||
Reference in New Issue
Block a user