fix(ai): use OpenRouter reasoning payload (#2298)

* fix(ai): use OpenRouter reasoning payload

* fix(coding-agent): stop updating packages on startup closes #1963

* fix(ai): add openrouter thinkingFormat compat

---------

Co-authored-by: Mario Zechner <badlogicgames@gmail.com>
This commit is contained in:
PriNova
2026-03-18 11:23:27 +01:00
committed by GitHub
parent 68da22f18c
commit f704ee7255
4 changed files with 46 additions and 3 deletions

View File

@@ -408,6 +408,12 @@ function buildParams(model: Model<"openai-completions">, context: Context, optio
(params as any).enable_thinking = !!options?.reasoningEffort;
} else if (compat.thinkingFormat === "qwen-chat-template" && model.reasoning) {
(params as any).chat_template_kwargs = { enable_thinking: !!options?.reasoningEffort };
} else if (compat.thinkingFormat === "openrouter" && options?.reasoningEffort && model.reasoning) {
// OpenRouter normalizes reasoning across providers via a nested reasoning object.
const openRouterParams = params as typeof params & { reasoning?: { effort?: string } };
openRouterParams.reasoning = {
effort: mapReasoningEffort(options.reasoningEffort, compat.reasoningEffortMap),
};
} else if (options?.reasoningEffort && model.reasoning && compat.supportsReasoningEffort) {
// OpenAI-style reasoning_effort
(params as any).reasoning_effort = mapReasoningEffort(options.reasoningEffort, compat.reasoningEffortMap);
@@ -816,7 +822,11 @@ function detectCompat(model: Model<"openai-completions">): Required<OpenAIComple
requiresToolResultName: false,
requiresAssistantAfterToolResult: false,
requiresThinkingAsText: false,
thinkingFormat: isZai ? "zai" : "openai",
thinkingFormat: isZai
? "zai"
: provider === "openrouter" || baseUrl.includes("openrouter.ai")
? "openrouter"
: "openai",
openRouterRouting: {},
vercelGatewayRouting: {},
supportsStrictMode: true,

View File

@@ -271,8 +271,8 @@ export interface OpenAICompletionsCompat {
requiresAssistantAfterToolResult?: boolean;
/** Whether thinking blocks must be converted to text blocks with <thinking> delimiters. Default: auto-detected from URL. */
requiresThinkingAsText?: boolean;
/** Format for reasoning/thinking parameter. "openai" uses reasoning_effort, "zai" uses top-level enable_thinking: boolean, "qwen" uses top-level enable_thinking: boolean, and "qwen-chat-template" uses chat_template_kwargs.enable_thinking. Default: "openai". */
thinkingFormat?: "openai" | "zai" | "qwen" | "qwen-chat-template";
/** Format for reasoning/thinking parameter. "openai" uses reasoning_effort, "openrouter" uses reasoning: { effort }, "zai" uses top-level enable_thinking: boolean, "qwen" uses top-level enable_thinking: boolean, and "qwen-chat-template" uses chat_template_kwargs.enable_thinking. Default: "openai". */
thinkingFormat?: "openai" | "openrouter" | "zai" | "qwen" | "qwen-chat-template";
/** OpenRouter-specific routing preferences. Only used when baseUrl points to OpenRouter. */
openRouterRouting?: OpenRouterRouting;
/** Vercel AI Gateway routing preferences. Only used when baseUrl points to Vercel AI Gateway. */

View File

@@ -233,4 +233,36 @@ describe("openai-completions tool_choice", () => {
expect(response.stopReason).toBe("error");
expect(response.errorMessage).toBe("Provider finish_reason: network_error");
});
it("uses OpenRouter reasoning object instead of reasoning_effort", async () => {
const model = getModel("openrouter", "deepseek/deepseek-r1")!;
let payload: unknown;
await streamSimple(
model,
{
messages: [
{
role: "user",
content: "Hi",
timestamp: Date.now(),
},
],
},
{
apiKey: "test",
reasoning: "high",
onPayload: (params: unknown) => {
payload = params;
},
},
).result();
const params = (payload ?? mockState.lastParams) as {
reasoning?: { effort?: string };
reasoning_effort?: string;
};
expect(params.reasoning).toEqual({ effort: "high" });
expect(params.reasoning_effort).toBeUndefined();
});
});

View File

@@ -63,6 +63,7 @@ const OpenAICompletionsCompatSchema = Type.Object({
thinkingFormat: Type.Optional(
Type.Union([
Type.Literal("openai"),
Type.Literal("openrouter"),
Type.Literal("zai"),
Type.Literal("qwen"),
Type.Literal("qwen-chat-template"),