mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-20 06:02:14 +03:00
Integrated into release/v3.8.3 — fix(codex): allowlist-based sanitization for gpt-5.5 Responses API
This commit is contained in:
committed by
GitHub
parent
ebe0b6607c
commit
f52df7b824
@@ -1286,21 +1286,35 @@ export class CodexExecutor extends BaseExecutor {
|
||||
return body;
|
||||
}
|
||||
|
||||
// Remove unsupported parameters for Codex API
|
||||
delete body.temperature;
|
||||
delete body.top_p;
|
||||
delete body.frequency_penalty;
|
||||
delete body.presence_penalty;
|
||||
delete body.logprobs;
|
||||
delete body.top_logprobs;
|
||||
delete body.n;
|
||||
delete body.seed;
|
||||
// max_tokens and max_output_tokens already deleted above (before passthrough return)
|
||||
delete body.user; // Cursor sends this but Codex doesn't support it
|
||||
// Issue #2608: Use an allowlist of known Responses API fields instead of a
|
||||
// denylist of Chat Completions fields. The denylist approach missed fields
|
||||
// like `stop`, `response_format`, `logit_bias`, `function_call`, `functions`,
|
||||
// `max_completion_tokens`, and `parallel_tool_calls` — causing gpt-5.5 to
|
||||
// reject with "routing_unsupported" (400). An allowlist is future-proof:
|
||||
// any unknown field from Chat Completions (or other formats) is stripped.
|
||||
const RESPONSES_API_ALLOWLIST = new Set([
|
||||
"model",
|
||||
"input",
|
||||
"instructions",
|
||||
"tools",
|
||||
"tool_choice",
|
||||
"stream",
|
||||
"store",
|
||||
"reasoning",
|
||||
"service_tier",
|
||||
"include",
|
||||
"previous_response_id",
|
||||
"prompt_cache_key",
|
||||
"client_metadata",
|
||||
// Internal markers used by OmniRoute pipeline
|
||||
"_omnirouteResponsesStore",
|
||||
]);
|
||||
|
||||
delete body.metadata; // Cursor sends this but Codex doesn't support it
|
||||
delete body.stream_options; // Cursor sends this but Codex doesn't support it
|
||||
delete body.safety_identifier; // Droid CLI sends this but Codex doesn't support it
|
||||
for (const key of Object.keys(body)) {
|
||||
if (!RESPONSES_API_ALLOWLIST.has(key)) {
|
||||
delete body[key];
|
||||
}
|
||||
}
|
||||
|
||||
return body;
|
||||
}
|
||||
|
||||
@@ -216,6 +216,76 @@ test("CodexExecutor.transformRequest injects default instructions, clamps reason
|
||||
assert.equal(result.stream_options, undefined);
|
||||
});
|
||||
|
||||
// Issue #2608: gpt-5.5 models reject residual Chat Completions fields via Codex OAuth.
|
||||
// The non-passthrough path must strip ALL non-Responses-API fields using an allowlist.
|
||||
test("CodexExecutor.transformRequest non-passthrough allowlist strips all residual Chat Completions fields (#2608)", () => {
|
||||
const executor = new CodexExecutor();
|
||||
const body = {
|
||||
model: "gpt-5.5",
|
||||
messages: [{ role: "user", content: "hello" }],
|
||||
instructions: "",
|
||||
// All of these are Chat Completions fields that must be stripped:
|
||||
temperature: 0.7,
|
||||
top_p: 0.9,
|
||||
frequency_penalty: 0.5,
|
||||
presence_penalty: 0.3,
|
||||
logprobs: true,
|
||||
top_logprobs: 3,
|
||||
n: 2,
|
||||
seed: 42,
|
||||
stop: ["\n"],
|
||||
response_format: { type: "json_object" },
|
||||
logit_bias: { "123": 1 },
|
||||
function_call: "auto",
|
||||
functions: [{ name: "test", parameters: {} }],
|
||||
max_completion_tokens: 1000,
|
||||
parallel_tool_calls: true,
|
||||
user: "cursor-user",
|
||||
metadata: { key: "value" },
|
||||
stream_options: { include_usage: true },
|
||||
safety_identifier: "safe-1",
|
||||
suffix: "end",
|
||||
// Custom/arbitrary fields that could be injected by middleware
|
||||
custom_field: "should be stripped",
|
||||
_internal_marker: true,
|
||||
};
|
||||
|
||||
const result = executor.transformRequest("gpt-5.5", body, false, {
|
||||
requestEndpointPath: "/responses",
|
||||
});
|
||||
|
||||
// Allowed Responses API fields should survive
|
||||
assert.equal(result.model, "gpt-5.5");
|
||||
assert.ok(Array.isArray(result.input));
|
||||
assert.equal(typeof result.instructions, "string");
|
||||
assert.equal(result.store, false);
|
||||
assert.equal(result.stream, true);
|
||||
|
||||
// All Chat Completions fields must be stripped
|
||||
assert.equal(result.temperature, undefined, "temperature should be stripped");
|
||||
assert.equal(result.top_p, undefined, "top_p should be stripped");
|
||||
assert.equal(result.frequency_penalty, undefined, "frequency_penalty should be stripped");
|
||||
assert.equal(result.presence_penalty, undefined, "presence_penalty should be stripped");
|
||||
assert.equal(result.logprobs, undefined, "logprobs should be stripped");
|
||||
assert.equal(result.top_logprobs, undefined, "top_logprobs should be stripped");
|
||||
assert.equal(result.n, undefined, "n should be stripped");
|
||||
assert.equal(result.seed, undefined, "seed should be stripped");
|
||||
assert.equal(result.stop, undefined, "stop should be stripped");
|
||||
assert.equal(result.response_format, undefined, "response_format should be stripped");
|
||||
assert.equal(result.logit_bias, undefined, "logit_bias should be stripped");
|
||||
assert.equal(result.function_call, undefined, "function_call should be stripped");
|
||||
assert.equal(result.functions, undefined, "functions should be stripped");
|
||||
assert.equal(result.max_completion_tokens, undefined, "max_completion_tokens should be stripped");
|
||||
assert.equal(result.parallel_tool_calls, undefined, "parallel_tool_calls should be stripped");
|
||||
assert.equal(result.user, undefined, "user should be stripped");
|
||||
assert.equal(result.metadata, undefined, "metadata should be stripped");
|
||||
assert.equal(result.stream_options, undefined, "stream_options should be stripped");
|
||||
assert.equal(result.safety_identifier, undefined, "safety_identifier should be stripped");
|
||||
assert.equal(result.suffix, undefined, "suffix should be stripped");
|
||||
assert.equal(result.custom_field, undefined, "arbitrary custom fields should be stripped");
|
||||
assert.equal(result._internal_marker, undefined, "internal markers should be stripped");
|
||||
});
|
||||
|
||||
test("CodexExecutor.transformRequest normalizes max reasoning_effort to xhigh", () => {
|
||||
const executor = new CodexExecutor();
|
||||
const result = executor.transformRequest(
|
||||
|
||||
Reference in New Issue
Block a user