mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-07-31 04:12:10 +03:00
fix(translator): preserve reasoning_effort for non-Copilot Responses clients (#4688)
Integrated into release/v3.8.37 — preserve reasoning_effort for non-Copilot Responses clients. Cherry-picked onto release tip; tests 47/47 green.
This commit is contained in:
committed by
GitHub
parent
a2ce88c8eb
commit
fd8a52ed64
@@ -445,11 +445,14 @@ export function openaiResponsesToOpenAIRequest(
|
||||
}
|
||||
delete result.store;
|
||||
|
||||
// Copilot-only: promote Responses `reasoning.{effort,summary}` to Chat fields
|
||||
// so the downstream openai-to-claude translator can enable extended thinking.
|
||||
// Gated by the UA marker from translateRequest; other clients see `reasoning` dropped.
|
||||
// Promote Responses `reasoning.effort` to the Chat-Completions-native
|
||||
// `reasoning_effort` field so OpenAI-family upstreams (and the downstream
|
||||
// openai-to-claude translator's extended-thinking path) keep the hint when a
|
||||
// Responses client is routed across formats. The Copilot-only `summary` ->
|
||||
// Claude summarized-thinking marker stays behind the UA gate from
|
||||
// translateRequest because it is Copilot-specific glue, not an OpenAI-native
|
||||
// field. Ported from upstream PR decolua/9router#1817 (ryanngit).
|
||||
if (
|
||||
credentialRecord._copilotClient === true &&
|
||||
root.reasoning &&
|
||||
typeof root.reasoning === "object" &&
|
||||
!Array.isArray(root.reasoning)
|
||||
@@ -459,7 +462,10 @@ export function openaiResponsesToOpenAIRequest(
|
||||
if (effort && result.reasoning_effort === undefined) {
|
||||
result.reasoning_effort = normalizeResponsesReasoningEffort(effort);
|
||||
}
|
||||
if (shouldRequestClaudeSummarizedThinking(reasoningRec.summary)) {
|
||||
if (
|
||||
credentialRecord._copilotClient === true &&
|
||||
shouldRequestClaudeSummarizedThinking(reasoningRec.summary)
|
||||
) {
|
||||
result[COPILOT_REASONING_SUMMARY_MARKER] = "summarized";
|
||||
}
|
||||
}
|
||||
|
||||
72
tests/unit/openai-responses-reasoning-effort.test.ts
Normal file
72
tests/unit/openai-responses-reasoning-effort.test.ts
Normal file
@@ -0,0 +1,72 @@
|
||||
/**
|
||||
* Reasoning-effort mapping across the OpenAI Chat <-> Responses request translators.
|
||||
*
|
||||
* Chat Completions carries the reasoning hint as top-level `reasoning_effort`; the
|
||||
* Responses API nests it as `reasoning.effort`. These tests pin both directions so
|
||||
* the hint survives when a request crosses formats (e.g. a Responses client routed
|
||||
* to an OpenAI-native Chat Completions upstream).
|
||||
*
|
||||
* Ported from upstream PR https://github.com/decolua/9router/pull/1817 (ryanngit).
|
||||
* Adapted: OmniRoute previously promoted `reasoning.effort` only behind the
|
||||
* Copilot-client gate (commit 75d9a83c25), which silently dropped the field for
|
||||
* every other Responses client (OpenCode, Cursor, raw OpenAI Responses, ...).
|
||||
* This test pins the unconditional promotion of effort while keeping the
|
||||
* Copilot-only `summary` -> Claude thinking marker behind its existing gate.
|
||||
*/
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import {
|
||||
openaiToOpenAIResponsesRequest,
|
||||
openaiResponsesToOpenAIRequest,
|
||||
} from "../../open-sse/translator/request/openai-responses.ts";
|
||||
import { convertResponsesApiFormat } from "../../open-sse/translator/helpers/responsesApiHelper.ts";
|
||||
|
||||
function asRecord(value: unknown): Record<string, unknown> {
|
||||
return value as Record<string, unknown>;
|
||||
}
|
||||
|
||||
test("Responses -> Chat promotes reasoning.effort for non-Copilot clients", () => {
|
||||
const out = asRecord(
|
||||
openaiResponsesToOpenAIRequest(
|
||||
"gpt-test",
|
||||
{ input: "hello", reasoning: { effort: "high" } },
|
||||
true,
|
||||
{} // no _copilotClient marker
|
||||
)
|
||||
);
|
||||
assert.equal(out.reasoning_effort, "high");
|
||||
assert.equal(out.reasoning, undefined);
|
||||
});
|
||||
|
||||
test("Responses -> Chat preserves reasoning.effort via the helper wrapper", () => {
|
||||
const out = asRecord(
|
||||
convertResponsesApiFormat({ input: "hello", reasoning: { effort: "medium" } })
|
||||
);
|
||||
assert.equal(out.reasoning_effort, "medium");
|
||||
assert.equal(out.reasoning, undefined);
|
||||
});
|
||||
|
||||
test("Responses -> Chat does not overwrite an explicit reasoning_effort", () => {
|
||||
const out = asRecord(
|
||||
openaiResponsesToOpenAIRequest(
|
||||
"gpt-test",
|
||||
{ input: "hello", reasoning_effort: "low", reasoning: { effort: "high" } },
|
||||
true,
|
||||
{}
|
||||
)
|
||||
);
|
||||
// Explicit Chat-level field wins over the Responses nesting.
|
||||
assert.equal(out.reasoning_effort, "low");
|
||||
});
|
||||
|
||||
test("Chat -> Responses already wraps reasoning_effort into reasoning.effort", () => {
|
||||
const out = asRecord(
|
||||
openaiToOpenAIResponsesRequest(
|
||||
"gpt-test",
|
||||
{ messages: [{ role: "user", content: "hi" }], reasoning_effort: "high" },
|
||||
true,
|
||||
{}
|
||||
)
|
||||
);
|
||||
assert.deepEqual(out.reasoning, { effort: "high" });
|
||||
});
|
||||
@@ -623,7 +623,10 @@ test("Chat -> Responses prefers max_completion_tokens over max_tokens when both
|
||||
assert.equal((result as any).max_completion_tokens, undefined);
|
||||
});
|
||||
|
||||
test("Responses -> Chat drops `reasoning` and does not synthesize reasoning_effort without Copilot marker", () => {
|
||||
test("Responses -> Chat drops `reasoning` and promotes effort to reasoning_effort even without Copilot marker", () => {
|
||||
// Updated per upstream PR decolua/9router#1817 (ryanngit): the OpenAI-native
|
||||
// `reasoning_effort` hint is always preserved across the Responses -> Chat
|
||||
// hop; only the Copilot-specific `summary` -> Claude marker stays gated.
|
||||
const result = openaiResponsesToOpenAIRequest(
|
||||
"claude-opus-4-7",
|
||||
{
|
||||
@@ -635,7 +638,7 @@ test("Responses -> Chat drops `reasoning` and does not synthesize reasoning_effo
|
||||
) as Record<string, unknown>;
|
||||
|
||||
assert.equal(result.reasoning, undefined);
|
||||
assert.equal(result.reasoning_effort, undefined);
|
||||
assert.equal(result.reasoning_effort, "high");
|
||||
});
|
||||
|
||||
test("Responses -> Chat promotes reasoning.effort to reasoning_effort when _copilotClient is set", () => {
|
||||
|
||||
Reference in New Issue
Block a user