import { resolveEntryProviderKind } from "../providers/connection-resolution.js"; import { ROUTING_IDENTITY_PROVIDERS } from "../providers/inference/auth.js"; import { getCatalogProviderForModel, PROVIDER_CATALOG, } from "../providers/model-catalog.js"; import { resolveCallSiteConfig } from "./llm-resolver.js"; import { type ContextWindow, DEFAULT_CONTEXT_WINDOW_MAX_INPUT_TOKENS, type LLMCallSite, type LLMConfig, } from "./schemas/llm.js"; import type { ContextWindowConfig } from "./types.js"; export interface EffectiveContextWindow { provider: string; model: string; enabled: boolean; maxInputTokens: number; modelMaxInputTokens: number; defaultInputTokens: number; compactThreshold: number; summaryBudgetRatio: number; targetBudgetRatio: number; overflowRecovery: ContextWindow["overflowRecovery"]; isLongContextEnabled: boolean; maxOutputTokens?: number; } export function resolveEffectiveContextWindow({ llm, callSite, overrideProfile, forceOverrideProfile, selectionSeed, }: { llm: LLMConfig; callSite: LLMCallSite; overrideProfile?: string; /** * Float `overrideProfile` above the call-site layers (see * `ResolveCallSiteOpts.forceOverrideProfile`). Threaded so context-window * sizing reflects the same profile the dispatch path resolves. */ forceOverrideProfile?: boolean; /** * Per-conversation mix seed (the conversation id). Threaded so context-window * sizing for a mix profile reflects the same arm the dispatch path picks. */ selectionSeed?: string; }): EffectiveContextWindow { const resolved = resolveCallSiteConfig(callSite, llm, { overrideProfile, forceOverrideProfile, selectionSeed, }); // Routing identities dispatch built-in catalog models, so the model's // catalog owner carries their limits. An entry-name provider resolves // through its row's kind instead: a custom-endpoint kind has no catalog // models, so its models keep the conservative default even when a model // id collides with a built-in one (the custom endpoint's "gpt-5.5" is not // OpenAI's). Labels with no row fall back to the model's catalog owner. const catalogProviderId = ROUTING_IDENTITY_PROVIDERS.has(resolved.provider) ? getCatalogProviderForModel(resolved.model) : PROVIDER_CATALOG.some((p) => p.id === resolved.provider) ? resolved.provider : (resolveEntryProviderKind(resolved.provider, resolved.model) ?? getCatalogProviderForModel(resolved.model)); const catalogModel = PROVIDER_CATALOG.find( (provider) => provider.id === catalogProviderId, )?.models.find((model) => model.id === resolved.model); const modelMaxInputTokens = catalogModel?.contextWindowTokens ?? DEFAULT_CONTEXT_WINDOW_MAX_INPUT_TOKENS; const defaultInputTokens = catalogModel?.defaultContextWindowTokens ?? DEFAULT_CONTEXT_WINDOW_MAX_INPUT_TOKENS; const maxInputTokens = Math.min( resolved.contextWindow.maxInputTokens, modelMaxInputTokens, ); return { provider: resolved.provider, model: resolved.model, enabled: resolved.contextWindow.enabled, maxInputTokens, modelMaxInputTokens, defaultInputTokens, compactThreshold: resolved.contextWindow.compactThreshold, summaryBudgetRatio: resolved.contextWindow.summaryBudgetRatio, targetBudgetRatio: resolved.contextWindow.targetBudgetRatio, overflowRecovery: resolved.contextWindow.overflowRecovery, isLongContextEnabled: maxInputTokens > defaultInputTokens, maxOutputTokens: catalogModel?.maxOutputTokens, }; } export function contextWindowConfigFromEffective( base: ContextWindowConfig, effective: EffectiveContextWindow, ): ContextWindowConfig { return { ...base, enabled: effective.enabled, maxInputTokens: effective.maxInputTokens, targetBudgetRatio: effective.targetBudgetRatio, compactThreshold: effective.compactThreshold, summaryBudgetRatio: effective.summaryBudgetRatio, overflowRecovery: effective.overflowRecovery, }; }