mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-09-20 13:52:28 +03:00
The Codex executor now whitelists the wire `reasoning` object to `effort`/`summary` before dispatch instead of spreading whatever the client sent, and maps `reasoning.enabled === false` to `effort: "none"` when no more specific effort was requested. OpenRouter-style keys (`enabled`, `max_tokens`, `exclude`) were reaching the Responses API and 400-ing the whole combo target with `Unknown parameter: 'reasoning.<key>'`. The precedence chain keeps an explicit per-request effort ahead of `enabled: false`, and the strip matches the siblings already removed in the same function (`truncation`, `user`, `prompt_cache_retention`). Validated as a combined board first (this PR merged with the 11 siblings of the same batch on the release tip): eslint on every changed file with the suppressions file, typecheck:core, check:open-sse-typecheck, complexity, cognitive-complexity, changelog-integrity, i18n new-key coverage, docs-sync, migration-numbering, provider-consistency and a duplicate-identifier audit all green, plus 275 passing / 0 failing focused node:test cases across the 28 test files the batch touches. Then re-validated alone on the fresh tip before this merge: conflicts re-resolved, file sizes rebaselined for this PR's own growth, eslint and this PR's focused tests re-run. Thanks @HouMinXi! Co-authored-by: Diego Rodrigues de Sa e Souza <diegosouza.pw@gmail.com>
97 lines
4.0 KiB
TypeScript
97 lines
4.0 KiB
TypeScript
import test from "node:test";
|
|
import assert from "node:assert/strict";
|
|
|
|
import { CodexExecutor } from "../../open-sse/executors/codex.ts";
|
|
import { setThinkingBudgetConfig, ThinkingMode } from "../../open-sse/services/thinkingBudget.ts";
|
|
|
|
// The Codex Responses API accepts only `effort` and `summary` inside
|
|
// `reasoning`. Client ecosystems send OpenRouter-style keys (`enabled`,
|
|
// `max_tokens`, `exclude`, ...) that the upstream rejects with HTTP 400
|
|
// "Unknown parameter: 'reasoning.<key>'", taking down every combo target
|
|
// with the same deterministic client error. The executor must whitelist the
|
|
// object before it reaches the wire; `enabled: false` maps to effort "none"
|
|
// when no more specific effort was requested.
|
|
|
|
const CTX = { requestEndpointPath: "/responses" };
|
|
|
|
function transform(body: Record<string, unknown>, model = "gpt-6-astra") {
|
|
const executor = new CodexExecutor();
|
|
return executor.transformRequest(model, body, false, CTX) as Record<string, unknown>;
|
|
}
|
|
|
|
function reasoningOf(result: Record<string, unknown>): Record<string, unknown> | null {
|
|
const r = result.reasoning;
|
|
if (r && typeof r === "object" && !Array.isArray(r)) return r as Record<string, unknown>;
|
|
return null;
|
|
}
|
|
|
|
test("reasoning.enabled is stripped; explicit effort survives", () => {
|
|
const r = reasoningOf(transform({ reasoning: { enabled: true, effort: "high" } }));
|
|
assert.ok(r, "reasoning object should be present");
|
|
assert.equal(r.effort, "high");
|
|
assert.equal("enabled" in r, false);
|
|
});
|
|
|
|
test("reasoning.enabled:false maps to effort none when nothing more specific is set", () => {
|
|
const r = reasoningOf(transform({ reasoning: { enabled: false } }));
|
|
assert.ok(r, "reasoning object should be present");
|
|
assert.equal(r.effort, "none");
|
|
assert.equal("enabled" in r, false);
|
|
assert.equal("summary" in r, false, "no summary for disabled reasoning");
|
|
});
|
|
|
|
test("OpenRouter-style reasoning.max_tokens never reaches the wire", () => {
|
|
const r = reasoningOf(transform({ reasoning: { max_tokens: 2048 } }));
|
|
assert.ok(!r || !("max_tokens" in r), "max_tokens must be stripped");
|
|
});
|
|
|
|
test("reasoning.exclude is stripped; sibling effort survives", () => {
|
|
const r = reasoningOf(transform({ reasoning: { exclude: true, effort: "low" } }));
|
|
assert.ok(r, "reasoning object should be present");
|
|
assert.equal(r.effort, "low");
|
|
assert.equal("exclude" in r, false);
|
|
});
|
|
|
|
test("client-provided summary is preserved", () => {
|
|
const r = reasoningOf(transform({ reasoning: { summary: "detailed", effort: "medium" } }));
|
|
assert.ok(r, "reasoning object should be present");
|
|
assert.equal(r.summary, "detailed");
|
|
assert.equal(r.effort, "medium");
|
|
});
|
|
|
|
test("model suffix effort still wins over enabled:false", () => {
|
|
const r = reasoningOf(transform({ reasoning: { enabled: false } }, "gpt-6-astra-high"));
|
|
assert.ok(r, "reasoning object should be present");
|
|
assert.equal(r.effort, "high");
|
|
});
|
|
|
|
test("enabled:false wins over an explicit connection reasoning default", () => {
|
|
setThinkingBudgetConfig({ mode: ThinkingMode.PASSTHROUGH });
|
|
try {
|
|
const executor = new CodexExecutor();
|
|
const result = executor.transformRequest(
|
|
"gpt-6-astra",
|
|
{ reasoning: { enabled: false } },
|
|
false,
|
|
{
|
|
requestEndpointPath: "/responses",
|
|
providerSpecificData: { requestDefaults: { reasoningEffort: "high" } },
|
|
}
|
|
) as Record<string, unknown>;
|
|
const r = reasoningOf(result);
|
|
assert.ok(r, "reasoning object should be present");
|
|
assert.equal(r.effort, "none", "client disable must beat the connection default");
|
|
} finally {
|
|
setThinkingBudgetConfig({});
|
|
}
|
|
});
|
|
|
|
test("flat reasoning_effort path stays clean of extra keys", () => {
|
|
const result = transform({ reasoning_effort: "low", reasoning: { enabled: true } });
|
|
assert.equal("reasoning_effort" in result, false, "flat key must never reach the wire");
|
|
const r = reasoningOf(result);
|
|
assert.ok(r, "reasoning object should be present");
|
|
assert.equal(r.effort, "low");
|
|
assert.equal("enabled" in r, false);
|
|
});
|