mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-17 12:42:21 +03:00
fix(sse): no-think/reasoning_effort reaches reasoning.effort shape on same-format /v1/responses lane (#6879)
This commit is contained in:
@@ -31,6 +31,7 @@ import {
|
||||
recordReplay,
|
||||
requiresReasoningReplay,
|
||||
} from "../services/reasoningCache.ts";
|
||||
import { normalizeResponsesReasoningEffort } from "./request/openai-responses/helpers.ts";
|
||||
|
||||
bootstrapTranslatorRegistry();
|
||||
export { register } from "./registry.ts";
|
||||
@@ -61,10 +62,37 @@ function normalizeResponsesInputItem(item) {
|
||||
return item;
|
||||
}
|
||||
|
||||
// Promote a stray top-level Chat-Completions-shaped `reasoning_effort` into the
|
||||
// Responses-shaped `reasoning:{effort}` object, in place, removing the top-level key.
|
||||
// No-op when `reasoning` is already present (an explicit Responses-shaped value always
|
||||
// wins) or when `reasoning_effort` is absent.
|
||||
//
|
||||
// This exists for the SAME-FORMAT lane (source === target === OPENAI_RESPONSES), where
|
||||
// translateRequest's hub-and-spoke translation block is skipped entirely (#7631): a
|
||||
// caller that lands a top-level `reasoning_effort` there — e.g. applyNoThinkingAlias
|
||||
// on the OpenAI path, which runs upstream of model-format resolution and cannot know
|
||||
// yet whether the target lane is Responses-native — would otherwise reach the upstream
|
||||
// with BOTH an unrecognized top-level field AND no `reasoning.effort`, so suppression
|
||||
// silently does not take effect. The cross-format path (openai -> openai-responses)
|
||||
// already performs the equivalent promotion in toResponses.ts; this covers the lane
|
||||
// that promotion never runs on.
|
||||
function promoteStrayReasoningEffort(body) {
|
||||
if (!body || typeof body !== "object") return body;
|
||||
if (body.reasoning !== undefined) return body;
|
||||
if (body.reasoning_effort === undefined) return body;
|
||||
|
||||
const effort = normalizeResponsesReasoningEffort(body.reasoning_effort);
|
||||
if (effort) {
|
||||
body.reasoning = { effort };
|
||||
}
|
||||
delete body.reasoning_effort;
|
||||
return body;
|
||||
}
|
||||
|
||||
function normalizeOpenAIResponsesRequest(body) {
|
||||
if (!body || typeof body !== "object") return body;
|
||||
|
||||
const normalized = { ...body };
|
||||
const normalized = promoteStrayReasoningEffort({ ...body });
|
||||
|
||||
if (typeof normalized.input === "string") {
|
||||
normalized.input = [
|
||||
|
||||
@@ -137,3 +137,70 @@ test("a lane known to reject reasoning_effort still drops it downstream (delete-
|
||||
const stripped = stripUnsupportedParams("github", "claude-3-5-sonnet", body);
|
||||
assert.equal("reasoning_effort" in stripped, false);
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// #7631: the same-format /v1/responses lane (source === target === OPENAI_RESPONSES)
|
||||
// skips translateRequest's hub-and-spoke translation block entirely, so a stray
|
||||
// top-level `reasoning_effort` (set upstream by applyNoThinkingAlias on the OpenAI
|
||||
// path, before model-format resolution knows the target lane is Responses-native)
|
||||
// must still be promoted into the Responses-shaped `reasoning.effort`, or thinking
|
||||
// suppression silently does not take effect on that lane.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
test("7631: translateRequest promotes a stray top-level reasoning_effort into reasoning.effort on the same-format OPENAI_RESPONSES lane", async () => {
|
||||
const { translateRequest } = await import("../../open-sse/translator/index.ts");
|
||||
const { FORMATS } = await import("../../open-sse/translator/formats.ts");
|
||||
|
||||
const body: Record<string, unknown> = {
|
||||
model: "gpt-5.1-codex",
|
||||
input: [{ type: "message", role: "user", content: [{ type: "input_text", text: "hi" }] }],
|
||||
reasoning_effort: "none",
|
||||
};
|
||||
|
||||
const result = translateRequest(
|
||||
FORMATS.OPENAI_RESPONSES,
|
||||
FORMATS.OPENAI_RESPONSES,
|
||||
"gpt-5.1-codex",
|
||||
body
|
||||
);
|
||||
|
||||
assert.equal("reasoning_effort" in result, false);
|
||||
assert.deepEqual(result.reasoning, { effort: "none" });
|
||||
});
|
||||
|
||||
test("7631: same-format OPENAI_RESPONSES lane leaves an explicit reasoning object untouched (no double-promotion)", async () => {
|
||||
const { translateRequest } = await import("../../open-sse/translator/index.ts");
|
||||
const { FORMATS } = await import("../../open-sse/translator/formats.ts");
|
||||
|
||||
const body: Record<string, unknown> = {
|
||||
model: "gpt-5.1-codex",
|
||||
input: [{ type: "message", role: "user", content: [{ type: "input_text", text: "hi" }] }],
|
||||
reasoning: { effort: "high", summary: "auto" },
|
||||
};
|
||||
|
||||
const result = translateRequest(
|
||||
FORMATS.OPENAI_RESPONSES,
|
||||
FORMATS.OPENAI_RESPONSES,
|
||||
"gpt-5.1-codex",
|
||||
body
|
||||
);
|
||||
|
||||
assert.deepEqual(result.reasoning, { effort: "high", summary: "auto" });
|
||||
assert.equal("reasoning_effort" in result, false);
|
||||
});
|
||||
|
||||
test("7631: cross-format openai -> openai-responses promotion is unchanged (no regression)", async () => {
|
||||
const { translateRequest } = await import("../../open-sse/translator/index.ts");
|
||||
const { FORMATS } = await import("../../open-sse/translator/formats.ts");
|
||||
|
||||
const body: Record<string, unknown> = {
|
||||
model: "gpt-5.1-codex",
|
||||
messages: [{ role: "user", content: "hi" }],
|
||||
reasoning_effort: "none",
|
||||
};
|
||||
|
||||
const result = translateRequest(FORMATS.OPENAI, FORMATS.OPENAI_RESPONSES, "gpt-5.1-codex", body);
|
||||
|
||||
assert.equal("reasoning_effort" in result, false);
|
||||
assert.deepEqual(result.reasoning, { effort: "none" });
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user