fix(reasoning): preserve effort through Codex translations
Carry Claude reasoning_effort/reasoning into OpenAI Chat, map into OpenAI Responses reasoning.effort, and keep request-level effort (incl. xhigh) across tool-result turns instead of collapsing to high. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
940a35e009
commit
3a866fe18d
6 changed files with 36 additions and 5 deletions
|
|
@ -159,12 +159,10 @@ export function hasThinkingConfig(body) {
|
||||||
return !!(body.reasoning_effort || body.thinking?.type === "enabled");
|
return !!(body.reasoning_effort || body.thinking?.type === "enabled");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Normalize thinking config based on last message role
|
// Normalize provider-native thinking config based on last message role.
|
||||||
// - If lastMessage is not user → remove thinking config
|
// OpenAI reasoning_effort is request-level and must survive tool-result turns.
|
||||||
// - If lastMessage is user AND has thinking config → keep it (force enable)
|
|
||||||
export function normalizeThinkingConfig(body) {
|
export function normalizeThinkingConfig(body) {
|
||||||
if (!isLastMessageFromUser(body)) {
|
if (!isLastMessageFromUser(body)) {
|
||||||
delete body.reasoning_effort;
|
|
||||||
delete body.thinking;
|
delete body.thinking;
|
||||||
}
|
}
|
||||||
return body;
|
return body;
|
||||||
|
|
|
||||||
|
|
@ -168,7 +168,7 @@ function applyFormat(fmt, body, cfg, caps) {
|
||||||
case "openai": {
|
case "openai": {
|
||||||
if (none && canDisable) { body.reasoning_effort = "none"; break; }
|
if (none && canDisable) { body.reasoning_effort = "none"; break; }
|
||||||
const level = toLevel(eff);
|
const level = toLevel(eff);
|
||||||
if (level) body.reasoning_effort = level === "xhigh" || level === "max" ? "high" : level;
|
if (level) body.reasoning_effort = level;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "claude-adaptive": {
|
case "claude-adaptive": {
|
||||||
|
|
|
||||||
|
|
@ -80,6 +80,16 @@ export function claudeToOpenAIRequest(model, body, stream) {
|
||||||
result.tool_choice = convertToolChoice(body.tool_choice);
|
result.tool_choice = convertToolChoice(body.tool_choice);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (body.reasoning_effort !== undefined) {
|
||||||
|
result.reasoning_effort = body.reasoning_effort;
|
||||||
|
} else if (body.reasoning?.effort !== undefined) {
|
||||||
|
result.reasoning_effort = body.reasoning.effort;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (body.reasoning !== undefined) {
|
||||||
|
result.reasoning = body.reasoning;
|
||||||
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -316,6 +316,8 @@ export function openaiToOpenAIResponsesRequest(model, body, stream, credentials)
|
||||||
if (body.temperature !== undefined) result.temperature = body.temperature;
|
if (body.temperature !== undefined) result.temperature = body.temperature;
|
||||||
if (body.max_tokens !== undefined) result.max_tokens = body.max_tokens;
|
if (body.max_tokens !== undefined) result.max_tokens = body.max_tokens;
|
||||||
if (body.top_p !== undefined) result.top_p = body.top_p;
|
if (body.top_p !== undefined) result.top_p = body.top_p;
|
||||||
|
if (body.reasoning !== undefined) result.reasoning = body.reasoning;
|
||||||
|
if (body.reasoning_effort !== undefined) result.reasoning = { effort: body.reasoning_effort, summary: "auto" };
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -123,6 +123,10 @@ describe("applyThinking per provider format", () => {
|
||||||
const out = apply("openai", "gpt-5(low)", { reasoning_effort: "high" }, "openai");
|
const out = apply("openai", "gpt-5(low)", { reasoning_effort: "high" }, "openai");
|
||||||
expect(out.reasoning_effort).toBe("low");
|
expect(out.reasoning_effort).toBe("low");
|
||||||
});
|
});
|
||||||
|
it("openai keeps xhigh for reasoning models", () => {
|
||||||
|
const out = apply("openai", "gpt-5.3-codex", { reasoning_effort: "xhigh" }, "codex");
|
||||||
|
expect(out.reasoning_effort).toBe("xhigh");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("extractReasoningText (response shapes)", () => {
|
describe("extractReasoningText (response shapes)", () => {
|
||||||
|
|
|
||||||
17
tests/unit/provider-thinking-config.test.js
Normal file
17
tests/unit/provider-thinking-config.test.js
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
import { describe, it, expect } from "vitest";
|
||||||
|
import { normalizeThinkingConfig } from "../../open-sse/services/provider.js";
|
||||||
|
|
||||||
|
describe("normalizeThinkingConfig", () => {
|
||||||
|
it("keeps openai reasoning_effort on non-user turns", () => {
|
||||||
|
const body = {
|
||||||
|
messages: [{ role: "assistant", content: "ok" }],
|
||||||
|
reasoning_effort: "xhigh",
|
||||||
|
thinking: { type: "enabled" },
|
||||||
|
};
|
||||||
|
|
||||||
|
normalizeThinkingConfig(body);
|
||||||
|
|
||||||
|
expect(body.reasoning_effort).toBe("xhigh");
|
||||||
|
expect(body.thinking).toBeUndefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
Loading…
Reference in a new issue