From 9faf28acacaa115599ae1918e739df03599f8365 Mon Sep 17 00:00:00 2001 From: Shariq Riaz Date: Sun, 6 Jul 2025 13:15:05 +0500 Subject: [PATCH] fix: use actual max_completion_tokens from OpenRouter API (#5240) - Update parseOpenRouterModel to always use actual max_completion_tokens from OpenRouter API - Remove artificial restriction that only reasoning budget and Anthropic models get their actual max tokens - Fall back to 20% of context window when max_completion_tokens is null - Update getModelMaxOutputTokens to use same fallback logic for consistency - Update tests to reflect new behavior - Fixes issue where reserved tokens showed ~209k instead of actual model limits (e.g. GPT-4o: 16,384) --- src/api/providers/fetchers/openrouter.ts | 4 +--- src/shared/__tests__/api.spec.ts | 5 +++-- src/shared/api.ts | 4 +++- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/api/providers/fetchers/openrouter.ts b/src/api/providers/fetchers/openrouter.ts index a98484ba0e..027f8c54fb 100644 --- a/src/api/providers/fetchers/openrouter.ts +++ b/src/api/providers/fetchers/openrouter.ts @@ -190,10 +190,8 @@ export const parseOpenRouterModel = ({ const supportsPromptCache = typeof cacheWritesPrice !== "undefined" && typeof cacheReadsPrice !== "undefined" - const useMaxTokens = OPEN_ROUTER_REASONING_BUDGET_MODELS.has(id) || id.startsWith("anthropic/") - const modelInfo: ModelInfo = { - maxTokens: useMaxTokens ? maxTokens || 0 : 0, + maxTokens: maxTokens || Math.ceil(model.context_length * 0.2), contextWindow: model.context_length, supportsImages: modality?.includes("image") ?? false, supportsPromptCache, diff --git a/src/shared/__tests__/api.spec.ts b/src/shared/__tests__/api.spec.ts index 0285c897fc..5cc005e19d 100644 --- a/src/shared/__tests__/api.spec.ts +++ b/src/shared/__tests__/api.spec.ts @@ -66,7 +66,7 @@ describe("getMaxTokensForModel", () => { expect(getModelMaxOutputTokens({ modelId, model, settings })).toBe(8000) }) - it("should return undefined for non-thinking models with undefined maxTokens", () => { + it("should return 20% of context window for non-thinking models with undefined maxTokens", () => { const model: ModelInfo = { contextWindow: 200_000, supportsPromptCache: true, @@ -76,7 +76,8 @@ describe("getMaxTokensForModel", () => { modelMaxTokens: 4000, } - expect(getModelMaxOutputTokens({ modelId, model, settings })).toBeUndefined() + // Should return 20% of context window when maxTokens is undefined + expect(getModelMaxOutputTokens({ modelId, model, settings })).toBe(40000) }) test("should return maxTokens from modelInfo when thinking is false", () => { diff --git a/src/shared/api.ts b/src/shared/api.ts index d1bfa2794b..d3d1a31cd1 100644 --- a/src/shared/api.ts +++ b/src/shared/api.ts @@ -71,7 +71,9 @@ export const getModelMaxOutputTokens = ({ return ANTHROPIC_DEFAULT_MAX_TOKENS } - return model.maxTokens ?? undefined + // If maxTokens is 0 or undefined, fall back to 20% of context window + // This matches the sliding window logic + return model.maxTokens || Math.ceil(model.contextWindow * 0.2) } // GetModelsOptions