diff --git a/config/quality/file-size-baseline.json b/config/quality/file-size-baseline.json index 763c8ae69e..d011602064 100644 --- a/config/quality/file-size-baseline.json +++ b/config/quality/file-size-baseline.json @@ -280,7 +280,8 @@ "_rebaseline_2026_06_28_5275_correlation_id_extract": "Extraction of the safe CorrelationId subset of #5275 (hartmark) — request correlation id stored in call_logs (migration 109) and returned via the X-Correlation-Id response header, WITHOUT the combo/resilience or build/lazy-loading changes (those stay in #5275). Own growth: callLogs.ts 975->985 (correlation_id column on CallLogSummaryRow + read/map), usageHistory.ts 983->988 (correlationId metadata normalize), chat.ts 1575->1632 (withCorrelationId response wiring + combo-failure log carrying correlationId), chatHelpers.ts new 811 (withCorrelationId helper + reqId threading; was 791786 (single ProviderAccountRoutingCard mount + import), auth.ts 2448->2458 (providerStrategies override resolution: fallbackStrategy/stickyRoundRobinLimit per-provider cascade in getProviderCredentials). Both additive, zero unrelated refactor; new UI/logic lives in new files (ProviderAccountRoutingCard.tsx, RoutingStrategyCard.tsx, rrState.ts::resolveComboStickyRoundRobinLimit). chat.ts value below reflects the current release tip (grown by other concurrent PRs, e.g. #6640), not this PR own change.", "src/sse/handlers/chat.ts": 1797, - "src/sse/handlers/chatHelpers.ts": 876, + "_rebaseline_2026_07_20_7779_routingcombo_thread": "PR #7779 own growth: chatHelpers.ts 876->877 (+1, thread routingComboId into executeChatWithBreaker for compression-combo assignment). Frozen so it can only shrink.", + "src/sse/handlers/chatHelpers.ts": 877, "src/sse/services/auth.ts": 2462, "open-sse/executors/default.ts": 890, "open-sse/translator/request/openai-responses.ts": 902, diff --git a/open-sse/handlers/chatCore.ts b/open-sse/handlers/chatCore.ts index 87d1e4650f..023c59c6b5 100644 --- a/open-sse/handlers/chatCore.ts +++ b/open-sse/handlers/chatCore.ts @@ -389,6 +389,7 @@ export async function handleChatCore({ comboName, comboStrategy = null, isCombo = false, + routingComboId = null, comboStepId = null, comboExecutionKey = null, cachedSettings = null, @@ -1146,11 +1147,11 @@ export async function handleChatCore({ compressionComboApplied = true; return true; }; - if (isCombo && comboName) { + if ((isCombo && comboName) || routingComboId) { try { const { getComboByName } = await import("../../src/lib/localDb"); let comboConfig = await getComboByName(comboName); - if (!comboConfig && comboName.startsWith("combo/")) { + if (!comboConfig && comboName?.startsWith("combo/")) { comboConfig = await getComboByName(comboName.substring(6)); } const comboRuntimeConfig = @@ -1185,7 +1186,8 @@ export async function handleChatCore({ const routingComboIds = [ comboConfig?.id, comboName, - comboName.startsWith("combo/") ? comboName.substring(6) : null, + routingComboId, + comboName?.startsWith("combo/") ? comboName.substring(6) : null, ].filter((id): id is string => typeof id === "string" && id.length > 0); if (routingComboIds.length > 0) { const { getCompressionComboForRoutingCombo } = diff --git a/open-sse/services/autoCombo/virtualFactory.ts b/open-sse/services/autoCombo/virtualFactory.ts index 126b1650c3..a2638fd046 100644 --- a/open-sse/services/autoCombo/virtualFactory.ts +++ b/open-sse/services/autoCombo/virtualFactory.ts @@ -233,7 +233,19 @@ function getNoAuthCandidates( * * Unknown candidates resolve through getTokenLimit()'s fallback chain, so a * non-empty pool always yields a positive contextLength. + * + * maxOutputTokens has no such guaranteed fallback in getResolvedModelCapabilities() + * — registry entries and models.dev sync data are both optional per model, so a + * candidate pool whose members all lack that specific field (e.g. #6453's + * provider-family combos, `auto/llama` and friends, over no-auth/free-tier + * registry entries that were never annotated with maxOutputTokens) would + * otherwise advertise `null`, which mirrors the `context: 0` bug this module's + * docstring describes for contextLength (opencode disables smart auto-compaction + * entirely when a limit is falsy). Fall back to a conservative generic default so + * a non-empty pool always yields a positive maxOutputTokens too. */ +const DEFAULT_ADVERTISED_MAX_OUTPUT_TOKENS = 8192; + export function computeAdvertisedLimits(candidates: Array<{ provider: string; model: string }>): { contextLength: number | null; maxOutputTokens: number | null; @@ -257,6 +269,9 @@ export function computeAdvertisedLimits(candidates: Array<{ provider: string; mo maxOutputTokens = maxOutputTokens === null ? output : Math.max(maxOutputTokens, output); } } + if (maxOutputTokens === null) { + maxOutputTokens = DEFAULT_ADVERTISED_MAX_OUTPUT_TOKENS; + } return { contextLength, maxOutputTokens }; } diff --git a/src/sse/handlers/chat.ts b/src/sse/handlers/chat.ts index cfb4196ebc..194da49dd8 100644 --- a/src/sse/handlers/chat.ts +++ b/src/sse/handlers/chat.ts @@ -888,6 +888,20 @@ export async function handleChat( telemetry.endPhase(); // Single model request + // Try to resolve routing combo from model prefix for compression combo lookup + let routingComboId: string | null = null; + if (!combo) { + const providerPrefix = resolvedModelStr.split("/")[0]; + if (providerPrefix) { + try { + const { getComboByName } = await import("@/lib/localDb"); + const routingCombo = await getComboByName(providerPrefix); + if (routingCombo?.id) { + routingComboId = routingCombo.id; + } + } catch {} + } + } const response = await handleSingleModelChat( body, resolvedModelStr, @@ -902,6 +916,7 @@ export async function handleChat( forceLiveComboTest: isComboLiveTest, forcedConnectionId: requestedConnectionId, correlationId: reqId, + routingComboId, reasoningDecision, reasoningIntent, reasoningRequestTags: requestRoutingTags.tags, @@ -953,6 +968,7 @@ async function handleSingleModelChat( cachedSettings?: any; providerId?: string | null; correlationId?: string | null; + routingComboId?: string | null; modelPinned?: boolean; reasoningDecision?: ReasoningRuleDecision | null; reasoningIntent?: ExtractedReasoningIntent | null; @@ -1380,6 +1396,7 @@ async function handleSingleModelChat( skipUpstreamRetry: runtimeOptions.skipUpstreamRetry ?? false, correlationId: runtimeOptions?.correlationId ?? null, modelPinned: runtimeOptions?.modelPinned ?? false, + routingComboId: runtimeOptions?.routingComboId ?? null, }); if (telemetry) telemetry.endPhase(); diff --git a/src/sse/handlers/chatHelpers.ts b/src/sse/handlers/chatHelpers.ts index 5cd5b90b95..bc3375cf91 100644 --- a/src/sse/handlers/chatHelpers.ts +++ b/src/sse/handlers/chatHelpers.ts @@ -395,6 +395,7 @@ export async function executeChatWithBreaker({ trafficType = "production", correlationId = null, modelPinned = false, + routingComboId = null, }: ExecuteChatWithBreakerOptions): Promise<{ result: any; tlsFingerprintUsed: boolean }> { let tlsFingerprintUsed = false; const normalizedTrafficType: TrafficType = @@ -432,6 +433,7 @@ export async function executeChatWithBreaker({ trafficType: normalizedTrafficType, correlationId, modelPinned, + routingComboId, onCredentialsRefreshed: async (newCreds: any) => { await updateProviderCredentials(credentials.connectionId, { accessToken: newCreds.accessToken,