fix(routing): preserve reasoning overrides across transports and fallbacks (#13556)

Merged. The failure mode was concrete — a matched reasoning rule dropped on native Responses/Anthropic paths, model-suffix/account defaults, or fallback preparation, and `_omnirouteReasoningRule` leaking upstream as `Unsupported parameter` — and the fix is carried in the request-local credential context through dispatch, refreshed credentials and fallbacks, with forced effort winning over defaults and client-forged markers dropped at ingress. The 11-case integration suite exercises the real routing/translation modules.

Validated as a combined board first (this PR merged with the 4 siblings of the JxnLexn wave on the release tip): eslint with the frozen suppressions, typecheck:core, check:open-sse-typecheck, complexity, cognitive-complexity, changelog-integrity, docs-sync, migration-numbering, provider-consistency and a duplicate-identifier audit all green, plus 77 passing / 0 failing focused node:test cases across the test files the wave touches. The wave's i18n fill (new keys carried to all 66 locales), free-tier doc counts and file-size rebaseline land in one follow-up PR right after the wave, as with #13904.

Thank you — and for keeping this a runtime-only change with the editor and service-tier work in their own PRs.
This commit is contained in:
Jan Leon
2026-09-16 22:01:51 +02:00
committed by GitHub
parent a928ea8762
commit f1e7148c19
8 changed files with 509 additions and 18 deletions

View File

@@ -0,0 +1 @@
- **fix(routing):** Preserve forced reasoning effort across native requests, account defaults and combo fallbacks while keeping internal routing directives out of upstream payloads. ([#13556](https://github.com/diegosouzapw/OmniRoute/pull/13556)) — thanks @JxnLexn

View File

@@ -35,6 +35,7 @@ import {
import { getAccessToken } from "../services/tokenRefresh.ts";
import { sanitizeResponsesInputItems } from "../services/responsesInputSanitizer.ts";
import { applyReasoningInputPolicy } from "../services/reasoningInputPolicy.ts";
import { getForcedReasoningEffort } from "../utils/reasoningRuleContext.ts";
import { normalizeCodexVerbosity } from "../services/codexVerbosity.ts";
import { getThinkingBudgetConfig, ThinkingMode } from "../services/thinkingBudget.ts";
import { CORS_HEADERS } from "../utils/cors.ts";
@@ -819,6 +820,22 @@ export class CodexExecutor extends BaseExecutor {
requestInput.body
);
const nextInput = { ...requestInput, credentials };
const forcedEffort = getForcedReasoningEffort(credentials);
if (forcedEffort) {
const nextBody =
nextInput.body && typeof nextInput.body === "object"
? (nextInput.body as Record<string, unknown>)
: {};
nextInput.body = {
...nextBody,
reasoning: {
...(nextBody.reasoning && typeof nextBody.reasoning === "object"
? nextBody.reasoning
: {}),
effort: forcedEffort,
},
};
}
if (isCodexAppServerRequired(nextInput.credentials)) {
if (!this.appServer) {
@@ -1380,8 +1397,13 @@ export class CodexExecutor extends BaseExecutor {
// Issue #2331: model suffix aliases (for example gpt-5.5-xhigh) represent an
// explicit model selection, so they must override client-injected defaults such
// as OpenCode's automatic reasoning.effort=medium for GPT-5-family requests.
// A server-selected force rule is stronger than either source.
const rawEffort =
modelEffort || explicitReasoning || requestReasoningEffort || fallbackReasoningEffort;
getForcedReasoningEffort(credentials) ||
modelEffort ||
explicitReasoning ||
requestReasoningEffort ||
fallbackReasoningEffort;
if (rawEffort) {
const clampedEffort = clampEffort(cleanModel, rawEffort);

View File

@@ -125,6 +125,8 @@ import { stripStore, usesClaudeBridge } from "./chatCore/agentRouterProtocol.ts"
import { normalizeClaudeToolsForDispatch } from "./chatCore/claudeToolDefaults.ts";
import { injectSystemPrompt, injectCustomSystemPrompt } from "../services/systemPrompt.ts";
import { translateRequest, needsTranslation } from "../translator/index.ts";
import { applyReasoningRuleDirective } from "@/lib/reasoningRouting/policy";
import { withReasoningRuleContext } from "../utils/reasoningRuleContext.ts";
import { FORMATS } from "../translator/formats.ts";
import { collectCustomToolNamesForSourceFormat } from "../translator/request/openai-responses/additionalTools.ts";
import { sanitizeKiroTools } from "../utils/kiroSanitizer.ts";
@@ -499,6 +501,9 @@ export async function handleChatCore({
fallbackAttempts = undefined,
}) {
let { provider, model, extendedContext } = modelInfo;
// Keep the selected rule across format conversion, retries and refreshed credentials.
// Each combo leg gets its own execution context; nothing is written to shared accounts.
const reasoningRuleDirective = body?._omnirouteReasoningRule;
// #12150 P1b: true iff the video-bridge guardrail rendered >=1 transcript
// cue into a replaced part of this request. Gates both request- and
// response-derived Memory extraction
@@ -1214,6 +1219,22 @@ export async function handleChatCore({
log?.debug?.("FORMAT", `${sourceFormat}${targetFormat} | stream=${stream}`);
if (reasoningRuleDirective) {
// Cache identity must use the effective effort, not the overridden client value.
// Retain the directive for the translation step, where general thinking defaults run.
body = {
...(applyReasoningRuleDirective(
body,
sourceFormat === FORMATS.OPENAI_RESPONSES
? "openai-responses"
: sourceFormat === FORMATS.CLAUDE
? "claude"
: undefined
) as Record<string, unknown>),
_omnirouteReasoningRule: reasoningRuleDirective,
};
}
// Preserve original body for cache signature — the body variable is mutated
// multiple times below (sanitization, memory/skills injection) before the
// cache store path runs at Phase 9.1 (non-streaming) / Phase 9.2 (streaming).
@@ -2267,7 +2288,7 @@ export async function handleChatCore({
try {
if (nativeResponsesPassthrough) {
translatedBody = stampNativeResponsesPassthroughBody(
body,
applyReasoningRuleDirective(body, "openai-responses") as Record<string, unknown>,
nativeCodexPassthrough
? "codex"
: nativeXaiResponsesPassthrough
@@ -2288,6 +2309,10 @@ export async function handleChatCore({
// Claude Code-compatible providers expect Anthropic Messages-shaped payloads,
// but we extract only role/text/max_tokens/effort from an OpenAI-like view first.
if (sourceFormat === FORMATS.CLAUDE && isClaudeCodeSemanticPassthrough) {
normalizedForCc = applyReasoningRuleDirective(
normalizedForCc,
"claude"
) as typeof normalizedForCc;
log?.debug?.("FORMAT", "claude-code semantic passthrough enabled for compatible bridge");
} else if (sourceFormat !== FORMATS.OPENAI) {
const normalizeToolCallId = getModelNormalizeToolCallId(
@@ -2323,6 +2348,10 @@ export async function handleChatCore({
const ccRequestDefaults = getClaudeCodeCompatibleRequestDefaults(
credentials?.providerSpecificData
);
// OpenAI-shaped bridge requests skip translateRequest too.
if (sourceFormat === FORMATS.OPENAI) {
normalizedForCc = applyReasoningRuleDirective(normalizedForCc) as typeof normalizedForCc;
}
translatedBody = buildClaudeCodeCompatibleRequest({
sourceBody: body,
normalizedBody: normalizedForCc,
@@ -2352,7 +2381,7 @@ export async function handleChatCore({
// payloads at high context (150+ msgs, 100+ tools). Fix: #1359.
// Claude Code sends well-formed Messages API payloads — trust them
// regardless of combo strategy or cache_control settings.
translatedBody = { ...body };
translatedBody = applyReasoningRuleDirective({ ...body }, "claude");
translatedBody._disableToolPrefix = true;
// Sanitize historical thinking-block signatures for Anthropic-native Claude OAuth.
@@ -2996,15 +3025,18 @@ export async function handleChatCore({
// Get executor for this provider (with optional upstream proxy routing)
const executor = await resolveExecutorWithProxy(provider);
const getExecutionCredentials = () =>
resolveExecutionCredentialsFor({
credentials,
nativeCodexPassthrough: nativeResponsesPassthrough,
endpointPath,
targetFormat,
provider,
ccSessionId,
modelInfo,
});
withReasoningRuleContext(
resolveExecutionCredentialsFor({
credentials,
nativeCodexPassthrough: nativeResponsesPassthrough,
endpointPath,
targetFormat,
provider,
ccSessionId,
modelInfo,
}),
reasoningRuleDirective
);
let onPipelineStreamError: streamFailure.PipelineStreamErrorHandler | null = null;
let onClientDisconnectFinalize:

View File

@@ -0,0 +1,28 @@
// Request-local execution metadata, not a provider credential or a wire field.
// Symbol keys survive credential object spreads but cannot be supplied through JSON.
const FORCED_EFFORT = Symbol.for("omniroute.forcedReasoningEffort");
const EFFORTS = new Set(["none", "low", "medium", "high", "xhigh", "max", "ultra"]);
export function withReasoningRuleContext<T>(credentials: T, directive: unknown): T {
if (
!credentials ||
typeof credentials !== "object" ||
!directive ||
typeof directive !== "object"
)
return credentials;
const rule = directive as Record<string, unknown>;
if (
!rule.id ||
rule.effortMode !== "force" ||
typeof rule.targetEffort !== "string" ||
!EFFORTS.has(rule.targetEffort)
)
return credentials;
return { ...credentials, [FORCED_EFFORT]: rule.targetEffort };
}
export function getForcedReasoningEffort(credentials: unknown): string | undefined {
if (!credentials || typeof credentials !== "object") return undefined;
return (credentials as { [FORCED_EFFORT]?: string })[FORCED_EFFORT];
}

View File

@@ -23,6 +23,7 @@ import { logger } from "@omniroute/open-sse/utils/logger.ts";
import { resolveProxy } from "@omniroute/open-sse/utils/networkProxy.ts";
import { withCodexFingerprintCredentials } from "@omniroute/open-sse/config/codexIdentity.ts";
import { proxyConfigToUrl } from "@omniroute/open-sse/utils/proxyDispatcher.ts";
import { withReasoningRuleContext } from "@omniroute/open-sse/utils/reasoningRuleContext.ts";
import {
attachReasoningRuleDirective,
applyReasoningRuleDirective,
@@ -544,12 +545,17 @@ async function prepare(body: JsonRecord) {
let responseBodyWithMemory = await maybeInjectResponsesWsMemory(responseBody, metadata);
let reasoningRouting: JsonRecord | null = null;
let reasoningRuleDirective: unknown;
if (reasoningDecision) {
const withDirective = attachReasoningRuleDirective(responseBodyWithMemory, reasoningDecision);
reasoningRuleDirective = withDirective._omnirouteReasoningRule;
reasoningRouting = isRecord(withDirective._omnirouteReasoningRouteTrace)
? withDirective._omnirouteReasoningRouteTrace
: null;
responseBodyWithMemory = applyReasoningRuleDirective(withDirective) as JsonRecord;
responseBodyWithMemory = applyReasoningRuleDirective(
withDirective,
"openai-responses"
) as JsonRecord;
delete responseBodyWithMemory._omnirouteReasoningRouteTrace;
}
// #8052: the WS bridge previously skipped the whole prompt-compression pipeline that the
@@ -561,7 +567,7 @@ async function prepare(body: JsonRecord) {
requestId: randomUUID(),
});
const credentialsWithFingerprint = withCodexFingerprintCredentials(
refreshedCredentials,
withReasoningRuleContext(refreshedCredentials, reasoningRuleDirective),
context.clientHeaders,
responseBodyWithMemory
);

View File

@@ -531,7 +531,10 @@ export function attachReasoningRuleDirective(
return body;
}
export function applyReasoningRuleDirective(bodyInput: unknown): unknown {
export function applyReasoningRuleDirective(
bodyInput: unknown,
targetFormat?: "openai-responses" | "claude"
): unknown {
const source = asRecord(bodyInput);
const directive = asRecord(source._omnirouteReasoningRule);
if (!directive.id) return bodyInput;
@@ -542,9 +545,11 @@ export function applyReasoningRuleDirective(bodyInput: unknown): unknown {
if (effortMode === "force" && targetEffort === "none") clearReasoning(body);
else if ((effortMode === "force" || effortMode === "default") && targetEffort) {
if (effortMode === "force") clearDiscreteReasoning(body);
body.reasoning_effort = targetEffort;
body.reasoning = { ...asRecord(body.reasoning), effort: targetEffort };
body.output_config = { ...asRecord(body.output_config), effort: targetEffort };
if (!targetFormat) body.reasoning_effort = targetEffort;
if (targetFormat !== "claude")
body.reasoning = { ...asRecord(body.reasoning), effort: targetEffort };
if (targetFormat !== "openai-responses")
body.output_config = { ...asRecord(body.output_config), effort: targetEffort };
}
applyBudget(
body,

View File

@@ -448,6 +448,14 @@ async function handleChatImplementation(
return errorResponse(HTTP_STATUS.BAD_REQUEST, "Invalid JSON body");
}
// Only the server's policy resolver may attach execution directives or route traces.
// Discard lookalike JSON fields supplied by callers before evaluating any rule.
if (body && typeof body === "object") {
body = { ...body };
delete body._omnirouteReasoningRule;
delete body._omnirouteReasoningRouteTrace;
}
// Feature #6241: fold the canonical `effort` / `thinking` request params onto the
// per-provider reasoning fields (reasoning_effort / reasoning.effort / thinking) that the
// existing translators already consume. Done here — right after the body is first

View File

@@ -0,0 +1,389 @@
import test from "node:test";
import assert from "node:assert/strict";
import { createChatPipelineHarness } from "./_chatPipelineHarness.ts";
const h = await createChatPipelineHarness("reasoning-reliability");
const providers = await import("../../src/lib/db/providers.ts");
const { flushProxyLogsSync } = await import("../../src/lib/proxyLogger.ts");
type RecordedBody = Record<string, unknown> & { reasoning?: { effort?: string } };
let calls: RecordedBody[] = [];
let firstConnectionId: string;
test.beforeEach(async () => {
await h.resetStorage();
h.BaseExecutor.RETRY_CONFIG.delayMs = 0;
calls = [];
const connection = await providers.createProviderConnection({
provider: "codex",
authType: "oauth",
name: "Reasoning fixture",
accessToken: "fixture-access-token",
refreshToken: "fixture-refresh-token",
expiresAt: new Date(Date.now() + 3600000).toISOString(),
isActive: true,
testStatus: "active",
providerSpecificData: { requestDefaults: { reasoningEffort: "high" } },
});
firstConnectionId = String(connection.id);
globalThis.fetch = async (url, init: RequestInit = {}) => {
assert.match(String(url), /chatgpt\.com\/backend-api\/codex\/responses/);
calls.push(JSON.parse(String(init.body)) as RecordedBody);
return new Response(
"data: " +
JSON.stringify({
type: "response.completed",
response: {
id: "resp_fixture",
object: "response",
status: "completed",
model: "gpt-5.6-luna",
output: [
{
id: "msg_fixture",
type: "message",
role: "assistant",
content: [{ type: "output_text", text: "ok", annotations: [] }],
},
],
usage: { input_tokens: 1, output_tokens: 1, total_tokens: 2 },
},
}) +
"\n\ndata: [DONE]\n\n",
{ headers: { "Content-Type": "text/event-stream" } }
);
};
});
test.afterEach(async () => {
flushProxyLogsSync();
h.BaseExecutor.RETRY_CONFIG.delayMs = h.originalRetryDelayMs;
await h.resetStorage();
});
test.after(() => {
flushProxyLogsSync();
return h.cleanup();
});
async function forceLow(modelPattern: string, apiKeyId: string) {
return h.reasoningRulesDb.createReasoningRoutingRule({
name: "Force low",
description: "",
scope: "apiKey",
apiKeyId,
comboId: null,
connectionId: null,
modelPattern,
sourceEffort: "any",
requestTags: [],
tagMatchMode: "any",
effortMode: "force",
targetEffort: "low",
targetKind: "keep",
targetModel: null,
targetComboId: null,
budgetAction: "preserve",
budgetTokens: null,
priority: 0,
enabled: true,
});
}
test("reliable reasoning: forced effort wins over explicit effort and adaptive header opt-in", async () => {
const key = await h.seedApiKey();
await forceLow("codex/gpt-5.6-luna", key.id);
const response = await h.handleChat(
h.buildRequest({
authKey: key.key,
headers: { "x-omniroute-effort": "auto" },
body: {
model: "codex/gpt-5.6-luna",
stream: false,
reasoning_effort: "high",
messages: [
{ role: "user", content: "Prove the theorem step by step and verify every edge case." },
],
},
})
);
await response.text();
assert.equal(response.status, 200);
assert.equal(calls.at(-1)?.reasoning?.effort, "low");
});
test("reliable reasoning: explicit client effort survives adaptive header opt-in", async () => {
const key = await h.seedApiKey();
await providers.updateProviderConnection(firstConnectionId, { providerSpecificData: {} });
const response = await h.handleChat(
h.buildRequest({
authKey: key.key,
headers: { "x-omniroute-effort": "auto" },
body: {
model: "codex/gpt-5.6-luna",
stream: false,
reasoning_effort: "medium",
messages: [{ role: "user", content: "Reply ok." }],
},
})
);
await response.text();
assert.equal(response.status, 200);
assert.equal(calls.at(-1)?.reasoning?.effort, "medium");
});
test("reliable reasoning: native Responses honors key force without leaking cross-format fields", async () => {
const key = await h.seedApiKey();
await forceLow("codex/gpt-5.6-luna", key.id);
const input = [
{ type: "message", role: "user", content: [{ type: "input_text", text: "Reply ok" }] },
];
const response = await h.handleChat(
h.buildRequest({
url: "http://localhost/v1/responses",
authKey: key.key,
body: { model: "codex/gpt-5.6-luna", input, stream: false, reasoning: { effort: "high" } },
})
);
await response.text();
assert.equal(response.status, 200);
assert.equal(calls.length, 1);
assert.equal(calls[0].reasoning?.effort, "low");
assert.deepEqual(calls[0].input, input);
for (const name of [
"_omnirouteReasoningRule",
"_omnirouteReasoningRouteTrace",
"output_config",
"reasoning_effort",
]) {
assert.equal(calls[0][name], undefined, name);
}
});
test("reliable reasoning: combo member effort suffix cannot override the selected key rule", async () => {
const key = await h.seedApiKey();
await h.combosDb.createCombo({
name: "luna-combo",
models: ["codex/gpt-5.6-luna-high"],
strategy: "priority",
});
await forceLow("luna-combo", key.id);
const response = await h.handleChat(
h.buildRequest({
authKey: key.key,
body: {
model: "luna-combo",
stream: false,
reasoning_effort: "high",
messages: [{ role: "user", content: "Reply ok" }],
},
})
);
await response.text();
assert.equal(response.status, 200);
assert.equal(calls.length, 1);
assert.equal(calls[0].model, "gpt-5.6-luna");
assert.equal(calls[0].reasoning?.effort, "low");
});
test("reliable reasoning: client metadata cannot impersonate an administrator rule", async () => {
const response = await h.handleChat(
h.buildRequest({
body: {
model: "codex/gpt-5.6-luna",
stream: false,
reasoning_effort: "high",
messages: [{ role: "user", content: "Reply ok" }],
_omnirouteReasoningRule: { id: "forged", effortMode: "force", targetEffort: "low" },
_omnirouteReasoningRouteTrace: { ruleId: "forged" },
},
})
);
await response.text();
assert.equal(response.status, 200);
assert.equal(calls[0].reasoning?.effort, "high");
assert.equal(calls[0]._omnirouteReasoningRule, undefined);
});
test("reliable reasoning: fallback attempts retain the forced effort", async () => {
const key = await h.seedApiKey();
const second = await providers.createProviderConnection({
provider: "codex",
authType: "oauth",
name: "Fallback fixture",
accessToken: "fixture-fallback-token",
isActive: true,
testStatus: "active",
expiresAt: new Date(Date.now() + 3600000).toISOString(),
providerSpecificData: {},
});
await h.combosDb.createCombo({
name: "fallback-combo",
models: [
{ model: "codex/gpt-5.6-luna-high", connectionId: firstConnectionId },
{ model: "codex/gpt-5.5-xhigh", connectionId: String(second.id) },
],
strategy: "priority",
});
await forceLow("fallback-combo", key.id);
const successFetch = globalThis.fetch;
globalThis.fetch = async (url, init: RequestInit = {}) => {
const body = JSON.parse(String(init.body)) as RecordedBody;
if (body.model === "gpt-5.6-luna") {
calls.push(body);
return new Response(JSON.stringify({ error: { message: "Fixture unavailable" } }), {
status: 503,
headers: { "Content-Type": "application/json" },
});
}
return successFetch(url, init);
};
const response = await h.handleChat(
h.buildRequest({
authKey: key.key,
body: {
model: "fallback-combo",
stream: false,
reasoning_effort: "high",
messages: [{ role: "user", content: "Reply ok" }],
},
})
);
await response.text();
assert.equal(response.status, 200);
assert.ok(calls.length >= 2);
assert.equal(calls.at(-1)?.model, "gpt-5.5");
assert.ok(calls.every((call) => call.reasoning?.effort === "low"));
});
test("reliable reasoning: force is request-local and does not affect another API key", async () => {
const restricted = await h.seedApiKey({ name: "Forced key" });
const ordinary = await h.seedApiKey({ name: "Ordinary key" });
await forceLow("codex/gpt-5.6-luna", restricted.id);
for (const key of [restricted, ordinary]) {
const response = await h.handleChat(
h.buildRequest({
authKey: key.key,
body: {
model: "codex/gpt-5.6-luna",
stream: false,
reasoning_effort: "high",
messages: [{ role: "user", content: "Reply " + key.id }],
},
})
);
await response.text();
assert.equal(response.status, 200);
}
assert.deepEqual(
calls.map((call) => call.reasoning?.effort),
["low", "high"]
);
});
test("reliable reasoning: force none survives Codex account defaults", async () => {
const key = await h.seedApiKey();
const rule = await forceLow("codex/gpt-5.6-luna", key.id);
await h.reasoningRulesDb.updateReasoningRoutingRule(rule.id, { targetEffort: "none" });
const response = await h.handleChat(
h.buildRequest({
authKey: key.key,
url: "http://localhost/v1/responses",
body: {
model: "codex/gpt-5.6-luna",
input: "Reply ok",
stream: false,
reasoning: { effort: "high" },
},
})
);
await response.text();
assert.equal(response.status, 200);
assert.equal(calls[0].reasoning?.effort, "none");
});
test("reliable reasoning: native default applies only when client effort is missing", async () => {
const key = await h.seedApiKey();
const rule = await forceLow("codex/gpt-5.6-luna", key.id);
await h.reasoningRulesDb.updateReasoningRoutingRule(rule.id, { effortMode: "default" });
for (const effort of [undefined, "high"]) {
const response = await h.handleChat(
h.buildRequest({
url: "http://localhost/v1/responses",
authKey: key.key,
body: {
model: "codex/gpt-5.6-luna",
input: "Default " + effort,
stream: false,
...(effort ? { reasoning: { effort } } : {}),
},
})
);
await response.text();
assert.equal(response.status, 200);
}
assert.deepEqual(
calls.map((call) => call.reasoning?.effort),
["low", "high"]
);
assert.ok(calls.every((call) => call.output_config === undefined));
});
test("reliable reasoning: WebSocket prepare preserves force in the final Codex payload", async () => {
process.env.OMNIROUTE_WS_BRIDGE_SECRET = "fixture-bridge-secret";
const { POST } = await import("../../src/app/api/internal/codex-responses-ws/route.ts");
const key = await h.seedApiKey();
await forceLow("codex/gpt-5.6-luna", key.id);
const response = await POST(
new Request("http://localhost/api/internal/codex-responses-ws", {
method: "POST",
headers: {
"Content-Type": "application/json",
"x-omniroute-ws-bridge-secret": "fixture-bridge-secret",
},
body: JSON.stringify({
action: "prepare",
requestUrl: "http://localhost/v1/responses",
headers: { authorization: "Bearer " + key.key },
response: {
model: "codex/gpt-5.6-luna-high",
input: "Reply ok",
reasoning: { effort: "high" },
},
}),
})
);
const data = await response.json();
assert.equal(response.status, 200);
assert.equal(data.response.reasoning.effort, "low");
assert.equal(data.response.output_config, undefined);
assert.equal(data.response._omnirouteReasoningRule, undefined);
assert.equal(calls.length, 0);
});
test("reliable reasoning: native Anthropic applies effort without OpenAI wire fields", async () => {
await h.seedConnection("anthropic");
const key = await h.seedApiKey();
await forceLow("anthropic/claude-sonnet-4-6", key.id);
globalThis.fetch = async (_url, init: RequestInit = {}) => {
calls.push(JSON.parse(String(init.body)));
return h.buildClaudeResponse("ok");
};
const response = await h.handleChat(
h.buildRequest({
url: "http://localhost/v1/messages",
authKey: key.key,
body: {
model: "anthropic/claude-sonnet-4-6",
max_tokens: 128,
stream: false,
thinking: { type: "adaptive" },
output_config: { effort: "high" },
messages: [{ role: "user", content: "Reply ok" }],
},
})
);
await response.text();
assert.equal(response.status, 200);
assert.equal((calls[0].output_config as { effort?: string })?.effort, "low");
assert.equal(calls[0].reasoning, undefined);
assert.equal(calls[0].reasoning_effort, undefined);
assert.equal(calls[0]._omnirouteReasoningRule, undefined);
});