Roo-Code/src/shared/api.ts
roomote[bot] 5e07bc42d0
feat: exclude GPT-5 models from 20% context window output token cap (#6963)
Co-authored-by: Roo Code <roomote@roocode.com>
2025-08-11 22:04:58 -04:00

143 lines
4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import {
type ModelInfo,
type ProviderSettings,
ANTHROPIC_DEFAULT_MAX_TOKENS,
CLAUDE_CODE_DEFAULT_MAX_OUTPUT_TOKENS,
} from "@roo-code/types"
// ApiHandlerOptions
// Extend ProviderSettings (minus apiProvider) with handler-specific toggles.
export type ApiHandlerOptions = Omit<ProviderSettings, "apiProvider"> & {
/**
* When true and using GPT5 Responses API, include reasoning.summary: "auto"
* so the API returns reasoning summaries (we already parse and surface them).
* Defaults to true; set to false to disable summaries.
*/
enableGpt5ReasoningSummary?: boolean
}
// RouterName
const routerNames = [
"openrouter",
"requesty",
"glama",
"unbound",
"litellm",
"ollama",
"lmstudio",
"io-intelligence",
] as const
export type RouterName = (typeof routerNames)[number]
export const isRouterName = (value: string): value is RouterName => routerNames.includes(value as RouterName)
export function toRouterName(value?: string): RouterName {
if (value && isRouterName(value)) {
return value
}
throw new Error(`Invalid router name: ${value}`)
}
// RouterModels
export type ModelRecord = Record<string, ModelInfo>
export type RouterModels = Record<RouterName, ModelRecord>
// Reasoning
export const shouldUseReasoningBudget = ({
model,
settings,
}: {
model: ModelInfo
settings?: ProviderSettings
}): boolean => !!model.requiredReasoningBudget || (!!model.supportsReasoningBudget && !!settings?.enableReasoningEffort)
export const shouldUseReasoningEffort = ({
model,
settings,
}: {
model: ModelInfo
settings?: ProviderSettings
}): boolean => (!!model.supportsReasoningEffort && !!settings?.reasoningEffort) || !!model.reasoningEffort
export const DEFAULT_HYBRID_REASONING_MODEL_MAX_TOKENS = 16_384
export const DEFAULT_HYBRID_REASONING_MODEL_THINKING_TOKENS = 8_192
export const GEMINI_25_PRO_MIN_THINKING_TOKENS = 128
// Max Tokens
export const getModelMaxOutputTokens = ({
modelId,
model,
settings,
format,
}: {
modelId: string
model: ModelInfo
settings?: ProviderSettings
format?: "anthropic" | "openai" | "gemini" | "openrouter"
}): number | undefined => {
// Check for Claude Code specific max output tokens setting
if (settings?.apiProvider === "claude-code") {
return settings.claudeCodeMaxOutputTokens || CLAUDE_CODE_DEFAULT_MAX_OUTPUT_TOKENS
}
if (shouldUseReasoningBudget({ model, settings })) {
return settings?.modelMaxTokens || DEFAULT_HYBRID_REASONING_MODEL_MAX_TOKENS
}
const isAnthropicContext =
modelId.includes("claude") ||
format === "anthropic" ||
(format === "openrouter" && modelId.startsWith("anthropic/"))
// For "Hybrid" reasoning models, discard the model's actual maxTokens for Anthropic contexts
if (model.supportsReasoningBudget && isAnthropicContext) {
return ANTHROPIC_DEFAULT_MAX_TOKENS
}
// For Anthropic contexts, always ensure a maxTokens value is set
if (isAnthropicContext && (!model.maxTokens || model.maxTokens === 0)) {
return ANTHROPIC_DEFAULT_MAX_TOKENS
}
// If model has explicit maxTokens, clamp it to 20% of the context window
// Exception: GPT-5 models should use their exact configured max output tokens
if (model.maxTokens) {
// Check if this is a GPT-5 model (case-insensitive)
const isGpt5Model = modelId.toLowerCase().includes("gpt-5")
// GPT-5 models bypass the 20% cap and use their full configured max tokens
if (isGpt5Model) {
return model.maxTokens
}
// All other models are clamped to 20% of context window
return Math.min(model.maxTokens, Math.ceil(model.contextWindow * 0.2))
}
// For non-Anthropic formats without explicit maxTokens, return undefined
if (format) {
return undefined
}
// Default fallback
return ANTHROPIC_DEFAULT_MAX_TOKENS
}
// GetModelsOptions
export type GetModelsOptions =
| { provider: "openrouter" }
| { provider: "glama" }
| { provider: "requesty"; apiKey?: string }
| { provider: "unbound"; apiKey?: string }
| { provider: "litellm"; apiKey: string; baseUrl: string }
| { provider: "ollama"; baseUrl?: string }
| { provider: "lmstudio"; baseUrl?: string }
| { provider: "io-intelligence"; apiKey: string }