diff --git a/src/adapters/openai-responses.ts b/src/adapters/openai-responses.ts index 27268a481b..177ba0e1f6 100644 --- a/src/adapters/openai-responses.ts +++ b/src/adapters/openai-responses.ts @@ -1,6 +1,6 @@ import { createHash } from "node:crypto"; import type { IncomingMeta, ProviderAdapter } from "./base"; -import { namespacedToolName, type AdapterEvent, type OcxParsedRequest, type OcxProviderConfig, type OcxUsage } from "../types"; +import { namespacedToolName, type AdapterEvent, type OcxParsedRequest, type OcxProviderConfig, type OcxUsage, type TierDecision } from "../types"; import { catalogModelSupportsReasoningSummaries } from "../codex/catalog"; import { COMPACT_PROMPT, decodeCompactionSummary, SUMMARY_PREFIX } from "../responses/compaction"; import { collectResponsesToolGroups } from "../responses/tool-groups"; @@ -764,6 +764,15 @@ function stripPreviousResponseId(body: unknown, strip: boolean): unknown { return rest; } +/** Apply the settled tier only to a fresh outbound object; `_rawBody` remains caller-owned. */ +function applyTierDecisionToResponsesBody(body: unknown, decision: TierDecision | undefined): unknown { + if (!decision || decision.kind === "forward-caller" || !isPlainObject(body)) return body; + const next: Record = { ...body }; + if (decision.kind === "set") next.service_tier = decision.value; + else delete next.service_tier; + return next; +} + /** * Drop request parameters a stateless Responses upstream cannot implement, and pin * `store` false. @@ -778,8 +787,8 @@ function stripPreviousResponseId(body: unknown, strip: boolean): unknown { * `prompt` is a reference to a server-stored prompt template — the most stateful * field in the accepted schema. * - * `service_tier` is deliberately NOT dropped: the server writes it for fast mode - * (`responses/core.ts`), and silently deleting a configured knob inside an adapter is + * `service_tier` is deliberately NOT dropped: the final TierDecision is applied to a + * detached outbound body before this sanitizer chain, and silently deleting a configured knob is * worse than forwarding a parameter the upstream ignores. * * MUST run before the composed sanitize chain below: `stripItemIdsWhenUnstored` keys @@ -1367,6 +1376,9 @@ export function createResponsesPassthroughAdapter(provider: OcxProviderConfig): parsed._rawBody, forward || parsed._previousResponseInputExpanded === true, ); + // stripPreviousResponseId() intentionally returns its input on a no-op. Detach before the + // tier write so a force-fast/default decision can never mutate parsed._rawBody. + outBody = applyTierDecisionToResponsesBody(outBody, parsed.options?.tierDecision); const stateless = provider.statelessResponses === true; if (stateless) outBody = stripStatefulResponsesParams(outBody); // A replay miss can leave a function_call_output whose paired function_call sat diff --git a/src/codex/catalog/provider-fetch.ts b/src/codex/catalog/provider-fetch.ts index c858524eff..393780763a 100644 --- a/src/codex/catalog/provider-fetch.ts +++ b/src/codex/catalog/provider-fetch.ts @@ -33,10 +33,10 @@ import { CODEX_REASONING_LEVELS, codexEffortRank, configuredReasoningEfforts, mo import { getModelMetadata, getModelMetadataCaseInsensitive, listModelMetadata, resolveMetadataProvider } from "../../generated/model-metadata"; import { enrichProviderFromRegistry, shouldCaseFoldMetadataModelId } from "../../providers/derive"; import { - captureServiceTierAdapterAuthority, + captureFastPolicyAuthority, serviceTierSupportForModel, - type CapturedServiceTierAdapterAuthority, } from "../../providers/service-tier"; +import type { FastPolicyAuthority } from "../../providers/fastwire"; import { effectiveGoogleMode, getProviderRegistryEntry, providerMatchesRegistryTransport } from "../../providers/registry"; import { parseAntigravityAvailableModels } from "../../providers/antigravity-models"; import { applyProviderContextCap, providerContextCap } from "../../providers/context-cap"; @@ -155,7 +155,7 @@ interface CapturedProviderGather { readonly discovery: ResolvedProviderModelDiscovery; readonly policy: CatalogProviderDiscoveryPolicySnapshot; readonly request: CapturedModelsRequest; - readonly serviceTierAdapterAuthority: CapturedServiceTierAdapterAuthority; + readonly fastPolicyAuthority: FastPolicyAuthority; readonly observedAuth?: ModelsAuthResolution; /** * Configured model ids this provider must keep even when live discovery omits @@ -408,7 +408,7 @@ function captureProviderGather( const enriched = detachedClone(withCanonicalOpenAiForwardAuthDefault(name, configured)); enrichProviderFromRegistry(name, enriched); const registryTransportMatch = providerMatchesRegistryTransport(name, enriched); - const serviceTierAdapterAuthority = captureServiceTierAdapterAuthority( + const fastPolicyAuthority = captureFastPolicyAuthority( name, enriched, registryTransportMatch, @@ -449,7 +449,7 @@ function captureProviderGather( discovery, policy, request, - serviceTierAdapterAuthority, + fastPolicyAuthority, ...(observedAuth ? { observedAuth: Object.freeze({ ...observedAuth }) } : {}), ...(retainConfiguredModelIds && retainConfiguredModelIds.size > 0 ? { retainConfiguredModelIds } @@ -518,7 +518,7 @@ function captureGatherFlight( // It is the one member of a provider row that is legitimately a function, // so it is dropped here rather than allowed to break every encode. provider: omitProviderTransportExecutor(provider.provider), - serviceTierAdapterAuthority: provider.serviceTierAdapterAuthority, + fastPolicyAuthority: provider.fastPolicyAuthority, // Combo retention is capture-time state, not a provider-row field. Two // gathers that share providers but differ in combo targets must not join. retainConfiguredModelIds: [...(provider.retainConfiguredModelIds ?? [])].sort(), diff --git a/src/config.ts b/src/config.ts index d4c0a3a0f3..3d8c416ccb 100644 --- a/src/config.ts +++ b/src/config.ts @@ -65,9 +65,11 @@ import { type OcxConfig, type OcxApiKeyEntry, type OcxProviderConfig, + type FastWire, type ProviderCostOverlay, } from "./types"; import { isCanonicalOpenAiForwardProvider, OPENAI_CODEX_PROVIDER_ID } from "./providers/openai-tiers"; +import { fastWireDeclarationError, hasFastWireCapabilityConflict } from "./providers/fastwire"; import { getProviderRegistryEntry, providerMatchesRegistryTransport, @@ -662,12 +664,14 @@ function resolveRuntimePortPath(): string { } const warnedConfigFallbacks = new Set(); +const warnedInheritedFastWireConflicts = new Set(); let lastWarningReconciledGeneration = 0; export function reconcileConfigWarningMemos(generation: number): number { if (generation <= lastWarningReconciledGeneration) return 0; - const removed = warnedConfigFallbacks.size; + const removed = warnedConfigFallbacks.size + warnedInheritedFastWireConflicts.size; warnedConfigFallbacks.clear(); + warnedInheritedFastWireConflicts.clear(); lastWarningReconciledGeneration = generation; return removed; } @@ -716,6 +720,16 @@ export function requestPacingConfigError(value: unknown): string | null { return "requestPacing must contain enabled and a valid requestsPerMinute/minIntervalMs provider rule or model overrides"; } +const fastWireSchema = z.object({ + kind: z.string(), + canonicalToWire: z.record(z.string().trim(), z.string().trim()), + foreignCallerTiers: z.string(), + betas: z.array(z.string().trim()).optional(), +}).strict().superRefine((fastWire, ctx) => { + const error = fastWireDeclarationError({ fastWire }); + if (error) ctx.addIssue({ code: "custom", message: error }); +}).transform(fastWire => fastWire as FastWire); + /** * Zod schema for one provider entry: known fields are validated strictly while unknown * fields pass through (preserved for runtime extensions). @@ -731,6 +745,7 @@ const providerConfigSchema = z.object({ responsesPath: z.string().min(1).optional(), statelessResponses: z.boolean().optional(), requiresAdjacentResponsesToolResults: z.boolean().optional(), + fastWire: fastWireSchema.nullable().optional(), supportsServiceTier: z.boolean().optional(), modelSupportsServiceTier: z.record(z.string().min(1), z.boolean()).optional(), preserveResponsesReasoningContent: z.boolean().optional(), @@ -753,7 +768,15 @@ const providerConfigSchema = z.object({ repairInvalidIds: z.boolean().optional(), }).strict().optional(), responsesSnapshotRepair: z.boolean().optional(), -}).passthrough(); +}).passthrough().superRefine((provider, ctx) => { + if (hasFastWireCapabilityConflict(provider)) { + ctx.addIssue({ + code: "custom", + path: ["fastWire"], + message: "fastWire=null conflicts with supportsServiceTier=true", + }); + } +}); const RESERVED_PROVIDER_NAMES = new Set([ // JavaScript prototype-pollution guards. @@ -2153,6 +2176,50 @@ function warnDegradedNativeSubagentConfig(rawParsed: unknown, config: OcxConfig) } } +/** + * Registry metadata can gain service-tier capability after a config was written. An explicit + * `fastWire: null` remains authoritative on load; rejecting the file would discard unrelated + * providers and API keys. Live writes remain strict through validateConfigCandidate(). + */ +function inheritedFastWireConflictProviderNames( + config: Pick, +): string[] { + const conflicts: string[] = []; + for (const [name, provider] of Object.entries(config.providers)) { + if (provider.fastWire !== null || provider.supportsServiceTier === false) continue; + const registry = providerMatchesRegistryTransport(name, provider) + ? getProviderRegistryEntry(name) + : undefined; + if (!registry) continue; + const effectiveProviderCapability = provider.supportsServiceTier ?? registry.supportsServiceTier; + const effectiveModelCapabilities = { + ...(registry.modelSupportsServiceTier ?? {}), + ...(provider.modelSupportsServiceTier ?? {}), + }; + if ( + effectiveProviderCapability === true + || Object.values(effectiveModelCapabilities).some(value => value === true) + ) { + conflicts.push(name); + } + } + return conflicts; +} + +function inheritedFastWireConflictWarning(name: string): string { + return `providers.${redactSecretString(name)}.fastWire=null overrides service-tier capability inherited from the matching registry entry`; +} + +function warnInheritedFastWireConflicts(configPath: string, config: OcxConfig): void { + const names = inheritedFastWireConflictProviderNames(config); + if (names.length === 0 || warnedInheritedFastWireConflicts.has(configPath)) return; + warnedInheritedFastWireConflicts.add(configPath); + console.warn( + `⚠️ config.json ${names.map(inheritedFastWireConflictWarning).join("; ")}. ` + + "The persisted providers and API keys were preserved.", + ); +} + /** * Load and validate config.json into an OcxConfig. Missing files reset to * defaults and clear stale overlays. Broken existing files also fall back to @@ -2177,6 +2244,7 @@ export function loadConfig(): OcxConfig { const result = configSchema.safeParse(parsed); if (result.success) { const config = normalizeApiKeyIds(result.data as OcxConfig); + warnInheritedFastWireConflicts(configPath, config); warnDegradedStreamMode(parsed, config); warnDegradedHostname(parsed, config); warnDegradedApiKeys(parsed, config); @@ -2201,6 +2269,7 @@ export function loadConfig(): OcxConfig { if (retryResult.success) { warnConfigRepaired(configPath, result.error); const config = normalizeApiKeyIds(retryResult.data as OcxConfig); + warnInheritedFastWireConflicts(configPath, config); warnDegradedHostname(parsed, config); warnDegradedApiKeys(parsed, config); warnDegradedCodexAccountPriorities(parsed, config); @@ -2220,6 +2289,7 @@ export function loadConfig(): OcxConfig { { warnDroppedConfigSections(configPath, salvaged.dropped, salvaged.issues); const config = normalizeApiKeyIds(salvaged.parsed); + warnInheritedFastWireConflicts(configPath, config); warnDegradedHostname(parsed, config); warnDegradedApiKeys(parsed, config); warnDegradedCodexAccountPriorities(parsed, config); @@ -2279,6 +2349,7 @@ function validFileConfigDiagnostics(config: OcxConfig, rawParsed: unknown): Conf const rawEffort = rawClaudeSubagentEffort(rawParsed); const normalized = normalizeClaudeSubagentEffort(normalizeNativeSubagentSync(config, rawParsed), rawParsed); const warnings = configPlaceholderWarnings(normalized); + warnings.push(...inheritedFastWireConflictProviderNames(normalized).map(inheritedFastWireConflictWarning)); warnings.push(...degradedCodexAccountPriorityWarnings(rawParsed, normalized)); if (rawEffort !== undefined && !isClaudeSubagentEffort(rawEffort)) { warnings.push(`claudeCode.subagentEffort ignored: expected one of ${CLAUDE_SUBAGENT_EFFORTS.join(", ")}`); @@ -2492,7 +2563,17 @@ export function validateConfigCandidate(value: unknown): { ok: true; config: Ocx ?? loopbackListenerPortError(value); if (boundaryError) return { ok: false, error: boundaryError }; const result = configSchema.safeParse(value); - if (result.success) return { ok: true, config: normalizeApiKeyIds(result.data as OcxConfig) }; + if (result.success) { + const config = normalizeApiKeyIds(result.data as OcxConfig); + const inheritedConflicts = inheritedFastWireConflictProviderNames(config); + if (inheritedConflicts.length > 0) { + return { + ok: false, + error: `schema_invalid: ${inheritedFastWireConflictWarning(inheritedConflicts[0]!)}`, + }; + } + return { ok: true, config }; + } return { ok: false, error: schemaDiagnosticsError(result.error) }; } diff --git a/src/providers/derive.ts b/src/providers/derive.ts index 712e2f020d..06643a05c1 100644 --- a/src/providers/derive.ts +++ b/src/providers/derive.ts @@ -449,6 +449,13 @@ export function enrichProviderFromRegistry(name: string, prov: OcxProviderConfig } // Registry-only metadata (never seeded into saved config): backfill straight from // the entry so an explicit user value stays distinguishable from the default. + if (prov.fastWire === undefined && entry.fastWire !== undefined) { + prov.fastWire = entry.fastWire === null ? null : { + ...entry.fastWire, + canonicalToWire: { ...entry.fastWire.canonicalToWire }, + ...(entry.fastWire.betas ? { betas: [...entry.fastWire.betas] } : {}), + }; + } if (prov.supportsServiceTier === undefined && entry.supportsServiceTier !== undefined) prov.supportsServiceTier = entry.supportsServiceTier; if (prov.preserveResponsesReasoningContent === undefined && entry.preserveResponsesReasoningContent !== undefined) prov.preserveResponsesReasoningContent = entry.preserveResponsesReasoningContent; applyReasoningSummaryDefaults(prov, entry.modelSupportsReasoningSummaries); diff --git a/src/providers/fastwire.ts b/src/providers/fastwire.ts new file mode 100644 index 0000000000..d7aec93a4a --- /dev/null +++ b/src/providers/fastwire.ts @@ -0,0 +1,268 @@ +import type { FastWire, OcxProviderConfig, TierDecision } from "../types"; +import { MODEL_ADAPTER_OVERRIDE_ALLOWED } from "../types"; +import type { InboundWire, ModelWireDefault } from "./registry"; + +const SERVICE_TIER_ADAPTERS = new Set(["openai-chat", "openai-responses"]); +const FAST_WIRE_ADAPTERS: Readonly>> = { + "service-tier": SERVICE_TIER_ADAPTERS, + // A1 deliberately has no adapter implementation for Anthropic speed. + "anthropic-speed": new Set(), +}; + +const DEFAULT_SERVICE_TIER_FAST_WIRE: FastWire = Object.freeze({ + kind: "service-tier" as const, + canonicalToWire: Object.freeze({ priority: "priority" }), + foreignCallerTiers: "verbatim" as const, +}); + +export type FastPolicyAuthTransport = + | "oauth_bearer" + | "forwarded_authorization" + | "none" + | "x_api_key" + | "authorization_bearer"; + +export interface FastPolicyAuthority { + readonly providerAdapter: string; + readonly fastWireDeclaration: FastWire | null | undefined; + readonly modelWireOverrideAllowed: boolean; + readonly authTransport: FastPolicyAuthTransport; + readonly capability: { + readonly provider?: boolean; + readonly models: Readonly>; + readonly chatServiceTier?: boolean; + }; + readonly modelAdapters: Readonly>; + readonly hardPins: Readonly>; + readonly registryWireDefaults: Readonly>; +} + +export interface ResolvedFastPolicy { + readonly capability: boolean | undefined; + readonly eligibility: + | "eligible" + | "capability-unsupported" + | "unclassified" + | "wire-unavailable" + | "pin-unavailable"; + readonly adapter: string; + readonly fastWire: FastWire | null; + readonly forwardCallerTier: boolean; +} + +function exactModelValue(record: Readonly>, modelId: string): T | undefined { + if (Object.prototype.hasOwnProperty.call(record, modelId)) return record[modelId]; + const folded = modelId.toLowerCase(); + for (const [key, value] of Object.entries(record)) { + if (key.toLowerCase() === folded) return value; + } + return undefined; +} + +export function resolveProviderAuthTransport( + adapter: string, + mode: NonNullable, + apiKeyTransport?: OcxProviderConfig["apiKeyTransport"], +): FastPolicyAuthTransport { + if (mode === "oauth") return "oauth_bearer"; + if (mode === "forward") return "forwarded_authorization"; + if (mode === "local") return "none"; + if (adapter === "anthropic" && apiKeyTransport !== "bearer") return "x_api_key"; + return "authorization_bearer"; +} + +/** Adapter-derived declaration. This runs only after the final model wire is known. */ +export function defaultFastWireForAdapter(adapter: string): FastWire | null { + return SERVICE_TIER_ADAPTERS.has(adapter) ? DEFAULT_SERVICE_TIER_FAST_WIRE : null; +} + +function registryDefaultForModel( + defaults: Readonly>, + modelId: string, + inbound: InboundWire, +): string | undefined { + const declared = defaults[modelId.trim().toLowerCase()]; + if (declared === undefined) return undefined; + if (typeof declared !== "string" && !declared.inbound.includes(inbound)) return undefined; + const wire = typeof declared === "string" ? declared : declared.wire; + return MODEL_ADAPTER_OVERRIDE_ALLOWED.has(wire) ? wire : undefined; +} + +function resolvePolicyAdapter( + authority: FastPolicyAuthority, + modelId: string, + inbound: InboundWire, +): { adapter: string; hardPinned: boolean } { + // Hard pins and configured overrides deliberately use the same exact-key semantics as + // resolveWireProtocolOverride(). Registry defaults alone normalize ids at their boundary. + const hardPin = authority.hardPins[modelId]; + if (hardPin !== undefined) return { adapter: hardPin, hardPinned: true }; + if (authority.modelWireOverrideAllowed) { + const configured = authority.modelAdapters[modelId]; + if (configured !== undefined && MODEL_ADAPTER_OVERRIDE_ALLOWED.has(configured)) { + return { adapter: configured, hardPinned: false }; + } + if (MODEL_ADAPTER_OVERRIDE_ALLOWED.has(authority.providerAdapter)) { + const registryDefault = registryDefaultForModel(authority.registryWireDefaults, modelId, inbound); + if (registryDefault !== undefined) return { adapter: registryDefault, hardPinned: false }; + } + } + return { adapter: authority.providerAdapter, hardPinned: false }; +} + +/** A1's retained Chat serializer gate (`chatServiceTier || exact model true`). */ +export function legacyChatEligibility(authority: FastPolicyAuthority, modelId: string): boolean { + const exact = exactModelValue(authority.capability.models, modelId); + if (authority.capability.provider === false || exact === false) return false; + return authority.capability.chatServiceTier === true || exact === true; +} + +export function resolveFastPolicy( + authority: FastPolicyAuthority, + modelId: string, + inbound: InboundWire = "responses", +): ResolvedFastPolicy { + const { adapter, hardPinned } = resolvePolicyAdapter(authority, modelId, inbound); + const exactCapability = exactModelValue(authority.capability.models, modelId); + const capability = authority.capability.provider === false + ? false + : exactCapability ?? authority.capability.provider; + const fastWire = authority.fastWireDeclaration === undefined + ? defaultFastWireForAdapter(adapter) + : authority.fastWireDeclaration; + const wireAvailable = fastWire !== null && FAST_WIRE_ADAPTERS[fastWire.kind].has(adapter); + const chatEligible = adapter !== "openai-chat" || legacyChatEligibility(authority, modelId); + // Explicit null disables Fast injection, but the defensive true+null branch still preserves + // a caller tier on an existing OpenAI service-tier wire. + const callerWireAvailable = wireAvailable + || (fastWire === null && SERVICE_TIER_ADAPTERS.has(adapter)); + const forwardCallerTier = capability !== false && callerWireAvailable && chatEligible; + + let eligibility: ResolvedFastPolicy["eligibility"]; + if (capability === false) eligibility = "capability-unsupported"; + else if (!wireAvailable) { + eligibility = hardPinned && authority.fastWireDeclaration !== null + ? "pin-unavailable" + : "wire-unavailable"; + } + else if (!chatEligible) eligibility = "capability-unsupported"; + else if (capability === undefined) eligibility = "unclassified"; + else eligibility = "eligible"; + + return { capability, eligibility, adapter, fastWire, forwardCallerTier }; +} + +export function canonicalFastTierMarker(callerTier: string | undefined): "priority" | undefined { + const folded = callerTier?.trim().toLowerCase(); + return folded === "priority" || folded === "fast" ? "priority" : undefined; +} + +/** Pure A1 tier state machine. It never changes a caller spelling on inherit. */ +export function decideTier( + policy: ResolvedFastPolicy, + fastMode: boolean | undefined, + callerTier: string | undefined, +): TierDecision { + if (policy.capability === false) return { kind: "drop" }; + if (policy.capability === undefined) { + return policy.forwardCallerTier ? { kind: "forward-caller" } : { kind: "drop" }; + } + if (policy.fastWire === null) { + return policy.forwardCallerTier ? { kind: "forward-caller" } : { kind: "drop" }; + } + if (policy.eligibility !== "eligible") return { kind: "drop" }; + if (fastMode === true) { + const value = policy.fastWire.canonicalToWire.priority; + return typeof value === "string" && value.length > 0 + ? { kind: "set", value } + : { kind: "drop" }; + } + if (fastMode === false) return { kind: "drop" }; + if ( + callerTier !== undefined + && canonicalFastTierMarker(callerTier) === undefined + && policy.fastWire.foreignCallerTiers === "drop" + ) { + return { kind: "drop" }; + } + return { kind: "forward-caller" }; +} + +export function tierValueAfterDecision( + decision: TierDecision, + callerTier: string | undefined, +): string | undefined { + if (decision.kind === "set") return decision.value; + if (decision.kind === "drop") return undefined; + return callerTier; +} + +function isPlainRecord(value: unknown): value is Record { + if (!value || typeof value !== "object" || Array.isArray(value)) return false; + const prototype = Object.getPrototypeOf(value); + return prototype === Object.prototype || prototype === null; +} + +export function hasFastWireCapabilityConflict(source: { + readonly fastWire?: unknown; + readonly supportsServiceTier?: unknown; + readonly modelSupportsServiceTier?: unknown; +}): boolean { + if (source.fastWire !== null) return false; + if (source.supportsServiceTier === false) return false; + if (source.supportsServiceTier === true) return true; + return isPlainRecord(source.modelSupportsServiceTier) + && Object.values(source.modelSupportsServiceTier).some(value => value === true); +} + +/** Runtime registry validation; config uses the equivalent Zod shape at its boundary. */ +export function fastWireDeclarationError(source: { + readonly fastWire?: unknown; + readonly supportsServiceTier?: unknown; + readonly modelSupportsServiceTier?: unknown; +}): string | null { + const value = source.fastWire; + if (value === undefined) return null; + if (hasFastWireCapabilityConflict(source)) { + return "fastWire=null conflicts with supportsServiceTier=true"; + } + if (value === null) return null; + if (!isPlainRecord(value)) return "fastWire must be an object, null, or absent"; + if (value.kind !== "service-tier" && value.kind !== "anthropic-speed") { + return "fastWire.kind must be service-tier or anthropic-speed"; + } + if (value.foreignCallerTiers !== "verbatim" && value.foreignCallerTiers !== "drop") { + return "fastWire.foreignCallerTiers must be verbatim or drop"; + } + if (!isPlainRecord(value.canonicalToWire)) return "fastWire.canonicalToWire must be an object"; + if (!Object.prototype.hasOwnProperty.call(value.canonicalToWire, "priority")) { + return "fastWire.canonicalToWire must include priority"; + } + const wireValues: string[] = []; + for (const [canonicalTier, wireValue] of Object.entries(value.canonicalToWire)) { + if (canonicalTier.trim().length === 0) { + return "fastWire.canonicalToWire keys must be nonblank strings"; + } + if (typeof wireValue !== "string" || wireValue.trim().length === 0 || wireValue.trim().length > 64) { + return "fastWire.canonicalToWire values must be nonblank strings of at most 64 characters"; + } + wireValues.push(wireValue.trim()); + } + if (new Set(wireValues).size !== wireValues.length) { + return "fastWire.canonicalToWire values must be unique"; + } + if (value.betas !== undefined) { + if (!Array.isArray(value.betas) || value.betas.length > 16) { + return "fastWire.betas must be an array of at most 16 values"; + } + const betas: string[] = []; + for (const beta of value.betas) { + if (typeof beta !== "string" || beta.trim().length === 0) { + return "fastWire.betas values must be nonblank strings"; + } + betas.push(beta.trim()); + } + if (new Set(betas).size !== betas.length) return "fastWire.betas values must be unique"; + } + return null; +} diff --git a/src/providers/registry.ts b/src/providers/registry.ts index d188185ed3..b3343b6018 100644 --- a/src/providers/registry.ts +++ b/src/providers/registry.ts @@ -1,4 +1,5 @@ -import type { CodexAccountMode, OcxProviderConfig } from "../types"; +import type { CodexAccountMode, FastWire, OcxProviderConfig } from "../types"; +import { fastWireDeclarationError } from "./fastwire"; import { KIRO_MODELS, KIRO_MODEL_CONTEXT_WINDOWS, KIRO_MODEL_REASONING_EFFORTS } from "./kiro-models"; import { ANTIGRAVITY_MODELS, ANTIGRAVITY_MODEL_CONTEXT_WINDOWS, ANTIGRAVITY_MODEL_EFFORTS, ANTIGRAVITY_MODEL_INPUT_MODALITIES } from "./antigravity-models"; import type { ProviderBaseUrlChoice } from "./base-url-choices"; @@ -165,6 +166,8 @@ export interface ProviderRegistryEntry { * of paying a translation hop. */ modelWireDefaults?: Record; + /** Explicit Fast wire declaration; absence derives from the final model adapter. */ + fastWire?: FastWire | null; /** * Registry-only per-model override for the upstream request shape used behind a * Codex Responses WebSocket turn. `false` keeps the client-facing WebSocket but @@ -2531,6 +2534,17 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [ { id: "gitlab-duo", label: "GitLab Duo", baseUrl: "https://cloud.gitlab.com/ai/v1/proxy/openai/v1", adapter: "openai-chat", authKind: "key", dashboardUrl: "https://gitlab.com/-/user_settings/personal_access_tokens" }, ]; +export function providerRegistryFastWireError( + entry: Pick, +): string | null { + return fastWireDeclarationError(entry); +} + +for (const entry of PROVIDER_REGISTRY) { + const error = providerRegistryFastWireError(entry); + if (error) throw new TypeError(`Invalid provider registry entry ${entry.id}: ${error}`); +} + export function getProviderRegistryEntry(id: string): ProviderRegistryEntry | undefined { return PROVIDER_REGISTRY.find(entry => entry.id === id); } diff --git a/src/providers/service-tier.ts b/src/providers/service-tier.ts index aa4fab044a..b2f81cbb9a 100644 --- a/src/providers/service-tier.ts +++ b/src/providers/service-tier.ts @@ -1,116 +1,199 @@ -import type { OcxProviderConfig } from "../types"; -import { MODEL_ADAPTER_OVERRIDE_ALLOWED } from "../types"; -import { getProviderRegistryEntry, providerModelWireDefault, type InboundWire } from "./registry"; +import type { FastWire, OcxProviderConfig } from "../types"; +import { captureWireAdapterHardPins } from "../types"; +import { isCanonicalOpenAiForwardProvider } from "./openai-tiers"; +import { + getProviderRegistryEntry, + providerMatchesRegistryTransport, + type InboundWire, + type ModelWireDefault, +} from "./registry"; +import { + resolveFastPolicy, + resolveProviderAuthTransport, + type FastPolicyAuthority, + type ResolvedFastPolicy, +} from "./fastwire"; /** OpenAI-compatible adapters that can carry the standard `service_tier` field. */ export const SERVICE_TIER_ADAPTERS = new Set(["openai-chat", "openai-responses"]); -export type CapturedServiceTierAdapterAuthority = Readonly>; +/** @deprecated A1 evolves this snapshot into the complete FastPolicyAuthority. */ +export type CapturedServiceTierAdapterAuthority = FastPolicyAuthority; -const capturedAdapterAuthority = new WeakMap(); +const capturedFastPolicyAuthorities = new WeakMap(); type ServiceTierCapabilityProvider = Pick< OcxProviderConfig, - "adapter" | "supportsServiceTier" | "modelSupportsServiceTier" | "modelAdapters" | "baseUrl" | "authMode" | "chatServiceTier" + | "adapter" + | "supportsServiceTier" + | "modelSupportsServiceTier" + | "modelAdapters" + | "baseUrl" + | "authMode" + | "apiKeyTransport" + | "chatServiceTier" + | "fastWire" >; +function cloneRegistryWireDefaults( + defaults: Readonly> | undefined, +): Readonly> { + if (!defaults) return Object.freeze({}); + const clone: Record = {}; + for (const [modelId, declaration] of Object.entries(defaults)) { + clone[modelId.trim().toLowerCase()] = typeof declaration === "string" + ? declaration + : Object.freeze({ wire: declaration.wire, inbound: Object.freeze([...declaration.inbound]) }); + } + return Object.freeze(clone); +} + +function cloneFastWire(value: FastWire | null | undefined): FastWire | null | undefined { + if (value === null || value === undefined) return value; + return Object.freeze({ + kind: value.kind, + canonicalToWire: Object.freeze({ ...value.canonicalToWire }), + foreignCallerTiers: value.foreignCallerTiers, + ...(value.betas ? { betas: Object.freeze([...value.betas]) } : {}), + }); +} + /** - * Read a model map by exact model identity. Service-tier capability is deliberately - * stricter than the older model metadata maps: a family key or a colon-qualified - * fallback must not silently advertise Fast for a sibling model that was never verified. - * A case-insensitive exact match keeps hand-edited ids consistent with the other maps - * without widening the model scope. + * Capture every registry-owned input before an asynchronous catalog flight begins. + * The resolver itself is pure and never reads the live provider registry. */ -function exactModelValue( - record: Record | undefined, - modelId: string, -): T | undefined { - if (!record) return undefined; - if (Object.prototype.hasOwnProperty.call(record, modelId)) return record[modelId]; - const folded = modelId.toLowerCase(); - for (const [key, value] of Object.entries(record)) { - if (key.toLowerCase() === folded) return value; +function buildFastPolicyAuthority( + providerName: string, + provider: ServiceTierCapabilityProvider, + registryTransportMatch: boolean, +): FastPolicyAuthority { + const registry = registryTransportMatch ? getProviderRegistryEntry(providerName) : undefined; + const authority: FastPolicyAuthority = Object.freeze({ + providerAdapter: provider.adapter, + fastWireDeclaration: cloneFastWire( + provider.fastWire !== undefined ? provider.fastWire : registry?.fastWire, + ), + modelWireOverrideAllowed: !isCanonicalOpenAiForwardProvider(provider as OcxProviderConfig), + authTransport: resolveProviderAuthTransport( + provider.adapter, + provider.authMode ?? registry?.authKind ?? "key", + provider.apiKeyTransport, + ), + capability: Object.freeze({ + ...(provider.supportsServiceTier !== undefined ? { provider: provider.supportsServiceTier } : {}), + models: Object.freeze({ ...(provider.modelSupportsServiceTier ?? {}) }), + ...(provider.chatServiceTier !== undefined ? { chatServiceTier: provider.chatServiceTier } : {}), + }), + modelAdapters: Object.freeze({ ...(provider.modelAdapters ?? {}) }), + hardPins: captureWireAdapterHardPins(providerName), + registryWireDefaults: cloneRegistryWireDefaults(registry?.modelWireDefaults), + }); + return authority; +} + +export function captureFastPolicyAuthority( + providerName: string, + provider: ServiceTierCapabilityProvider, + registryTransportMatch: boolean, +): FastPolicyAuthority { + const authority = buildFastPolicyAuthority(providerName, provider, registryTransportMatch); + capturedFastPolicyAuthorities.set(provider, authority); + return authority; +} + +/** @deprecated Use captureFastPolicyAuthority. The legacy inbound argument is now snapshot data. */ +export function captureServiceTierAdapterAuthority( + providerName: string, + provider: ServiceTierCapabilityProvider, + registryTransportMatch: boolean, + _inbound: InboundWire = "responses", +): FastPolicyAuthority { + return captureFastPolicyAuthority(providerName, provider, registryTransportMatch); +} + +function authorityForProvider( + provider: ServiceTierCapabilityProvider, + providerName?: string, +): FastPolicyAuthority { + // Preserve the legacy no-name short circuit: serviceTierSupportForModel() used the + // provider adapter directly when no provider identity was available, so no configured + // override, hard pin, or registry default may participate on this path in A1. + if (providerName === undefined) { + const authority = buildFastPolicyAuthority("", provider, false); + return Object.freeze({ + ...authority, + modelAdapters: Object.freeze({}), + hardPins: Object.freeze({}), + registryWireDefaults: Object.freeze({}), + }); } - return undefined; + const captured = capturedFastPolicyAuthorities.get(provider); + if (captured) return captured; + const registryTransportMatch = providerMatchesRegistryTransport(providerName, provider); + const authority = buildFastPolicyAuthority(providerName, provider, registryTransportMatch); + // Frozen provider snapshots cannot drift, so repeated catalog/runtime projections may safely + // reuse the registry lookup and detached declaration maps. Mutable configs still rebuild. + if (Object.isFrozen(provider)) capturedFastPolicyAuthorities.set(provider, authority); + return authority; +} + +/** Resolve the pure Fast policy for a provider/model pair. */ +export function fastPolicyForModel( + provider: ServiceTierCapabilityProvider, + modelId: string, + providerName?: string, + inbound: InboundWire = "responses", +): ResolvedFastPolicy { + return resolveFastPolicy(authorityForProvider(provider, providerName), modelId, inbound); } /** - * Resolve the declared provider/model capability. An explicit provider-level false is a - * fail-closed boundary and cannot be reopened by a model map. Otherwise an exact model - * declaration wins over the provider default, including an explicit false. The resolver is - * provider-local: the caller must first resolve the final provider, so identical bare model ids - * on two providers cannot share capability state. + * Resolve the declared provider/model capability without applying wire availability. + * Kept as a public compatibility helper for callers that need the pure tri-state. */ export function supportsServiceTierForModel( provider: Pick, modelId: string, ): boolean | undefined { - if (provider.supportsServiceTier === false) return false; - return exactModelValue(provider.modelSupportsServiceTier, modelId) - ?? provider.supportsServiceTier; + const authority: FastPolicyAuthority = { + providerAdapter: "openai-responses", + fastWireDeclaration: undefined, + modelWireOverrideAllowed: true, + authTransport: "authorization_bearer", + capability: { + ...(provider.supportsServiceTier !== undefined ? { provider: provider.supportsServiceTier } : {}), + models: provider.modelSupportsServiceTier ?? {}, + }, + modelAdapters: {}, + hardPins: {}, + registryWireDefaults: {}, + }; + return resolveFastPolicy(authority, modelId).capability; } -/** Whether the Chat serializer may emit a tier for this exact model. */ +/** A1 name retained for the legacy Chat serializer gate. */ export function canSerializeServiceTierForChatModel( provider: Pick, modelId: string, ): boolean { - const exact = exactModelValue(provider.modelSupportsServiceTier, modelId); + const exact = supportsServiceTierForModel({ + modelSupportsServiceTier: provider.modelSupportsServiceTier, + }, modelId); if (provider.supportsServiceTier === false || exact === false) return false; return provider.chatServiceTier === true || exact === true; } -/** Capture registry-owned model wire defaults before an asynchronous catalog flight begins. */ -export function captureServiceTierAdapterAuthority( - providerName: string, - provider: Pick, - registryTransportMatch: boolean, - inbound: InboundWire = "responses", -): CapturedServiceTierAdapterAuthority { - const authority: Record = {}; - const defaults = registryTransportMatch - ? getProviderRegistryEntry(providerName)?.modelWireDefaults - : undefined; - for (const modelId of Object.keys(defaults ?? {})) { - const adapter = providerModelWireDefault( - providerName, - provider, - modelId, - MODEL_ADAPTER_OVERRIDE_ALLOWED, - inbound, - ); - if (adapter !== undefined) authority[modelId.trim().toLowerCase()] = adapter; - } - const frozen = Object.freeze(authority); - capturedAdapterAuthority.set(provider, frozen); - return frozen; -} - -/** Resolve an explicit model wire override for catalog-time capability projection. */ +/** Final adapter selected by the Fast policy's four-level wire resolver. */ export function serviceTierAdapterForModel( providerName: string, - provider: Pick, + provider: ServiceTierCapabilityProvider, modelId: string, inbound: InboundWire = "responses", ): string { - // Keep this lookup identical to resolveWireProtocolOverride(): configured model-adapter - // entries are exact-case keys, while registry defaults intentionally normalize ids there. - const configured = provider.modelAdapters?.[modelId]; - if (configured !== undefined && MODEL_ADAPTER_OVERRIDE_ALLOWED.has(configured)) return configured; - const captured = capturedAdapterAuthority.get(provider); - if (captured !== undefined) { - return captured[modelId.trim().toLowerCase()] ?? provider.adapter; - } - return providerModelWireDefault( - providerName, - provider, - modelId, - MODEL_ADAPTER_OVERRIDE_ALLOWED, - inbound, - ) ?? provider.adapter; + return fastPolicyForModel(provider, modelId, providerName, inbound).adapter; } -/** Whether the final provider/model pair can actually publish/send OpenAI service tiers. */ +/** Whether the final provider/model pair can publish/send OpenAI service tiers. */ export function canForwardServiceTierForModel( provider: ServiceTierCapabilityProvider, modelId: string, @@ -121,9 +204,8 @@ export function canForwardServiceTierForModel( } /** - * Return the tri-state capability after resolving the model's final wire adapter. - * `false` means either an explicit provider/model denial or an adapter that cannot carry the - * field; `undefined` keeps the existing conservative contract for an unclassified OpenAI wire. + * Compatibility projection for catalog, routing, and fingerprint consumers. The new + * resolver carries richer eligibility internally while preserving the old tri-state bytes. */ export function serviceTierSupportForModel( provider: ServiceTierCapabilityProvider, @@ -131,13 +213,15 @@ export function serviceTierSupportForModel( providerName?: string, inbound: InboundWire = "responses", ): boolean | undefined { - const adapter = providerName === undefined - ? provider.adapter - : serviceTierAdapterForModel(providerName, provider, modelId, inbound); - if (!SERVICE_TIER_ADAPTERS.has(adapter)) return false; - // Treat the Chat serializer decision as authoritative so catalog metadata, routing - // evidence, fast-mode injection, and caller-tier stripping cannot claim support that the - // final request builder will omit. A provider-wide false and an exact false stay closed. - if (adapter === "openai-chat" && !canSerializeServiceTierForChatModel(provider, modelId)) return false; - return supportsServiceTierForModel(provider, modelId); + const policy = fastPolicyForModel(provider, modelId, providerName, inbound); + return serviceTierSupportFromPolicy(policy); +} + +/** Compatibility projection shared by catalog, routing, and request logging. */ +export function serviceTierSupportFromPolicy( + policy: Pick, +): boolean | undefined { + if (policy.eligibility === "eligible") return true; + if (policy.eligibility === "unclassified") return undefined; + return false; } diff --git a/src/router.ts b/src/router.ts index 6dcb00fba3..38668b754b 100644 --- a/src/router.ts +++ b/src/router.ts @@ -341,6 +341,15 @@ export function routedProviderConfig(providerName: string, provider: OcxProvider && registryEntry.requiresAdjacentResponsesToolResults !== undefined ? { requiresAdjacentResponsesToolResults: registryEntry.requiresAdjacentResponsesToolResults } : {}), + ...(provider.fastWire === undefined && registryEntry.fastWire !== undefined + ? { + fastWire: registryEntry.fastWire === null ? null : { + ...registryEntry.fastWire, + canonicalToWire: { ...registryEntry.fastWire.canonicalToWire }, + ...(registryEntry.fastWire.betas ? { betas: [...registryEntry.fastWire.betas] } : {}), + }, + } + : {}), ...(provider.supportsServiceTier === undefined && registryEntry.supportsServiceTier !== undefined ? { supportsServiceTier: registryEntry.supportsServiceTier } : {}), diff --git a/src/routing/compatibility/behavior.ts b/src/routing/compatibility/behavior.ts index 5eaa46c4fc..87abca2282 100644 --- a/src/routing/compatibility/behavior.ts +++ b/src/routing/compatibility/behavior.ts @@ -1,6 +1,7 @@ import type { OcxConfig, OcxProviderConfig } from "../../types"; -import { PROVIDER_REGISTRY, type ProviderAuthKind } from "../../providers/registry"; +import { PROVIDER_REGISTRY } from "../../providers/registry"; import { serviceTierSupportForModel } from "../../providers/service-tier"; +import { resolveProviderAuthTransport } from "../../providers/fastwire"; import { localFingerprint } from "../../lab/digest"; import type { LabBehaviorSource, LabBehaviorValues } from "../../lab/live/types"; @@ -57,18 +58,6 @@ function nonCredentialHeaderDigest( return localFingerprint("nonCredentialHeaders", rows, installationSalt); } -function authTransportFor( - effective: OcxProviderConfig, - adapter: string, - mode: ProviderAuthKind, -): string { - if (mode === "oauth") return "oauth_bearer"; - if (mode === "forward") return "forwarded_authorization"; - if (mode === "local") return "none"; - if (adapter === "anthropic" && effective.apiKeyTransport !== "bearer") return "x_api_key"; - return "authorization_bearer"; -} - function effectiveOpenRouterRouting(effective: OcxProviderConfig, modelId: string) { return effective.modelOpenRouterRouting?.[modelId] ?? effective.openRouterRouting; } @@ -115,7 +104,10 @@ export function resolveProductionBehaviorValues( effective.modelSuffixBracketStrip === true ? "bracket_strip" : "none", ), "auth.mode": behaviorRow("provider_config", authMode), - "auth.transport": behaviorRow("provider_config", authTransportFor(effective, adapter, authMode)), + "auth.transport": behaviorRow( + "provider_config", + resolveProviderAuthTransport(adapter, authMode, effective.apiKeyTransport), + ), "responses.stateful": behaviorRow("provider_config", effective.statelessResponses !== true), "responses.serviceTier": behaviorRow( "provider_config", diff --git a/src/server/responses/core.ts b/src/server/responses/core.ts index 8df2fc672d..caaba352a6 100644 --- a/src/server/responses/core.ts +++ b/src/server/responses/core.ts @@ -121,7 +121,12 @@ import { createTranslatorBudget, isTranslatorBudgetExceededError, type Translato import { listOpenAiForwardSidecarCandidates, resolveFirstUsableOpenAiSidecar, type ResolvedOpenAiForwardSidecar } from "../../providers/openai-sidecar"; import { isCanonicalOpenAiForwardProvider, OPENAI_CODEX_PROVIDER_ID } from "../../providers/openai-tiers"; import { providerContextCap } from "../../providers/context-cap"; -import { SERVICE_TIER_ADAPTERS, serviceTierSupportForModel } from "../../providers/service-tier"; +import { + fastPolicyForModel, + serviceTierSupportFromPolicy, + SERVICE_TIER_ADAPTERS, +} from "../../providers/service-tier"; +import { decideTier, tierValueAfterDecision, type ResolvedFastPolicy } from "../../providers/fastwire"; import { RequestPacingQueueOverloadError, waitForProviderRequestSlot, @@ -966,6 +971,23 @@ const UNREADABLE_ENCRYPTED_AGENT_TASK_MESSAGE = const MAX_UPSTREAM_JSON_BODY_BYTES = 32 * 1024 * 1024; const UPSTREAM_JSON_BODY_TOTAL_TIMEOUT_MS = 180_000; const UPSTREAM_JSON_BODY_INACTIVITY_TIMEOUT_MS = 30_000; +const MAX_FAST_WIRE_CAPABILITY_WARNINGS = 256; +const warnedFastWireCapabilityGaps = new Set(); + +function warnFastWireCapabilityGap(providerName: string, modelId: string): void { + const safeProvider = redactSecretString(providerName); + const safeModel = redactSecretString(modelId); + const key = `${safeProvider}\0${safeModel}`; + if (warnedFastWireCapabilityGaps.has(key)) return; + if (warnedFastWireCapabilityGaps.size >= MAX_FAST_WIRE_CAPABILITY_WARNINGS) { + const oldest = warnedFastWireCapabilityGaps.values().next().value; + if (oldest !== undefined) warnedFastWireCapabilityGaps.delete(oldest); + } + warnedFastWireCapabilityGaps.add(key); + console.warn( + `[opencodex] Fast policy for ${safeProvider}/${safeModel} has service-tier capability but no Fast wire; preserving only caller-permitted tier behavior`, + ); +} export const UPSTREAM_JSON_BODY_READ_OPTIONS = { maxBytes: MAX_UPSTREAM_JSON_BODY_BYTES, totalTimeoutMs: UPSTREAM_JSON_BODY_TOTAL_TIMEOUT_MS, @@ -1151,23 +1173,20 @@ async function applyFinalRouteRequestNormalization(args: { logCtx.preserveResolvedModelFromRoute = true; } - // Fast mode override only where the final provider/model route explicitly documents - // service-tier support. The same model-scoped resolver is used by catalog generation. - const modelServiceTierSupport = serviceTierSupportForModel( + // Resolve Fast policy after the final route/wire settles. A1 records the decision on parsed + // options; the Responses adapter owns the final outbound body write. + const fastPolicy = fastPolicyForModel( route.provider, route.modelId, route.providerName, inboundWire, ); - if (config.fastMode !== undefined - && SERVICE_TIER_ADAPTERS.has(route.provider.adapter) - && modelServiceTierSupport === true) { - const tier = config.fastMode ? "priority" : undefined; - if (parsed._rawBody && typeof parsed._rawBody === "object") { - if (tier) (parsed._rawBody as Record).service_tier = tier; - else delete (parsed._rawBody as Record).service_tier; - } - parsed.options.serviceTier = tier; + const modelServiceTierSupport = serviceTierSupportFromPolicy(fastPolicy); + const callerTier = parsed.options.serviceTier; + parsed.options.tierDecision = decideTier(fastPolicy, config.fastMode, callerTier); + parsed.options.serviceTier = tierValueAfterDecision(parsed.options.tierDecision, callerTier); + if (fastPolicy.capability === true && fastPolicy.fastWire === null) { + warnFastWireCapabilityGap(route.providerName, route.modelId); } applyServiceTierGate( route.provider, @@ -1176,6 +1195,7 @@ async function applyFinalRouteRequestNormalization(args: { route.modelId, route.providerName, inboundWire, + fastPolicy, ); if (modelServiceTierSupport === false) { logCtx.requestedServiceTier = undefined; @@ -1576,16 +1596,17 @@ export function applyServiceTierGate( modelId?: string, providerName?: string, inbound: InboundWire = "responses", + resolvedPolicy?: ResolvedFastPolicy, ): void { // A direct unit caller without a model id retains the historical tri-state behavior for // adapters outside the OpenAI service-tier family. Once a model is known, resolve the final // model adapter as well: an explicit override to Anthropic (or another non-OpenAI wire) must // not carry a caller-supplied `service_tier` through a route that cannot forward it. if (modelId === undefined && !SERVICE_TIER_ADAPTERS.has(provider.adapter)) return; - const support = modelId === undefined - ? provider.supportsServiceTier - : serviceTierSupportForModel(provider, modelId, providerName, inbound); - if (support !== false) return; + const forwardCallerTier = modelId === undefined + ? provider.supportsServiceTier !== false + : (resolvedPolicy ?? fastPolicyForModel(provider, modelId, providerName, inbound)).forwardCallerTier; + if (forwardCallerTier) return; if (rawBody && typeof rawBody === "object") { delete (rawBody as Record).service_tier; } diff --git a/src/types.ts b/src/types.ts index 565118b287..344dddc461 100644 --- a/src/types.ts +++ b/src/types.ts @@ -296,6 +296,8 @@ export interface OcxRequestOptions { reasoning?: string; hideThinkingSummary?: boolean; serviceTier?: string; + /** Final outbound tier action, resolved after the provider/model wire is settled. */ + tierDecision?: TierDecision; presencePenalty?: number; frequencyPenalty?: number; /** Responses prompt-cache affinity key. Passthrough preserves it via _rawBody; routed adapters do not consume it unless their upstream wire supports it. */ @@ -1310,6 +1312,21 @@ export interface ProviderRequestPacingConfig extends RequestPacingRule { models?: Record; } +export interface FastWire { + kind: "service-tier" | "anthropic-speed"; + /** Canonical tier name to upstream wire spelling. */ + canonicalToWire: Readonly>; + /** Policy for non-canonical caller-provided tier values. */ + foreignCallerTiers: "verbatim" | "drop"; + /** Anthropic speed headers/betas reserved for the later wire implementation. */ + betas?: readonly string[]; +} + +export type TierDecision = + | { readonly kind: "forward-caller" } + | { readonly kind: "drop" } + | { readonly kind: "set"; readonly value: string }; + /** * One configured provider entry. `authMode` (default `"key"`) decides whether same-target 429 * retries are allowed; OAuth/forward credentials and local runtimes are never replayed. @@ -1333,6 +1350,11 @@ export interface OcxProviderConfig { * as before. */ modelAdapters?: Record; + /** + * Fast-wire declaration. `null` explicitly disables adapter-derived defaults; + * absence derives from the final model adapter. + */ + fastWire?: FastWire | null; baseUrl: string; /** * Optional relative resource path for key-auth openai-responses requests. Must start with `/` @@ -1738,6 +1760,13 @@ const ANTHROPIC_WIRE_MODELS: Record> = { "opencode-go": new Set(["minimax-m2.5", "minimax-m2.7", "minimax-m3"]), }; +/** Detached provider-local hard-pin table for pure wire-policy resolution. */ +export function captureWireAdapterHardPins(providerName: string): Readonly> { + const models = ANTHROPIC_WIRE_MODELS[providerName]; + if (!models) return Object.freeze({}); + return Object.freeze(Object.fromEntries([...models].map(modelId => [modelId, "anthropic"]))); +} + /** * True when the upstream speaks exactly one wire for this model, so a configured * override must not apply. diff --git a/tests/config.test.ts b/tests/config.test.ts index b949b9bd8b..2a5def0bdd 100644 --- a/tests/config.test.ts +++ b/tests/config.test.ts @@ -362,6 +362,44 @@ describe("opencodex config defaults", () => { expect(backupNames()).toEqual([]); }); + test("an inherited FastWire conflict warns without wiping persisted providers or keys", () => { + writeConfig({ + port: 12345, + defaultProvider: "openai-apikey", + providers: { + "openai-apikey": { + adapter: "openai-responses", + baseUrl: "https://api.openai.com/v1", + authMode: "key", + fastWire: null, + }, + }, + apiKeys: [{ id: "key-1", name: "default", key: "ocx_persisted", createdAt: "2026-07-28T00:00:00.000Z" }], + }); + const warnSpy = spyOn(console, "warn").mockImplementation(() => {}); + + try { + const loaded = loadConfig(); + const diagnostics = readConfigDiagnostics(); + + expect(loaded).toMatchObject({ + port: 12345, + defaultProvider: "openai-apikey", + providers: { "openai-apikey": { fastWire: null } }, + apiKeys: [expect.objectContaining({ id: "key-1", key: "ocx_persisted" })], + }); + expect(diagnostics).toMatchObject({ + source: "file", + error: null, + warnings: [expect.stringContaining("fastWire=null overrides service-tier capability")], + }); + expect(backupNames()).toEqual([]); + expect(warnSpy).toHaveBeenCalledWith(expect.stringContaining("persisted providers and API keys were preserved")); + } finally { + warnSpy.mockRestore(); + } + }); + test("a non-string experimentalRealtimeWsBaseUrl degrades to unset without wiping config", () => { // The sideband builder calls overrideBaseUrl?.trim(); a boolean here would crash // it, so the schema degrades the field instead of rejecting the whole config. diff --git a/tests/fastwire-characterization-routing.test.ts b/tests/fastwire-characterization-routing.test.ts new file mode 100644 index 0000000000..c1f4322296 --- /dev/null +++ b/tests/fastwire-characterization-routing.test.ts @@ -0,0 +1,132 @@ +import { describe, expect, test } from "bun:test"; +import { applyCatalogModelMetadata } from "../src/codex/catalog/effort"; +import type { CatalogModel, RawEntry } from "../src/codex/catalog/parsing"; +import { candidateCapabilityEvidence } from "../src/routing/capability"; +import { resolveProductionBehaviorValues } from "../src/routing/compatibility/behavior"; +import { evaluatePolicyProfile } from "../src/routing/evaluator"; +import type { OcxConfig, OcxProviderConfig } from "../src/types"; + +describe("FastWire characterization: routing profile service-tier evidence", () => { + test("require.serviceTier sees supportsServiceTier=true plus chatServiceTier=false as unsupported", () => { + const provider: OcxProviderConfig = { + adapter: "openai-chat", + baseUrl: "https://chat-no-tier.example.test/v1", + authMode: "key", + apiKey: "sk-test", + supportsServiceTier: true, + chatServiceTier: false, + }; + const config = { + port: 0, + defaultProvider: "chat-no-tier", + providers: { "chat-no-tier": provider }, + routingProfiles: { + fast: { + candidates: [{ provider: "chat-no-tier", model: "model" }], + require: { serviceTier: "supported" }, + }, + }, + } as OcxConfig; + const capability = candidateCapabilityEvidence(config, "chat-no-tier", "model"); + + expect(capability.serviceTier).toBe("unsupported"); + const result = evaluatePolicyProfile(config, "fast", {}, [{ + provider: "chat-no-tier", + model: "model", + capability, + }]); + expect(result.candidates[0]).toMatchObject({ + eligible: false, + requirements: [{ + id: "service-tier", + expected: "supported", + actual: "unsupported", + outcome: "unsatisfied", + }], + }); + expect(result.selectedIndex).toBeNull(); + }); +}); + +describe("FastWire characterization: compatibility fingerprint projection", () => { + const cases: Array<{ label: string; expected: boolean; provider: OcxProviderConfig }> = [ + { + label: "supported", + expected: true, + provider: { + adapter: "openai-responses", + baseUrl: "https://supported.example.test/v1", + supportsServiceTier: true, + }, + }, + { + label: "unsupported", + expected: false, + provider: { + adapter: "openai-responses", + baseUrl: "https://unsupported.example.test/v1", + supportsServiceTier: false, + }, + }, + ]; + + test.each(cases)("projects $label service-tier behavior", ({ label, expected, provider }) => { + const config = { + port: 0, + defaultProvider: label, + fastMode: true, + providers: { [label]: provider }, + } as OcxConfig; + const values = resolveProductionBehaviorValues( + config, + label, + "model", + provider, + "fastwire-characterization-salt", + ); + + expect(values?.["responses.serviceTier"]).toEqual({ + source: "provider_config", + value: expected, + }); + expect(values?.["runtime.fastMode"]).toEqual({ + source: "global_config", + value: true, + }); + }); +}); + +describe("FastWire characterization: catalog service-tier bytes", () => { + function apply(supportsServiceTier?: boolean): RawEntry { + const entry: RawEntry = {}; + const model: CatalogModel = { + id: "model", + provider: "fixture", + ...(supportsServiceTier === undefined ? {} : { supportsServiceTier }), + }; + applyCatalogModelMetadata(entry, model); + return entry; + } + + test("supportsServiceTier=true emits the current narrow byte golden", () => { + const entry = apply(true); + const projection = { + default_service_tier: entry.default_service_tier, + service_tiers: entry.service_tiers, + additional_speed_tiers: entry.additional_speed_tiers, + }; + expect(JSON.stringify(projection)).toBe( + '{"default_service_tier":null,"service_tiers":[{"id":"priority","name":"Fast","description":"1.5x speed, increased usage"}],"additional_speed_tiers":["fast"]}', + ); + }); + + test.each([ + { label: "false", supportsServiceTier: false }, + { label: "unset", supportsServiceTier: undefined }, + ])("supportsServiceTier=$label omits all catalog tier fields", ({ supportsServiceTier }) => { + const entry = apply(supportsServiceTier); + expect(entry).not.toHaveProperty("default_service_tier"); + expect(entry).not.toHaveProperty("service_tiers"); + expect(entry).not.toHaveProperty("additional_speed_tiers"); + }); +}); diff --git a/tests/fastwire-characterization-wire.test.ts b/tests/fastwire-characterization-wire.test.ts new file mode 100644 index 0000000000..5f1efc39bc --- /dev/null +++ b/tests/fastwire-characterization-wire.test.ts @@ -0,0 +1,295 @@ +import { afterEach, describe, expect, spyOn, test } from "bun:test"; +import { buildOpenAIChatPassthroughRequest } from "../src/adapters/openai-chat"; +import { chatCompletionsToResponsesBody } from "../src/chat/inbound"; +import * as adapterResolveModule from "../src/server/adapter-resolve"; +import type { RequestLogContext } from "../src/server/request-log"; +import { handleResponses } from "../src/server/responses/core"; +import type { OcxConfig, OcxProviderConfig } from "../src/types"; + +const originalFetch = globalThis.fetch; + +afterEach(() => { + globalThis.fetch = originalFetch; +}); + +async function driveResponses(args: { + provider: OcxProviderConfig; + providerName?: string; + model?: string; + callerTier?: string; + fastMode?: boolean; +}): Promise<{ outboundBody: Record; logCtx: RequestLogContext }> { + const providerName = args.providerName ?? "fastwire-fixture"; + const model = args.model ?? "model"; + const bodies: Record[] = []; + globalThis.fetch = (async (_input: RequestInfo | URL, init?: RequestInit) => { + bodies.push(JSON.parse(String(init?.body ?? "{}")) as Record); + return new Response("data: [DONE]\n\n", { + status: 200, + headers: { "content-type": "text/event-stream" }, + }); + }) as typeof fetch; + + const config = { + port: 0, + defaultProvider: providerName, + providers: { [providerName]: args.provider }, + ...(args.fastMode === undefined ? {} : { fastMode: args.fastMode }), + } as OcxConfig; + const logCtx: RequestLogContext = { model: "", provider: "" }; + const requestBody = { + model: `${providerName}/${model}`, + input: "ping", + stream: true, + ...(args.callerTier === undefined ? {} : { service_tier: args.callerTier }), + }; + + await handleResponses( + new Request("http://localhost/v1/responses", { + method: "POST", + headers: { "content-type": "application/json" }, + body: JSON.stringify(requestBody), + }), + config, + logCtx, + {}, + ); + + expect(bodies).toHaveLength(1); + return { outboundBody: bodies[0]!, logCtx }; +} + +const supportedResponsesProvider = (): OcxProviderConfig => ({ + adapter: "openai-responses", + baseUrl: "https://supported.example.test/v1", + authMode: "key", + apiKey: "sk-test", + supportsServiceTier: true, +}); + +const unclassifiedResponsesProvider = (): OcxProviderConfig => ({ + adapter: "openai-responses", + baseUrl: "https://unclassified.example.test/v1", + authMode: "key", + apiKey: "sk-test", +}); + +describe("FastWire characterization: supported-route fastMode tri-state", () => { + test("fastMode=true overrides caller flex with priority", async () => { + const { outboundBody } = await driveResponses({ + provider: supportedResponsesProvider(), + callerTier: "flex", + fastMode: true, + }); + expect(outboundBody.service_tier).toBe("priority"); + }); + + test("fastMode=false removes the caller tier", async () => { + const { outboundBody } = await driveResponses({ + provider: supportedResponsesProvider(), + callerTier: "turbo-x", + fastMode: false, + }); + expect(outboundBody).not.toHaveProperty("service_tier"); + }); + + test("fastMode=undefined preserves caller flex", async () => { + const { outboundBody } = await driveResponses({ + provider: supportedResponsesProvider(), + callerTier: "flex", + }); + expect(outboundBody.service_tier).toBe("flex"); + }); + + test("a capability-without-wire warning is redacted and throttled per provider/model", async () => { + const providerName = `sk-ant-api03-${"A".repeat(40)}`; + const warnSpy = spyOn(console, "warn").mockImplementation(() => {}); + const provider: OcxProviderConfig = { + ...supportedResponsesProvider(), + fastWire: null, + }; + + try { + await driveResponses({ provider, providerName, callerTier: "flex" }); + await driveResponses({ provider, providerName, callerTier: "flex" }); + const fastWireWarnings = warnSpy.mock.calls + .map(call => String(call[0])) + .filter(message => message.includes("Fast policy")); + expect(fastWireWarnings).toHaveLength(1); + expect(fastWireWarnings[0]).not.toContain(providerName); + } finally { + warnSpy.mockRestore(); + } + }); +}); + +describe("FastWire characterization: resolved model adapter controls fast override", () => { + test.each([ + { fastMode: true, expectedTier: "priority" }, + { fastMode: false, expectedTier: undefined }, + ])( + "anthropic provider overridden to openai-chat emits $expectedTier with fastMode=$fastMode", + async ({ fastMode, expectedTier }) => { + const { outboundBody } = await driveResponses({ + provider: { + adapter: "anthropic", + baseUrl: "https://mixed-wire.example.test/v1", + authMode: "key", + apiKey: "sk-test", + modelAdapters: { model: "openai-chat" }, + supportsServiceTier: true, + chatServiceTier: true, + }, + callerTier: "flex", + fastMode, + }); + if (expectedTier === undefined) expect(outboundBody).not.toHaveProperty("service_tier"); + else expect(outboundBody.service_tier).toBe(expectedTier); + }, + ); +}); + +describe("FastWire characterization: unclassified support matrix", () => { + const cells = ([true, false, undefined] as const).flatMap(fastMode => + (["priority", "fast", "flex"] as const).map(callerTier => ({ fastMode, callerTier })) + ); + + test.each(cells)( + "support=undefined preserves caller $callerTier with fastMode=$fastMode", + async ({ fastMode, callerTier }) => { + const { outboundBody } = await driveResponses({ + provider: unclassifiedResponsesProvider(), + callerTier, + fastMode, + }); + expect(outboundBody.service_tier).toBe(callerTier); + }, + ); +}); + +describe("FastWire characterization: exact-model Chat tier forwarding", () => { + test.each(["flex", "turbo-x"])( + "exact model true forwards foreign caller tier %s without chatServiceTier", + async callerTier => { + const { outboundBody } = await driveResponses({ + provider: { + adapter: "openai-chat", + baseUrl: "https://chat.example.test/v1", + authMode: "key", + apiKey: "sk-test", + modelSupportsServiceTier: { model: true }, + }, + callerTier, + }); + expect(outboundBody.service_tier).toBe(callerTier); + }, + ); +}); + +describe("FastWire characterization: requestedServiceTier timing", () => { + test("records the raw caller tier when fastMode overrides the wire tier", async () => { + const { outboundBody, logCtx } = await driveResponses({ + provider: supportedResponsesProvider(), + callerTier: "flex", + fastMode: true, + }); + expect(outboundBody.service_tier).toBe("priority"); + expect(logCtx.requestedServiceTier).toBe("flex"); + }); + + test("clears the caller tier after an unsupported route strips it", async () => { + const { outboundBody, logCtx } = await driveResponses({ + provider: { + ...supportedResponsesProvider(), + supportsServiceTier: false, + }, + callerTier: "priority", + }); + expect(outboundBody).not.toHaveProperty("service_tier"); + expect(logCtx.requestedServiceTier).toBeUndefined(); + }); +}); + +describe("FastWire characterization: rawBody observation point", () => { + test("Responses writes the decision outbound without changing parsed._rawBody", async () => { + let adapterRawBody: Record | undefined; + let outboundBody: Record | undefined; + const actualResolveAdapter = adapterResolveModule.resolveAdapter; + const adapterSpy = spyOn(adapterResolveModule, "resolveAdapter").mockImplementation((provider, cacheRetention) => { + const actualAdapter = actualResolveAdapter(provider, cacheRetention); + return { + ...actualAdapter, + buildRequest(parsed, incoming) { + adapterRawBody = parsed._rawBody as Record; + const request = actualAdapter.buildRequest!(parsed, incoming); + outboundBody = JSON.parse(request.body) as Record; + return request; + }, + }; + }); + + try { + globalThis.fetch = (async () => new Response("data: [DONE]\n\n", { + status: 200, + headers: { "content-type": "text/event-stream" }, + })) as typeof fetch; + const providerName = "fastwire-raw-body"; + const config = { + port: 0, + defaultProvider: providerName, + fastMode: true, + providers: { [providerName]: supportedResponsesProvider() }, + } as OcxConfig; + const request = new Request("http://localhost/v1/responses", { + method: "POST", + headers: { "content-type": "application/json" }, + body: JSON.stringify({ + model: `${providerName}/model`, + input: "ping", + stream: true, + service_tier: "flex", + }), + }); + + await handleResponses(request, config, { model: "", provider: "" }, {}); + expect(outboundBody?.service_tier).toBe("priority"); + expect(adapterRawBody?.service_tier).toBe("flex"); + } finally { + adapterSpy.mockRestore(); + } + }); +}); + +describe("FastWire characterization: known bugs", () => { + test("characterization (known bug): native chat passthrough ignores exact-model false", () => { + const request = buildOpenAIChatPassthroughRequest( + { + adapter: "openai-chat", + baseUrl: "https://native-chat.example.test/v1", + authMode: "key", + apiKey: "sk-test", + supportsServiceTier: true, + chatServiceTier: true, + modelSupportsServiceTier: { model: false }, + }, + { + model: "model", + messages: [{ role: "user", content: "ping" }], + service_tier: "flex", + }, + "model", + false, + ); + const body = JSON.parse(request.body) as Record; + expect(body.service_tier).toBe("flex"); + }); + + test("characterization (known bug): chat-to-responses conversion drops service_tier", () => { + const body = chatCompletionsToResponsesBody({ + model: "model", + messages: [{ role: "user", content: "ping" }], + service_tier: "priority", + }); + expect(body).not.toHaveProperty("service_tier"); + }); +}); diff --git a/tests/fastwire-policy.test.ts b/tests/fastwire-policy.test.ts new file mode 100644 index 0000000000..bcd94c8653 --- /dev/null +++ b/tests/fastwire-policy.test.ts @@ -0,0 +1,487 @@ +import { describe, expect, test } from "bun:test"; + +import { createResponsesPassthroughAdapter } from "../src/adapters/openai-responses"; +import { validateConfigCandidate } from "../src/config"; +import { + canonicalFastTierMarker, + decideTier, + legacyChatEligibility, + resolveFastPolicy, + tierValueAfterDecision, + type FastPolicyAuthority, + type ResolvedFastPolicy, +} from "../src/providers/fastwire"; +import { fastPolicyForModel } from "../src/providers/service-tier"; +import { PROVIDER_REGISTRY, providerRegistryFastWireError } from "../src/providers/registry"; +import type { FastWire, OcxConfig, OcxParsedRequest, TierDecision } from "../src/types"; +import { withTestTranslatorBudget } from "./helpers/translator-budget"; + +const MODEL = "model"; +const SERVICE_WIRE: FastWire = { + kind: "service-tier", + canonicalToWire: { priority: "priority" }, + foreignCallerTiers: "verbatim", +}; + +type AdapterSource = "hard-pin" | "override" | "registry-default" | "provider-adapter"; +type DeclarationState = "undefined" | "null" | "explicit"; +type CapabilityState = "false" | "undefined" | "true"; + +function authorityForMatrix(args: { + source: AdapterSource; + declaration: DeclarationState; + overrideAllowed: boolean; + capability: CapabilityState; + legacyChatEligible: boolean; +}): FastPolicyAuthority { + const providerAdapter = args.source === "provider-adapter" ? "openai-chat" : "openai-responses"; + return { + providerAdapter, + fastWireDeclaration: args.declaration === "undefined" + ? undefined + : args.declaration === "null" ? null : SERVICE_WIRE, + modelWireOverrideAllowed: args.overrideAllowed, + authTransport: "authorization_bearer", + capability: { + ...(args.capability === "undefined" ? {} : { provider: args.capability === "true" }), + models: {}, + chatServiceTier: args.legacyChatEligible, + }, + modelAdapters: args.source === "hard-pin" || args.source === "override" + ? { [MODEL]: args.source === "override" ? "openai-chat" : "openai-responses" } + : {}, + hardPins: args.source === "hard-pin" ? { [MODEL]: "openai-chat" } : {}, + registryWireDefaults: args.source === "hard-pin" || args.source === "override" + ? { [MODEL]: "openai-responses" } + : args.source === "registry-default" ? { [MODEL]: "openai-chat" } : {}, + }; +} + +const policyMatrix = (["undefined", "null", "explicit"] as const).flatMap(declaration => + ([false, true] as const).flatMap(overrideAllowed => + (["hard-pin", "override", "registry-default", "provider-adapter"] as const).flatMap(source => + (["false", "undefined", "true"] as const).flatMap(capability => + ([false, true] as const).map(legacyChatEligible => ({ + declaration, + overrideAllowed, + source, + capability, + legacyChatEligible, + })), + ), + ), + ), +); + +describe("resolveFastPolicy matrix", () => { + test.each(policyMatrix)( + "$declaration declaration, overrideAllowed=$overrideAllowed, $source, capability=$capability, legacy=$legacyChatEligible", + row => { + const authority = authorityForMatrix(row); + const policy = resolveFastPolicy(authority, MODEL); + const overrideCanWin = row.overrideAllowed && row.source !== "provider-adapter"; + const expectedAdapter = row.source === "hard-pin" + ? "openai-chat" + : overrideCanWin ? "openai-chat" + : row.source === "provider-adapter" ? "openai-chat" : "openai-responses"; + const capability = row.capability === "undefined" ? undefined : row.capability === "true"; + const chatEligible = expectedAdapter !== "openai-chat" || row.legacyChatEligible; + const wireAvailable = row.declaration !== "null"; + const expectedEligibility: ResolvedFastPolicy["eligibility"] = capability === false + ? "capability-unsupported" + : !wireAvailable + ? "wire-unavailable" + : !chatEligible + ? "capability-unsupported" + : capability === undefined ? "unclassified" : "eligible"; + + expect(policy.adapter).toBe(expectedAdapter); + expect(policy.capability).toBe(capability); + expect(policy.eligibility).toBe(expectedEligibility); + expect(policy.fastWire === null ? null : policy.fastWire?.kind).toBe( + row.declaration === "null" ? null : "service-tier", + ); + expect(policy.forwardCallerTier).toBe(capability !== false && chatEligible); + }, + ); + + test("registry defaults retain their inbound constraint", () => { + const authority: FastPolicyAuthority = { + ...authorityForMatrix({ + source: "registry-default", + declaration: "undefined", + overrideAllowed: true, + capability: "true", + legacyChatEligible: true, + }), + providerAdapter: "openai-responses", + registryWireDefaults: { [MODEL]: { wire: "openai-chat", inbound: ["chat"] } }, + }; + expect(resolveFastPolicy(authority, MODEL, "chat").adapter).toBe("openai-chat"); + expect(resolveFastPolicy(authority, MODEL, "responses").adapter).toBe("openai-responses"); + }); + + test("hard pins and configured overrides retain exact runtime model-key semantics", () => { + const authority: FastPolicyAuthority = { + ...authorityForMatrix({ + source: "provider-adapter", + declaration: "undefined", + overrideAllowed: true, + capability: "true", + legacyChatEligible: true, + }), + providerAdapter: "openai-responses", + modelAdapters: { Model: "openai-chat" }, + hardPins: { Pinned: "anthropic" }, + }; + expect(resolveFastPolicy(authority, "model").adapter).toBe("openai-responses"); + expect(resolveFastPolicy(authority, "Model").adapter).toBe("openai-chat"); + expect(resolveFastPolicy(authority, "pinned").adapter).toBe("openai-responses"); + expect(resolveFastPolicy(authority, "Pinned").adapter).toBe("anthropic"); + }); + + test("invalid configured overrides fall through to the captured registry default", () => { + const authority: FastPolicyAuthority = { + ...authorityForMatrix({ + source: "registry-default", + declaration: "undefined", + overrideAllowed: true, + capability: "true", + legacyChatEligible: true, + }), + modelAdapters: { [MODEL]: "anthropic" }, + registryWireDefaults: { [MODEL]: "openai-chat" }, + }; + expect(resolveFastPolicy(authority, MODEL).adapter).toBe("openai-chat"); + }); + + test("registry defaults do not move a provider outside the allowed base-wire family", () => { + const authority: FastPolicyAuthority = { + ...authorityForMatrix({ + source: "registry-default", + declaration: "undefined", + overrideAllowed: true, + capability: "true", + legacyChatEligible: true, + }), + providerAdapter: "anthropic", + registryWireDefaults: { [MODEL]: "openai-chat" }, + }; + expect(resolveFastPolicy(authority, MODEL).adapter).toBe("anthropic"); + }); + + test("anthropic-speed has no A1 adapter mapping", () => { + const policy = resolveFastPolicy({ + ...authorityForMatrix({ + source: "provider-adapter", + declaration: "explicit", + overrideAllowed: true, + capability: "true", + legacyChatEligible: true, + }), + fastWireDeclaration: { + kind: "anthropic-speed", + canonicalToWire: { priority: "fast" }, + foreignCallerTiers: "drop", + betas: ["fast-beta"], + }, + }, MODEL); + expect(policy).toMatchObject({ eligibility: "wire-unavailable", forwardCallerTier: false }); + }); + + test("an incompatible hard pin reports pin-unavailable", () => { + const policy = resolveFastPolicy({ + ...authorityForMatrix({ + source: "provider-adapter", + declaration: "explicit", + overrideAllowed: true, + capability: "true", + legacyChatEligible: true, + }), + hardPins: { [MODEL]: "anthropic" }, + }, MODEL); + expect(policy).toMatchObject({ adapter: "anthropic", eligibility: "pin-unavailable" }); + }); + + test("a missing provider name preserves the legacy provider-adapter short circuit", () => { + const provider = { + adapter: "anthropic", + baseUrl: "https://fixture.example/v1", + modelAdapters: { [MODEL]: "openai-responses" }, + supportsServiceTier: true, + } as const; + expect(fastPolicyForModel(provider, MODEL)).toMatchObject({ + adapter: "anthropic", + capability: true, + eligibility: "wire-unavailable", + }); + expect(fastPolicyForModel(provider, MODEL, "fixture")).toMatchObject({ + adapter: "openai-responses", + capability: true, + eligibility: "eligible", + }); + }); +}); + +describe("legacyChatEligibility", () => { + test.each([ + { + label: "chatServiceTier opt-in", + provider: undefined, + models: {}, + chatServiceTier: true, + expected: true, + }, + { + label: "case-insensitive exact-model opt-in", + provider: undefined, + models: { MODEL: true }, + chatServiceTier: false, + expected: true, + }, + { + label: "provider false closes an exact-model opt-in", + provider: false, + models: { model: true }, + chatServiceTier: true, + expected: false, + }, + { + label: "exact false closes a provider Chat opt-in", + provider: true, + models: { model: false }, + chatServiceTier: true, + expected: false, + }, + ])("$label", ({ provider, models, chatServiceTier, expected }) => { + const authority = authorityForMatrix({ + source: "provider-adapter", + declaration: "undefined", + overrideAllowed: true, + capability: "undefined", + legacyChatEligible: false, + }); + expect(legacyChatEligibility({ + ...authority, + capability: { ...(provider === undefined ? {} : { provider }), models, chatServiceTier }, + }, MODEL)).toBe(expected); + }); +}); + +const tierGrid = ([false, undefined, true] as const).flatMap(support => + ([false, undefined, true] as const).flatMap(fastMode => + (["priority", "fast", "flex", undefined] as const).map(callerTier => ({ + support, + fastMode, + callerTier, + })), + ), +); + +function tierPolicy(support: boolean | undefined): ResolvedFastPolicy { + return { + capability: support, + eligibility: support === true ? "eligible" : support === false ? "capability-unsupported" : "unclassified", + adapter: "openai-responses", + fastWire: SERVICE_WIRE, + forwardCallerTier: support !== false, + }; +} + +describe("TierDecision state machine", () => { + test.each(tierGrid)( + "support=$support fastMode=$fastMode caller=$callerTier", + ({ support, fastMode, callerTier }) => { + const decision = decideTier(tierPolicy(support), fastMode, callerTier); + const expectedValue = support === false + ? undefined + : support === undefined ? callerTier + : fastMode === true ? "priority" : fastMode === false ? undefined : callerTier; + const expectedKind: TierDecision["kind"] = support === false || (support === true && fastMode === false) + ? "drop" + : support === true && fastMode === true ? "set" : "forward-caller"; + expect(decision.kind).toBe(expectedKind); + expect(tierValueAfterDecision(decision, callerTier)).toBe(expectedValue); + expect(canonicalFastTierMarker(callerTier)).toBe( + callerTier === "priority" || callerTier === "fast" ? "priority" : undefined, + ); + }, + ); + + test.each(([false, undefined, true] as const).flatMap(fastMode => + (["priority", "fast", "flex", undefined] as const).map(callerTier => ({ fastMode, callerTier })), + ))("true capability plus null wire preserves caller with fastMode=$fastMode caller=$callerTier", ({ fastMode, callerTier }) => { + const decision = decideTier({ + capability: true, + eligibility: "wire-unavailable", + adapter: "openai-responses", + fastWire: null, + forwardCallerTier: true, + }, fastMode, callerTier); + expect(decision).toEqual({ kind: "forward-caller" }); + expect(tierValueAfterDecision(decision, callerTier)).toBe(callerTier); + }); + + test.each(["Priority", "FAST", " fast "])("normalizes %s only into an internal marker", callerTier => { + expect(canonicalFastTierMarker(callerTier)).toBe("priority"); + expect(tierValueAfterDecision({ kind: "forward-caller" }, callerTier)).toBe(callerTier); + }); + + test.each([ + { callerTier: "priority", expected: { kind: "forward-caller" } }, + { callerTier: "fast", expected: { kind: "forward-caller" } }, + { callerTier: "flex", expected: { kind: "drop" } }, + { callerTier: undefined, expected: { kind: "forward-caller" } }, + ])("foreign-tier drop policy resolves caller=$callerTier to $expected.kind", ({ callerTier, expected }) => { + expect(decideTier({ + ...tierPolicy(true), + fastWire: { ...SERVICE_WIRE, foreignCallerTiers: "drop" }, + }, undefined, callerTier)).toEqual(expected); + }); + + test("unclassified capability keeps the full caller passthrough contract", () => { + expect(decideTier({ + ...tierPolicy(undefined), + fastWire: { ...SERVICE_WIRE, foreignCallerTiers: "drop" }, + }, true, "flex")).toEqual({ kind: "forward-caller" }); + }); +}); + +function configWithFastWire(fastWire: unknown, capability?: { provider?: boolean; exact?: boolean }): unknown { + return { + port: 10100, + defaultProvider: "fixture", + providers: { + fixture: { + adapter: "openai-responses", + baseUrl: "https://fixture.example/v1", + fastWire, + ...(capability?.provider === undefined ? {} : { supportsServiceTier: capability.provider }), + ...(capability?.exact === undefined ? {} : { modelSupportsServiceTier: { [MODEL]: capability.exact } }), + }, + }, + }; +} + +describe("FastWire config and registry validation", () => { + test("accepts a complete declaration and trims its wire values", () => { + const result = validateConfigCandidate(configWithFastWire({ + kind: "service-tier", + canonicalToWire: { priority: " priority ", flex: " flex " }, + foreignCallerTiers: "verbatim", + betas: [" beta-one "], + })); + expect(result.ok).toBe(true); + if (result.ok) { + expect((result.config as OcxConfig).providers.fixture?.fastWire).toEqual({ + kind: "service-tier", + canonicalToWire: { priority: "priority", flex: "flex" }, + foreignCallerTiers: "verbatim", + betas: ["beta-one"], + }); + } + }); + + test.each([ + { label: "closed kind", value: { kind: "future", canonicalToWire: { priority: "priority" }, foreignCallerTiers: "verbatim" } }, + { label: "missing priority", value: { kind: "service-tier", canonicalToWire: { fast: "fast" }, foreignCallerTiers: "verbatim" } }, + { label: "blank priority", value: { kind: "service-tier", canonicalToWire: { priority: " " }, foreignCallerTiers: "verbatim" } }, + { label: "overlong wire value", value: { kind: "service-tier", canonicalToWire: { priority: "x".repeat(65) }, foreignCallerTiers: "verbatim" } }, + { label: "duplicate wire values", value: { kind: "service-tier", canonicalToWire: { priority: "fast", other: " fast " }, foreignCallerTiers: "verbatim" } }, + { label: "blank beta", value: { kind: "anthropic-speed", canonicalToWire: { priority: "fast" }, foreignCallerTiers: "drop", betas: [" "] } }, + { label: "duplicate betas", value: { kind: "anthropic-speed", canonicalToWire: { priority: "fast" }, foreignCallerTiers: "drop", betas: ["one", " one "] } }, + { label: "too many betas", value: { kind: "anthropic-speed", canonicalToWire: { priority: "fast" }, foreignCallerTiers: "drop", betas: Array.from({ length: 17 }, (_, index) => `b${index}`) } }, + { label: "unknown declaration key", value: { kind: "service-tier", canonicalToWire: { priority: "priority" }, foreignCallerTiers: "verbatim", future: true } }, + ])("rejects $label", ({ value }) => { + expect(validateConfigCandidate(configWithFastWire(value)).ok).toBe(false); + }); + + test.each([ + { label: "provider capability", capability: { provider: true } }, + { label: "exact-model capability", capability: { exact: true } }, + ])("rejects null against $label", ({ capability }) => { + expect(validateConfigCandidate(configWithFastWire(null, capability)).ok).toBe(false); + }); + + test("provider-level false keeps null valid even with an exact-model true", () => { + expect(validateConfigCandidate(configWithFastWire(null, { provider: false, exact: true })).ok) + .toBe(true); + }); + + test("rejects null against an inherited registry capability", () => { + expect(validateConfigCandidate({ + port: 10100, + defaultProvider: "openai-apikey", + providers: { + "openai-apikey": { + adapter: "openai-responses", + baseUrl: "https://api.openai.com/v1", + authMode: "key", + fastWire: null, + }, + }, + }).ok).toBe(false); + }); + + test("provider-level false closes an inherited registry capability", () => { + expect(validateConfigCandidate({ + port: 10100, + defaultProvider: "openai-apikey", + providers: { + "openai-apikey": { + adapter: "openai-responses", + baseUrl: "https://api.openai.com/v1", + authMode: "key", + supportsServiceTier: false, + fastWire: null, + }, + }, + }).ok).toBe(true); + }); + + test("registry validation rejects the same null/capability conflict", () => { + expect(providerRegistryFastWireError({ fastWire: null, supportsServiceTier: true })) + .toContain("conflicts"); + expect(providerRegistryFastWireError({ fastWire: null, modelSupportsServiceTier: { [MODEL]: true } })) + .toContain("conflicts"); + expect(providerRegistryFastWireError({ + fastWire: null, + supportsServiceTier: false, + modelSupportsServiceTier: { [MODEL]: true }, + })).toBeNull(); + }); + + test("A1 adds no explicit registry FastWire declaration", () => { + expect(PROVIDER_REGISTRY.every(entry => entry.fastWire === undefined)).toBeTrue(); + }); +}); + +describe("Responses TierDecision immutability", () => { + test.each([ + { label: "set", decision: { kind: "set", value: "priority" } as TierDecision, expected: "priority" }, + { label: "drop", decision: { kind: "drop" } as TierDecision, expected: undefined }, + { label: "forward-caller", decision: { kind: "forward-caller" } as TierDecision, expected: "flex" }, + ])("$label preserves the caller-owned raw body", ({ decision, expected }) => { + const rawBody = { model: MODEL, input: "ping", service_tier: "flex" }; + const original = { ...rawBody }; + const parsed: OcxParsedRequest = { + modelId: MODEL, + context: { messages: [] }, + stream: true, + options: { serviceTier: expected, tierDecision: decision }, + _rawBody: rawBody, + }; + const adapter = withTestTranslatorBudget(createResponsesPassthroughAdapter({ + adapter: "openai-responses", + baseUrl: "https://fixture.example/v1", + authMode: "key", + apiKey: "sk-test", + })); + const outbound = JSON.parse(adapter.buildRequest(parsed).body) as Record; + + expect(parsed._rawBody).toBe(rawBody); + expect(rawBody).toEqual(original); + if (expected === undefined) expect(outbound).not.toHaveProperty("service_tier"); + else expect(outbound.service_tier).toBe(expected); + }); +});