Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 23 additions & 9 deletions lib/api/__tests__/chat-stream-helpers-fallback.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ const DEEPSEEK_V4_PRO_0813_SLUG = "deepseek/deepseek-v4-pro-0813";
const HIGH_REASONING_ROUTES = [
"ask-model",
"agent-model",
"agent-model-free",
"model-grok-4.5-pro",
"model-grok-4.6",
"model-grok-4.6-pro",
Expand All @@ -51,7 +50,6 @@ const HIGH_REASONING_ROUTES = [
"model-opus-4.6",
"model-glm-5.2",
"model-glm-5.3",
"model-glm-5.3-flash-agent",
"model-kimi-k3",
"fallback-agent-model",
"fallback-ask-model",
Expand Down Expand Up @@ -175,12 +173,12 @@ describe("buildProviderOptions fallback chain", () => {
});

it.each([
["model-glm-5.3-flash", "low"],
["model-glm-5.3-flash", "high"],
["model-glm-5.3-flash-pro", "high"],
] as const)(
"routes %s directly with %s reasoning and DeepSeek Vision fallback",
"routes Ask %s directly with %s reasoning and DeepSeek Vision fallback",
(modelName, effort) => {
const opts = buildProviderOptions(true, "user-1", modelName, "agent");
const opts = buildProviderOptions(false, "user-1", modelName, "ask");
expect(opts.openrouter).toMatchObject({
reasoning: { enabled: true, effort },
models: [DEEPSEEK_VISION_SLUG],
Expand Down Expand Up @@ -281,36 +279,52 @@ describe("buildProviderOptions fallback chain", () => {
expect(opts.openrouter.models).toHaveLength(3);
});

it("runs free Agent on GLM Flash high and falls back through DeepSeek Flash, Pro 0813, and GLM 5.3", () => {
it("runs free Agent on the GLM Flash default and falls back through DeepSeek Flash, Pro 0813, and GLM 5.3", () => {
const opts = buildProviderOptions(
true,
"user-1",
"agent-model-free",
"agent",
);
expect(opts.openrouter).toMatchObject({
reasoning: { enabled: true, effort: "high" },
provider: { sort: "latency", data_collection: "deny" },
models: [DEEPSEEK_FLASH_SLUG, DEEPSEEK_V4_PRO_0813_SLUG, GLM_SLUG],
user: "user-1",
});
expect(opts.openrouter).not.toHaveProperty("reasoning");
});

it("uses the same high-reasoning fallback route for the paid GLM Agent treatment", () => {
it("uses provider-default reasoning for the paid GLM Agent route", () => {
const opts = buildProviderOptions(
true,
"user-1",
"model-glm-5.3-flash-agent",
"agent",
);
expect(opts.openrouter).toMatchObject({
reasoning: { enabled: true, effort: "high" },
provider: { sort: "latency", data_collection: "deny" },
models: [DEEPSEEK_FLASH_SLUG, DEEPSEEK_V4_PRO_0813_SLUG, GLM_SLUG],
user: "user-1",
});
expect(opts.openrouter).not.toHaveProperty("reasoning");
});

it.each([
"agent-model-free",
"model-glm-5.3-flash-agent",
"model-glm-5.3-flash",
"model-glm-5.3-flash-pro",
] as const)(
"keeps Agent %s on provider-default reasoning even when an override is supplied",
(modelName) => {
const opts = buildProviderOptions(true, "user-1", modelName, "agent", {
reasoningOverride: { enabled: true, effort: "high" },
});

expect(opts.openrouter).not.toHaveProperty("reasoning");
},
);

it("falls back from explicit DeepSeek Pro ask model through GLM 5.3 then Kimi K3", () => {
const opts = buildProviderOptions(
false,
Expand Down
13 changes: 7 additions & 6 deletions lib/api/chat-stream-helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -685,7 +685,6 @@ export function isExplicitDeepSeekProSelectionForRetry({
}

const HIGH_REASONING_MODELS = [
"agent-model-free",
"model-grok-4.5-pro",
"model-grok-4.6",
"model-grok-4.6-pro",
Expand All @@ -694,7 +693,6 @@ const HIGH_REASONING_MODELS = [
"model-glm-5.2",
"model-glm-5.3",
"model-glm-5.3-flash-pro",
"model-glm-5.3-flash-agent",
"model-opus-4.6",
] as const satisfies readonly ModelName[];

Expand Down Expand Up @@ -971,10 +969,13 @@ export function buildProviderOptions(
})
: fallbackSlugs;
// OpenRouter applies one reasoning configuration to both the primary model
// and every provider fallback. Standard GLM vision uses low, legacy Standard
// Grok vision uses medium, and Pro/full reasoning routes remain high.
// and every provider fallback. Ask GLM vision uses high, legacy Standard Grok
// vision uses medium, and Pro/full reasoning routes remain high. Agent GLM
// Flash routes omit this option so each provider model uses its default.
const isMediumGrok45Vision = modelName === "model-grok-4.5" && isGrok45;
const isStandardGlmFlashVision = modelName === "model-glm-5.3-flash";
const usesDefaultGlmFlashAgentReasoning =
mode === "agent" && modelId === GLM_5_3_FLASH_SLUG;
const routesThroughHighReasoningModel =
isGrok45 ||
isGrok46 ||
Expand All @@ -987,7 +988,7 @@ export function buildProviderOptions(
const reasoning = isStandardGlmFlashVision
? {
enabled: true,
effort: "low",
effort: "high",
}
: isMediumGrok45Vision
? {
Expand Down Expand Up @@ -1016,7 +1017,7 @@ export function buildProviderOptions(

return {
openrouter: {
reasoning,
...(!usesDefaultGlmFlashAgentReasoning && { reasoning }),
...(options.hasPdfAttachments && isDeepSeekV4
? {
plugins: [
Expand Down