diff --git a/config/quality/file-size-baseline.json b/config/quality/file-size-baseline.json index e97f86d599..8b2d5e4761 100644 --- a/config/quality/file-size-baseline.json +++ b/config/quality/file-size-baseline.json @@ -381,7 +381,7 @@ "tests/unit/stream-utils.test.ts": 2445, "tests/unit/token-refresh-service.test.ts": 1378, "tests/unit/translator-openai-responses-req.test.ts": 1194, - "tests/unit/translator-openai-to-gemini.test.ts": 1616, + "tests/unit/translator-openai-to-gemini.test.ts": 1619, "tests/unit/translator-openai-to-kiro.test.ts": 1275, "tests/unit/translator-resp-gemini-to-openai.test.ts": 1234, "tests/unit/usage-service-hardening.test.ts": 1483, @@ -608,5 +608,6 @@ "_rebaseline_2026_08_05_9529_own_growth": "PR #9529 own growth (base release/v3.8.50 medida EXATAMENTE nos frozen antigos, entao o modo base-relative #8522 nao cobre): open-sse/services/rateLimitManager.ts 1060->1105 (+45: helper applyLimiterSettings() que re-arma o heartbeat do reservoir apos updateSettings — fix do bug Bottleneck 2.19.5 que congelava a fila weighted; TDD em tests/unit/ratelimit-reservoir-refresh.test.ts); tests/integration/chat-pipeline.test.ts 1592->1598 (+6: User-Agent do codex derivado de getCodexClientVersion() em vez de literal pinado — teste-irmao alinhado ao contrato); tests/unit/provider-validation-specialty.test.ts 2980->2985 (+5: cobertura NOVA claude-web 429 -> valid:false, alinhamento #9406); open-sse/translator/response/openai-responses.ts 1174->1204 (+30: buildResponsesReasoningSummaryDelta MOVIDA do leaf pureHelpers.ts para o host — a funcao do #9500 muta stream state e violava o contrato do leaf puro; o LOC total do par host+leaf nao cresceu, o pureHelpers encolheu o mesmo tanto). Crescimento por fix de producao + cobertura adicional + realocacao arquitetural, nao inchaco.", "_rebaseline_2026_08_06_v3850_inherited_drift_reconcile": "Reconciliacao 2026-08-06 do drift ACUMULADO da release/v3.8.50 apos o lote de merges de 08-05/06: 13 arquivos acima do frozen no tip puro 8180b49ce1 (medidos pelo proprio gate). O modo PR base-relative (#8522) deixa PRs inocentes passarem, e os rebaselines individuais dos PRs se perderam nas resolucoes sucessivas de conflito deste hot-file — o drift so aparece no modo absoluto (nightly/local). Crescimentos funcionais dos PRs mergeados: #9024 topology click-nav src/app/(dashboard)/dashboard/HomePageClient.tsx; #9324 OpenRouter enrich src/app/(dashboard)/dashboard/providers/page.tsx; #9329 quota card ordering src/app/(dashboard)/dashboard/usage/components/ProviderLimits/index.tsx; #9193 context-window suffixes src/sse/handlers/chat.ts; #9332 nested Claude server tool ids open-sse/executors/base.ts; #9228 strip orphaned tool outputs open-sse/executors/codex.ts; #9236 nvidia tool-name normalize open-sse/executors/default.ts; #9314 nested tool_call validation open-sse/executors/kiro.ts; #9260 caller identity REST hops open-sse/mcp-server/server.ts; #8934 cache breakpoints tests tests/unit/chatcore-translation-paths.test.ts; #9193 suffix tests tests/unit/combo-routing-engine.test.ts; #9196 reasoning-on-tool-finish tests tests/unit/sse-auth.test.ts; #9163 GPT-5.6 Max reasoning tests tests/unit/translator-openai-to-kiro.test.ts. default.ts e kiro.ts entram no frozen (estavam sem entrada, acima do cap 1000). Atualizacao pos-medicao (a base avancou durante o ciclo do PR): src/sse/handlers/chat.ts 1857->1877 (#9184 affinity EOF evict) e open-sse/executors/default.ts 1027->1042 (#9005 Kimi K3 tool-name backfill).", "_rebaseline_2026_08_06b_v3850_sweepreds_drift": "Segunda reconciliacao de 2026-08-06 (/sweep-reds sobre o tip puro 2ddbbc61a6): 3 arquivos voltaram a passar do frozen apos os merges do mesmo dia, com atribuicao 1:1 por commit. (1) src/app/(dashboard)/dashboard/providers/page.tsx 1928->1944 e (2) open-sse/executors/base.ts 1635->1640, ambos do #9515 (feat(radar): flag-gated signed free-model catalog overlay, commit e7f6b1d130) — o overlay do Radar entra por wiring nos chokepoints ja existentes (a resolucao/verificacao do catalogo assinado mora fora destes dois arquivos); +16 e +5 linhas liquidas nao sao extraiveis sem inventar um leaf por callsite. (3) open-sse/services/accountFallback.ts 1966->1972 do #8704 (commit c4527f97bd), +6 linhas de dados em CREDITS_EXHAUSTED_SIGNALS ('has been exhausted', fixes #8631). src/sse/handlers/chat.ts 1880>1877 tambem estava violando e NAO entra aqui de proposito: e drenado por encolhimento na PR #9598, sem rebaseline. Crescimento proprio DESTA PR: src/lib/db/migrationRunner.ts 1077->1084 (+7) — o guard retroativo em isSchemaAlreadyApplied para os arquivos renumerados 137/138, exigido pela propria mensagem de erro de colisao do runner (ambas as migracoes sao ALTER TABLE ADD COLUMN puro, nao idempotente). Dois `case` + dois `return hasColumn(...)` + 3 linhas de comentario dentro do switch existente; nao extraivel.", - "_rebaseline_2026_08_06c_v3850_sweepreds_pr2": "Segunda PR do /sweep-reds (fix/release-v3.8.50-basereds-0806b): tests/unit/provider-models-route.test.ts 1784->1787 (medido pelo gate, que conta split(\"\\n\").length) (+2 apos compressao de comentarios) — alinhamento de contrato forcado por dois merges do dia: #9106 tornou gemini-3.1-pro-high user-callable (a entry do alias entra na lista esperada do teste de discovery-retry, +1 linha de dado + 1 de comentario) e ff012ff420 adicionou onboardUser como bootstrap hop (exclusao no mock, ja comprimida a 1 linha). Nao ha o que encolher sem apagar o comentario que explica o porque." + "_rebaseline_2026_08_06c_v3850_sweepreds_pr2": "Segunda PR do /sweep-reds (fix/release-v3.8.50-basereds-0806b): tests/unit/provider-models-route.test.ts 1784->1787 (medido pelo gate, que conta split(\"\\n\").length) (+2 apos compressao de comentarios) — alinhamento de contrato forcado por dois merges do dia: #9106 tornou gemini-3.1-pro-high user-callable (a entry do alias entra na lista esperada do teste de discovery-retry, +1 linha de dado + 1 de comentario) e ff012ff420 adicionou onboardUser como bootstrap hop (exclusao no mock, ja comprimida a 1 linha). Nao ha o que encolher sem apagar o comentario que explica o porque.", + "_rebaseline_2026_08_07_v3850_sweepreds_pr2_toolnamemap": "tests/unit/translator-openai-to-gemini.test.ts 1616->1619 (+3). O frozen estava EXATAMENTE no tamanho da base, entao qualquer linha nova viola. #9568 (c9a3361e5a) fez buildChangedToolNameMap emitir entradas IDENTIDADE (o Gemini minusculiza nomes de tool nas respostas, entao o tradutor de resposta precisa da chave para mapear de volta), o que passou a incluir `_toolNameMap` no envelope Antigravity de qualquer request com tools. As 3 linhas sao: a chave nova na lista esperada de Object.keys, 1 comentario explicando POR QUE ela aparece (sem ele o proximo leitor tenta remove-la de novo) e 1 assert do CONTEUDO do map — presenca de chave sozinha nao provaria a entrada identidade, que e justamente o comportamento novo. Nao ha o que extrair: e alinhamento de contrato dentro de um teste existente." } diff --git a/src/lib/db/models/activeSyncedCatalog.ts b/src/lib/db/models/activeSyncedCatalog.ts index 7ef7604374..f5d649d030 100644 --- a/src/lib/db/models/activeSyncedCatalog.ts +++ b/src/lib/db/models/activeSyncedCatalog.ts @@ -1,6 +1,10 @@ import { providerUsesAuthoritativeLiveCatalog } from "@omniroute/open-sse/config/providerRegistry"; import { PROVIDER_ID_TO_ALIAS } from "@omniroute/open-sse/config/providerModels.ts"; -import { getSyncedAvailableModelsByConnection, type SyncedAvailableModel } from "../models"; +import { + getSyncedAvailableModels, + getSyncedAvailableModelsByConnection, + type SyncedAvailableModel, +} from "../models"; import { getRawProviderConnections } from "../providers"; export type ActiveSyncedCatalog = { @@ -102,11 +106,26 @@ export async function getActiveSyncedCatalog(providerId: string): Promise connection.id); const models = collectModelsForConnections(modelsByConnection, activeConnectionIds); + if (models.length > 0) { + return { + authoritative: providerUsesAuthoritativeLiveCatalog(providerId), + models, + }; + } - return { - authoritative: models.length > 0 && providerUsesAuthoritativeLiveCatalog(providerId), - models, - }; + // No ACTIVE CONNECTION carries a catalog for this provider — but a provider + // NODE can: nodes live in `provider_nodes`, never in `provider_connections`, + // so filtering by active connection ids drops their synced catalog entirely. + // Before #9294 this path read the provider-wide key_value set, and losing it + // took every node's runtime metadata with it (supportedThinkingEfforts, so + // `-high`/`-low` effort suffixes stopped resolving, plus contextWindow / + // maxInputTokens used by the combo context-window filter). + // + // Fall back to that provider-wide set, and deliberately keep it + // NON-authoritative: #9294's live-catalog gating is about what an active + // connection actually serves, so a node-backed catalog must inform metadata + // without ever being used to reject a model as unavailable. + return { authoritative: false, models: await getSyncedAvailableModels(storedProviderId) }; } catch { return { authoritative: false, models: [] }; } diff --git a/tests/unit/combo-context-window-filter.test.ts b/tests/unit/combo-context-window-filter.test.ts index f20ce67522..e8f198c629 100644 --- a/tests/unit/combo-context-window-filter.test.ts +++ b/tests/unit/combo-context-window-filter.test.ts @@ -56,7 +56,11 @@ function capabilityEntry(limitContext: number | null) { }; } -function capabilityEntryWithLimits(limitInput: number | null, limitContext: number | null, limitOutput = 4096) { +function capabilityEntryWithLimits( + limitInput: number | null, + limitContext: number | null, + limitOutput = 4096 +) { return { ...capabilityEntry(limitContext), limit_input: limitInput, @@ -325,10 +329,12 @@ test("small input-only maxInputTokens keeps a target whose input fits even thoug ); }); -test("input-only maxInputTokens still rejects when the input itself exceeds the cap", () => { - // The fix must not let a genuinely-too-small input cap pass. `too-small` has - // maxInputTokens = 1, which cannot even hold the ~11-token input, so it must - // still be dropped while the compatible target survives. +test("input-only maxInputTokens is demoted when the input itself exceeds the cap", () => { + // #8944 made context metadata ADVISORY: a catalog-too-small target is no longer + // removed (a stale catalog entry must never delete the only target that could + // accept the request at runtime), it is ordered AFTER the known-fitting ones. + // `too-small` has maxInputTokens = 1, which cannot hold the ~11-token input, so + // it must lose the ordering to `huge` while remaining available as a fallback. saveModelsDevCapabilities({ "unit-7039-too-small": { "too-small": capabilityEntryWithLimits(1, 1_000_000, 500), @@ -344,14 +350,14 @@ test("input-only maxInputTokens still rejects when the input itself exceeds the assert.deepEqual( out.map((entry) => entry.modelStr), - ["unit-7039-too-small/huge"] + ["unit-7039-too-small/huge", "unit-7039-too-small/too-small"] ); }); -test("maxInputTokens defaulting to contextWindow still rejects when input + output exceeds the total window (#7039 follow-up)", () => { - // Shared-window model where maxInputTokens equals the total window size. - // The input alone fits the input cap, but input + output overflows the - // window, so the target must be rejected instead of passing on the input cap. +test("maxInputTokens defaulting to contextWindow is demoted when input + output exceeds the total window (#7039 follow-up)", () => { + // Shared-window model where maxInputTokens equals the total window size. The + // input alone fits the input cap but input + output overflows the window, so the + // target must not be PREFERRED — since #8944 it is demoted rather than dropped. saveModelsDevCapabilities({ "unit-7039-window": { "shared-window": capabilityEntryWithLimits(400_000, 400_000, 200_000), @@ -367,7 +373,7 @@ test("maxInputTokens defaulting to contextWindow still rejects when input + outp assert.deepEqual( out.map((entry) => entry.modelStr), - ["unit-7039-window/huge"] + ["unit-7039-window/huge", "unit-7039-window/shared-window"] ); }); @@ -391,16 +397,18 @@ test("model_context_override lets a small-catalog target survive a large-context largeContextBody(), noopLog ); - assert.deepEqual( - out.map((entry) => entry.modelStr).sort(), - ["unit-override/big", "unit-override/capped"] - ); + assert.deepEqual(out.map((entry) => entry.modelStr).sort(), [ + "unit-override/big", + "unit-override/capped", + ]); } finally { removeModelContextOverride("unit-override", "capped"); } }); -test("without an override the small-catalog target is still dropped for the large request", () => { +// #8944: "dropped" became "demoted" — the small-catalog target survives as a +// runtime fallback but must never outrank the one whose known limit fits. +test("without an override the small-catalog target is ordered last for the large request", () => { saveModelsDevCapabilities({ "unit-override": { big: capabilityEntry(1_000_000), @@ -417,6 +425,6 @@ test("without an override the small-catalog target is still dropped for the larg assert.deepEqual( out.map((entry) => entry.modelStr), - ["unit-override/big"] + ["unit-override/big", "unit-override/capped"] ); }); diff --git a/tests/unit/translator-openai-to-gemini.test.ts b/tests/unit/translator-openai-to-gemini.test.ts index 2b4affc2cc..ce1b0934db 100644 --- a/tests/unit/translator-openai-to-gemini.test.ts +++ b/tests/unit/translator-openai-to-gemini.test.ts @@ -582,16 +582,10 @@ test("OpenAI -> Antigravity wraps Gemini requests in a Cloud Code envelope", () "model", "userAgent", "requestType", - // #9568 (c9a3361e5a): buildChangedToolNameMap now emits IDENTITY entries too, - // because Gemini lowercases tool names in functionCall responses and the - // response translator needs a key to map them back. So any request carrying - // tools now carries `_toolNameMap` in the envelope. + // #9568: identity entries are emitted too (Gemini lowercases tool names in responses). "_toolNameMap", ]); - assert.deepEqual( - [...(result._toolNameMap as Map).entries()], - [["weather", "weather"]] - ); + assert.equal((result._toolNameMap as Map).get("weather"), "weather"); assert.equal(result.userAgent, "antigravity"); assert.equal(result.requestType, "agent"); assert.match(result.requestId, /^agent\/\d+\/[0-9a-f]{8}$/);