diff --git a/src/responses/state.ts b/src/responses/state.ts index 1037f0d28f0..9bcfea2d164 100644 --- a/src/responses/state.ts +++ b/src/responses/state.ts @@ -674,10 +674,13 @@ async function writeBoundedSnapshot(path: string, attemptLimit: number): Promise try { for (let attempt = 0; attempt < attemptLimit; attempt += 1) { const revision = stateRevision; - const entries: Array<[string, unknown]> = []; + const serializedEntries: string[] = []; let total = 0; // Newest-first so the most recent chains survive both legacy snapshot caps. - for (const [id, state] of [...states].reverse()) { + // Walk backwards by index without duplicating or reversing the map entries array. + const stateList = Array.from(states); + for (let i = stateList.length - 1; i >= 0; i--) { + const [id, state] = stateList[i]; let persistable: unknown; if (state.kind === "resident") { const { sizeBytes: _sizeBytes, kind: _kind, ...resident } = state; @@ -689,14 +692,15 @@ async function writeBoundedSnapshot(path: string, attemptLimit: number): Promise const persistEntry: [string, unknown] = [id, persistable]; // UTF-8 bytes, not UTF-16 code units: multibyte items otherwise slip // past both snapshot caps at up to 2x the intended size. - const size = Buffer.byteLength(JSON.stringify(persistEntry), "utf8"); + const serialized = JSON.stringify(persistEntry); + const size = Buffer.byteLength(serialized, "utf8"); if (state.kind === "resident" && size > SNAPSHOT_ENTRY_MAX_BYTES) continue; if (total + size > SNAPSHOT_TOTAL_MAX_BYTES) break; total += size; - entries.push(persistEntry); + serializedEntries.push(serialized); } - entries.reverse(); - const payload = JSON.stringify({ version: 2, states: entries }); + serializedEntries.reverse(); + const payload = '{"version":2,"states":[' + serializedEntries.join(",") + ']}'; const payloadBytes = Buffer.byteLength(payload, "utf8"); const payloadDigest = Bun.hash(payload).toString(36); // A mutation does not always change what gets persisted: entries past the diff --git a/src/router.ts b/src/router.ts index 84e4347bb61..aa1d9e9d8e3 100644 --- a/src/router.ts +++ b/src/router.ts @@ -87,6 +87,19 @@ export interface RouteResult { routeDecision?: RouteDecisionTraceV1; } +const REGISTRY_BY_ID = new Map(PROVIDER_REGISTRY.map(entry => [entry.id, entry])); + +const REGISTRY_STATIC_MODEL_IDS = new Map( + PROVIDER_REGISTRY.map(entry => { + const ids = new Set(); + for (const id of entry.models ?? []) ids.add(id); + for (const id of registryModelIdKeys(entry)) ids.add(id); + return [entry.id, Object.freeze([...ids])]; + }), +); + +const REGISTRY_ALIAS_BY_ID = new Map(PROVIDER_REGISTRY.flatMap(entry => entry.alias ? [[entry.id, entry.alias]] : [])); + export function captureRouteStaticPolicy( providerName: string, modelId: string, @@ -94,7 +107,8 @@ export function captureRouteStaticPolicy( effectiveAlias?: string | null, inboundWire: "responses" | "chat" | "anthropic" = "responses", ): ResolvedModelPolicy { - const registryEntry = PROVIDER_REGISTRY.find(entry => entry.id === providerName); + const registryEntry = REGISTRY_BY_ID.get(providerName) + ?? PROVIDER_REGISTRY.find(entry => entry.id === providerName); const transportMatchedRegistry = !!registryEntry && providerMatchesRegistryTransportWithStaticGuards(providerName, provider); return resolveModelPolicy({ @@ -125,6 +139,7 @@ const MODEL_PROVIDER_PATTERNS: Array<{ providerNames: string[]; prefixes: string }, ]; + /** * Known native model ids for a provider — the decode source for the Codex slug codec * (src/providers/slug-codec.ts). Union of static config ids, registry seeds, and the @@ -139,16 +154,18 @@ export function knownModelIdsForProvider( const ids = new Set(); for (const id of prov.models ?? []) ids.add(id); if (prov.defaultModel) ids.add(prov.defaultModel); - const registry = providerMatchesRegistryTransportWithStaticGuards(provName, prov) - ? PROVIDER_REGISTRY.find(entry => entry.id === provName) - : undefined; - for (const id of registry?.models ?? []) ids.add(id); - // Registry model-keyed hint maps double as known native ids (e.g. NVIDIA carries no - // static models list but names `moonshotai/kimi-k2.6` in its effort/window maps). Which - // maps count is classified by the registry itself rather than listed here, so an id declared - // only in a map this function forgot is no longer undecodable, and a new model-keyed field - // fails typecheck until its keys are given a meaning. - for (const id of registry ? registryModelIdKeys(registry) : []) ids.add(id); + if (providerMatchesRegistryTransportWithStaticGuards(provName, prov)) { + const staticIds = REGISTRY_STATIC_MODEL_IDS.get(provName); + if (staticIds) { + for (let i = 0; i < staticIds.length; i++) ids.add(staticIds[i]); + } else { + const dynamicEntry = PROVIDER_REGISTRY.find(entry => entry.id === provName); + if (dynamicEntry) { + for (const id of dynamicEntry.models ?? []) ids.add(id); + for (const id of registryModelIdKeys(dynamicEntry)) ids.add(id); + } + } + } for (const cached of getStaleCached(provName) ?? []) ids.add(cached.id); for (const model of config?.customModels ?? []) { if (model.provider === provName && model.modelId) ids.add(model.modelId); @@ -258,7 +275,8 @@ function usableResolvedApiKey(apiKey: string | undefined): string | undefined { export function routedProviderConfig(providerName: string, provider: OcxProviderConfig): OcxProviderConfig { provider = { ...provider, _apiKeyAttempt: provider._apiKeyAttempt ?? captureProviderApiKeySelection(provider) }; - const registryEntry = PROVIDER_REGISTRY.find(entry => entry.id === providerName); + const registryEntry = REGISTRY_BY_ID.get(providerName) + ?? PROVIDER_REGISTRY.find(entry => entry.id === providerName); if (!registryEntry || !providerMatchesRegistryTransportWithStaticGuards(providerName, provider)) { assertProviderDestinationAllowed(providerName, provider); // A row whose adapter no longer matches its registry entry still reaches the Responses @@ -698,7 +716,8 @@ function routeModelInternal( // and whose registry alias has not been claimed by another configured provider name or alias const registryMatches = Object.entries(config.providers).filter(([name, provider]) => { if (provider.alias !== undefined) return false; - const regAlias = PROVIDER_REGISTRY.find(e => e.id === name)?.alias; + const regAlias = REGISTRY_ALIAS_BY_ID.get(name) + ?? PROVIDER_REGISTRY.find(entry => entry.id === name)?.alias; if (!regAlias || regAlias.toLowerCase() !== requestedLower) return false; const claimedByOther = Object.entries(config.providers).some(([otherName, p]) => otherName !== name && ( diff --git a/tests/responses/responses-state-write-amplification.test.ts b/tests/responses/responses-state-write-amplification.test.ts index 9c339ee69b2..ac11dc64745 100644 --- a/tests/responses/responses-state-write-amplification.test.ts +++ b/tests/responses/responses-state-write-amplification.test.ts @@ -235,4 +235,21 @@ describe("responses-state snapshot write amplification (#2460)", () => { expect(parsed.version).toBe(2); expect(parsed.states.map(([id]) => id)).toContain("resp_amp_roundtrip"); }); +test("writeBoundedSnapshot serializes entries with version 2 and preserves parse integrity", async () => { + remember("resp_amp_order_1", "entry1"); + remember("resp_amp_order_2", "entry2"); + await flushResponseState(); + + const raw = await Bun.file(snapshot).text(); + expect(raw.startsWith('{"version":2,"states":[')).toBe(true); + expect(raw.endsWith("]}")); + const parsed = JSON.parse(raw) as { + version: number; + states: [string, Record][]; + }; + expect(parsed.version).toBe(2); + const ids = parsed.states.map(([id]) => id); + expect(ids).toContain("resp_amp_order_1"); + expect(ids).toContain("resp_amp_order_2"); + }); }); diff --git a/tests/routing/router.test.ts b/tests/routing/router.test.ts index d8b92dfdc72..c386208247b 100644 --- a/tests/routing/router.test.ts +++ b/tests/routing/router.test.ts @@ -1,6 +1,6 @@ import { describe, expect, test } from "bun:test"; import { mapReasoningEffort } from "../../src/reasoning-effort"; -import { NoEnabledOpenAiProviderError, routeCompactionModel, routeModel } from "../../src/router"; +import { NoEnabledOpenAiProviderError, knownModelIdsForProvider, routeCompactionModel, routeModel } from "../../src/router"; import type { OcxConfig, OcxProviderConfig } from "../../src/types"; describe("routeModel registry effort defaults", () => { @@ -771,4 +771,13 @@ describe("routeCompactionModel (#2901)", () => { }; expect(() => routeCompactionModel(openAiDefaultDisabled, "gpt-5.6-sol")).toThrow(NoEnabledOpenAiProviderError); }); +test("knownModelIdsForProvider retrieves static registry models and hint maps via cached maps", () => { + const provConfig = { + adapter: "openai-chat", + baseUrl: "https://api.nvidia.com/v1", + apiKey: "test-key", + }; + const knownIds = knownModelIdsForProvider("nvidia", provConfig as any); + expect(knownIds).toContain("moonshotai/kimi-k2.6"); + }); });