diff --git a/scripts/generate-pi-native-oracle.mjs b/scripts/generate-pi-native-oracle.mjs new file mode 100644 index 000000000..a577f8f17 --- /dev/null +++ b/scripts/generate-pi-native-oracle.mjs @@ -0,0 +1,1354 @@ +#!/usr/bin/env node + +/** + * Generate the vendored Pi raw-schema and composer oracles. + * + * The composer expectations are not a local reimplementation. This harness + * bundles and executes the pinned upstream `composeModelProvider` function. + * Two inert transport shims satisfy imports that are unreachable while + * composing credential-blind custom catalog models; the exact shim bytes and + * harness bytes are recorded in provenance. + */ + +import { createHash } from "node:crypto"; +import { execFileSync } from "node:child_process"; +import { + mkdirSync, + mkdtempSync, + readFileSync, + rmSync, + writeFileSync, +} from "node:fs"; +import { createRequire } from "node:module"; +import { dirname, join, resolve } from "node:path"; +import { fileURLToPath, pathToFileURL } from "node:url"; + +const [piRootArgument, outputArgument] = process.argv.slice(2); +if (!piRootArgument || !outputArgument) { + throw new Error( + "usage: generate-pi-native-oracle.mjs ", + ); +} + +const piRoot = resolve(piRootArgument); +const outputDirectory = resolve(outputArgument); +const repositoryRoot = resolve(dirname(fileURLToPath(import.meta.url)), ".."); +const generatorRelativePath = "scripts/generate-pi-native-oracle.mjs"; +const generatorPath = join(repositoryRoot, generatorRelativePath); +const requireFromPi = createRequire(join(piRoot, "package.json")); +const { Type } = requireFromPi("typebox"); +const { Check } = requireFromPi("typebox/value"); +const { buildSync, version: esbuildVersion } = requireFromPi("esbuild"); +const typeboxVersion = JSON.parse( + readFileSync( + join(dirname(dirname(requireFromPi.resolve("typebox"))), "package.json"), + "utf8", + ), +).version; + +const expectedPiCommit = "ab366ebe94cacd419d986be454f12b1b9913aaca"; +const piRepository = "https://github.com/earendil-works/pi.git"; +const modelConfigRelativePath = + "packages/coding-agent/src/core/model-config.ts"; +const composerRelativePath = + "packages/coding-agent/src/core/provider-composer.ts"; +const resolverRelativePath = + "packages/coding-agent/src/core/resolve-config-value.ts"; + +const modelConfigPath = join(piRoot, modelConfigRelativePath); +const composerPath = join(piRoot, composerRelativePath); +const resolverPath = join(piRoot, resolverRelativePath); +const modelConfigSource = readFileSync(modelConfigPath, "utf8"); +const composerSource = readFileSync(composerPath, "utf8"); +const resolverSource = readFileSync(resolverPath, "utf8"); +const generatorSource = readFileSync(generatorPath, "utf8"); +const actualPiCommit = execFileSync( + "git", + ["-C", piRoot, "rev-parse", "HEAD"], + { encoding: "utf8" }, +).trim(); + +if (actualPiCommit !== expectedPiCommit) { + throw new Error( + `Pi checkout is ${actualPiCommit}, expected ${expectedPiCommit}`, + ); +} + +function sha256(content) { + return createHash("sha256").update(content).digest("hex"); +} + +function writeJson(filename, value) { + const content = `${JSON.stringify(value, null, 2)}\n`; + writeFileSync(join(outputDirectory, filename), content); + return { content, sha256: sha256(content) }; +} + +function extractModelsSchema(source) { + const start = source.indexOf("const PercentileCutoffsSchema"); + const end = source.indexOf("const validateModelsConfig"); + if (start < 0 || end <= start) { + throw new Error("pinned model-config.ts schema block was not found"); + } + const schemaProgram = source.slice(start, end); + return Function( + "Type", + `${schemaProgram}; return ModelsConfigSchema;`, + )(Type); +} + +const modelsSchema = extractModelsSchema(modelConfigSource); +const providerSchema = + modelsSchema.properties.providers.patternProperties["^.*$"]; + +const allThinkingLevels = { + off: null, + minimal: "minimal-effort", + low: "low-effort", + medium: "medium-effort", + high: "high-effort", + xhigh: "xhigh-effort", + max: "max-effort", +}; + +const allCostFields = { + input: 0.11, + output: 0.22, + cacheRead: 0.033, + cacheWrite: 0.044, + tiers: [ + { + inputTokensAbove: 1000.5, + input: 0.55, + output: 0.66, + cacheRead: 0.077, + cacheWrite: 0.088, + }, + ], +}; + +const allCompatFields = { + supportsStore: true, + supportsDeveloperRole: true, + supportsReasoningEffort: true, + supportsUsageInStreaming: true, + maxTokensField: "max_completion_tokens", + requiresToolResultName: true, + requiresAssistantAfterToolResult: true, + requiresThinkingAsText: true, + requiresReasoningContentOnAssistantMessages: true, + thinkingFormat: "chat-template", + chatTemplateKwargs: { + stringValue: "literal", + numberValue: 1.25, + booleanValue: true, + nullValue: null, + variableValue: { + $var: "thinking.effort", + omitWhenOff: true, + }, + }, + cacheControlFormat: "anthropic", + openRouterRouting: { + allow_fallbacks: true, + require_parameters: true, + data_collection: "deny", + zdr: true, + enforce_distillable_text: true, + order: ["provider-a", "provider-b"], + only: ["provider-a"], + ignore: ["provider-z"], + quantizations: ["fp8"], + sort: { + by: "price", + partition: null, + }, + max_price: { + prompt: 1.1, + completion: "2.2", + image: 3.3, + audio: "4.4", + request: 5.5, + }, + preferred_min_throughput: { + p50: 10.5, + p75: 9.5, + p90: 8.5, + p99: 7.5, + }, + preferred_max_latency: { + p50: 100.5, + p75: 200.5, + p90: 300.5, + p99: 400.5, + }, + }, + vercelGatewayRouting: { + only: ["provider-a"], + order: ["provider-a", "provider-b"], + }, + supportsOpenAIGrammarTools: true, + supportsStrictMode: true, + sendSessionAffinityHeaders: true, + deferredToolsMode: "kimi", + sessionAffinityFormat: "openrouter", + supportsLongCacheRetention: true, + supportsToolSearch: true, + supportsEagerToolInputStreaming: true, + supportsCacheControlOnTools: true, + supportsTemperature: true, + forceAdaptiveThinking: true, + allowEmptySignature: true, + supportsStrictTools: true, + supportsToolReferences: true, +}; + +const rawInputs = [ + { + id: "all-schema-fields-valid", + input: { + name: "All Fields Provider", + baseUrl: "https://all-fields.example/v1", + apiKey: "literal-all-fields-key", + api: "openai-responses", + oauth: "radius", + headers: { + "x-provider-field": "provider-value", + }, + compat: allCompatFields, + authHeader: true, + models: [ + { + id: "all-fields-model", + name: "All Fields Model", + api: "anthropic-messages", + baseUrl: "https://all-fields-model.example/v1", + reasoning: true, + thinkingLevelMap: allThinkingLevels, + input: ["text", "image"], + cost: allCostFields, + contextWindow: 128000.5, + maxTokens: 16384.25, + headers: { + "x-model-field": "model-value", + }, + compat: allCompatFields, + }, + ], + modelOverrides: { + "all-fields-model": { + name: "All Fields Override", + reasoning: false, + thinkingLevelMap: allThinkingLevels, + input: ["image", "text"], + cost: allCostFields, + contextWindow: 256000.75, + maxTokens: 32768.5, + headers: { + "x-override-field": "override-value", + }, + compat: allCompatFields, + }, + }, + }, + }, + { id: "empty-provider-object", input: {} }, + { id: "null-provider", input: null }, + { + id: "additional-provider-property", + input: { futureProviderField: { nested: true } }, + }, + { id: "empty-present-base-url", input: { baseUrl: "" } }, + { id: "non-url-base-url-is-raw-string", input: { baseUrl: "not a URL" } }, + { id: "radius-oauth", input: { oauth: "radius" } }, + { id: "unknown-oauth-literal", input: { oauth: "other" } }, + { id: "models-null", input: { models: null } }, + { + id: "model-missing-id", + input: { models: [{ api: "openai-responses" }] }, + }, + { id: "model-empty-id", input: { models: [{ id: "" }] } }, + { + id: "integer-model-numbers", + input: { + models: [{ id: "integer", contextWindow: 128000, maxTokens: 16384 }], + }, + }, + { + id: "fractional-model-numbers", + input: { + models: [ + { + id: "fractional", + contextWindow: 128000.5, + maxTokens: 16384.25, + }, + ], + }, + }, + { + id: "negative-number-is-still-typebox-number", + input: { models: [{ id: "negative", maxTokens: -1.5 }] }, + }, + { + id: "string-is-not-number", + input: { models: [{ id: "string-limit", maxTokens: "16384" }] }, + }, + { + id: "complete-cost-with-fractions", + input: { + models: [ + { + id: "priced", + cost: { + input: 0.1, + output: 0.2, + cacheRead: 0.03, + cacheWrite: 0.04, + tiers: [ + { + inputTokensAbove: 1000.5, + input: 0.5, + output: 0.6, + cacheRead: 0.07, + cacheWrite: 0.08, + }, + ], + }, + }, + ], + }, + }, + { + id: "incomplete-model-cost", + input: { + models: [ + { + id: "priced", + cost: { input: 0.1, output: 0.2, cacheRead: 0.03 }, + }, + ], + }, + }, + { + id: "compat-openrouter-recursive-valid", + input: { + compat: { + thinkingFormat: "chat-template", + chatTemplateKwargs: { + temperature: 0.25, + thinking: { + $var: "thinking.effort", + omitWhenOff: true, + }, + nullable: null, + }, + openRouterRouting: { + data_collection: "deny", + sort: { by: "price", partition: null }, + max_price: { prompt: 1.5, completion: "2.0" }, + preferred_min_throughput: { p50: 10.5, p99: 2 }, + }, + }, + }, + }, + { + id: "compat-nested-union-invalid-in-every-branch", + input: { + compat: { + openRouterRouting: { + preferred_min_throughput: { p50: "fast" }, + }, + supportsToolSearch: "yes", + supportsTemperature: 1, + }, + }, + }, + { + id: "compat-union-additional-field", + input: { compat: { futureCompatField: [1, 2, 3] } }, + }, + { id: "compat-null", input: { compat: null } }, + { + id: "thinking-map-unknown-key-and-string-value", + input: { + models: [ + { + id: "thinking", + thinkingLevelMap: { + low: null, + max: "future-effort", + future: { nested: true }, + }, + }, + ], + }, + }, + { + id: "thinking-map-known-key-invalid-value", + input: { + models: [{ id: "thinking", thinkingLevelMap: { low: 2 } }], + }, + }, + { + id: "compat-sort-string-union-branch", + input: { compat: { openRouterRouting: { sort: "price" } } }, + }, + { + id: "compat-sort-object-union-branch", + input: { + compat: { + openRouterRouting: { sort: { by: "price", partition: null } }, + }, + }, + }, + { + id: "compat-sort-invalid-union-boundary", + input: { compat: { openRouterRouting: { sort: false } } }, + }, + { + id: "headers-record-valid", + input: { headers: { Authorization: "Bearer literal", "x-count": "2" } }, + }, + { + id: "headers-record-invalid-value", + input: { headers: { "x-count": 2 } }, + }, + { + id: "model-override-fractional-and-extra", + input: { + modelOverrides: { + "model/a": { + contextWindow: 10.5, + maxTokens: 2.25, + futureOverrideField: true, + }, + }, + }, + }, + { + id: "input-union-invalid", + input: { models: [{ id: "input", input: ["text", "audio"] }] }, + }, +]; + +const rawOracle = { + version: 1, + execution: { + engine: "typebox Value.Check", + typeboxVersion, + schemaTarget: + "ModelsConfigSchema.properties.providers.patternProperties['^.*$']", + }, + cases: rawInputs.map(({ id, input }) => ({ + id, + input, + expectedValid: Check(providerSchema, input), + })), +}; + +const composerCases = [ + { + id: "combined-all-fields-precedence", + providerId: "custom-all-fields", + input: { + name: "All Fields Provider", + baseUrl: "https://all-fields.example/v1", + apiKey: "literal-all-fields-key", + api: "openai-responses", + oauth: "radius", + headers: { + "x-provider-field": "provider-value", + }, + compat: allCompatFields, + authHeader: true, + models: [ + { + id: "all-fields-model", + name: "All Fields Model", + api: "anthropic-messages", + baseUrl: "https://all-fields-model.example/v1", + reasoning: true, + thinkingLevelMap: allThinkingLevels, + input: ["text", "image"], + cost: allCostFields, + contextWindow: 128000.5, + maxTokens: 16384.25, + headers: { + "x-model-field": "model-value", + }, + compat: allCompatFields, + }, + ], + modelOverrides: { + "all-fields-model": { + name: "All Fields Override", + reasoning: false, + thinkingLevelMap: allThinkingLevels, + input: ["image", "text"], + cost: allCostFields, + contextWindow: 256000.75, + maxTokens: 32768.5, + headers: { + "x-override-field": "override-value", + }, + compat: allCompatFields, + }, + }, + }, + }, + { + id: "provider-fields-inherited", + providerId: "custom-provider-fields", + input: { + name: "Provider Layer Name", + baseUrl: "https://provider-fields.example/v1", + apiKey: "provider-layer-key", + api: "openai-responses", + oauth: "radius", + headers: { + "x-provider-field": "provider-layer-value", + }, + compat: allCompatFields, + authHeader: true, + models: [{ id: "provider-inherited-model" }], + }, + }, + { + id: "model-fields-executed", + providerId: "custom-model-fields", + input: { + apiKey: "model-layer-key", + models: [ + { + id: "model-layer-id", + name: "Model Layer Name", + api: "anthropic-messages", + baseUrl: "https://model-fields.example/v1", + reasoning: true, + thinkingLevelMap: allThinkingLevels, + input: ["text", "image"], + cost: allCostFields, + contextWindow: 64000.5, + maxTokens: 8192.25, + headers: { + "x-model-field": "model-layer-value", + }, + compat: allCompatFields, + }, + ], + }, + }, + { + id: "override-fields-executed", + providerId: "custom-override-fields", + input: { + apiKey: "override-layer-key", + api: "openai-completions", + baseUrl: "https://override-provider.example/v1", + models: [ + { + id: "override-target", + name: "Definition Before Override", + reasoning: true, + }, + ], + modelOverrides: { + "override-target": { + name: "Override Layer Name", + reasoning: false, + thinkingLevelMap: allThinkingLevels, + input: ["image", "text"], + cost: allCostFields, + contextWindow: 96000.75, + maxTokens: 12288.5, + headers: { + "x-override-field": "override-layer-value", + }, + compat: allCompatFields, + }, + }, + }, + }, + { + id: "radius-oauth-requires-provider-base-url", + providerId: "custom-radius-missing-base", + input: { + apiKey: "radius-key", + oauth: "radius", + models: [ + { + id: "radius-model", + api: "openai-responses", + baseUrl: "https://model-only.example/v1", + }, + ], + }, + }, + { + id: "official-defaults", + providerId: "custom-defaults", + input: { + api: "openai-responses", + baseUrl: "https://default.example/v1", + apiKey: "literal-secret", + models: [{ id: "default-model" }], + }, + }, + { + id: "later-model-first-model-fallback", + providerId: "custom-fallback", + input: { + apiKey: "literal-secret", + models: [ + { + id: "first", + api: "anthropic-messages", + baseUrl: "https://first.example/v1", + }, + { id: "second" }, + ], + }, + }, + { + id: "duplicate-model-replaces-existing-slot", + providerId: "custom-duplicate", + input: { + apiKey: "literal-secret", + models: [ + { + id: "same", + name: "First", + api: "openai-completions", + baseUrl: "https://first.example/v1", + }, + { + id: "same", + name: "Second", + api: "openai-responses", + baseUrl: "https://second.example/v1", + }, + ], + }, + }, + { + id: "provider-model-override-precedence", + providerId: "custom-precedence", + input: { + api: "openai-completions", + baseUrl: "https://provider.example/v1", + apiKey: "literal-secret", + headers: { layer: "provider", providerOnly: "yes" }, + compat: { + supportsDeveloperRole: true, + openRouterRouting: { zdr: true }, + }, + models: [ + { + id: "precedence", + name: "Definition", + api: "openai-responses", + baseUrl: "https://model.example/v1", + reasoning: false, + thinkingLevelMap: { high: "model-high", future: "model-future" }, + contextWindow: 1000.5, + maxTokens: 100.25, + headers: { layer: "definition", definitionOnly: "yes" }, + compat: { + supportsStore: true, + openRouterRouting: { only: ["definition"], zdr: false }, + }, + cost: { + input: 1, + output: 2, + cacheRead: 0.1, + cacheWrite: 0.2, + }, + }, + ], + modelOverrides: { + precedence: { + name: "Override", + reasoning: true, + thinkingLevelMap: { + high: "override-high", + futureOverride: "preserve", + }, + contextWindow: 2000.75, + headers: { layer: "override", overrideOnly: "yes" }, + compat: { + supportsStore: false, + openRouterRouting: { order: ["override"] }, + }, + cost: { output: 3.5 }, + }, + }, + }, + }, + { + id: "fractional-cost-and-limits", + providerId: "custom-fractional", + input: { + api: "google-generative-ai", + baseUrl: "https://fractional.example/v1", + apiKey: "literal-secret", + models: [ + { + id: "fractional", + contextWindow: 128000.5, + maxTokens: 16384.25, + cost: { + input: 0.125, + output: 0.375, + cacheRead: 0.0625, + cacheWrite: 0.1875, + }, + }, + ], + }, + }, + { + id: "unmatched-override-is-ignored-by-composer", + providerId: "custom-unmatched", + input: { + api: "google-generative-ai", + baseUrl: "https://google.example/v1", + apiKey: "literal-secret", + models: [{ id: "known" }], + modelOverrides: { + missing: { + maxTokens: 7.5, + headers: { ignored: "yes" }, + }, + }, + }, + }, + { + id: "invalid-nonempty-url-is-composed", + providerId: "custom-invalid-url", + input: { + api: "openai-responses", + baseUrl: "not a URL", + apiKey: "literal-secret", + models: [{ id: "model" }], + }, + }, + { + id: "unknown-api-is-composed", + providerId: "custom-future-api", + input: { + api: "future-wire-v9", + baseUrl: "https://future.example/v9", + apiKey: "literal-secret", + models: [{ id: "future-model" }], + }, + }, + { + id: "unknown-thinking-fields-are-preserved", + providerId: "custom-thinking", + input: { + api: "anthropic-messages", + baseUrl: "https://thinking.example/v1", + apiKey: "literal-secret", + models: [ + { + id: "thinking", + thinkingLevelMap: { + high: "future-high", + future: { nested: ["opaque"] }, + }, + }, + ], + }, + }, + { + id: "empty-url-fails-upstream-composition", + providerId: "custom-empty-url", + input: { + api: "openai-responses", + baseUrl: "", + apiKey: "literal-secret", + models: [{ id: "model" }], + }, + }, +]; + +function joinFieldPath(parent, token) { + const escaped = token.replaceAll("~", "~0").replaceAll("/", "~1"); + return `${parent}/${escaped}`; +} + +function collectSchemaFieldPaths(schema, pointer = "", output = new Set()) { + if (!schema || typeof schema !== "object" || Array.isArray(schema)) { + return output; + } + for (const branch of schema.anyOf ?? []) { + collectSchemaFieldPaths(branch, pointer, output); + } + for (const [name, child] of Object.entries(schema.properties ?? {})) { + const childPointer = joinFieldPath(pointer, name); + output.add(childPointer); + collectSchemaFieldPaths(child, childPointer, output); + } + for (const child of Object.values(schema.patternProperties ?? {})) { + const childPointer = joinFieldPath(pointer, "*"); + output.add(childPointer); + collectSchemaFieldPaths(child, childPointer, output); + } + if (schema.items) { + collectSchemaFieldPaths( + schema.items, + joinFieldPath(pointer, "*"), + output, + ); + } + return output; +} + +function isPatternContainer(pointer) { + return ( + pointer.endsWith("/headers") || + pointer.endsWith("/modelOverrides") || + pointer.endsWith("/chatTemplateKwargs") + ); +} + +function collectInputFieldPaths(value, pointer = "", output = new Set()) { + if (Array.isArray(value)) { + for (const child of value) { + collectInputFieldPaths(child, joinFieldPath(pointer, "*"), output); + } + return output; + } + if (!value || typeof value !== "object") { + return output; + } + for (const [name, child] of Object.entries(value)) { + const childPointer = joinFieldPath( + pointer, + isPatternContainer(pointer) ? "*" : name, + ); + output.add(childPointer); + collectInputFieldPaths(child, childPointer, output); + } + return output; +} + +const schemaFieldPaths = [...collectSchemaFieldPaths(providerSchema)].sort(); +const rawCaseFieldPaths = new Map( + rawInputs.map((entry) => [entry.id, collectInputFieldPaths(entry.input)]), +); +const composerCaseFieldPaths = new Map( + composerCases.map((entry) => [entry.id, collectInputFieldPaths(entry.input)]), +); +function requiredComposerBehaviorCase(fieldPath) { + if (fieldPath === "/models" || fieldPath.startsWith("/models/")) { + return "model-fields-executed"; + } + if ( + fieldPath === "/modelOverrides" || + fieldPath.startsWith("/modelOverrides/") + ) { + return "override-fields-executed"; + } + return "provider-fields-inherited"; +} + +const fieldCoverageEntries = schemaFieldPaths.map((fieldPath) => { + const rawOracleCases = [...rawCaseFieldPaths] + .filter(([, paths]) => paths.has(fieldPath)) + .map(([id]) => id); + const composerOracleCases = [...composerCaseFieldPaths] + .filter(([, paths]) => paths.has(fieldPath)) + .map(([id]) => id); + const composerBehaviorCase = requiredComposerBehaviorCase(fieldPath); + if (!composerOracleCases.includes(composerBehaviorCase)) { + throw new Error( + `Pi field ${fieldPath} is not exercised at its own composition layer by ${composerBehaviorCase}`, + ); + } + return { + fieldPath, + rawOracleCases, + composerOracleCases, + composerBehaviorCase, + }; +}); +const uncoveredRawFields = fieldCoverageEntries + .filter((entry) => entry.rawOracleCases.length === 0) + .map((entry) => entry.fieldPath); +const uncoveredComposerFields = fieldCoverageEntries + .filter((entry) => entry.composerOracleCases.length === 0) + .map((entry) => entry.fieldPath); +if (uncoveredRawFields.length > 0 || uncoveredComposerFields.length > 0) { + throw new Error( + `Pi field coverage is incomplete; raw=${JSON.stringify( + uncoveredRawFields, + )}, composer=${JSON.stringify(uncoveredComposerFields)}`, + ); +} + +const aiShimSource = ` +export function lazyStream() { + throw new Error("transport shim must not execute during composer oracle generation"); +} +`; +const compatShimSource = ` +export function getApiProvider() { + throw new Error("transport shim must not execute during composer oracle generation"); +} +`; + +function projectUpstreamModel( + model, + providerConfig, + resolveCompatibilityRequestConfig, +) { + const requestConfig = resolveCompatibilityRequestConfig( + model, + providerConfig, + undefined, + ); + return { + id: model.id, + name: model.name, + api: model.api, + provider: model.provider, + baseUrl: model.baseUrl, + reasoning: model.reasoning, + ...(model.thinkingLevelMap === undefined + ? {} + : { thinkingLevelMap: model.thinkingLevelMap }), + input: model.input, + cost: model.cost, + contextWindow: model.contextWindow, + maxTokens: model.maxTokens, + ...(model.compat === undefined ? {} : { compat: model.compat }), + ...(requestConfig.headers === undefined + ? {} + : { headers: requestConfig.headers }), + authHeader: requestConfig.authHeader, + }; +} + +async function runPinnedComposer() { + const harnessDirectory = mkdtempSync( + join(piRoot, ".cc-switch-composer-oracle-"), + ); + try { + const aiShimPath = join(harnessDirectory, "pi-ai-shim.mjs"); + const compatShimPath = join(harnessDirectory, "pi-ai-compat-shim.mjs"); + const bundlePath = join(harnessDirectory, "provider-composer.mjs"); + writeFileSync(aiShimPath, aiShimSource); + writeFileSync(compatShimPath, compatShimSource); + buildSync({ + entryPoints: [composerPath], + bundle: true, + platform: "node", + format: "esm", + target: "node22", + outfile: bundlePath, + packages: "external", + alias: { + "@earendil-works/pi-ai": aiShimPath, + "@earendil-works/pi-ai/compat": compatShimPath, + }, + logLevel: "silent", + }); + const bundledComposer = await import(pathToFileURL(bundlePath).href); + const { + composeModelProvider, + resolveCompatibilityRequestConfig, + } = bundledComposer; + const cases = await Promise.all(composerCases.map(async ({ id, providerId, input }) => { + try { + const modelConfig = { + getProvider(candidateId) { + return candidateId === providerId ? input : undefined; + }, + }; + const provider = composeModelProvider( + providerId, + undefined, + modelConfig, + undefined, + ); + const models = provider.getModels(); + const modelIds = new Set(models.map((model) => model.id)); + let authExecution; + if (provider.auth.apiKey) { + try { + const result = await provider.auth.apiKey.resolve({ + ctx: { + env: async () => undefined, + }, + }); + authExecution = { + status: "success", + entryFunction: "Provider.auth.apiKey.resolve", + result: result ?? null, + }; + } catch (error) { + authExecution = { + status: "error", + entryFunction: "Provider.auth.apiKey.resolve", + error: error instanceof Error ? error.message : String(error), + }; + } + } else { + authExecution = { + status: "unavailable", + entryFunction: "Provider.auth.apiKey.resolve", + reason: "pinned composer exposed no API-key auth method", + }; + } + return { + id, + providerId, + input, + execution: { + status: "success", + entryFunctions: [ + "composeModelProvider", + "Provider.getModels", + "resolveCompatibilityRequestConfig", + "Provider.auth.apiKey.resolve", + ], + }, + authExecution, + expected: { + provider: { + id: provider.id, + name: provider.name, + ...(provider.baseUrl === undefined + ? {} + : { baseUrl: provider.baseUrl }), + }, + models: models.map((model) => + projectUpstreamModel( + model, + input, + resolveCompatibilityRequestConfig, + ), + ), + ignoredOverrideKeys: Object.keys(input.modelOverrides ?? {}).filter( + (modelId) => !modelIds.has(modelId), + ), + }, + }; + } catch (error) { + return { + id, + providerId, + input, + execution: { + status: "error", + entryFunctions: ["composeModelProvider"], + }, + expectedError: + error instanceof Error ? error.message : String(error), + }; + } + })); + return { + cases, + harness: { + bundler: `esbuild@${esbuildVersion}`, + upstreamEntry: composerRelativePath, + entryFunctions: [ + "composeModelProvider", + "Provider.getModels", + "resolveCompatibilityRequestConfig", + "Provider.auth.apiKey.resolve", + ], + transportShims: { + "pi-ai": sha256(aiShimSource), + "pi-ai/compat": sha256(compatShimSource), + }, + }, + }; + } finally { + rmSync(harnessDirectory, { recursive: true, force: true }); + } +} + +async function runPinnedTransportResolver() { + const harnessDirectory = mkdtempSync( + join(piRoot, ".cc-switch-transport-oracle-"), + ); + try { + const bundlePath = join(harnessDirectory, "resolve-config-value.mjs"); + buildSync({ + entryPoints: [resolverPath], + bundle: true, + platform: "node", + format: "esm", + target: "node22", + outfile: bundlePath, + packages: "external", + logLevel: "silent", + }); + const resolver = await import(pathToFileURL(bundlePath).href); + const cases = [ + { + id: "literal-value", + input: "literal-secret", + environment: {}, + }, + { + id: "environment-template", + input: "prefix-${PI_ORACLE_VALUE}-suffix", + environment: { PI_ORACLE_VALUE: "environment-secret" }, + }, + { + id: "escaped-dollar-and-bang", + input: "$$literal-$!bang", + environment: {}, + }, + { + id: "shell-command", + input: "!printf pi-command-value", + environment: {}, + }, + { + id: "missing-environment", + input: "${PI_ORACLE_MISSING}", + environment: {}, + }, + ].map((entry) => { + try { + const value = resolver.resolveConfigValueOrThrow( + entry.input, + `oracle ${entry.id}`, + entry.environment, + ); + return { + ...entry, + execution: { + status: "success", + entryFunction: "resolveConfigValueOrThrow", + }, + expected: value, + }; + } catch (error) { + return { + ...entry, + execution: { + status: "error", + entryFunction: "resolveConfigValueOrThrow", + }, + expectedError: + error instanceof Error ? error.message : String(error), + }; + } + }); + const headerInput = { + "x-literal": "literal-header", + "x-environment": "${PI_ORACLE_HEADER}", + "x-command": "!printf pi-command-header", + }; + const headerEnvironment = { PI_ORACLE_HEADER: "environment-header" }; + const expectedHeaders = resolver.resolveHeadersOrThrow( + headerInput, + "oracle headers", + headerEnvironment, + ); + return { + engine: "pinned upstream TypeScript", + bundler: `esbuild@${esbuildVersion}`, + upstreamEntry: resolverRelativePath, + platform: process.platform, + cases, + headerCase: { + id: "provider-header-materialization", + input: headerInput, + environment: headerEnvironment, + execution: { + status: "success", + entryFunction: "resolveHeadersOrThrow", + }, + expected: expectedHeaders, + }, + }; + } finally { + rmSync(harnessDirectory, { recursive: true, force: true }); + } +} + +const composerExecution = await runPinnedComposer(); +const transportExecution = await runPinnedTransportResolver(); +if ( + !rawOracle.cases.find((entry) => entry.id === "all-schema-fields-valid") + ?.expectedValid +) { + throw new Error("the all-fields raw vector was not accepted by pinned TypeBox"); +} +if ( + composerExecution.cases.find( + (entry) => entry.id === "combined-all-fields-precedence", + )?.execution.status !== "success" +) { + throw new Error("the all-fields vector did not execute in the pinned composer"); +} +const composerOracle = { + version: 1, + execution: { + engine: "pinned upstream TypeScript", + piCommit: actualPiCommit, + ...composerExecution.harness, + }, + cases: composerExecution.cases, + failClosedCases: [ + { + id: "builtin-overlay-without-pinned-base-catalog", + kind: "built_in_overlay", + unavailableContext: "pinned built-in Provider instance and model catalog", + rustExpectedStatus: "unknown", + reasonCode: "catalog_required", + }, + { + id: "extension-overlay-without-extension-registration", + kind: "extension_overlay", + unavailableContext: "extension ProviderConfigInput and registered model catalog", + rustExpectedStatus: "unknown", + reasonCode: "catalog_required", + }, + ], +}; +const transportOracle = { + version: 1, + piCommit: actualPiCommit, + ...transportExecution, +}; + +function collectSchemaOperators(value, operators = new Set()) { + if (Array.isArray(value)) { + for (const child of value) collectSchemaOperators(child, operators); + return operators; + } + if (!value || typeof value !== "object") return operators; + for (const [key, child] of Object.entries(value)) { + if ( + [ + "additionalProperties", + "anyOf", + "const", + "items", + "minLength", + "patternProperties", + "properties", + "required", + "type", + ].includes(key) + ) { + operators.add(key); + } + collectSchemaOperators(child, operators); + } + return operators; +} + +mkdirSync(outputDirectory, { recursive: true }); +const schemaArtifact = writeJson( + "provider-schema.snapshot.json", + providerSchema, +); +const rawArtifact = writeJson("raw-oracle-v1.json", rawOracle); +const composerArtifact = writeJson( + "composer-oracle-v1.json", + composerOracle, +); +const transportArtifact = writeJson( + "transport-oracle-v1.json", + transportOracle, +); +const fieldCoverageArtifact = writeJson("field-coverage-v1.json", { + version: 1, + piCommit: actualPiCommit, + schemaTarget: + "ModelsConfigSchema.properties.providers.patternProperties['^.*$']", + assertion: + "every schema field is present in an actual TypeBox Value.Check vector and in a pinned composer execution dedicated to its provider, model, or override layer", + fields: fieldCoverageEntries, +}); + +const provenance = { + version: 1, + pi: { + repository: piRepository, + commit: actualPiCommit, + }, + typeboxVersion, + sources: { + modelConfig: { + path: modelConfigRelativePath, + sha256: sha256(modelConfigSource), + }, + providerComposer: { + path: composerRelativePath, + sha256: sha256(composerSource), + }, + resolveConfigValue: { + path: resolverRelativePath, + sha256: sha256(resolverSource), + }, + }, + artifacts: { + "provider-schema.snapshot.json": schemaArtifact.sha256, + "raw-oracle-v1.json": rawArtifact.sha256, + "composer-oracle-v1.json": composerArtifact.sha256, + "transport-oracle-v1.json": transportArtifact.sha256, + "field-coverage-v1.json": fieldCoverageArtifact.sha256, + }, + harness: { + path: generatorRelativePath, + sha256: sha256(generatorSource), + upstreamEntry: composerRelativePath, + entryFunctions: composerExecution.harness.entryFunctions, + bundler: composerExecution.harness.bundler, + transportShims: composerExecution.harness.transportShims, + assertion: + "expected composer outputs were captured by executing the pinned upstream entry functions; transport shims are unreachable during credential-blind model composition", + transportResolver: { + upstreamEntry: resolverRelativePath, + entryFunctions: [ + "resolveConfigValueOrThrow", + "resolveHeadersOrThrow", + ], + bundler: transportExecution.bundler, + platform: transportExecution.platform, + }, + }, + uncoveredSemantics: [ + ...composerOracle.failClosedCases.map( + ({ id, unavailableContext, rustExpectedStatus, reasonCode }) => ({ + id, + unavailableContext, + rustExpectedStatus, + reasonCode, + }), + ), + { + id: "radius-oauth-credential-lifecycle", + unavailableContext: + "interactive Radius login, refresh credentials, and network exchange", + rustExpectedStatus: "direct_only", + reasonCode: "missing_gateway_credential", + }, + ], + schemaOperatorInventory: [ + ...collectSchemaOperators(providerSchema), + ].sort(), + evaluatorOperatorAllowlist: [ + "additionalProperties", + "anyOf", + "const", + "items", + "minLength", + "patternProperties", + "properties", + "required", + "type", + ], +}; +writeJson("provenance-v1.json", provenance); diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 7c9bd484b..1f4204289 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -783,6 +783,7 @@ dependencies = [ "indexmap 2.13.0", "json-five", "json5", + "jsonc-parser", "libc", "log", "objc2 0.5.2", @@ -800,6 +801,7 @@ dependencies = [ "serde_yaml", "serial_test", "sha2", + "syn 2.0.117", "sys-locale", "tauri", "tauri-build", @@ -2799,6 +2801,15 @@ dependencies = [ "serde", ] +[[package]] +name = "jsonc-parser" +version = "0.33.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0560e3f9a9a03ea6b6e90b41138c5db9e21526c99eb192c1a26c68176593285" +dependencies = [ + "serde_json", +] + [[package]] name = "jsonptr" version = "0.6.3" @@ -4687,7 +4698,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.4.15", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 22e3c442b..6e55ba485 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -24,6 +24,7 @@ tauri-build = { version = "2.4.0", features = [] } [dependencies] serde_json = { version = "1.0", features = ["preserve_order"] } +jsonc-parser = { version = "0.33", features = ["cst", "serde_json"] } serde = { version = "1.0", features = ["derive"] } log = "0.4" chrono = { version = "0.4", features = ["serde"] } @@ -117,3 +118,4 @@ strip = "symbols" [dev-dependencies] serial_test = "3" tempfile = "3" +syn = { version = "2", features = ["full", "visit"] } diff --git a/src-tauri/src/architecture_tests.rs b/src-tauri/src/architecture_tests.rs new file mode 100644 index 000000000..951020060 --- /dev/null +++ b/src-tauri/src/architecture_tests.rs @@ -0,0 +1,554 @@ +use regex::Regex; +use serde_json::json; +use std::collections::{BTreeMap, BTreeSet}; +use std::fs; +use std::path::{Path, PathBuf}; +use std::sync::LazyLock; +use syn::visit::{self, Visit}; +use syn::{ + Attribute, ExprLit, ImplItem, Item, ItemImpl, ItemStruct, ItemUse, Lit, Meta, Path as SynPath, + Type, UseTree, +}; + +static PROVIDER_DML: LazyLock = LazyLock::new(|| { + Regex::new( + r#"(?is)\b(?:INSERT(?:\s+OR\s+(?:ABORT|FAIL|IGNORE|REPLACE|ROLLBACK))?\s+INTO|UPDATE(?:\s+OR\s+(?:ABORT|FAIL|IGNORE|REPLACE|ROLLBACK))?|DELETE\s+FROM)\s+["`\[]?(providers|provider_endpoints)\b"#, + ) + .expect("compile provider DML classifier") +}); +static RESTORE_CREATE: LazyLock = LazyLock::new(|| { + Regex::new(r"(?is)\bCREATE\s+(?:TEMP(?:ORARY)?\s+)?(?:TABLE|INDEX|TRIGGER|VIEW)\b") + .expect("compile restore DDL classifier") +}); + +#[derive(Debug, Clone, PartialEq, Eq)] +struct Violation { + kind: &'static str, + path: String, + detail: String, +} + +fn is_cfg_test(attrs: &[Attribute]) -> bool { + attrs.iter().any(|attribute| { + if !attribute.path().is_ident("cfg") { + return false; + } + match &attribute.meta { + Meta::List(list) => list + .tokens + .to_string() + .split(|character: char| !character.is_ascii_alphanumeric() && character != '_') + .any(|token| token == "test"), + _ => false, + } + }) +} + +fn flatten_use(tree: &UseTree, prefix: &mut Vec, output: &mut Vec>) { + match tree { + UseTree::Path(path) => { + prefix.push(path.ident.to_string()); + flatten_use(&path.tree, prefix, output); + prefix.pop(); + } + UseTree::Name(name) => { + let mut path = prefix.clone(); + path.push(name.ident.to_string()); + output.push(path); + } + UseTree::Rename(rename) => { + let mut path = prefix.clone(); + path.push(rename.ident.to_string()); + output.push(path); + } + UseTree::Group(group) => { + for item in &group.items { + flatten_use(item, prefix, output); + } + } + UseTree::Glob(_) => output.push(prefix.clone()), + } +} + +fn path_segments(path: &SynPath) -> Vec { + path.segments + .iter() + .map(|segment| segment.ident.to_string()) + .collect() +} + +struct ArchitectureVisitor<'a> { + path: &'a str, + violations: Vec, + internal_edges: BTreeSet, +} + +impl ArchitectureVisitor<'_> { + fn record_dependency(&mut self, segments: &[String]) { + let source_module = if self.path.ends_with("pi_config/raw_schema.rs") { + Some("raw_schema") + } else if self.path.ends_with("pi_config/composer.rs") { + Some("composer") + } else if self.path.ends_with("pi_config/gateway.rs") { + Some("gateway") + } else if self.path.ends_with("pi_config/native.rs") { + Some("native") + } else if self.path.ends_with("pi_config/model.rs") { + Some("model") + } else { + None + }; + let Some(source_module) = source_module else { + return; + }; + let qualified_internal_path = segments.len() > 1 + && segments.iter().any(|segment| { + matches!(segment.as_str(), "super" | "self" | "crate" | "pi_config") + }); + if qualified_internal_path { + for module in [ + "raw_schema", + "composer", + "gateway", + "native", + "model", + "document", + ] { + if module != source_module && segments.iter().any(|segment| segment == module) { + self.internal_edges + .insert(format!("{source_module}->{module}")); + } + } + } + + let imports_gateway = segments + .iter() + .any(|segment| segment == "gateway" || segment.starts_with("PiGateway")); + let imports_model_module = segments.len() > 1 + && segments.iter().any(|segment| segment == "model") + && segments.iter().any(|segment| { + matches!(segment.as_str(), "super" | "self" | "crate" | "pi_config") + }); + let imports_managed = imports_model_module + || segments.iter().any(|segment| { + segment == "PiApiFamily" + || segment.starts_with("PiManaged") + || segment == "PiEffectiveModel" + }); + if matches!(source_module, "raw_schema" | "composer") + && (imports_gateway || imports_managed) + { + self.violations.push(Violation { + kind: "cross_layer_import", + path: self.path.to_string(), + detail: format!( + "{source_module} imports managed/gateway path {}", + segments.join("::") + ), + }); + } + + let imports_raw_valid = segments + .iter() + .any(|segment| segment == "raw_schema" || segment == "PiRawValidProvider"); + if source_module == "gateway" && imports_raw_valid { + self.violations.push(Violation { + kind: "cross_layer_import", + path: self.path.to_string(), + detail: format!( + "gateway constructs or imports raw-valid path {}", + segments.join("::") + ), + }); + } + } + + fn inspect_string(&mut self, literal: &str) { + if PROVIDER_DML.is_match(literal) && !provider_dml_path_allowed(self.path) { + self.violations.push(Violation { + kind: "provider_dml", + path: self.path.to_string(), + detail: "provider table DML exists outside typed row/state, endpoint, migration, or canonical-copy authority".to_string(), + }); + } + if self.path.ends_with("database/backup.rs") && RESTORE_CREATE.is_match(literal) { + self.violations.push(Violation { + kind: "restore_create", + path: self.path.to_string(), + detail: "restore production code contains CREATE TABLE/INDEX/TRIGGER/VIEW" + .to_string(), + }); + } + } +} + +impl<'ast> Visit<'ast> for ArchitectureVisitor<'_> { + fn visit_item(&mut self, item: &'ast Item) { + let attrs = match item { + Item::Const(item) => &item.attrs, + Item::Enum(item) => &item.attrs, + Item::ExternCrate(item) => &item.attrs, + Item::Fn(item) => &item.attrs, + Item::ForeignMod(item) => &item.attrs, + Item::Impl(item) => &item.attrs, + Item::Macro(item) => &item.attrs, + Item::Mod(item) => &item.attrs, + Item::Static(item) => &item.attrs, + Item::Struct(item) => &item.attrs, + Item::Trait(item) => &item.attrs, + Item::TraitAlias(item) => &item.attrs, + Item::Type(item) => &item.attrs, + Item::Union(item) => &item.attrs, + Item::Use(item) => &item.attrs, + Item::Verbatim(_) => { + visit::visit_item(self, item); + return; + } + _ => { + visit::visit_item(self, item); + return; + } + }; + if !is_cfg_test(attrs) { + visit::visit_item(self, item); + } + } + + fn visit_impl_item(&mut self, item: &'ast ImplItem) { + let attrs = match item { + ImplItem::Const(item) => &item.attrs, + ImplItem::Fn(item) => &item.attrs, + ImplItem::Type(item) => &item.attrs, + ImplItem::Macro(item) => &item.attrs, + ImplItem::Verbatim(_) => { + visit::visit_impl_item(self, item); + return; + } + _ => { + visit::visit_impl_item(self, item); + return; + } + }; + if !is_cfg_test(attrs) { + visit::visit_impl_item(self, item); + } + } + + fn visit_ident(&mut self, identifier: &'ast syn::Ident) { + let identifier = identifier.to_string(); + if matches!( + identifier.as_str(), + "save_provider" | "save_provider_row_on_tx" | "save_provider_aggregate" + ) || identifier.starts_with("upsert_provider") + { + self.violations.push(Violation { + kind: "forbidden_provider_symbol", + path: self.path.to_string(), + detail: format!("forbidden provider write symbol '{identifier}'"), + }); + } + } + + fn visit_expr_lit(&mut self, expression: &'ast ExprLit) { + if let Lit::Str(literal) = &expression.lit { + self.inspect_string(&literal.value()); + } + visit::visit_expr_lit(self, expression); + } + + fn visit_item_use(&mut self, item: &'ast ItemUse) { + let mut paths = Vec::new(); + flatten_use(&item.tree, &mut Vec::new(), &mut paths); + for path in paths { + self.record_dependency(&path); + } + visit::visit_item_use(self, item); + } + + fn visit_path(&mut self, path: &'ast SynPath) { + self.record_dependency(&path_segments(path)); + visit::visit_path(self, path); + } +} + +fn provider_dml_path_allowed(path: &str) -> bool { + [ + "database/dao/provider_write.rs", + "database/dao/providers.rs", + "database/dao/failover.rs", + "database/schema.rs", + "database/migration.rs", + "database/backup.rs", + ] + .iter() + .any(|allowed| path.ends_with(allowed)) +} + +fn scan_source(path: &str, source: &str) -> (Vec, BTreeSet) { + let syntax = match syn::parse_file(source) { + Ok(syntax) => syntax, + Err(error) => { + return ( + vec![Violation { + kind: "parse_error", + path: path.to_string(), + detail: error.to_string(), + }], + BTreeSet::new(), + ) + } + }; + let mut visitor = ArchitectureVisitor { + path, + violations: Vec::new(), + internal_edges: BTreeSet::new(), + }; + visitor.visit_file(&syntax); + (visitor.violations, visitor.internal_edges) +} + +fn rust_sources(root: &Path) -> Vec { + fn walk(directory: &Path, output: &mut Vec) { + let mut entries = fs::read_dir(directory) + .unwrap_or_else(|error| panic!("read {}: {error}", directory.display())) + .collect::, _>>() + .unwrap_or_else(|error| panic!("read entry in {}: {error}", directory.display())); + entries.sort_by_key(|entry| entry.path()); + for entry in entries { + let path = entry.path(); + if path.is_dir() { + walk(&path, output); + } else if path.extension().is_some_and(|extension| extension == "rs") { + output.push(path); + } + } + } + let mut output = Vec::new(); + walk(root, &mut output); + output +} + +fn type_leaf(ty: &Type) -> String { + match ty { + Type::Path(path) => path + .path + .segments + .last() + .map(|segment| segment.ident.to_string()) + .unwrap_or_else(|| "unknown".to_string()), + Type::Reference(reference) => type_leaf(&reference.elem), + _ => "other".to_string(), + } +} + +fn provider_write_api_snapshot(source: &str) -> serde_json::Value { + let syntax = syn::parse_file(source).expect("parse provider write authority"); + let type_names = [ + "ProviderKey", + "ProviderRowUpdate", + "NewEndpoint", + "NewProviderAggregate", + "RenameProvider", + ]; + let method_names = [ + "create_provider", + "update_provider", + "rename_db_only_additive_provider", + "add_provider_endpoint", + "remove_provider_endpoint", + "touch_provider_endpoint", + ]; + let mut types = BTreeMap::>::new(); + let mut methods = BTreeMap::>::new(); + for item in syntax.items { + if let Item::Struct(ItemStruct { ident, fields, .. }) = &item { + if type_names.contains(&ident.to_string().as_str()) { + types.insert( + ident.to_string(), + fields + .iter() + .filter_map(|field| field.ident.as_ref().map(ToString::to_string)) + .collect(), + ); + } + } + if let Item::Impl(ItemImpl { self_ty, items, .. }) = item { + if type_leaf(&self_ty) != "Database" { + continue; + } + for item in items { + let ImplItem::Fn(function) = item else { + continue; + }; + let name = function.sig.ident.to_string(); + if !method_names.contains(&name.as_str()) { + continue; + } + let inputs = function + .sig + .inputs + .iter() + .filter_map(|argument| match argument { + syn::FnArg::Receiver(_) => None, + syn::FnArg::Typed(argument) => Some(type_leaf(&argument.ty)), + }) + .collect(); + methods.insert(name, inputs); + } + } + } + json!({ + "manifestVersion": 1, + "codeAuthority": "src-tauri/src/database/dao/provider_write.rs", + "types": types, + "databaseMethods": methods, + }) +} + +#[test] +fn architecture_scanner_accepts_production_tree_and_matches_machine_snapshots() { + let manifest_dir = Path::new(env!("CARGO_MANIFEST_DIR")); + let source_root = manifest_dir.join("src"); + let mut violations = Vec::new(); + let mut edges = BTreeSet::new(); + for path in rust_sources(&source_root) { + let relative = path + .strip_prefix(manifest_dir) + .expect("source is under manifest") + .to_string_lossy() + .replace('\\', "/"); + if relative.ends_with("architecture_tests.rs") + || relative.ends_with("/tests.rs") + || relative.contains("/tests/") + { + continue; + } + let source = fs::read_to_string(&path) + .unwrap_or_else(|error| panic!("read {}: {error}", path.display())); + let (mut source_violations, source_edges) = scan_source(&relative, &source); + violations.append(&mut source_violations); + edges.extend(source_edges); + } + assert!( + violations.is_empty(), + "architecture violations:\n{}", + violations + .iter() + .map(|violation| format!( + "{} {}: {}", + violation.kind, violation.path, violation.detail + )) + .collect::>() + .join("\n") + ); + + // Integration tests compile as external consumers and used to retain + // calls to the deleted generic writer even after `cargo test --lib` + // passed. They may contain deliberate SQL fixtures, so this pass checks + // only that the forbidden API surface is absent from actual Rust syntax. + let mut forbidden_test_symbols = Vec::new(); + for path in rust_sources(&manifest_dir.join("tests")) { + let relative = path + .strip_prefix(manifest_dir) + .expect("integration test is under manifest") + .to_string_lossy() + .replace('\\', "/"); + let source = fs::read_to_string(&path) + .unwrap_or_else(|error| panic!("read {}: {error}", path.display())); + let (source_violations, _) = scan_source(&relative, &source); + forbidden_test_symbols.extend( + source_violations + .into_iter() + .filter(|violation| violation.kind == "forbidden_provider_symbol"), + ); + } + assert!( + forbidden_test_symbols.is_empty(), + "forbidden provider write symbols remain in integration tests:\n{}", + forbidden_test_symbols + .iter() + .map(|violation| format!("{}: {}", violation.path, violation.detail)) + .collect::>() + .join("\n") + ); + + let module_snapshot: serde_json::Value = serde_json::from_str(include_str!( + "../../tests/fixtures/pi/module-boundaries-v1.json" + )) + .expect("parse module boundary snapshot"); + assert_eq!( + json!({ + "manifestVersion": 1, + "codeAuthority": "src-tauri/src/architecture_tests.rs", + "edges": edges, + }), + module_snapshot + ); + + let provider_source = fs::read_to_string(source_root.join("database/dao/provider_write.rs")) + .expect("read provider write authority"); + let provider_snapshot: serde_json::Value = serde_json::from_str(include_str!( + "../../tests/fixtures/pi/provider-write-api-v1.json" + )) + .expect("parse provider write API snapshot"); + assert_eq!( + provider_write_api_snapshot(&provider_source), + provider_snapshot + ); +} + +#[test] +fn architecture_scanner_negative_fixtures_prove_each_guard_fires() { + let cases = [ + ( + "src/services/forbidden.rs", + "fn call() { save_provider(); }", + "forbidden_provider_symbol", + ), + ( + "src/services/rogue.rs", + r#"fn write(conn: &Db) { conn.execute("UPDATE OR REPLACE providers SET name = 'x'", []); }"#, + "provider_dml", + ), + ( + "src/database/backup.rs", + r#"fn stage(conn: &Db) { conn.execute("CREATE TABLE leaked (id INTEGER)", []); }"#, + "restore_create", + ), + ( + "src/pi_config/composer.rs", + "use super::gateway::PiGatewayApiFamily;", + "cross_layer_import", + ), + ( + "src/pi_config/gateway.rs", + "use super::raw_schema::PiRawValidProvider;", + "cross_layer_import", + ), + ]; + for (path, source, expected_kind) in cases { + let (violations, _) = scan_source(path, source); + assert!( + violations + .iter() + .any(|violation| violation.kind == expected_kind), + "negative fixture {path} did not trigger {expected_kind}: {violations:?}" + ); + } + + let ignored_test_fixture = r#" + #[cfg(test)] + mod tests { + fn legacy() { + save_provider(); + let _ = "CREATE TABLE ignored (id INTEGER)"; + let _ = "DELETE FROM providers"; + } + } + "#; + let (violations, _) = scan_source("src/services/ignored.rs", ignored_test_fixture); + assert!( + violations.is_empty(), + "#[cfg(test)] content must be excluded: {violations:?}" + ); +} diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index def70df1f..1236b3bde 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -39,6 +39,9 @@ mod tray; mod usage_events; mod usage_script; +#[cfg(test)] +mod architecture_tests; + pub use app_config::{AppType, InstalledSkill, McpApps, McpServer, MultiAppConfig, SkillApps}; pub use codex_config::{get_codex_auth_path, get_codex_config_path, write_codex_live_atomic}; pub use commands::open_provider_terminal; diff --git a/src-tauri/src/pi_config/composer.rs b/src-tauri/src/pi_config/composer.rs new file mode 100644 index 000000000..29decaed9 --- /dev/null +++ b/src-tauri/src/pi_config/composer.rs @@ -0,0 +1,752 @@ +//! Credential-blind Pi native model composition. +//! +//! The only Pi-layer input is [`PiRawValidProvider`]. This module does not +//! import managed DTOs or gateway families, and it never resolves credentials, +//! environment variables, commands, files, or network resources. + +#![allow(dead_code)] + +use super::raw_schema::{PiRawApiId, PiRawValidProvider}; +use serde_json::{json, Map, Value}; +use std::collections::{BTreeMap, HashSet}; + +const PROVIDER_FIELDS: &[&str] = &[ + "name", + "baseUrl", + "apiKey", + "api", + "oauth", + "headers", + "compat", + "authHeader", + "models", + "modelOverrides", +]; +const MODEL_FIELDS: &[&str] = &[ + "id", + "name", + "baseUrl", + "api", + "reasoning", + "thinkingLevelMap", + "input", + "cost", + "contextWindow", + "maxTokens", + "headers", + "compat", +]; +const OVERRIDE_FIELDS: &[&str] = &[ + "name", + "reasoning", + "thinkingLevelMap", + "input", + "cost", + "contextWindow", + "maxTokens", + "headers", + "compat", +]; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) enum PiComposerStatus { + Composed, + Failed, + Unknown, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) enum PiComposerReasonCode { + CatalogRequired, + MissingExplicitModels, + MissingEffectiveApi, + MissingEffectiveEndpoint, + NonPositiveModelLimit, + CompositionFailed, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub(crate) struct PiComposerReason { + pub code: PiComposerReasonCode, + pub json_pointer: String, +} + +/// The lossless native result of pinned Pi composition. +#[derive(Debug, Clone, PartialEq)] +pub(crate) struct PiComposedNativeModel { + pub id: String, + pub name: String, + pub api: PiRawApiId, + pub provider: String, + pub base_url: String, + pub reasoning: bool, + pub thinking_level_map: Option, + pub input: Value, + pub cost: Value, + pub context_window: Value, + pub max_tokens: Value, + pub headers: BTreeMap, + pub compat: Option, + pub api_key: Option, + pub oauth: Option, + pub auth_header: bool, + pub provider_extra: BTreeMap, + pub model_extra: BTreeMap, + pub override_extra: BTreeMap, +} + +#[derive(Debug, Clone, PartialEq)] +pub(crate) struct PiNativeComposition { + pub status: PiComposerStatus, + pub provider_id: Option, + pub provider_name: Option, + pub provider_base_url: Option, + pub models: Vec, + pub ignored_override_keys: Vec, + pub reasons: Vec, +} + +impl PiNativeComposition { + pub(super) fn unavailable_without_valid_raw() -> Self { + Self { + status: PiComposerStatus::Unknown, + provider_id: None, + provider_name: None, + provider_base_url: None, + models: Vec::new(), + ignored_override_keys: Vec::new(), + reasons: Vec::new(), + } + } + + pub(super) fn catalog_required(pointer: &str) -> Self { + Self { + status: PiComposerStatus::Unknown, + provider_id: None, + provider_name: None, + provider_base_url: None, + models: Vec::new(), + ignored_override_keys: Vec::new(), + reasons: vec![PiComposerReason { + code: PiComposerReasonCode::CatalogRequired, + json_pointer: pointer.to_string(), + }], + } + } + + fn failed(code: PiComposerReasonCode, pointer: impl Into) -> Self { + Self { + status: PiComposerStatus::Failed, + provider_id: None, + provider_name: None, + provider_base_url: None, + models: Vec::new(), + ignored_override_keys: Vec::new(), + reasons: vec![PiComposerReason { + code, + json_pointer: pointer.into(), + }], + } + } +} + +pub(super) fn compose_explicit_custom_catalog( + provider_id: &str, + provider: &PiRawValidProvider, +) -> PiNativeComposition { + let Some(provider_object) = provider.raw().as_object() else { + return PiNativeComposition::failed(PiComposerReasonCode::CompositionFailed, ""); + }; + let Some(definitions) = provider_object + .get("models") + .and_then(Value::as_array) + .filter(|models| !models.is_empty()) + else { + return PiNativeComposition::failed(PiComposerReasonCode::MissingExplicitModels, "/models"); + }; + + let provider_api = provider_object.get("api").and_then(Value::as_str); + let provider_base_url = provider_object.get("baseUrl").and_then(Value::as_str); + if provider_object.get("oauth").and_then(Value::as_str) == Some("radius") + && provider_base_url.is_none() + { + return PiNativeComposition::failed( + PiComposerReasonCode::MissingEffectiveEndpoint, + "/baseUrl", + ); + } + let provider_compat = provider_object.get("compat").cloned(); + let provider_headers = string_map(provider_object.get("headers")); + let provider_extra = unknown_fields(provider_object, PROVIDER_FIELDS); + let api_key = provider_object + .get("apiKey") + .and_then(Value::as_str) + .map(ToOwned::to_owned); + let oauth = provider_object.get("oauth").cloned(); + let auth_header = provider_object + .get("authHeader") + .and_then(Value::as_bool) + .unwrap_or(false); + let overrides = provider_object + .get("modelOverrides") + .and_then(Value::as_object); + + let mut models: Vec = Vec::with_capacity(definitions.len()); + for (index, definition_value) in definitions.iter().enumerate() { + let Some(definition) = definition_value.as_object() else { + return PiNativeComposition::failed( + PiComposerReasonCode::CompositionFailed, + format!("/models/{index}"), + ); + }; + let Some(id) = definition.get("id").and_then(Value::as_str) else { + return PiNativeComposition::failed( + PiComposerReasonCode::CompositionFailed, + format!("/models/{index}/id"), + ); + }; + let existing_index = models.iter().position(|model| model.id == id); + let defaults = existing_index + .and_then(|position| models.get(position)) + .or_else(|| models.first()); + + let api_value = definition + .get("api") + .and_then(Value::as_str) + .or(provider_api) + .or_else(|| defaults.map(|model| model.api.as_str())); + let Some(api_value) = api_value else { + return PiNativeComposition::failed( + PiComposerReasonCode::MissingEffectiveApi, + format!("/models/{index}/api"), + ); + }; + let Some(api) = PiRawApiId::new(api_value) else { + return PiNativeComposition::failed( + PiComposerReasonCode::MissingEffectiveApi, + format!("/models/{index}/api"), + ); + }; + + let base_url = definition + .get("baseUrl") + .and_then(Value::as_str) + .or(provider_base_url) + .or_else(|| defaults.map(|model| model.base_url.as_str())); + let Some(base_url) = base_url.filter(|value| !value.is_empty()) else { + return PiNativeComposition::failed( + PiComposerReasonCode::MissingEffectiveEndpoint, + format!("/models/{index}/baseUrl"), + ); + }; + + for (field, code) in [ + ("contextWindow", PiComposerReasonCode::NonPositiveModelLimit), + ("maxTokens", PiComposerReasonCode::NonPositiveModelLimit), + ] { + if definition + .get(field) + .and_then(Value::as_f64) + .is_some_and(|value| value <= 0.0) + { + return PiNativeComposition::failed(code, format!("/models/{index}/{field}")); + } + } + + let model = PiComposedNativeModel { + id: id.to_string(), + name: definition + .get("name") + .and_then(Value::as_str) + .unwrap_or(id) + .to_string(), + api, + provider: provider_id.to_string(), + base_url: base_url.to_string(), + reasoning: definition + .get("reasoning") + .and_then(Value::as_bool) + .unwrap_or(false), + thinking_level_map: definition.get("thinkingLevelMap").cloned(), + input: definition + .get("input") + .cloned() + .unwrap_or_else(|| json!(["text"])), + cost: definition.get("cost").cloned().unwrap_or_else(default_cost), + context_window: definition + .get("contextWindow") + .cloned() + .unwrap_or_else(|| json!(128000)), + max_tokens: definition + .get("maxTokens") + .cloned() + .unwrap_or_else(|| json!(16384)), + headers: BTreeMap::new(), + compat: merge_compat(provider_compat.clone(), definition.get("compat").cloned()), + api_key: api_key.clone(), + oauth: oauth.clone(), + auth_header, + provider_extra: provider_extra.clone(), + model_extra: unknown_fields(definition, MODEL_FIELDS), + override_extra: BTreeMap::new(), + }; + if let Some(existing_index) = existing_index { + models[existing_index] = model; + } else { + models.push(model); + } + } + + for model in &mut models { + // Pinned Pi's rawModelHeaders uses Array.find, so duplicate model + // definitions obtain request headers from the first definition even + // though the later definition replaces the composed model slot. + let definition = definitions + .iter() + .filter_map(Value::as_object) + .find(|definition| { + definition.get("id").and_then(Value::as_str) == Some(model.id.as_str()) + }) + .expect("raw-valid composed model has a source definition"); + let model_override = + overrides.and_then(|overrides| overrides.get(&model.id).and_then(Value::as_object)); + + let mut headers = provider_headers.clone(); + if let Some(model_override) = model_override { + headers.extend(string_map(model_override.get("headers"))); + } + headers.extend(string_map(definition.get("headers"))); + model.headers = headers; + + if let Some(model_override) = model_override { + if let Some(name) = model_override.get("name").and_then(Value::as_str) { + model.name = name.to_string(); + } + if let Some(reasoning) = model_override.get("reasoning").and_then(Value::as_bool) { + model.reasoning = reasoning; + } + if let Some(override_map) = model_override + .get("thinkingLevelMap") + .and_then(Value::as_object) + { + let mut merged = model + .thinking_level_map + .take() + .and_then(|value| value.as_object().cloned()) + .unwrap_or_default(); + merged.extend(override_map.clone()); + model.thinking_level_map = Some(Value::Object(merged)); + } + if let Some(input) = model_override.get("input") { + model.input = input.clone(); + } + if let Some(cost) = model_override.get("cost").and_then(Value::as_object) { + model.cost = merge_cost(&model.cost, cost); + } + if let Some(context_window) = model_override.get("contextWindow") { + model.context_window = context_window.clone(); + } + if let Some(max_tokens) = model_override.get("maxTokens") { + model.max_tokens = max_tokens.clone(); + } + model.compat = + merge_compat(model.compat.clone(), model_override.get("compat").cloned()); + model.override_extra = unknown_fields(model_override, OVERRIDE_FIELDS); + } + } + + let model_ids = models + .iter() + .map(|model| model.id.as_str()) + .collect::>(); + let ignored_override_keys = overrides + .into_iter() + .flat_map(|overrides| overrides.keys()) + .filter(|model_id| !model_ids.contains(model_id.as_str())) + .cloned() + .collect(); + + PiNativeComposition { + status: PiComposerStatus::Composed, + provider_id: Some(provider_id.to_string()), + provider_name: Some( + provider_object + .get("name") + .and_then(Value::as_str) + .unwrap_or(provider_id) + .to_string(), + ), + provider_base_url: provider_base_url.map(ToOwned::to_owned), + models, + ignored_override_keys, + reasons: Vec::new(), + } +} + +fn default_cost() -> Value { + json!({ + "input": 0, + "output": 0, + "cacheRead": 0, + "cacheWrite": 0 + }) +} + +fn merge_cost(base: &Value, overlay: &Map) -> Value { + let mut merged = base.as_object().cloned().unwrap_or_default(); + for key in ["input", "output", "cacheRead", "cacheWrite", "tiers"] { + if let Some(value) = overlay.get(key) { + merged.insert(key.to_string(), value.clone()); + } + } + Value::Object(merged) +} + +fn merge_compat(base: Option, overlay: Option) -> Option { + let Some(overlay) = overlay else { + return base; + }; + let mut merged = base + .as_ref() + .and_then(Value::as_object) + .cloned() + .unwrap_or_default(); + let Some(overlay_object) = overlay.as_object() else { + return Some(overlay); + }; + for (key, value) in overlay_object { + if matches!( + key.as_str(), + "openRouterRouting" | "vercelGatewayRouting" | "chatTemplateKwargs" + ) { + let base_nested = merged.get(key).and_then(Value::as_object); + let override_nested = value.as_object(); + if base_nested.is_some() || override_nested.is_some() { + let mut nested = base_nested.cloned().unwrap_or_default(); + if let Some(override_nested) = override_nested { + nested.extend(override_nested.clone()); + } + merged.insert(key.clone(), Value::Object(nested)); + continue; + } + } + merged.insert(key.clone(), value.clone()); + } + Some(Value::Object(merged)) +} + +fn string_map(value: Option<&Value>) -> BTreeMap { + value + .and_then(Value::as_object) + .into_iter() + .flat_map(|object| object.iter()) + .filter_map(|(key, value)| value.as_str().map(|value| (key.clone(), value.to_string()))) + .collect() +} + +fn unknown_fields(object: &Map, recognized: &[&str]) -> BTreeMap { + object + .iter() + .filter(|(key, _)| !recognized.contains(&key.as_str())) + .map(|(key, value)| (key.clone(), value.clone())) + .collect() +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::pi_config::raw_schema::{evaluate_provider_value, PiRawValidity}; + use serde::Deserialize; + + const COMPOSER_ORACLE_SOURCE: &str = + include_str!("../../../tests/fixtures/pi/native-oracle/composer-oracle-v1.json"); + + #[derive(Deserialize)] + #[serde(rename_all = "camelCase")] + struct ComposerOracle { + cases: Vec, + fail_closed_cases: Vec, + } + + #[derive(Deserialize)] + #[serde(rename_all = "camelCase")] + struct ComposerOracleCase { + id: String, + provider_id: String, + input: Value, + execution: Execution, + #[serde(default)] + auth_execution: Option, + #[serde(default)] + expected: Option, + #[serde(default)] + expected_error: Option, + } + + #[derive(Deserialize)] + struct Execution { + status: String, + } + + #[derive(Deserialize)] + #[serde(rename_all = "camelCase")] + struct FailClosedCase { + id: String, + rust_expected_status: String, + reason_code: String, + } + + fn model_as_oracle_value(model: &PiComposedNativeModel) -> Value { + let mut object = Map::new(); + object.insert("id".into(), json!(model.id)); + object.insert("name".into(), json!(model.name)); + object.insert("api".into(), json!(model.api.as_str())); + object.insert("provider".into(), json!(model.provider)); + object.insert("baseUrl".into(), json!(model.base_url)); + object.insert("reasoning".into(), json!(model.reasoning)); + if let Some(thinking) = &model.thinking_level_map { + object.insert("thinkingLevelMap".into(), thinking.clone()); + } + object.insert("input".into(), model.input.clone()); + object.insert("cost".into(), model.cost.clone()); + object.insert("contextWindow".into(), model.context_window.clone()); + object.insert("maxTokens".into(), model.max_tokens.clone()); + object.insert("authHeader".into(), json!(model.auth_header)); + if let Some(compat) = &model.compat { + object.insert("compat".into(), compat.clone()); + } + if !model.headers.is_empty() { + object.insert("headers".into(), json!(model.headers)); + } + Value::Object(object) + } + + fn provider_as_oracle_value(composition: &PiNativeComposition) -> Value { + let mut object = Map::new(); + object.insert( + "id".into(), + json!(composition + .provider_id + .as_ref() + .expect("composed provider id")), + ); + object.insert( + "name".into(), + json!(composition + .provider_name + .as_ref() + .expect("composed provider name")), + ); + if let Some(base_url) = &composition.provider_base_url { + object.insert("baseUrl".into(), json!(base_url)); + } + Value::Object(object) + } + + fn json_numbers_equal(left: &Value, right: &Value) -> bool { + match (left, right) { + (Value::Number(left), Value::Number(right)) => left.as_f64() == right.as_f64(), + (Value::Array(left), Value::Array(right)) => { + left.len() == right.len() + && left + .iter() + .zip(right) + .all(|(left, right)| json_numbers_equal(left, right)) + } + (Value::Object(left), Value::Object(right)) => { + left.len() == right.len() + && left.iter().all(|(key, left)| { + right + .get(key) + .is_some_and(|right| json_numbers_equal(left, right)) + }) + } + _ => left == right, + } + } + + #[test] + fn rust_composer_matches_actual_pinned_upstream_execution() { + let oracle: ComposerOracle = + serde_json::from_str(COMPOSER_ORACLE_SOURCE).expect("parse composer oracle"); + for case in oracle.cases { + let raw = evaluate_provider_value(&case.input); + if case.execution.status == "error" { + assert!( + case.expected_error.is_some(), + "upstream error vector '{}' records its actual error", + case.id + ); + match raw.validity { + PiRawValidity::Invalid => {} + PiRawValidity::Valid => { + let result = compose_explicit_custom_catalog( + &case.provider_id, + raw.valid_provider.as_ref().expect("raw-valid provider"), + ); + assert_eq!( + result.status, + PiComposerStatus::Failed, + "raw-valid upstream error case '{}'", + case.id + ); + } + PiRawValidity::Unknown => { + panic!("oracle case '{}' unexpectedly became Unknown", case.id) + } + } + continue; + } + assert_eq!(raw.validity, PiRawValidity::Valid, "case '{}'", case.id); + let result = compose_explicit_custom_catalog( + &case.provider_id, + raw.valid_provider.as_ref().expect("raw-valid provider"), + ); + assert_eq!( + result.status, + PiComposerStatus::Composed, + "case '{}'", + case.id + ); + let auth_execution = case + .auth_execution + .as_ref() + .expect("successful composer case records actual auth execution"); + assert_eq!( + auth_execution.pointer("/status").and_then(Value::as_str), + Some("success"), + "case '{}'", + case.id + ); + let actual_resolved_key = auth_execution + .pointer("/result/auth/apiKey") + .and_then(Value::as_str) + .expect("successful literal composer vector resolves an API key"); + assert!( + result + .models + .iter() + .all(|model| { model.api_key.as_deref() == Some(actual_resolved_key) }), + "case '{}' preserves the same literal key that pinned Pi resolved", + case.id + ); + if case + .input + .get("authHeader") + .and_then(Value::as_bool) + .unwrap_or(false) + { + let expected_bearer = format!("Bearer {actual_resolved_key}"); + assert_eq!( + auth_execution + .pointer("/result/auth/headers/Authorization") + .and_then(Value::as_str), + Some(expected_bearer.as_str()), + "case '{}' uses pinned Pi authHeader behavior", + case.id + ); + } + let actual = json!({ + "provider": provider_as_oracle_value(&result), + "models": result + .models + .iter() + .map(model_as_oracle_value) + .collect::>(), + "ignoredOverrideKeys": result.ignored_override_keys, + }); + let expected = case.expected.expect("successful upstream expected output"); + assert!( + json_numbers_equal(&actual, &expected), + "oracle case '{}'\nactual: {actual:#}\nexpected: {expected:#}", + case.id + ); + } + } + + #[test] + fn unavailable_upstream_catalog_semantics_are_explicitly_unknown() { + let oracle: ComposerOracle = + serde_json::from_str(COMPOSER_ORACLE_SOURCE).expect("parse composer oracle"); + assert_eq!(oracle.fail_closed_cases.len(), 2); + for case in oracle.fail_closed_cases { + assert_eq!(case.rust_expected_status, "unknown", "case '{}'", case.id); + assert_eq!(case.reason_code, "catalog_required", "case '{}'", case.id); + let result = PiNativeComposition::catalog_required("/models"); + assert_eq!(result.status, PiComposerStatus::Unknown); + assert_eq!( + result.reasons[0].code, + PiComposerReasonCode::CatalogRequired + ); + } + } + + #[test] + fn credential_expressions_are_preserved_without_execution() { + let value = json!({ + "api": "openai-responses", + "baseUrl": "https://example.test/v1", + "apiKey": "!read-secret", + "oauth": "radius", + "authHeader": true, + "headers": {"x-tenant": "${TENANT}"}, + "models": [{"id": "m"}] + }); + let raw = evaluate_provider_value(&value); + let composed = compose_explicit_custom_catalog( + "deferred", + raw.valid_provider.as_ref().expect("raw-valid"), + ); + assert_eq!(composed.status, PiComposerStatus::Composed); + assert_eq!(composed.models[0].api_key.as_deref(), Some("!read-secret")); + assert_eq!(composed.models[0].oauth, Some(json!("radius"))); + assert!(composed.models[0].auth_header); + assert_eq!(composed.models[0].headers["x-tenant"], "${TENANT}"); + } + + #[test] + fn unknown_provider_model_and_override_fields_are_retained_losslessly() { + let value = json!({ + "api": "future-wire-v9", + "baseUrl": "https://example.test/v9", + "apiKey": "literal", + "futureProviderShape": { + "nested": [1, {"flag": true}] + }, + "models": [{ + "id": "m", + "futureModelShape": { + "mode": "novel", + "threshold": 0.125 + } + }], + "modelOverrides": { + "m": { + "futureOverrideShape": [ + null, + {"preserve": "exactly"} + ] + } + } + }); + let raw = evaluate_provider_value(&value); + let composed = compose_explicit_custom_catalog( + "lossless", + raw.valid_provider.as_ref().expect("raw-valid"), + ); + assert_eq!(composed.status, PiComposerStatus::Composed); + let model = &composed.models[0]; + assert_eq!( + model.provider_extra["futureProviderShape"], + json!({"nested": [1, {"flag": true}]}) + ); + assert_eq!( + model.model_extra["futureModelShape"], + json!({"mode": "novel", "threshold": 0.125}) + ); + assert_eq!( + model.override_extra["futureOverrideShape"], + json!([null, {"preserve": "exactly"}]) + ); + } +} diff --git a/src-tauri/src/pi_config/document.rs b/src-tauri/src/pi_config/document.rs new file mode 100644 index 000000000..01e89f71b --- /dev/null +++ b/src-tauri/src/pi_config/document.rs @@ -0,0 +1,334 @@ +//! Read-only access to Pi's shared `models.json` document. +//! +//! The semantic parse mirrors Pi's pinned `stripJsonComments()` behavior. +//! A CST parse is additionally required so callers can fingerprint one exact +//! provider value without making unrelated entries part of the revision. + +use crate::error::AppError; +use indexmap::IndexMap; +use jsonc_parser::cst::{CstContainerNode, CstNode, CstObject, CstObjectProp, CstRootNode}; +use jsonc_parser::ParseOptions; +use regex::Regex; +use serde_json::Value; +use std::fs::{self, File, Metadata, OpenOptions}; +use std::io::{Read, Take}; +use std::path::Path; +use std::sync::LazyLock; + +const MAX_PI_MODELS_BYTES: u64 = 8 * 1024 * 1024; +const EMPTY_MODELS_DOCUMENT: &str = "{\"providers\":{}}"; + +static PI_JSON_LINE_COMMENTS: LazyLock = LazyLock::new(|| { + Regex::new(r#""(?:\\.|[^"\\])*"|//[^\n]*"#).expect("Pi JSON line-comment regex must compile") +}); +static PI_JSON_TRAILING_COMMAS: LazyLock = LazyLock::new(|| { + Regex::new(r#""(?:\\.|[^"\\])*"|,(\s*[}\]])"#) + .expect("Pi JSON trailing-comma regex must compile") +}); + +#[derive(Debug, Clone)] +pub(super) struct PiRawProviderEntry { + pub value: Value, + pub raw_source: String, +} + +#[derive(Debug, Clone)] +pub(super) struct PiModelsDocument { + providers: IndexMap, +} + +impl PiModelsDocument { + pub fn providers(&self) -> &IndexMap { + &self.providers + } +} + +fn pi_models_parse_options() -> ParseOptions { + // Pinned Pi accepts standard double-quoted JSON with `//` comments and + // trailing commas. jsonc-parser is broader by default, so all other + // extensions stay disabled. + ParseOptions { + allow_comments: true, + allow_loose_object_property_names: false, + allow_trailing_commas: true, + allow_missing_commas: false, + allow_single_quoted_strings: false, + allow_hexadecimal_numbers: false, + allow_unary_plus_numbers: false, + } +} + +fn jsonc_error(path: &Path, message: impl std::fmt::Display) -> AppError { + AppError::Config(format!( + "JSON parse error in Pi models file {}: {message}", + path.display() + )) +} + +/// Mirrors Pi commit `ab366ebe94cacd419d986be454f12b1b9913aaca` +/// (`packages/coding-agent/src/utils/json.ts`). +fn strip_pi_json_comments(input: &str) -> String { + let without_comments = + PI_JSON_LINE_COMMENTS.replace_all(input, |captures: ®ex::Captures<'_>| { + let matched = captures + .get(0) + .expect("the full regex match is always present") + .as_str(); + if matched.starts_with('"') { + matched.to_string() + } else { + String::new() + } + }); + PI_JSON_TRAILING_COMMAS + .replace_all(&without_comments, |captures: ®ex::Captures<'_>| { + captures + .get(1) + .or_else(|| captures.get(0)) + .expect("the full regex match is always present") + .as_str() + .to_string() + }) + .into_owned() +} + +fn cst_property_name(property: &CstObjectProp) -> Option { + property.name()?.decoded_value().ok() +} + +fn last_cst_property(object: &CstObject, name: &str) -> Option { + object + .properties() + .into_iter() + .rev() + .find(|property| cst_property_name(property).as_deref() == Some(name)) +} + +fn cst_object(node: CstNode, path: &Path, label: &str) -> Result { + match node { + CstNode::Container(CstContainerNode::Object(object)) => Ok(object), + _ => Err(jsonc_error(path, format!("{label} must be an object"))), + } +} + +fn parse_models_source(path: &Path, source: &str) -> Result { + let document: Value = serde_json::from_str(&strip_pi_json_comments(source)) + .map_err(|error| AppError::json(path, error))?; + let root = CstRootNode::parse(source, &pi_models_parse_options()) + .map_err(|error| jsonc_error(path, error))?; + if root.to_serde_value().as_ref() != Some(&document) { + return Err(jsonc_error(path, "CST does not match Pi's parsed document")); + } + + let semantic_providers = document + .as_object() + .and_then(|root| root.get("providers")) + .and_then(Value::as_object) + .ok_or_else(|| jsonc_error(path, "root must contain a providers object"))?; + + let root_object = cst_object( + root.value() + .ok_or_else(|| jsonc_error(path, "document must contain a JSON value"))?, + path, + "root", + )?; + let providers_node = last_cst_property(&root_object, "providers") + .and_then(|property| property.value()) + .ok_or_else(|| jsonc_error(path, "CST is missing the providers value"))?; + let providers_object = cst_object(providers_node, path, "providers")?; + + let mut providers = IndexMap::with_capacity(semantic_providers.len()); + for (provider_key, value) in semantic_providers { + let raw_source = last_cst_property(&providers_object, provider_key) + .and_then(|property| property.value()) + .ok_or_else(|| { + jsonc_error( + path, + format!("CST is missing provider entry '{provider_key}'"), + ) + })? + .to_string(); + providers.insert( + provider_key.clone(), + PiRawProviderEntry { + value: value.clone(), + raw_source, + }, + ); + } + Ok(PiModelsDocument { providers }) +} + +#[cfg(unix)] +fn open_read_only(path: &Path) -> std::io::Result { + use std::os::unix::fs::OpenOptionsExt; + + OpenOptions::new() + .read(true) + .custom_flags(libc::O_NOFOLLOW) + .open(path) +} + +#[cfg(not(unix))] +fn open_read_only(path: &Path) -> std::io::Result { + OpenOptions::new().read(true).open(path) +} + +#[cfg(unix)] +fn same_file(opened: &Metadata, current: &Metadata) -> bool { + use std::os::unix::fs::MetadataExt; + + opened.dev() == current.dev() && opened.ino() == current.ino() +} + +#[cfg(not(unix))] +fn same_file(opened: &Metadata, current: &Metadata) -> bool { + opened.len() == current.len() && opened.modified().ok() == current.modified().ok() +} + +fn read_limited(mut reader: Take<&mut File>, path: &Path) -> Result, AppError> { + let mut bytes = Vec::new(); + reader + .read_to_end(&mut bytes) + .map_err(|error| AppError::io(path, error))?; + if bytes.len() as u64 > MAX_PI_MODELS_BYTES { + return Err(AppError::Config(format!( + "Pi models file exceeds {} bytes: {}", + MAX_PI_MODELS_BYTES, + path.display() + ))); + } + Ok(bytes) +} + +fn read_models_bytes(path: &Path) -> Result>, AppError> { + let initial = match fs::symlink_metadata(path) { + Ok(metadata) => metadata, + Err(error) if error.kind() == std::io::ErrorKind::NotFound => return Ok(None), + Err(error) => return Err(AppError::io(path, error)), + }; + if !initial.file_type().is_file() { + return Err(AppError::Config(format!( + "Pi models path must be a regular file, not a symlink or special file: {}", + path.display() + ))); + } + if initial.len() > MAX_PI_MODELS_BYTES { + return Err(AppError::Config(format!( + "Pi models file exceeds {} bytes: {}", + MAX_PI_MODELS_BYTES, + path.display() + ))); + } + + let mut file = open_read_only(path).map_err(|error| AppError::io(path, error))?; + let opened = file.metadata().map_err(|error| AppError::io(path, error))?; + if !opened.file_type().is_file() || opened.len() > MAX_PI_MODELS_BYTES { + return Err(AppError::Config(format!( + "Pi models path changed or exceeded its size limit: {}", + path.display() + ))); + } + let bytes = read_limited(file.by_ref().take(MAX_PI_MODELS_BYTES + 1), path)?; + let completed = file.metadata().map_err(|error| AppError::io(path, error))?; + + let current = fs::symlink_metadata(path).map_err(|error| AppError::io(path, error))?; + if !current.file_type().is_file() + || !same_file(&completed, ¤t) + || opened.len() != bytes.len() as u64 + || completed.len() != bytes.len() as u64 + || current.len() != bytes.len() as u64 + || opened.modified().ok() != completed.modified().ok() + { + return Err(AppError::Config(format!( + "Pi models file changed during inspection: {}", + path.display() + ))); + } + Ok(Some(bytes)) +} + +pub(super) fn read_pi_models_document(path: &Path) -> Result { + let bytes = + read_models_bytes(path)?.unwrap_or_else(|| EMPTY_MODELS_DOCUMENT.as_bytes().to_vec()); + let source = std::str::from_utf8(&bytes) + .map_err(|error| jsonc_error(path, format!("file is not UTF-8: {error}")))?; + parse_models_source(path, source) +} + +#[cfg(test)] +mod tests { + use super::*; + use std::io::Write; + + #[test] + fn reads_exact_pi_jsonc_dialect_and_retains_entry_cst() { + let temp = tempfile::tempdir().expect("tempdir"); + let path = temp.path().join("models.json"); + fs::write( + &path, + r#"{ + // root + "providers": { + "custom": { + // nested comment + "baseUrl": "https://example.test/v1", + "models": [{"id": "model//literal",},], + }, + }, +} +"#, + ) + .expect("write"); + + let document = read_pi_models_document(&path).expect("read"); + let entry = &document.providers()["custom"]; + assert_eq!(entry.value["models"][0]["id"], "model//literal"); + assert!(entry.raw_source.contains("// nested comment")); + assert!(entry.raw_source.contains("\"model//literal\"")); + } + + #[test] + fn rejects_json_extensions_that_pinned_pi_rejects() { + let temp = tempfile::tempdir().expect("tempdir"); + for (case, source) in [ + ("single-quoted", "{'providers': {}}"), + ("bare-key", "{providers: {}}"), + ("block-comment", "{\"providers\": {/* nope */}}"), + ("missing-comma", "{\"providers\": {} \"other\": 1}"), + ] { + let path = temp.path().join(format!("{case}.json")); + fs::write(&path, source).expect("write"); + assert!(read_pi_models_document(&path).is_err(), "{case} must fail"); + } + } + + #[test] + fn missing_file_is_an_empty_catalog_and_oversized_file_fails() { + let temp = tempfile::tempdir().expect("tempdir"); + let missing = temp.path().join("missing-models.json"); + assert!(read_pi_models_document(&missing) + .expect("missing") + .providers() + .is_empty()); + + let oversized = temp.path().join("oversized-models.json"); + let mut file = File::create(&oversized).expect("create"); + file.write_all(b"{").expect("seed"); + file.set_len(MAX_PI_MODELS_BYTES + 1).expect("extend"); + assert!(read_pi_models_document(&oversized).is_err()); + } + + #[cfg(unix)] + #[test] + fn symlink_is_never_followed() { + use std::os::unix::fs::symlink; + + let temp = tempfile::tempdir().expect("tempdir"); + let target = temp.path().join("target.json"); + let link = temp.path().join("models.json"); + fs::write(&target, EMPTY_MODELS_DOCUMENT).expect("target"); + symlink(&target, &link).expect("symlink"); + assert!(read_pi_models_document(&link).is_err()); + } +} diff --git a/src-tauri/src/pi_config/gateway.rs b/src-tauri/src/pi_config/gateway.rs new file mode 100644 index 000000000..095d51ecf --- /dev/null +++ b/src-tauri/src/pi_config/gateway.rs @@ -0,0 +1,767 @@ +//! Gateway-only Pi API families and candidate header planning. +//! +//! The four-family enum is intentionally confined to this module. Raw and +//! managed layers retain opaque API identifiers and cannot accidentally reject +//! a future Pi family merely because the gateway has not implemented it. + +#![allow(dead_code)] + +use super::composer::{PiComposedNativeModel, PiComposerStatus, PiNativeComposition}; +use http::{HeaderMap, HeaderName, HeaderValue}; +use url::Url; + +const PROTECTED_HEADERS: &[&str] = &[ + "authorization", + "connection", + "content-length", + "host", + "keep-alive", + "proxy-authenticate", + "proxy-authorization", + "te", + "trailer", + "transfer-encoding", + "upgrade", + "x-api-key", + "x-goog-api-key", +]; +const PROTOCOL_HEADERS: &[&str] = &[ + "anthropic-version", + "anthropic-beta", + "openai-beta", + "openai-version", +]; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub(super) enum PiGatewayApiFamily { + AnthropicMessages, + OpenAiCompletions, + OpenAiResponses, + GoogleGenerativeAi, +} + +impl PiGatewayApiFamily { + pub(super) const ALL: [Self; 4] = [ + Self::AnthropicMessages, + Self::OpenAiCompletions, + Self::OpenAiResponses, + Self::GoogleGenerativeAi, + ]; + + pub(super) const fn as_str(self) -> &'static str { + match self { + Self::AnthropicMessages => "anthropic-messages", + Self::OpenAiCompletions => "openai-completions", + Self::OpenAiResponses => "openai-responses", + Self::GoogleGenerativeAi => "google-generative-ai", + } + } + + fn parse(value: &str) -> Option { + Self::ALL + .into_iter() + .find(|family| family.as_str() == value) + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(super) enum PiGatewayCapability { + Proxyable, + DirectOnly, + Unknown, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(super) enum PiGatewayReasonCode { + UnsupportedFamily, + InvalidEndpoint, + MissingCredential, + InvalidHeaderName, + InvalidHeaderValue, + ProtectedHeader, + DeferredValueUnavailable, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub(super) struct PiGatewayReason { + pub code: PiGatewayReasonCode, + pub json_pointer: String, +} + +#[derive(Debug, Clone)] +pub(super) struct PiGatewayAssessment { + pub capability: PiGatewayCapability, + pub reasons: Vec, + pub plans: Vec, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +struct DeferredHeaderValue { + raw: String, +} + +impl DeferredHeaderValue { + fn new(raw: impl Into) -> Self { + Self { raw: raw.into() } + } + + fn materialize( + &self, + resolver: &impl DeferredValueResolver, + pointer: &str, + ) -> Result { + let materialized = if is_deferred(&self.raw) { + resolver.resolve(&self.raw).ok_or_else(|| PiGatewayReason { + code: PiGatewayReasonCode::DeferredValueUnavailable, + json_pointer: pointer.to_string(), + })? + } else { + self.raw.clone() + }; + HeaderValue::from_str(&materialized).map_err(|_| PiGatewayReason { + code: PiGatewayReasonCode::InvalidHeaderValue, + json_pointer: pointer.to_string(), + }) + } +} + +#[derive(Debug, Clone)] +pub(super) struct CandidateHeaderPlan { + family: PiGatewayApiFamily, + endpoint: Url, + credential: DeferredHeaderValue, + auth_header: bool, + custom_headers: Vec<(HeaderName, String, DeferredHeaderValue)>, + protocol_headers: Vec<(HeaderName, String, DeferredHeaderValue)>, + protocol_identity_predictable: bool, +} + +#[derive(Debug, Clone)] +pub(super) struct MaterializedCandidate { + pub endpoint: Url, + pub headers: HeaderMap, + family: PiGatewayApiFamily, + protocol_headers: HeaderMap, + protocol_identity_predictable: bool, +} + +pub(super) trait DeferredValueResolver { + fn resolve(&self, expression: &str) -> Option; +} + +impl DeferredValueResolver for F +where + F: Fn(&str) -> Option, +{ + fn resolve(&self, expression: &str) -> Option { + self(expression) + } +} + +impl CandidateHeaderPlan { + fn build( + model: &PiComposedNativeModel, + model_index: usize, + ) -> Result> { + let mut reasons = Vec::new(); + let Some(family) = PiGatewayApiFamily::parse(model.api.as_str()) else { + reasons.push(PiGatewayReason { + code: PiGatewayReasonCode::UnsupportedFamily, + json_pointer: format!("/models/{model_index}/api"), + }); + return Err(reasons); + }; + let endpoint = match Url::parse(&model.base_url) { + Ok(endpoint) + if matches!(endpoint.scheme(), "http" | "https") && endpoint.host().is_some() => + { + endpoint + } + _ => { + reasons.push(PiGatewayReason { + code: PiGatewayReasonCode::InvalidEndpoint, + json_pointer: format!("/models/{model_index}/baseUrl"), + }); + return Err(reasons); + } + }; + let Some(credential) = model.api_key.as_ref() else { + reasons.push(PiGatewayReason { + code: PiGatewayReasonCode::MissingCredential, + json_pointer: "/apiKey".to_string(), + }); + return Err(reasons); + }; + + let mut custom_headers = Vec::new(); + let mut protocol_headers = Vec::new(); + let mut protocol_identity_predictable = true; + for (name, value) in &model.headers { + let pointer = format!( + "/models/{model_index}/headers/{}", + escape_json_pointer(name) + ); + let Ok(parsed_name) = HeaderName::from_bytes(name.as_bytes()) else { + reasons.push(PiGatewayReason { + code: PiGatewayReasonCode::InvalidHeaderName, + json_pointer: pointer, + }); + continue; + }; + if PROTECTED_HEADERS.contains(&parsed_name.as_str()) { + reasons.push(PiGatewayReason { + code: PiGatewayReasonCode::ProtectedHeader, + json_pointer: pointer, + }); + continue; + } + if HeaderValue::from_str(value).is_err() { + reasons.push(PiGatewayReason { + code: PiGatewayReasonCode::InvalidHeaderValue, + json_pointer: pointer, + }); + continue; + } + let planned = ( + parsed_name, + pointer, + DeferredHeaderValue::new(value.clone()), + ); + if PROTOCOL_HEADERS.contains(&planned.0.as_str()) { + protocol_identity_predictable &= !value.starts_with('!'); + protocol_headers.push(planned); + } else { + custom_headers.push(planned); + } + } + if !reasons.is_empty() { + return Err(reasons); + } + + Ok(Self { + family, + endpoint, + credential: DeferredHeaderValue::new(credential.clone()), + auth_header: model.auth_header, + custom_headers, + protocol_headers, + protocol_identity_predictable, + }) + } + + pub(super) fn materialize( + &self, + resolver: &impl DeferredValueResolver, + ) -> Result { + // A new map is allocated for every candidate. No value from a prior + // candidate can survive failover. + let mut headers = HeaderMap::new(); + let mut protocol_headers = HeaderMap::new(); + let credential = self.credential.materialize(resolver, "/apiKey")?; + let bearer_credential = credential.clone(); + let (auth_name, auth_value) = match self.family { + PiGatewayApiFamily::AnthropicMessages => { + (HeaderName::from_static("x-api-key"), credential) + } + PiGatewayApiFamily::GoogleGenerativeAi => { + (HeaderName::from_static("x-goog-api-key"), credential) + } + PiGatewayApiFamily::OpenAiCompletions | PiGatewayApiFamily::OpenAiResponses => { + let credential = credential.to_str().map_err(|_| PiGatewayReason { + code: PiGatewayReasonCode::InvalidHeaderValue, + json_pointer: "/apiKey".to_string(), + })?; + let bearer = + HeaderValue::from_str(&format!("Bearer {credential}")).map_err(|_| { + PiGatewayReason { + code: PiGatewayReasonCode::InvalidHeaderValue, + json_pointer: "/apiKey".to_string(), + } + })?; + (HeaderName::from_static("authorization"), bearer) + } + }; + headers.insert(auth_name, auth_value); + if self.auth_header { + let credential = bearer_credential.to_str().map_err(|_| PiGatewayReason { + code: PiGatewayReasonCode::InvalidHeaderValue, + json_pointer: "/apiKey".to_string(), + })?; + let bearer = HeaderValue::from_str(&format!("Bearer {credential}")).map_err(|_| { + PiGatewayReason { + code: PiGatewayReasonCode::InvalidHeaderValue, + json_pointer: "/apiKey".to_string(), + } + })?; + headers.insert(HeaderName::from_static("authorization"), bearer); + } + + for (name, pointer, value) in &self.custom_headers { + headers.insert(name.clone(), value.materialize(resolver, pointer)?); + } + for (name, pointer, value) in &self.protocol_headers { + let value = value.materialize(resolver, pointer)?; + protocol_headers.insert(name.clone(), value.clone()); + headers.insert(name.clone(), value); + } + let host = authority_header(&self.endpoint).ok_or_else(|| PiGatewayReason { + code: PiGatewayReasonCode::InvalidEndpoint, + json_pointer: "/baseUrl".to_string(), + })?; + headers.insert(HeaderName::from_static("host"), host); + Ok(MaterializedCandidate { + endpoint: self.endpoint.clone(), + headers, + family: self.family, + protocol_headers, + protocol_identity_predictable: self.protocol_identity_predictable, + }) + } +} + +impl MaterializedCandidate { + pub(super) fn failover_protocol_identity(&self) -> Option<(PiGatewayApiFamily, &HeaderMap)> { + // Auth, tenant and arbitrary custom headers are deliberately excluded. + self.protocol_identity_predictable + .then_some((self.family, &self.protocol_headers)) + } +} + +pub(super) fn assess_composition(composition: &PiNativeComposition) -> PiGatewayAssessment { + if composition.status != PiComposerStatus::Composed { + return PiGatewayAssessment { + capability: PiGatewayCapability::Unknown, + reasons: Vec::new(), + plans: Vec::new(), + }; + } + let mut plans = Vec::with_capacity(composition.models.len()); + let mut reasons = Vec::new(); + for (index, model) in composition.models.iter().enumerate() { + match CandidateHeaderPlan::build(model, index) { + Ok(plan) => plans.push(plan), + Err(mut model_reasons) => reasons.append(&mut model_reasons), + } + } + if reasons.is_empty() && plans.len() == composition.models.len() { + PiGatewayAssessment { + capability: PiGatewayCapability::Proxyable, + reasons, + plans, + } + } else { + PiGatewayAssessment { + capability: PiGatewayCapability::DirectOnly, + reasons, + plans: Vec::new(), + } + } +} + +fn authority_header(url: &Url) -> Option { + let host = url.host_str()?; + let authority = match url.port() { + Some(port) => format!("{host}:{port}"), + None => host.to_string(), + }; + HeaderValue::from_str(&authority).ok() +} + +fn is_deferred(value: &str) -> bool { + value.starts_with('!') || value.contains('$') +} + +fn escape_json_pointer(value: &str) -> String { + value.replace('~', "~0").replace('/', "~1") +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::pi_config::composer::compose_explicit_custom_catalog; + use crate::pi_config::raw_schema::evaluate_provider_value; + use serde_json::{json, Value}; + use std::collections::BTreeMap; + + const TRANSPORT_ORACLE_SOURCE: &str = + include_str!("../../../tests/fixtures/pi/native-oracle/transport-oracle-v1.json"); + + fn composed(input: serde_json::Value) -> PiNativeComposition { + let raw = evaluate_provider_value(&input); + compose_explicit_custom_catalog( + "candidate", + raw.valid_provider.as_ref().expect("raw-valid input"), + ) + } + + #[test] + fn only_gateway_module_closes_the_four_family_set() { + assert_eq!( + PiGatewayApiFamily::ALL.map(PiGatewayApiFamily::as_str), + [ + "anthropic-messages", + "openai-completions", + "openai-responses", + "google-generative-ai", + ] + ); + } + + #[test] + fn unknown_api_is_composed_but_direct_only() { + let composition = composed(json!({ + "api": "future-wire-v9", + "baseUrl": "https://future.example/v9", + "apiKey": "literal", + "models": [{"id": "future"}] + })); + assert_eq!(composition.status, PiComposerStatus::Composed); + let gateway = assess_composition(&composition); + assert_eq!(gateway.capability, PiGatewayCapability::DirectOnly); + assert_eq!( + gateway.reasons[0].code, + PiGatewayReasonCode::UnsupportedFamily + ); + } + + #[test] + fn capability_plan_rejects_invalid_protected_and_hop_headers() { + for (name, expected) in [ + ("bad header", PiGatewayReasonCode::InvalidHeaderName), + ("Host", PiGatewayReasonCode::ProtectedHeader), + ("Content-Length", PiGatewayReasonCode::ProtectedHeader), + ("Connection", PiGatewayReasonCode::ProtectedHeader), + ] { + let composition = composed(json!({ + "api": "openai-responses", + "baseUrl": "https://example.test/v1", + "apiKey": "literal", + "headers": {name: "value"}, + "models": [{"id": "m"}] + })); + let gateway = assess_composition(&composition); + assert_eq!( + gateway.capability, + PiGatewayCapability::DirectOnly, + "{name}" + ); + assert_eq!(gateway.reasons[0].code, expected, "{name}"); + } + + let composition = composed(json!({ + "api": "openai-responses", + "baseUrl": "https://example.test/v1", + "apiKey": "literal", + "headers": {"x-inject": "ok\r\nbad: value"}, + "models": [{"id": "m"}] + })); + // TypeBox rejects non-header string syntax only at the gateway layer; + // the raw schema intentionally accepts arbitrary strings. + let gateway = assess_composition(&composition); + assert_eq!( + gateway.reasons[0].code, + PiGatewayReasonCode::InvalidHeaderValue + ); + } + + #[test] + fn deferred_materialization_is_per_candidate_and_precedes_network_io() { + let first = composed(json!({ + "api": "openai-responses", + "baseUrl": "https://first.example:8443/v1", + "apiKey": "${FIRST_KEY}", + "headers": {"x-tenant": "${FIRST_TENANT}"}, + "models": [{"id": "m"}] + })); + let second = composed(json!({ + "api": "openai-responses", + "baseUrl": "https://second.example/v1", + "apiKey": "${SECOND_KEY}", + "headers": {"x-tenant": "${SECOND_TENANT}"}, + "models": [{"id": "m"}] + })); + let first_plan = assess_composition(&first).plans.remove(0); + let second_plan = assess_composition(&second).plans.remove(0); + + let first_values = BTreeMap::from([ + ("${FIRST_KEY}".to_string(), "first-secret".to_string()), + ("${FIRST_TENANT}".to_string(), "tenant-a".to_string()), + ]); + let first_materialized = first_plan + .materialize(&|expression: &str| first_values.get(expression).cloned()) + .expect("first materialization"); + assert_eq!( + first_materialized.headers[&HeaderName::from_static("host")], + "first.example:8443" + ); + assert_eq!( + first_materialized.headers[&HeaderName::from_static("authorization")], + "Bearer first-secret" + ); + + let missing = second_plan.materialize(&|_expression: &str| None); + assert_eq!( + missing.expect_err("missing deferred value").code, + PiGatewayReasonCode::DeferredValueUnavailable + ); + + let second_values = BTreeMap::from([ + ("${SECOND_KEY}".to_string(), "second-secret".to_string()), + ("${SECOND_TENANT}".to_string(), "tenant-b".to_string()), + ]); + let second_materialized = second_plan + .materialize(&|expression: &str| second_values.get(expression).cloned()) + .expect("second materialization"); + assert_eq!( + second_materialized.headers[&HeaderName::from_static("host")], + "second.example" + ); + assert_eq!( + second_materialized.headers[&HeaderName::from_static("authorization")], + "Bearer second-secret" + ); + assert_eq!( + second_materialized.headers[&HeaderName::from_static("x-tenant")], + "tenant-b" + ); + assert_ne!( + first_materialized.headers[&HeaderName::from_static("authorization")], + second_materialized.headers[&HeaderName::from_static("authorization")] + ); + assert_eq!( + first_materialized.failover_protocol_identity(), + second_materialized.failover_protocol_identity() + ); + } + + #[test] + fn protocol_identity_uses_final_values_but_excludes_auth_tenant_and_custom_headers() { + let first = composed(json!({ + "api": "anthropic-messages", + "baseUrl": "https://first.example/v1", + "apiKey": "first-secret", + "headers": { + "anthropic-version": "${VERSION_A}", + "x-tenant": "tenant-a", + "x-private": "private-a" + }, + "models": [{"id": "m"}] + })); + let second = composed(json!({ + "api": "anthropic-messages", + "baseUrl": "https://second.example/v1", + "apiKey": "second-secret", + "headers": { + "anthropic-version": "${VERSION_B}", + "x-tenant": "tenant-b", + "x-private": "private-b" + }, + "models": [{"id": "m"}] + })); + let first = assess_composition(&first) + .plans + .remove(0) + .materialize(&|expression: &str| { + (expression == "${VERSION_A}").then(|| "2023-06-01".to_string()) + }) + .expect("first protocol materialization"); + let same_protocol = assess_composition(&second) + .plans + .remove(0) + .materialize(&|expression: &str| { + (expression == "${VERSION_B}").then(|| "2023-06-01".to_string()) + }) + .expect("second protocol materialization"); + assert_eq!( + first.failover_protocol_identity(), + same_protocol.failover_protocol_identity(), + "auth, origin, tenant and arbitrary custom headers do not affect wire identity" + ); + + let changed = composed(json!({ + "api": "anthropic-messages", + "baseUrl": "https://third.example/v1", + "apiKey": "third-secret", + "headers": {"anthropic-version": "2024-01-01"}, + "models": [{"id": "m"}] + })); + let changed = assess_composition(&changed) + .plans + .remove(0) + .materialize(&|_expression: &str| None) + .expect("changed protocol materialization"); + assert_ne!( + first.failover_protocol_identity(), + changed.failover_protocol_identity() + ); + } + + #[test] + fn deferred_protocol_headers_fail_before_candidate_use_and_commands_are_ineligible() { + let composition = composed(json!({ + "api": "openai-responses", + "baseUrl": "https://candidate.example/v1", + "apiKey": "literal", + "headers": {"openai-version": "${OPENAI_VERSION}"}, + "models": [{"id": "m"}] + })); + let plan = assess_composition(&composition).plans.remove(0); + assert_eq!( + plan.materialize(&|_expression: &str| None) + .expect_err("unresolved protocol material") + .code, + PiGatewayReasonCode::DeferredValueUnavailable + ); + + let command = composed(json!({ + "api": "openai-responses", + "baseUrl": "https://candidate.example/v1", + "apiKey": "literal", + "headers": {"openai-version": "!resolve-version"}, + "models": [{"id": "m"}] + })); + let command = assess_composition(&command) + .plans + .remove(0) + .materialize(&|expression: &str| { + (expression == "!resolve-version").then(|| "2024-01-01".to_string()) + }) + .expect("command materializes for direct candidate use"); + assert!( + command.failover_protocol_identity().is_none(), + "unpredictable protocol commands are failover-ineligible" + ); + } + + #[test] + fn auth_header_adds_candidate_local_bearer_without_reusing_another_candidate() { + let composition = composed(json!({ + "api": "anthropic-messages", + "baseUrl": "https://candidate.example/v1", + "apiKey": "${KEY}", + "authHeader": true, + "models": [{"id": "m"}] + })); + let materialized = assess_composition(&composition) + .plans + .remove(0) + .materialize(&|expression: &str| { + (expression == "${KEY}").then(|| "candidate-secret".to_string()) + }) + .expect("authHeader materialization"); + assert_eq!( + materialized.headers[&HeaderName::from_static("x-api-key")], + "candidate-secret" + ); + assert_eq!( + materialized.headers[&HeaderName::from_static("authorization")], + "Bearer candidate-secret" + ); + } + + #[test] + fn four_families_materialize_their_own_auth_headers() { + for (family, auth_name, expected_value) in [ + ("anthropic-messages", "x-api-key", "secret"), + ("openai-completions", "authorization", "Bearer secret"), + ("openai-responses", "authorization", "Bearer secret"), + ("google-generative-ai", "x-goog-api-key", "secret"), + ] { + let composition = composed(json!({ + "api": family, + "baseUrl": "https://candidate.example/v1", + "apiKey": "secret", + "models": [{"id": "m"}] + })); + let mut assessment = assess_composition(&composition); + assert_eq!(assessment.capability, PiGatewayCapability::Proxyable); + let materialized = assessment + .plans + .remove(0) + .materialize(&|_expression: &str| None) + .expect("literal materialization"); + assert_eq!( + materialized.headers[&HeaderName::from_bytes(auth_name.as_bytes()).unwrap()], + expected_value + ); + } + } + + #[test] + fn deferred_values_replay_actual_pinned_pi_transport_results() { + let oracle: Value = + serde_json::from_str(TRANSPORT_ORACLE_SOURCE).expect("parse transport oracle"); + for case in oracle["cases"].as_array().expect("transport cases") { + let input = case["input"].as_str().expect("transport input"); + let composition = composed(json!({ + "api": "openai-responses", + "baseUrl": "https://candidate.example/v1", + "apiKey": input, + "models": [{"id": "m"}] + })); + let plan = assess_composition(&composition).plans.remove(0); + match case.pointer("/execution/status").and_then(Value::as_str) { + Some("success") => { + let expected = case["expected"].as_str().expect("actual Pi result"); + let materialized = plan + .materialize(&|expression: &str| { + (expression == input).then(|| expected.to_string()) + }) + .expect("replay actual Pi resolver result"); + let expected_bearer = format!("Bearer {expected}"); + assert_eq!( + materialized.headers[&HeaderName::from_static("authorization")], + expected_bearer, + "transport case '{}'", + case["id"] + ); + } + Some("error") => { + assert!(case["expectedError"].is_string()); + assert_eq!( + plan.materialize(&|_expression: &str| None) + .expect_err("actual Pi resolver failure must discard candidate") + .code, + PiGatewayReasonCode::DeferredValueUnavailable, + "transport case '{}'", + case["id"] + ); + } + status => panic!("unexpected transport status {status:?}"), + } + } + + let header_case = &oracle["headerCase"]; + let input_headers = header_case["input"] + .as_object() + .expect("transport header input"); + let expected_headers = header_case["expected"] + .as_object() + .expect("actual Pi header output"); + let composition = composed(json!({ + "api": "openai-responses", + "baseUrl": "https://candidate.example/v1", + "apiKey": "literal-key", + "headers": input_headers, + "models": [{"id": "m"}] + })); + let materialized = assess_composition(&composition) + .plans + .remove(0) + .materialize(&|expression: &str| { + input_headers.iter().find_map(|(name, configured)| { + (configured.as_str() == Some(expression)) + .then(|| expected_headers[name].as_str().map(ToOwned::to_owned)) + .flatten() + }) + }) + .expect("replay actual Pi header resolver results"); + for (name, expected) in expected_headers { + let name = HeaderName::from_bytes(name.as_bytes()).expect("oracle header name"); + assert_eq!( + materialized.headers[&name], + expected.as_str().expect("oracle header value") + ); + } + } +} diff --git a/src-tauri/src/pi_config/mod.rs b/src-tauri/src/pi_config/mod.rs index 26198e369..c9f61e2a0 100644 --- a/src-tauri/src/pi_config/mod.rs +++ b/src-tauri/src/pi_config/mod.rs @@ -4,4 +4,9 @@ //! Pi's shared files and from the proxy data plane. Callers must use the //! typed model resolver rather than reimplementing provider/model inheritance. +mod composer; +mod document; +mod gateway; pub(crate) mod model; +pub(crate) mod native; +mod raw_schema; diff --git a/src-tauri/src/pi_config/native.rs b/src-tauri/src/pi_config/native.rs new file mode 100644 index 000000000..f6bd9b2f4 --- /dev/null +++ b/src-tauri/src/pi_config/native.rs @@ -0,0 +1,968 @@ +//! Public, side-effect-free inspection service for Pi's native catalog. +//! +//! This module orchestrates independent raw, managed, composer and gateway +//! assessments. No assessment is allowed to gate execution of a sibling layer. + +#![allow(dead_code)] + +use super::composer::{ + compose_explicit_custom_catalog, PiComposerReasonCode, PiComposerStatus, PiNativeComposition, +}; +use super::document::{read_pi_models_document, PiRawProviderEntry}; +use super::gateway::{ + assess_composition, PiGatewayAssessment, PiGatewayCapability, PiGatewayReasonCode, +}; +use super::model::{ + validate_pi_managed_provider, PiCompositionStatus, PiConfigError, PiDiagnosticLayer, + PiDiagnosticReason, PiGatewayStatus, PiManagedAssessment, PiManagedProviderConfig, + PiManagementStatus, PiNativeDiagnostic, PiNativeEntryKind, PiRawNativeValidity, PiReasonCode, +}; +use super::raw_schema::{ + evaluate_provider_value, PiRawReasonCode, PiRawSchemaEvaluation, PiRawValidity, +}; +use crate::config::get_home_dir; +use crate::error::AppError; +use serde_json::Value; +use sha2::{Digest, Sha256}; +use std::collections::{BTreeMap, HashSet}; +use std::path::{Path, PathBuf}; +use url::Url; + +/// Built-in provider IDs at Pi commit +/// `ab366ebe94cacd419d986be454f12b1b9913aaca`. +const PI_BUILTIN_PROVIDER_KEYS: &[&str] = &[ + "amazon-bedrock", + "ant-ling", + "anthropic", + "azure-openai-responses", + "cerebras", + "cloudflare-ai-gateway", + "cloudflare-workers-ai", + "deepseek", + "fireworks", + "github-copilot", + "google", + "google-vertex", + "groq", + "huggingface", + "kimi-coding", + "minimax", + "minimax-cn", + "mistral", + "moonshotai", + "moonshotai-cn", + "nvidia", + "openai", + "openai-codex", + "opencode", + "opencode-go", + "openrouter", + "qwen-token-plan", + "qwen-token-plan-cn", + "radius", + "together", + "vercel-ai-gateway", + "xai", + "xiaomi", + "xiaomi-token-plan-ams", + "xiaomi-token-plan-cn", + "xiaomi-token-plan-sgp", + "zai", + "zai-coding-cn", +]; + +const RECOGNIZED_PROVIDER_FIELDS: &[&str] = &[ + "name", + "baseUrl", + "apiKey", + "api", + "oauth", + "headers", + "compat", + "authHeader", + "models", + "modelOverrides", +]; + +#[derive(Debug, Clone)] +pub(crate) struct PiNativeEntryInspection { + pub diagnostic: PiNativeDiagnostic, + pub managed_config: Option, + pub composition: PiNativeComposition, +} + +#[derive(Debug)] +struct ManagedResult { + assessment: PiManagedAssessment, + config: Option, + reasons: Vec, +} + +/// The public read-only service entry used by commands and certification tests. +pub(crate) struct PiNativeInspectionService; + +impl PiNativeInspectionService { + pub(crate) fn inspect_current( + managed_claims: &BTreeMap, + ) -> Result, AppError> { + Self::inspect_catalog(&get_pi_models_path()?, managed_claims) + } + + pub(crate) fn inspect_catalog( + path: &Path, + managed_claims: &BTreeMap, + ) -> Result, AppError> { + let document = read_pi_models_document(path)?; + Ok(document + .providers() + .iter() + .map(|(provider_key, entry)| { + analyze_native_entry(provider_key, entry, managed_claims).diagnostic + }) + .collect()) + } + + pub(crate) fn inspect_entry( + path: &Path, + provider_key: &str, + managed_claims: &BTreeMap, + ) -> Result, AppError> { + let document = read_pi_models_document(path)?; + Ok(document + .providers() + .get(provider_key) + .map(|entry| analyze_native_entry(provider_key, entry, managed_claims))) + } +} + +pub(crate) fn inspect_current_pi_native_catalog( + managed_claims: &BTreeMap, +) -> Result, AppError> { + PiNativeInspectionService::inspect_current(managed_claims) +} + +pub(crate) fn inspect_pi_native_catalog( + path: &Path, + managed_claims: &BTreeMap, +) -> Result, AppError> { + PiNativeInspectionService::inspect_catalog(path, managed_claims) +} + +pub(crate) fn inspect_pi_native_entry( + path: &Path, + provider_key: &str, + managed_claims: &BTreeMap, +) -> Result, AppError> { + PiNativeInspectionService::inspect_entry(path, provider_key, managed_claims) +} + +fn normalize_pi_agent_dir(value: &str, home: &Path) -> Result { + if value == "~" { + return Ok(home.to_path_buf()); + } + if let Some(suffix) = value.strip_prefix("~/") { + return Ok(home.join(suffix)); + } + #[cfg(windows)] + if let Some(suffix) = value.strip_prefix("~\\") { + return Ok(home.join(suffix)); + } + if value.starts_with("file://") { + let url = Url::parse(value).map_err(|error| { + AppError::Config(format!("invalid Pi agent directory URL: {error}")) + })?; + return url.to_file_path().map_err(|_| { + AppError::Config(format!( + "Pi agent directory URL is not a local file path: {value}" + )) + }); + } + Ok(PathBuf::from(value)) +} + +pub(crate) fn get_pi_agent_dir() -> Result { + let Some(raw) = std::env::var_os("PI_CODING_AGENT_DIR") else { + return Ok(get_home_dir().join(".pi").join("agent")); + }; + if raw.is_empty() { + return Ok(get_home_dir().join(".pi").join("agent")); + } + normalize_pi_agent_dir(&raw.to_string_lossy(), &get_home_dir()) +} + +pub(crate) fn get_pi_models_path() -> Result { + Ok(get_pi_agent_dir()?.join("models.json")) +} + +fn analyze_native_entry( + provider_key: &str, + entry: &PiRawProviderEntry, + managed_claims: &BTreeMap, +) -> PiNativeEntryInspection { + // Raw, composition and managed conversion are deliberately invoked from + // the same immutable JSON value. Neither result controls whether a sibling + // assessment is attempted. + let raw = evaluate_provider_value(&entry.value); + let raw_validity = map_raw_validity(raw.validity); + let kind = classify_kind(provider_key, &entry.value, raw.validity); + let composition = match (raw.valid_provider.as_ref(), kind) { + (Some(provider), PiNativeEntryKind::CustomCatalog) => { + compose_explicit_custom_catalog(provider_key, provider) + } + (Some(_), _) => PiNativeComposition::catalog_required("/models"), + (None, _) => PiNativeComposition::unavailable_without_valid_raw(), + }; + let managed = assess_managed(raw.validity, kind, &entry.value); + let gateway = if raw.validity == PiRawValidity::Valid { + assess_composition(&composition) + } else { + PiGatewayAssessment { + capability: PiGatewayCapability::Unknown, + reasons: Vec::new(), + plans: Vec::new(), + } + }; + + let management_status = managed_claims + .get(provider_key) + .map(|provider_id| PiManagementStatus::Managed { + provider_id: provider_id.clone(), + }) + .unwrap_or_else(|| { + if raw.validity == PiRawValidity::Valid + && managed.assessment == PiManagedAssessment::Manageable + { + PiManagementStatus::Importable + } else { + PiManagementStatus::Unsupported + } + }); + + let mut reasons = map_raw_reasons(&raw); + extend_reasons(&mut reasons, managed.reasons.clone()); + extend_reasons(&mut reasons, map_composer_reasons(&composition)); + extend_reasons(&mut reasons, map_gateway_reasons(&gateway)); + + PiNativeEntryInspection { + diagnostic: PiNativeDiagnostic { + provider_key: provider_key.to_string(), + display_name: entry + .value + .get("name") + .and_then(Value::as_str) + .map(ToOwned::to_owned), + fingerprint: fingerprint(&entry.raw_source), + kind, + raw_validity, + managed_assessment: managed.assessment, + composition_status: map_composition_status(composition.status), + management_status, + gateway_status: map_gateway_status(gateway.capability), + reasons, + }, + managed_config: managed.config, + composition, + } +} + +fn assess_managed( + raw_validity: PiRawValidity, + kind: PiNativeEntryKind, + value: &Value, +) -> ManagedResult { + if raw_validity != PiRawValidity::Valid { + return ManagedResult { + assessment: PiManagedAssessment::Unsupported, + config: None, + reasons: Vec::new(), + }; + } + if kind != PiNativeEntryKind::CustomCatalog { + let mut reasons = vec![diagnostic_reason( + PiDiagnosticLayer::Managed, + PiReasonCode::CatalogRequired, + "/models", + )]; + if value + .get("modelOverrides") + .and_then(Value::as_object) + .is_some_and(|overrides| !overrides.is_empty()) + { + reasons.push(diagnostic_reason( + PiDiagnosticLayer::Managed, + PiReasonCode::ModelOverridesOnly, + "/modelOverrides", + )); + } + return ManagedResult { + assessment: PiManagedAssessment::Unsupported, + config: None, + reasons, + }; + } + + let config = match serde_json::from_value::(value.clone()) { + Ok(config) => config, + Err(_) => { + return ManagedResult { + assessment: PiManagedAssessment::Unsupported, + config: None, + reasons: vec![diagnostic_reason( + PiDiagnosticLayer::Managed, + PiReasonCode::ManagedTypeConversionFailed, + &managed_conversion_pointer(value), + )], + }; + } + }; + let mut reasons = collect_managed_reasons(&config); + if reasons.is_empty() { + if let Err(error) = validate_pi_managed_provider(&config) { + reasons.push(managed_validation_reason(error)); + } + } + if !reasons.is_empty() { + return ManagedResult { + assessment: PiManagedAssessment::Unsupported, + config: None, + reasons, + }; + } + ManagedResult { + assessment: PiManagedAssessment::Manageable, + config: Some(config), + reasons, + } +} + +fn collect_managed_reasons(config: &PiManagedProviderConfig) -> Vec { + let mut reasons = Vec::new(); + let mut ids = HashSet::with_capacity(config.models.len()); + if config.models.is_empty() { + add_reason( + &mut reasons, + diagnostic_reason( + PiDiagnosticLayer::Managed, + PiReasonCode::MissingExplicitModels, + "/models", + ), + ); + } + if config + .base_url + .as_deref() + .is_some_and(|value| !valid_http_endpoint(value)) + { + add_reason( + &mut reasons, + diagnostic_reason( + PiDiagnosticLayer::Managed, + PiReasonCode::InvalidEndpoint, + "/baseUrl", + ), + ); + } + for (index, model) in config.models.iter().enumerate() { + let pointer = format!("/models/{index}"); + if model.id.trim().is_empty() { + add_reason( + &mut reasons, + diagnostic_reason( + PiDiagnosticLayer::Managed, + PiReasonCode::EmptyModelId, + &format!("{pointer}/id"), + ), + ); + } else if !ids.insert(model.id.as_str()) { + add_reason( + &mut reasons, + diagnostic_reason( + PiDiagnosticLayer::Managed, + PiReasonCode::DuplicateModelId, + &format!("{pointer}/id"), + ), + ); + } + if model + .base_url + .as_deref() + .is_some_and(|value| !valid_http_endpoint(value)) + { + add_reason( + &mut reasons, + diagnostic_reason( + PiDiagnosticLayer::Managed, + PiReasonCode::InvalidEndpoint, + &format!("{pointer}/baseUrl"), + ), + ); + } + if model.api.is_none() && config.api.is_none() { + add_reason( + &mut reasons, + diagnostic_reason( + PiDiagnosticLayer::Managed, + PiReasonCode::MissingEffectiveApi, + &format!("{pointer}/api"), + ), + ); + } + if model.base_url.is_none() && config.base_url.is_none() { + add_reason( + &mut reasons, + diagnostic_reason( + PiDiagnosticLayer::Managed, + PiReasonCode::MissingEffectiveEndpoint, + &format!("{pointer}/baseUrl"), + ), + ); + } + for (field, value) in [ + ("contextWindow", model.context_window), + ("maxTokens", model.max_tokens), + ] { + if value.is_some_and(|value| value.get() <= 0.0) { + add_reason( + &mut reasons, + diagnostic_reason( + PiDiagnosticLayer::Managed, + PiReasonCode::NonPositiveModelLimit, + &format!("{pointer}/{field}"), + ), + ); + } + } + for level in model.thinking_level_map.keys() { + if !is_managed_thinking_level(level) { + add_reason( + &mut reasons, + diagnostic_reason( + PiDiagnosticLayer::Managed, + PiReasonCode::InvalidThinkingLevel, + &format!("{pointer}/thinkingLevelMap/{}", escape_json_pointer(level)), + ), + ); + } + } + } + + for (model_id, model_override) in &config.model_overrides { + let pointer = format!("/modelOverrides/{}", escape_json_pointer(model_id)); + if !ids.contains(model_id.as_str()) { + add_reason( + &mut reasons, + diagnostic_reason( + PiDiagnosticLayer::Managed, + PiReasonCode::UnknownModelOverride, + &pointer, + ), + ); + } + for (field, value) in [ + ("contextWindow", model_override.context_window), + ("maxTokens", model_override.max_tokens), + ] { + if value.is_some_and(|value| value.get() <= 0.0) { + add_reason( + &mut reasons, + diagnostic_reason( + PiDiagnosticLayer::Managed, + PiReasonCode::NonPositiveModelLimit, + &format!("{pointer}/{field}"), + ), + ); + } + } + for level in model_override.thinking_level_map.keys() { + if !is_managed_thinking_level(level) { + add_reason( + &mut reasons, + diagnostic_reason( + PiDiagnosticLayer::Managed, + PiReasonCode::InvalidThinkingLevel, + &format!("{pointer}/thinkingLevelMap/{}", escape_json_pointer(level)), + ), + ); + } + } + } + reasons +} + +fn managed_conversion_pointer(value: &Value) -> String { + if let Some(models) = value.get("models").and_then(Value::as_array) { + for (index, model) in models.iter().enumerate() { + if let Some(map) = model.get("thinkingLevelMap").and_then(Value::as_object) { + for (key, value) in map { + if !(value.is_string() || value.is_null()) { + return format!( + "/models/{index}/thinkingLevelMap/{}", + escape_json_pointer(key) + ); + } + } + } + } + } + if let Some(overrides) = value.get("modelOverrides").and_then(Value::as_object) { + for (model_id, model_override) in overrides { + if let Some(map) = model_override + .get("thinkingLevelMap") + .and_then(Value::as_object) + { + for (key, value) in map { + if !(value.is_string() || value.is_null()) { + return format!( + "/modelOverrides/{}/thinkingLevelMap/{}", + escape_json_pointer(model_id), + escape_json_pointer(key) + ); + } + } + } + } + } + String::new() +} + +fn managed_validation_reason(error: PiConfigError) -> PiDiagnosticReason { + let (code, pointer) = match error { + PiConfigError::ProviderHasNoModels => (PiReasonCode::MissingExplicitModels, "/models"), + PiConfigError::EmptyApiId => (PiReasonCode::ManagedTypeConversionFailed, "/api"), + PiConfigError::EmptyModelId => (PiReasonCode::EmptyModelId, "/models"), + PiConfigError::DuplicateModelId(_) => (PiReasonCode::DuplicateModelId, "/models"), + PiConfigError::ModelNotFound(_) => (PiReasonCode::ManagedTypeConversionFailed, "/models"), + PiConfigError::MissingEffectiveApi { .. } => (PiReasonCode::MissingEffectiveApi, "/models"), + PiConfigError::MissingEffectiveEndpoint { .. } => { + (PiReasonCode::MissingEffectiveEndpoint, "/models") + } + PiConfigError::InvalidEndpoint { .. } => (PiReasonCode::InvalidEndpoint, "/baseUrl"), + PiConfigError::UnknownModelOverride(_) => { + (PiReasonCode::UnknownModelOverride, "/modelOverrides") + } + PiConfigError::InvalidCompat { .. } => (PiReasonCode::InvalidCompat, "/compat"), + PiConfigError::EmptyOptionalField { .. } => (PiReasonCode::EmptyOptionalField, ""), + PiConfigError::NonPositiveModelLimit { .. } => { + (PiReasonCode::NonPositiveModelLimit, "/models") + } + PiConfigError::InvalidThinkingLevel { .. } => { + (PiReasonCode::InvalidThinkingLevel, "/models") + } + }; + diagnostic_reason(PiDiagnosticLayer::Managed, code, pointer) +} + +fn classify_kind( + provider_key: &str, + value: &Value, + raw_validity: PiRawValidity, +) -> PiNativeEntryKind { + if PI_BUILTIN_PROVIDER_KEYS.contains(&provider_key) { + return PiNativeEntryKind::BuiltInOverlay; + } + if raw_validity != PiRawValidity::Valid { + return PiNativeEntryKind::UnknownShape; + } + let Some(object) = value.as_object() else { + return PiNativeEntryKind::UnknownShape; + }; + if object + .get("models") + .and_then(Value::as_array) + .is_some_and(|models| !models.is_empty()) + { + PiNativeEntryKind::CustomCatalog + } else if object + .keys() + .any(|key| RECOGNIZED_PROVIDER_FIELDS.contains(&key.as_str())) + { + PiNativeEntryKind::ExtensionOverlay + } else { + PiNativeEntryKind::UnknownShape + } +} + +fn map_raw_validity(validity: PiRawValidity) -> PiRawNativeValidity { + match validity { + PiRawValidity::Valid => PiRawNativeValidity::Valid, + PiRawValidity::Invalid => PiRawNativeValidity::Invalid, + PiRawValidity::Unknown => PiRawNativeValidity::Unknown, + } +} + +fn map_composition_status(status: PiComposerStatus) -> PiCompositionStatus { + match status { + PiComposerStatus::Composed => PiCompositionStatus::Composed, + PiComposerStatus::Failed => PiCompositionStatus::Failed, + PiComposerStatus::Unknown => PiCompositionStatus::Unknown, + } +} + +fn map_gateway_status(capability: PiGatewayCapability) -> PiGatewayStatus { + match capability { + PiGatewayCapability::Proxyable => PiGatewayStatus::Proxyable, + PiGatewayCapability::DirectOnly => PiGatewayStatus::DirectOnly, + PiGatewayCapability::Unknown => PiGatewayStatus::Unknown, + } +} + +fn map_raw_reasons(raw: &PiRawSchemaEvaluation) -> Vec { + raw.reasons + .iter() + .map(|reason| { + diagnostic_reason( + PiDiagnosticLayer::RawSchema, + match reason.code { + PiRawReasonCode::SchemaMismatch => PiReasonCode::RawSchemaMismatch, + PiRawReasonCode::UnsupportedOperator => { + PiReasonCode::RawSchemaUnsupportedOperator + } + PiRawReasonCode::PinDrift => PiReasonCode::RawSchemaPinDrift, + PiRawReasonCode::AmbiguousSchema => PiReasonCode::RawSchemaAmbiguous, + }, + &reason.json_pointer, + ) + }) + .collect() +} + +fn map_composer_reasons(composition: &PiNativeComposition) -> Vec { + composition + .reasons + .iter() + .map(|reason| { + diagnostic_reason( + PiDiagnosticLayer::Composition, + match reason.code { + PiComposerReasonCode::CatalogRequired => PiReasonCode::CatalogRequired, + PiComposerReasonCode::MissingExplicitModels => { + PiReasonCode::MissingExplicitModels + } + PiComposerReasonCode::MissingEffectiveApi => PiReasonCode::MissingEffectiveApi, + PiComposerReasonCode::MissingEffectiveEndpoint => { + PiReasonCode::MissingEffectiveEndpoint + } + PiComposerReasonCode::NonPositiveModelLimit => { + PiReasonCode::NonPositiveModelLimit + } + PiComposerReasonCode::CompositionFailed => PiReasonCode::CompositionFailed, + }, + &reason.json_pointer, + ) + }) + .collect() +} + +fn map_gateway_reasons(gateway: &PiGatewayAssessment) -> Vec { + gateway + .reasons + .iter() + .map(|reason| { + diagnostic_reason( + PiDiagnosticLayer::Gateway, + match reason.code { + PiGatewayReasonCode::UnsupportedFamily => { + PiReasonCode::UnsupportedGatewayFamily + } + PiGatewayReasonCode::InvalidEndpoint => PiReasonCode::InvalidEndpoint, + PiGatewayReasonCode::MissingCredential => { + PiReasonCode::GatewayCredentialUnavailable + } + PiGatewayReasonCode::InvalidHeaderName => PiReasonCode::InvalidHeaderName, + PiGatewayReasonCode::InvalidHeaderValue => PiReasonCode::InvalidHeaderValue, + PiGatewayReasonCode::ProtectedHeader => PiReasonCode::ProtectedHeader, + PiGatewayReasonCode::DeferredValueUnavailable => { + PiReasonCode::DeferredValueUnavailable + } + }, + &reason.json_pointer, + ) + }) + .collect() +} + +fn diagnostic_reason( + layer: PiDiagnosticLayer, + code: PiReasonCode, + pointer: &str, +) -> PiDiagnosticReason { + PiDiagnosticReason::new(layer, code, Some(pointer.to_string())) +} + +fn add_reason(reasons: &mut Vec, reason: PiDiagnosticReason) { + if !reasons.contains(&reason) { + reasons.push(reason); + } +} + +fn extend_reasons( + reasons: &mut Vec, + candidates: impl IntoIterator, +) { + for reason in candidates { + add_reason(reasons, reason); + } +} + +fn valid_http_endpoint(value: &str) -> bool { + Url::parse(value) + .ok() + .is_some_and(|url| matches!(url.scheme(), "http" | "https") && url.host().is_some()) +} + +fn is_managed_thinking_level(value: &str) -> bool { + matches!( + value, + "off" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max" + ) +} + +fn escape_json_pointer(value: &str) -> String { + value.replace('~', "~0").replace('/', "~1") +} + +fn fingerprint(raw_source: &str) -> String { + format!("sha256:{:x}", Sha256::digest(raw_source.as_bytes())) +} + +#[cfg(test)] +mod tests { + use super::*; + use serde_json::json; + use std::fs; + + fn by_key<'a>(diagnostics: &'a [PiNativeDiagnostic], key: &str) -> &'a PiNativeDiagnostic { + diagnostics + .iter() + .find(|diagnostic| diagnostic.provider_key == key) + .expect("diagnostic") + } + + fn has_reason( + diagnostic: &PiNativeDiagnostic, + layer: PiDiagnosticLayer, + code: PiReasonCode, + pointer: &str, + ) -> bool { + diagnostic.reasons.iter().any(|reason| { + reason.layer == layer + && reason.code == code + && reason.json_pointer.as_deref() == Some(pointer) + }) + } + + #[test] + fn public_inspection_service_certifies_the_native_state_matrix() { + let temp = tempfile::tempdir().expect("tempdir"); + let path = temp.path().join("models.json"); + fs::write( + &path, + r#"{ + "providers": { + "anthropic": {"baseUrl": "https://builtin.example"}, + "extension": {"api": "openai-responses"}, + "future-custom": { + "api": "future-wire-v9", + "baseUrl": "https://future.example/v9", + "apiKey": "$FUTURE_KEY", + "models": [{"id": "future"}] + }, + "known-custom": { + "api": "openai-responses", + "baseUrl": "https://known.example/v1", + "apiKey": "!read-secret", + "headers": {"x-tenant": "${TENANT}"}, + "models": [{"id": "known"}] + }, + "malformed": {"models": "not-an-array"} + } +}"#, + ) + .expect("write fixture"); + let bytes_before = fs::read(&path).expect("before"); + let claims = BTreeMap::new(); + let diagnostics = + PiNativeInspectionService::inspect_catalog(&path, &claims).expect("inspect service"); + assert_eq!(fs::read(&path).expect("after"), bytes_before); + assert_eq!(diagnostics.len(), 5); + + for key in ["anthropic", "extension"] { + let diagnostic = by_key(&diagnostics, key); + assert_eq!(diagnostic.raw_validity, PiRawNativeValidity::Valid); + assert_eq!(diagnostic.composition_status, PiCompositionStatus::Unknown); + assert_eq!(diagnostic.gateway_status, PiGatewayStatus::Unknown); + assert!(has_reason( + diagnostic, + PiDiagnosticLayer::Composition, + PiReasonCode::CatalogRequired, + "/models" + )); + } + + let future = by_key(&diagnostics, "future-custom"); + assert_eq!(future.raw_validity, PiRawNativeValidity::Valid); + assert_eq!(future.composition_status, PiCompositionStatus::Composed); + assert_eq!(future.managed_assessment, PiManagedAssessment::Manageable); + assert_eq!(future.management_status, PiManagementStatus::Importable); + assert_eq!(future.gateway_status, PiGatewayStatus::DirectOnly); + assert!(has_reason( + future, + PiDiagnosticLayer::Gateway, + PiReasonCode::UnsupportedGatewayFamily, + "/models/0/api" + )); + + let known = by_key(&diagnostics, "known-custom"); + assert_eq!(known.composition_status, PiCompositionStatus::Composed); + assert_eq!(known.management_status, PiManagementStatus::Importable); + assert_eq!(known.gateway_status, PiGatewayStatus::Proxyable); + + let malformed = by_key(&diagnostics, "malformed"); + assert_eq!(malformed.raw_validity, PiRawNativeValidity::Invalid); + assert_eq!(malformed.composition_status, PiCompositionStatus::Unknown); + assert_eq!(malformed.gateway_status, PiGatewayStatus::Unknown); + } + + #[test] + fn managed_rejection_does_not_control_raw_composition() { + let temp = tempfile::tempdir().expect("tempdir"); + let path = temp.path().join("models.json"); + fs::write( + &path, + r#"{"providers":{"duplicate":{ + "apiKey":"literal", + "models":[ + {"id":"same","api":"openai-responses","baseUrl":"https://one.example"}, + {"id":"same","api":"future-wire","baseUrl":"https://two.example"} + ], + "modelOverrides":{"missing":{"maxTokens":7.5}} +}}}"#, + ) + .expect("write"); + let diagnostic = + &PiNativeInspectionService::inspect_catalog(&path, &BTreeMap::new()).unwrap()[0]; + assert_eq!(diagnostic.raw_validity, PiRawNativeValidity::Valid); + assert_eq!( + diagnostic.managed_assessment, + PiManagedAssessment::Unsupported + ); + assert_eq!(diagnostic.composition_status, PiCompositionStatus::Composed); + assert!(has_reason( + diagnostic, + PiDiagnosticLayer::Managed, + PiReasonCode::DuplicateModelId, + "/models/1/id" + )); + assert!(has_reason( + diagnostic, + PiDiagnosticLayer::Managed, + PiReasonCode::UnknownModelOverride, + "/modelOverrides/missing" + )); + } + + #[test] + fn unknown_thinking_shape_is_lossless_for_composer_and_narrowed_separately() { + let temp = tempfile::tempdir().expect("tempdir"); + let path = temp.path().join("models.json"); + fs::write( + &path, + r#"{"providers":{"thinking":{ + "api":"anthropic-messages", + "baseUrl":"https://thinking.example", + "apiKey":"literal", + "models":[{ + "id":"m", + "thinkingLevelMap":{"high":"future-high","future":{"opaque":true}} + }] +}}}"#, + ) + .expect("write"); + let inspection = + PiNativeInspectionService::inspect_entry(&path, "thinking", &BTreeMap::new()) + .expect("inspect") + .expect("entry"); + assert_eq!( + inspection.diagnostic.raw_validity, + PiRawNativeValidity::Valid + ); + assert_eq!( + inspection.diagnostic.composition_status, + PiCompositionStatus::Composed + ); + assert_eq!( + inspection.composition.models[0] + .thinking_level_map + .as_ref() + .expect("opaque thinking")["future"], + json!({"opaque": true}) + ); + assert_eq!( + inspection.diagnostic.managed_assessment, + PiManagedAssessment::Unsupported + ); + assert!(has_reason( + &inspection.diagnostic, + PiDiagnosticLayer::Managed, + PiReasonCode::ManagedTypeConversionFailed, + "/models/0/thinkingLevelMap/future" + )); + } + + #[test] + fn exact_entry_fingerprint_changes_only_when_that_raw_entry_changes() { + let temp = tempfile::tempdir().expect("tempdir"); + let path = temp.path().join("models.json"); + let claims = BTreeMap::new(); + fs::write( + &path, + r#"{"providers":{ + "target":{"api":"openai-responses","baseUrl":"https://target","apiKey":"x","models":[{"id":"m"}]}, + "sibling":{"api":"openai-responses","baseUrl":"https://sibling","apiKey":"x","models":[{"id":"m"}]} +}}"#, + ) + .expect("write"); + let first = PiNativeInspectionService::inspect_entry(&path, "target", &claims) + .unwrap() + .unwrap() + .diagnostic + .fingerprint; + fs::write( + &path, + r#"{"providers":{ + "target":{"api":"openai-responses","baseUrl":"https://target","apiKey":"x","models":[{"id":"m"}]}, + "sibling":{"api":"openai-responses","baseUrl":"https://changed","apiKey":"x","models":[{"id":"m"}]} +}}"#, + ) + .expect("write sibling"); + let after_sibling = PiNativeInspectionService::inspect_entry(&path, "target", &claims) + .unwrap() + .unwrap() + .diagnostic + .fingerprint; + assert_eq!(first, after_sibling); + } + + #[test] + fn agent_dir_normalization_matches_pi_path_semantics() { + let temp = tempfile::tempdir().expect("tempdir"); + let file_url = Url::from_file_path(temp.path()) + .expect("absolute temp path") + .to_string(); + assert_eq!( + normalize_pi_agent_dir(&file_url, Path::new("/unused")).expect("file URL"), + temp.path() + ); + let spaced = " relative agent dir "; + assert_eq!( + normalize_pi_agent_dir(spaced, Path::new("/unused")).expect("spaced path"), + PathBuf::from(spaced) + ); + } + + #[test] + fn pi_config_error_stays_managed_only() { + let error = PiConfigError::EmptyApiId; + assert_eq!(error.to_string(), "Pi API id cannot be empty"); + } +} diff --git a/src-tauri/src/pi_config/raw_schema.rs b/src-tauri/src/pi_config/raw_schema.rs new file mode 100644 index 000000000..eb6353ffe --- /dev/null +++ b/src-tauri/src/pi_config/raw_schema.rs @@ -0,0 +1,1036 @@ +//! Lossless raw Pi catalog types and pinned TypeBox schema evaluation. +//! +//! This module deliberately imports neither managed control-plane types nor +//! gateway types. A raw-valid provider remains its original JSON value until +//! the independent managed assessor or composer explicitly consumes it. + +#![allow(dead_code)] + +use regex::Regex; +use serde_json::{Map, Value}; +use sha2::{Digest, Sha256}; +use std::collections::BTreeSet; +use std::sync::LazyLock; + +const PI_REPOSITORY: &str = "https://github.com/earendil-works/pi.git"; +const PI_COMMIT: &str = "ab366ebe94cacd419d986be454f12b1b9913aaca"; +const TYPEBOX_VERSION: &str = "1.3.7"; +const MODEL_CONFIG_PATH: &str = "packages/coding-agent/src/core/model-config.ts"; +const MODEL_CONFIG_SHA256: &str = + "62141770d675ad6357a72e07354355f0eda29281c0e5be1b48d2360f341c7360"; +const PROVIDER_COMPOSER_PATH: &str = "packages/coding-agent/src/core/provider-composer.ts"; +const PROVIDER_COMPOSER_SHA256: &str = + "17308a4179b330526eabf6c917fa13e9dbd9ece90d1555b870e87d39b5b60d9d"; +const RESOLVE_CONFIG_VALUE_PATH: &str = "packages/coding-agent/src/core/resolve-config-value.ts"; +const RESOLVE_CONFIG_VALUE_SHA256: &str = + "0f53dad47fe5d5d8837c022b7951ccd3bd5a9b577bd662f0986272110e83bcc7"; +const SCHEMA_SHA256: &str = "e498c9f1b344eee1bd3c3ba74d1b648dcb835378cfad92800ec80078b825745c"; +const RAW_ORACLE_SHA256: &str = "5aaa37160f96a0fe50867d900ca38c73f13aba769e156a883324368d9dbeeb9a"; +const COMPOSER_ORACLE_SHA256: &str = + "f7e54bb84e5fd6d50e5762dc304834410fa73ef608c2f9c42475c5983f8e0cf5"; +const TRANSPORT_ORACLE_SHA256: &str = + "b2c816e53b60da5cd6352d2c23934939e9f6dd0077971488fe9dd36fa723e855"; +const FIELD_COVERAGE_SHA256: &str = + "b8b85e611cf1dbef86c611df185ba8ac2d64160087d0c6e47747f838a0fafe42"; +const HARNESS_PATH: &str = "scripts/generate-pi-native-oracle.mjs"; +const HARNESS_SHA256: &str = "f7a138831284b48ef655ef500a63313f0fc89cf08d319094895027ba4777cc20"; +const EVALUATOR_OPERATOR_ALLOWLIST: &[&str] = &[ + "additionalProperties", + "anyOf", + "const", + "items", + "minLength", + "patternProperties", + "properties", + "required", + "type", +]; + +const SCHEMA_SOURCE: &str = + include_str!("../../../tests/fixtures/pi/native-oracle/provider-schema.snapshot.json"); +const RAW_ORACLE_SOURCE: &str = + include_str!("../../../tests/fixtures/pi/native-oracle/raw-oracle-v1.json"); +const COMPOSER_ORACLE_SOURCE: &str = + include_str!("../../../tests/fixtures/pi/native-oracle/composer-oracle-v1.json"); +const TRANSPORT_ORACLE_SOURCE: &str = + include_str!("../../../tests/fixtures/pi/native-oracle/transport-oracle-v1.json"); +const FIELD_COVERAGE_SOURCE: &str = + include_str!("../../../tests/fixtures/pi/native-oracle/field-coverage-v1.json"); +const PROVENANCE_SOURCE: &str = + include_str!("../../../tests/fixtures/pi/native-oracle/provenance-v1.json"); +const GENERATOR_SOURCE: &str = include_str!("../../../scripts/generate-pi-native-oracle.mjs"); + +#[derive(Debug, Clone, PartialEq, Eq)] +pub(crate) struct PiRawApiId(String); + +impl PiRawApiId { + pub(crate) fn new(value: impl Into) -> Option { + let value = value.into(); + (!value.is_empty()).then_some(Self(value)) + } + + pub(crate) fn as_str(&self) -> &str { + &self.0 + } +} + +#[derive(Debug, Clone, PartialEq)] +pub(super) struct PiRawValidProvider { + raw: Value, +} + +impl PiRawValidProvider { + fn new(raw: Value) -> Self { + Self { raw } + } + + pub(super) fn raw(&self) -> &Value { + &self.raw + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(super) enum PiRawValidity { + Valid, + Invalid, + Unknown, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(super) enum PiRawReasonCode { + SchemaMismatch, + UnsupportedOperator, + PinDrift, + AmbiguousSchema, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub(super) struct PiRawReason { + pub code: PiRawReasonCode, + pub json_pointer: String, +} + +#[derive(Debug, Clone)] +pub(super) struct PiRawSchemaEvaluation { + pub validity: PiRawValidity, + pub valid_provider: Option, + pub reasons: Vec, +} + +#[derive(Debug)] +struct OracleBundle { + provider_schema: Value, +} + +#[derive(Debug, Clone, Copy)] +struct OracleSources<'a> { + schema: &'a str, + raw_oracle: &'a str, + composer_oracle: &'a str, + transport_oracle: &'a str, + field_coverage: &'a str, + provenance: &'a str, + generator: &'a str, +} + +static ORACLE_BUNDLE: LazyLock> = + LazyLock::new(load_and_verify_oracle_bundle); + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum UnknownKind { + UnsupportedOperator, + AmbiguousSchema, +} + +#[derive(Debug, PartialEq, Eq)] +enum SchemaOutcome { + Valid, + Invalid(String), + Unknown { + kind: UnknownKind, + instance_pointer: String, + }, +} + +pub(super) fn evaluate_provider_value(value: &Value) -> PiRawSchemaEvaluation { + evaluate_provider_value_against(value, &ORACLE_BUNDLE) +} + +fn evaluate_provider_value_against( + value: &Value, + oracle_bundle: &Result, +) -> PiRawSchemaEvaluation { + let bundle = match oracle_bundle { + Ok(bundle) => bundle, + Err(_) => { + return PiRawSchemaEvaluation { + validity: PiRawValidity::Unknown, + valid_provider: None, + reasons: vec![PiRawReason { + code: PiRawReasonCode::PinDrift, + json_pointer: String::new(), + }], + }; + } + }; + outcome_to_evaluation(evaluate_schema(&bundle.provider_schema, value, ""), value) +} + +fn outcome_to_evaluation(outcome: SchemaOutcome, value: &Value) -> PiRawSchemaEvaluation { + match outcome { + SchemaOutcome::Valid => PiRawSchemaEvaluation { + validity: PiRawValidity::Valid, + valid_provider: Some(PiRawValidProvider::new(value.clone())), + reasons: Vec::new(), + }, + SchemaOutcome::Invalid(pointer) => PiRawSchemaEvaluation { + validity: PiRawValidity::Invalid, + valid_provider: None, + reasons: vec![PiRawReason { + code: PiRawReasonCode::SchemaMismatch, + json_pointer: pointer, + }], + }, + SchemaOutcome::Unknown { + kind, + instance_pointer, + } => PiRawSchemaEvaluation { + validity: PiRawValidity::Unknown, + valid_provider: None, + reasons: vec![PiRawReason { + code: match kind { + UnknownKind::UnsupportedOperator => PiRawReasonCode::UnsupportedOperator, + UnknownKind::AmbiguousSchema => PiRawReasonCode::AmbiguousSchema, + }, + json_pointer: instance_pointer, + }], + }, + } +} + +fn load_and_verify_oracle_bundle() -> Result { + load_and_verify_oracle_bundle_from(OracleSources { + schema: SCHEMA_SOURCE, + raw_oracle: RAW_ORACLE_SOURCE, + composer_oracle: COMPOSER_ORACLE_SOURCE, + transport_oracle: TRANSPORT_ORACLE_SOURCE, + field_coverage: FIELD_COVERAGE_SOURCE, + provenance: PROVENANCE_SOURCE, + generator: GENERATOR_SOURCE, + }) +} + +fn load_and_verify_oracle_bundle_from(sources: OracleSources<'_>) -> Result { + let provenance: Value = + serde_json::from_str(sources.provenance).map_err(|error| error.to_string())?; + if provenance.pointer("/version").and_then(Value::as_u64) != Some(1) { + return Err("provenance version does not match the pinned value".to_string()); + } + verify_string( + "/pi/repository", + provenance.pointer("/pi/repository"), + PI_REPOSITORY, + )?; + verify_string("/pi/commit", provenance.pointer("/pi/commit"), PI_COMMIT)?; + verify_string( + "/typeboxVersion", + provenance.pointer("/typeboxVersion"), + TYPEBOX_VERSION, + )?; + verify_string( + "/sources/modelConfig/path", + provenance.pointer("/sources/modelConfig/path"), + MODEL_CONFIG_PATH, + )?; + verify_string( + "/sources/modelConfig/sha256", + provenance.pointer("/sources/modelConfig/sha256"), + MODEL_CONFIG_SHA256, + )?; + verify_string( + "/sources/providerComposer/path", + provenance.pointer("/sources/providerComposer/path"), + PROVIDER_COMPOSER_PATH, + )?; + verify_string( + "/sources/providerComposer/sha256", + provenance.pointer("/sources/providerComposer/sha256"), + PROVIDER_COMPOSER_SHA256, + )?; + verify_string( + "/sources/resolveConfigValue/path", + provenance.pointer("/sources/resolveConfigValue/path"), + RESOLVE_CONFIG_VALUE_PATH, + )?; + verify_string( + "/sources/resolveConfigValue/sha256", + provenance.pointer("/sources/resolveConfigValue/sha256"), + RESOLVE_CONFIG_VALUE_SHA256, + )?; + for (filename, source, expected_hash) in [ + ( + "provider-schema.snapshot.json", + sources.schema, + SCHEMA_SHA256, + ), + ("raw-oracle-v1.json", sources.raw_oracle, RAW_ORACLE_SHA256), + ( + "composer-oracle-v1.json", + sources.composer_oracle, + COMPOSER_ORACLE_SHA256, + ), + ( + "transport-oracle-v1.json", + sources.transport_oracle, + TRANSPORT_ORACLE_SHA256, + ), + ( + "field-coverage-v1.json", + sources.field_coverage, + FIELD_COVERAGE_SHA256, + ), + ] { + if sha256_hex(source.as_bytes()) != expected_hash { + return Err(format!("{filename} does not match the code pin")); + } + let pointer = format!("/artifacts/{}", escape_json_pointer(filename)); + verify_string(&pointer, provenance.pointer(&pointer), expected_hash)?; + } + verify_string( + "/harness/path", + provenance.pointer("/harness/path"), + HARNESS_PATH, + )?; + if sha256_hex(sources.generator.as_bytes()) != HARNESS_SHA256 { + return Err("oracle execution harness does not match the code pin".to_string()); + } + verify_string( + "/harness/sha256", + provenance.pointer("/harness/sha256"), + HARNESS_SHA256, + )?; + verify_string( + "/harness/upstreamEntry", + provenance.pointer("/harness/upstreamEntry"), + PROVIDER_COMPOSER_PATH, + )?; + let entry_functions = provenance + .pointer("/harness/entryFunctions") + .and_then(Value::as_array) + .ok_or_else(|| "composer harness entry functions are missing".to_string())?; + for expected in [ + "composeModelProvider", + "Provider.getModels", + "resolveCompatibilityRequestConfig", + "Provider.auth.apiKey.resolve", + ] { + if !entry_functions + .iter() + .any(|value| value.as_str() == Some(expected)) + { + return Err(format!( + "composer harness does not record upstream entry function '{expected}'" + )); + } + } + let transport_functions = provenance + .pointer("/harness/transportResolver/entryFunctions") + .and_then(Value::as_array) + .ok_or_else(|| "transport resolver entry functions are missing".to_string())?; + for expected in ["resolveConfigValueOrThrow", "resolveHeadersOrThrow"] { + if !transport_functions + .iter() + .any(|value| value.as_str() == Some(expected)) + { + return Err(format!( + "transport harness does not record upstream entry function '{expected}'" + )); + } + } + + let allowlist = provenance + .pointer("/evaluatorOperatorAllowlist") + .and_then(Value::as_array) + .ok_or_else(|| "provenance evaluator allowlist is missing".to_string())? + .iter() + .map(|value| { + value + .as_str() + .map(ToOwned::to_owned) + .ok_or_else(|| "provenance evaluator allowlist is malformed".to_string()) + }) + .collect::, _>>()?; + if allowlist + != EVALUATOR_OPERATOR_ALLOWLIST + .iter() + .map(|value| (*value).to_string()) + .collect::>() + { + return Err("provenance evaluator allowlist drifted".to_string()); + } + + let schema: Value = serde_json::from_str(sources.schema).map_err(|error| error.to_string())?; + // The vendored snapshot is the provider schema itself. The harness records + // the extraction target used in the pinned ModelsConfigSchema. + let provider_schema = schema.clone(); + let mut inventory = BTreeSet::new(); + collect_schema_operators(&schema, &mut inventory)?; + let expected_inventory = provenance + .pointer("/schemaOperatorInventory") + .and_then(Value::as_array) + .ok_or_else(|| "provenance schema operator inventory is missing".to_string())? + .iter() + .map(|value| { + value + .as_str() + .map(ToOwned::to_owned) + .ok_or_else(|| "provenance schema operator inventory is malformed".to_string()) + }) + .collect::, _>>()?; + if inventory != expected_inventory { + return Err("schema operator inventory drifted".to_string()); + } + verify_field_coverage( + &schema, + sources.field_coverage, + sources.raw_oracle, + sources.composer_oracle, + )?; + Ok(OracleBundle { provider_schema }) +} + +fn verify_field_coverage( + schema: &Value, + source: &str, + raw_oracle_source: &str, + composer_oracle_source: &str, +) -> Result<(), String> { + let coverage: Value = serde_json::from_str(source).map_err(|error| error.to_string())?; + let raw_oracle: Value = + serde_json::from_str(raw_oracle_source).map_err(|error| error.to_string())?; + let composer_oracle: Value = + serde_json::from_str(composer_oracle_source).map_err(|error| error.to_string())?; + verify_string("/piCommit", coverage.pointer("/piCommit"), PI_COMMIT)?; + let entries = coverage + .pointer("/fields") + .and_then(Value::as_array) + .ok_or_else(|| "field coverage entries are missing".to_string())?; + let mut covered = BTreeSet::new(); + for entry in entries { + let field_path = entry + .get("fieldPath") + .and_then(Value::as_str) + .ok_or_else(|| "field coverage path is malformed".to_string())?; + if !covered.insert(field_path.to_string()) { + return Err(format!("field coverage duplicates '{field_path}'")); + } + let raw_cases = entry + .get("rawOracleCases") + .and_then(Value::as_array) + .filter(|cases| !cases.is_empty()) + .ok_or_else(|| { + format!("field coverage '{field_path}' has no rawOracleCases evidence") + })?; + let mut has_successful_raw_execution = false; + for case_id in raw_cases { + let case_id = case_id.as_str().ok_or_else(|| { + format!("field coverage '{field_path}' has a malformed raw case id") + })?; + let case = oracle_case(&raw_oracle, case_id).ok_or_else(|| { + format!("field coverage '{field_path}' cites unknown raw case '{case_id}'") + })?; + if !input_has_field_path(&case["input"], field_path) { + return Err(format!( + "raw case '{case_id}' does not contain covered field '{field_path}'" + )); + } + has_successful_raw_execution |= + case.get("expectedValid").and_then(Value::as_bool) == Some(true); + } + if !has_successful_raw_execution { + return Err(format!( + "field coverage '{field_path}' has no successful pinned TypeBox execution" + )); + } + + let composer_cases = entry + .get("composerOracleCases") + .and_then(Value::as_array) + .filter(|cases| !cases.is_empty()) + .ok_or_else(|| { + format!("field coverage '{field_path}' has no composerOracleCases evidence") + })?; + let expected_behavior_case = if field_path == "/models" + || field_path.starts_with("/models/") + { + "model-fields-executed" + } else if field_path == "/modelOverrides" || field_path.starts_with("/modelOverrides/") { + "override-fields-executed" + } else { + "provider-fields-inherited" + }; + verify_string( + &format!("{field_path}/composerBehaviorCase"), + entry.get("composerBehaviorCase"), + expected_behavior_case, + )?; + let mut has_own_layer_execution = false; + for case_id in composer_cases { + let case_id = case_id.as_str().ok_or_else(|| { + format!("field coverage '{field_path}' has a malformed composer case id") + })?; + let case = oracle_case(&composer_oracle, case_id).ok_or_else(|| { + format!("field coverage '{field_path}' cites unknown composer case '{case_id}'") + })?; + if !input_has_field_path(&case["input"], field_path) { + return Err(format!( + "composer case '{case_id}' does not contain covered field '{field_path}'" + )); + } + if case_id == expected_behavior_case + && case.pointer("/execution/status").and_then(Value::as_str) == Some("success") + && case.get("expected").is_some_and(|value| !value.is_null()) + { + has_own_layer_execution = true; + } + } + if !has_own_layer_execution { + return Err(format!( + "field coverage '{field_path}' has no successful pinned Pi execution at its own layer" + )); + } + } + let mut expected = BTreeSet::new(); + collect_schema_field_paths(schema, "", &mut expected)?; + if covered != expected { + let missing = expected.difference(&covered).cloned().collect::>(); + let stale = covered.difference(&expected).cloned().collect::>(); + return Err(format!( + "field coverage differs from pinned schema; missing={missing:?}, stale={stale:?}" + )); + } + Ok(()) +} + +fn oracle_case<'a>(oracle: &'a Value, id: &str) -> Option<&'a Value> { + oracle + .get("cases") + .and_then(Value::as_array)? + .iter() + .find(|case| case.get("id").and_then(Value::as_str) == Some(id)) +} + +fn input_has_field_path(value: &Value, field_path: &str) -> bool { + fn descend(value: &Value, segments: &[&str]) -> bool { + let Some((head, tail)) = segments.split_first() else { + return true; + }; + if *head == "*" { + return match value { + Value::Array(values) => values.iter().any(|value| descend(value, tail)), + Value::Object(values) => values.values().any(|value| descend(value, tail)), + _ => false, + }; + } + let decoded = head.replace("~1", "/").replace("~0", "~"); + value + .get(decoded.as_str()) + .is_some_and(|value| descend(value, tail)) + } + + let segments = field_path + .strip_prefix('/') + .unwrap_or(field_path) + .split('/') + .collect::>(); + descend(value, &segments) +} + +fn collect_schema_field_paths( + schema: &Value, + pointer: &str, + output: &mut BTreeSet, +) -> Result<(), String> { + let object = schema + .as_object() + .ok_or_else(|| format!("schema field node at '{pointer}' is not an object"))?; + if let Some(branches) = object.get("anyOf") { + for branch in branches + .as_array() + .ok_or_else(|| format!("schema anyOf at '{pointer}' is not an array"))? + { + collect_schema_field_paths(branch, pointer, output)?; + } + } + if let Some(properties) = object.get("properties") { + for (name, child) in properties + .as_object() + .ok_or_else(|| format!("schema properties at '{pointer}' are malformed"))? + { + let child_pointer = join_json_pointer(pointer, name); + output.insert(child_pointer.clone()); + collect_schema_field_paths(child, &child_pointer, output)?; + } + } + if let Some(patterns) = object.get("patternProperties") { + for child in patterns + .as_object() + .ok_or_else(|| format!("schema patterns at '{pointer}' are malformed"))? + .values() + { + let child_pointer = join_json_pointer(pointer, "*"); + output.insert(child_pointer.clone()); + collect_schema_field_paths(child, &child_pointer, output)?; + } + } + if let Some(items) = object.get("items") { + collect_schema_field_paths(items, &join_json_pointer(pointer, "*"), output)?; + } + Ok(()) +} + +fn verify_string(label: &str, actual: Option<&Value>, expected: &str) -> Result<(), String> { + if actual.and_then(Value::as_str) == Some(expected) { + Ok(()) + } else { + Err(format!("{label} does not match the pinned value")) + } +} + +fn sha256_hex(bytes: &[u8]) -> String { + format!("{:x}", Sha256::digest(bytes)) +} + +fn collect_schema_operators( + schema: &Value, + inventory: &mut BTreeSet, +) -> Result<(), String> { + let object = schema + .as_object() + .ok_or_else(|| "schema node is not an object".to_string())?; + for key in object.keys() { + inventory.insert(key.clone()); + } + for map_name in ["properties", "patternProperties"] { + if let Some(children) = object.get(map_name) { + let children = children + .as_object() + .ok_or_else(|| format!("{map_name} is not an object"))?; + for child in children.values() { + collect_schema_operators(child, inventory)?; + } + } + } + if let Some(branches) = object.get("anyOf") { + for branch in branches + .as_array() + .ok_or_else(|| "anyOf is not an array".to_string())? + { + collect_schema_operators(branch, inventory)?; + } + } + if let Some(items) = object.get("items") { + collect_schema_operators(items, inventory)?; + } + if let Some(Value::Object(_)) = object.get("additionalProperties") { + collect_schema_operators( + object + .get("additionalProperties") + .expect("checked additionalProperties"), + inventory, + )?; + } + Ok(()) +} + +fn evaluate_schema(schema: &Value, instance: &Value, pointer: &str) -> SchemaOutcome { + let Some(schema) = schema.as_object() else { + return ambiguous(pointer); + }; + if schema + .keys() + .any(|key| !EVALUATOR_OPERATOR_ALLOWLIST.contains(&key.as_str())) + { + return unsupported(pointer); + } + + if let Some(expected_type) = schema.get("type") { + let Some(expected_type) = expected_type.as_str() else { + return ambiguous(pointer); + }; + let matches = match expected_type { + "object" => instance.is_object(), + "array" => instance.is_array(), + "string" => instance.is_string(), + "number" => instance.is_number(), + "boolean" => instance.is_boolean(), + "null" => instance.is_null(), + _ => return unsupported(pointer), + }; + if !matches { + return SchemaOutcome::Invalid(pointer.to_string()); + } + } + + if let Some(constant) = schema.get("const") { + if !json_value_equal(instance, constant) { + return SchemaOutcome::Invalid(pointer.to_string()); + } + } + + if let Some(min_length) = schema.get("minLength") { + let Some(min_length) = min_length.as_u64() else { + return ambiguous(pointer); + }; + let Some(value) = instance.as_str() else { + return SchemaOutcome::Invalid(pointer.to_string()); + }; + if value.encode_utf16().count() < min_length as usize { + return SchemaOutcome::Invalid(pointer.to_string()); + } + } + + if let Some(branches) = schema.get("anyOf") { + let Some(branches) = branches.as_array() else { + return ambiguous(pointer); + }; + if branches.is_empty() { + return SchemaOutcome::Invalid(pointer.to_string()); + } + let mut first_unknown = None; + for branch in branches { + match evaluate_schema(branch, instance, pointer) { + SchemaOutcome::Valid => return SchemaOutcome::Valid, + SchemaOutcome::Invalid(_) => {} + unknown @ SchemaOutcome::Unknown { .. } => { + first_unknown.get_or_insert(unknown); + } + } + } + return first_unknown.unwrap_or_else(|| SchemaOutcome::Invalid(pointer.to_string())); + } + + if let Some(items) = schema.get("items") { + let Some(values) = instance.as_array() else { + return SchemaOutcome::Invalid(pointer.to_string()); + }; + for (index, value) in values.iter().enumerate() { + let child_pointer = join_json_pointer(pointer, &index.to_string()); + match evaluate_schema(items, value, &child_pointer) { + SchemaOutcome::Valid => {} + outcome => return outcome, + } + } + } + + if schema.contains_key("required") + || schema.contains_key("properties") + || schema.contains_key("patternProperties") + || schema.contains_key("additionalProperties") + { + let Some(object) = instance.as_object() else { + return SchemaOutcome::Invalid(pointer.to_string()); + }; + match evaluate_object(schema, object, pointer) { + SchemaOutcome::Valid => {} + outcome => return outcome, + } + } + + SchemaOutcome::Valid +} + +fn evaluate_object( + schema: &Map, + instance: &Map, + pointer: &str, +) -> SchemaOutcome { + if let Some(required) = schema.get("required") { + let Some(required) = required.as_array() else { + return ambiguous(pointer); + }; + for name in required { + let Some(name) = name.as_str() else { + return ambiguous(pointer); + }; + if !instance.contains_key(name) { + return SchemaOutcome::Invalid(join_json_pointer(pointer, name)); + } + } + } + + let properties = match schema.get("properties") { + None => None, + Some(Value::Object(properties)) => Some(properties), + Some(_) => return ambiguous(pointer), + }; + let pattern_properties = match schema.get("patternProperties") { + None => Vec::new(), + Some(Value::Object(patterns)) => { + let mut compiled = Vec::with_capacity(patterns.len()); + for (pattern, child_schema) in patterns { + let Ok(regex) = Regex::new(pattern) else { + return ambiguous(pointer); + }; + compiled.push((regex, child_schema)); + } + compiled + } + Some(_) => return ambiguous(pointer), + }; + + for (name, value) in instance { + let child_pointer = join_json_pointer(pointer, name); + let mut covered = false; + if let Some(child_schema) = properties.and_then(|properties| properties.get(name)) { + covered = true; + match evaluate_schema(child_schema, value, &child_pointer) { + SchemaOutcome::Valid => {} + outcome => return outcome, + } + } + for (pattern, child_schema) in &pattern_properties { + if pattern.is_match(name) { + covered = true; + match evaluate_schema(child_schema, value, &child_pointer) { + SchemaOutcome::Valid => {} + outcome => return outcome, + } + } + } + if !covered { + match schema.get("additionalProperties") { + None | Some(Value::Bool(true)) => {} + Some(Value::Bool(false)) => { + return SchemaOutcome::Invalid(child_pointer); + } + Some(child_schema @ Value::Object(_)) => { + match evaluate_schema(child_schema, value, &child_pointer) { + SchemaOutcome::Valid => {} + outcome => return outcome, + } + } + Some(_) => return ambiguous(pointer), + } + } + } + SchemaOutcome::Valid +} + +fn json_value_equal(left: &Value, right: &Value) -> bool { + match (left, right) { + (Value::Number(left), Value::Number(right)) => left.as_f64() == right.as_f64(), + _ => left == right, + } +} + +fn join_json_pointer(parent: &str, token: &str) -> String { + format!("{parent}/{}", escape_json_pointer(token)) +} + +fn escape_json_pointer(value: &str) -> String { + value.replace('~', "~0").replace('/', "~1") +} + +fn unsupported(pointer: &str) -> SchemaOutcome { + SchemaOutcome::Unknown { + kind: UnknownKind::UnsupportedOperator, + instance_pointer: pointer.to_string(), + } +} + +fn ambiguous(pointer: &str) -> SchemaOutcome { + SchemaOutcome::Unknown { + kind: UnknownKind::AmbiguousSchema, + instance_pointer: pointer.to_string(), + } +} + +#[cfg(test)] +mod tests { + use super::*; + use serde::Deserialize; + use serde_json::json; + + #[derive(Deserialize)] + #[serde(rename_all = "camelCase")] + struct RawOracle { + cases: Vec, + } + + #[derive(Deserialize)] + #[serde(rename_all = "camelCase")] + struct RawOracleCase { + id: String, + input: Value, + expected_valid: bool, + } + + #[test] + fn rust_evaluator_matches_typebox_1_3_7_oracle() { + assert!( + ORACLE_BUNDLE.is_ok(), + "vendored oracle bundle must verify: {:?}", + ORACLE_BUNDLE.as_ref().err() + ); + let oracle: RawOracle = serde_json::from_str(RAW_ORACLE_SOURCE).expect("parse raw oracle"); + for case in oracle.cases { + let actual = evaluate_provider_value(&case.input).validity; + let expected = if case.expected_valid { + PiRawValidity::Valid + } else { + PiRawValidity::Invalid + }; + assert_eq!(actual, expected, "oracle case '{}'", case.id); + let evaluated = evaluate_provider_value(&case.input); + assert_eq!( + evaluated + .valid_provider + .as_ref() + .map(PiRawValidProvider::raw), + case.expected_valid.then_some(&case.input), + "raw-valid type barrier case '{}'", + case.id + ); + } + } + + #[test] + fn every_schema_field_is_bound_to_real_successful_pi_executions() { + let schema: Value = serde_json::from_str(SCHEMA_SOURCE).expect("parse schema"); + verify_field_coverage( + &schema, + FIELD_COVERAGE_SOURCE, + RAW_ORACLE_SOURCE, + COMPOSER_ORACLE_SOURCE, + ) + .expect("all fields cite actual pinned Pi executions"); + + let invented_raw_case = + FIELD_COVERAGE_SOURCE.replacen("all-schema-fields-valid", "invented-raw-case", 1); + assert!( + verify_field_coverage( + &schema, + &invented_raw_case, + RAW_ORACLE_SOURCE, + COMPOSER_ORACLE_SOURCE, + ) + .is_err(), + "a field cannot be certified by a made-up execution id" + ); + + let invented_composer_case = FIELD_COVERAGE_SOURCE.replacen( + "combined-all-fields-precedence", + "invented-composer-case", + 1, + ); + assert!( + verify_field_coverage( + &schema, + &invented_composer_case, + RAW_ORACLE_SOURCE, + COMPOSER_ORACLE_SOURCE, + ) + .is_err(), + "a field cannot cite a composer execution that did not occur" + ); + } + + #[test] + fn unsupported_operator_is_unknown_only_when_input_traverses_it() { + let schema = json!({ + "type": "object", + "properties": { + "optional": { + "type": "string", + "unevaluatedProperties": false + } + } + }); + assert_eq!( + evaluate_schema(&schema, &json!({}), ""), + SchemaOutcome::Valid + ); + assert!(matches!( + evaluate_schema(&schema, &json!({"optional": "value"}), ""), + SchemaOutcome::Unknown { + kind: UnknownKind::UnsupportedOperator, + instance_pointer + } if instance_pointer == "/optional" + )); + } + + #[test] + fn additional_properties_operator_is_explicitly_supported() { + let schema = json!({ + "type": "object", + "properties": {"known": {"type": "string"}}, + "additionalProperties": false + }); + assert_eq!( + evaluate_schema(&schema, &json!({"known": "yes"}), ""), + SchemaOutcome::Valid + ); + assert_eq!( + evaluate_schema(&schema, &json!({"unknown": true}), ""), + SchemaOutcome::Invalid("/unknown".into()) + ); + } + + #[test] + fn artifact_or_provenance_drift_fails_closed_as_raw_unknown() { + let tampered_schema = SCHEMA_SOURCE.replacen("\"baseUrl\"", "\"baseUrl-tampered\"", 1); + let schema_bundle = load_and_verify_oracle_bundle_from(OracleSources { + schema: &tampered_schema, + raw_oracle: RAW_ORACLE_SOURCE, + composer_oracle: COMPOSER_ORACLE_SOURCE, + transport_oracle: TRANSPORT_ORACLE_SOURCE, + field_coverage: FIELD_COVERAGE_SOURCE, + provenance: PROVENANCE_SOURCE, + generator: GENERATOR_SOURCE, + }); + assert!(schema_bundle.is_err()); + let schema_result = evaluate_provider_value_against(&json!({}), &schema_bundle); + assert_eq!(schema_result.validity, PiRawValidity::Unknown); + assert_eq!( + schema_result.reasons, + vec![PiRawReason { + code: PiRawReasonCode::PinDrift, + json_pointer: String::new(), + }] + ); + + let tampered_provenance = + PROVENANCE_SOURCE.replacen(PI_COMMIT, "0000000000000000000000000000000000000000", 1); + let provenance_bundle = load_and_verify_oracle_bundle_from(OracleSources { + schema: SCHEMA_SOURCE, + raw_oracle: RAW_ORACLE_SOURCE, + composer_oracle: COMPOSER_ORACLE_SOURCE, + transport_oracle: TRANSPORT_ORACLE_SOURCE, + field_coverage: FIELD_COVERAGE_SOURCE, + provenance: &tampered_provenance, + generator: GENERATOR_SOURCE, + }); + assert!(provenance_bundle.is_err()); + assert_eq!( + evaluate_provider_value_against(&json!({}), &provenance_bundle).validity, + PiRawValidity::Unknown + ); + + let tampered_coverage = FIELD_COVERAGE_SOURCE.replacen("\"/api\"", "\"/api-tampered\"", 1); + let coverage_bundle = load_and_verify_oracle_bundle_from(OracleSources { + schema: SCHEMA_SOURCE, + raw_oracle: RAW_ORACLE_SOURCE, + composer_oracle: COMPOSER_ORACLE_SOURCE, + transport_oracle: TRANSPORT_ORACLE_SOURCE, + field_coverage: &tampered_coverage, + provenance: PROVENANCE_SOURCE, + generator: GENERATOR_SOURCE, + }); + assert!(coverage_bundle.is_err()); + assert_eq!( + evaluate_provider_value_against(&json!({}), &coverage_bundle).validity, + PiRawValidity::Unknown + ); + } +} diff --git a/tests/fixtures/pi/module-boundaries-v1.json b/tests/fixtures/pi/module-boundaries-v1.json new file mode 100644 index 000000000..537f0bf11 --- /dev/null +++ b/tests/fixtures/pi/module-boundaries-v1.json @@ -0,0 +1,13 @@ +{ + "manifestVersion": 1, + "codeAuthority": "src-tauri/src/architecture_tests.rs", + "edges": [ + "composer->raw_schema", + "gateway->composer", + "native->composer", + "native->document", + "native->gateway", + "native->model", + "native->raw_schema" + ] +} diff --git a/tests/fixtures/pi/native-oracle/composer-oracle-v1.json b/tests/fixtures/pi/native-oracle/composer-oracle-v1.json new file mode 100644 index 000000000..d54e49c38 --- /dev/null +++ b/tests/fixtures/pi/native-oracle/composer-oracle-v1.json @@ -0,0 +1,2186 @@ +{ + "version": 1, + "execution": { + "engine": "pinned upstream TypeScript", + "piCommit": "ab366ebe94cacd419d986be454f12b1b9913aaca", + "bundler": "esbuild@0.28.1", + "upstreamEntry": "packages/coding-agent/src/core/provider-composer.ts", + "entryFunctions": [ + "composeModelProvider", + "Provider.getModels", + "resolveCompatibilityRequestConfig", + "Provider.auth.apiKey.resolve" + ], + "transportShims": { + "pi-ai": "debdf80a40ad2086467c7c3ed6fe43d6167caa85e5bff86e10f28cc36d8fcc53", + "pi-ai/compat": "533cd8f8a242bb25b42db8249abcb539daf30889ea748b5114c177e44c3849d2" + } + }, + "cases": [ + { + "id": "combined-all-fields-precedence", + "providerId": "custom-all-fields", + "input": { + "name": "All Fields Provider", + "baseUrl": "https://all-fields.example/v1", + "apiKey": "literal-all-fields-key", + "api": "openai-responses", + "oauth": "radius", + "headers": { + "x-provider-field": "provider-value" + }, + "compat": { + "supportsStore": true, + "supportsDeveloperRole": true, + "supportsReasoningEffort": true, + "supportsUsageInStreaming": true, + "maxTokensField": "max_completion_tokens", + "requiresToolResultName": true, + "requiresAssistantAfterToolResult": true, + "requiresThinkingAsText": true, + "requiresReasoningContentOnAssistantMessages": true, + "thinkingFormat": "chat-template", + "chatTemplateKwargs": { + "stringValue": "literal", + "numberValue": 1.25, + "booleanValue": true, + "nullValue": null, + "variableValue": { + "$var": "thinking.effort", + "omitWhenOff": true + } + }, + "cacheControlFormat": "anthropic", + "openRouterRouting": { + "allow_fallbacks": true, + "require_parameters": true, + "data_collection": "deny", + "zdr": true, + "enforce_distillable_text": true, + "order": [ + "provider-a", + "provider-b" + ], + "only": [ + "provider-a" + ], + "ignore": [ + "provider-z" + ], + "quantizations": [ + "fp8" + ], + "sort": { + "by": "price", + "partition": null + }, + "max_price": { + "prompt": 1.1, + "completion": "2.2", + "image": 3.3, + "audio": "4.4", + "request": 5.5 + }, + "preferred_min_throughput": { + "p50": 10.5, + "p75": 9.5, + "p90": 8.5, + "p99": 7.5 + }, + "preferred_max_latency": { + "p50": 100.5, + "p75": 200.5, + "p90": 300.5, + "p99": 400.5 + } + }, + "vercelGatewayRouting": { + "only": [ + "provider-a" + ], + "order": [ + "provider-a", + "provider-b" + ] + }, + "supportsOpenAIGrammarTools": true, + "supportsStrictMode": true, + "sendSessionAffinityHeaders": true, + "deferredToolsMode": "kimi", + "sessionAffinityFormat": "openrouter", + "supportsLongCacheRetention": true, + "supportsToolSearch": true, + "supportsEagerToolInputStreaming": true, + "supportsCacheControlOnTools": true, + "supportsTemperature": true, + "forceAdaptiveThinking": true, + "allowEmptySignature": true, + "supportsStrictTools": true, + "supportsToolReferences": true + }, + "authHeader": true, + "models": [ + { + "id": "all-fields-model", + "name": "All Fields Model", + "api": "anthropic-messages", + "baseUrl": "https://all-fields-model.example/v1", + "reasoning": true, + "thinkingLevelMap": { + "off": null, + "minimal": "minimal-effort", + "low": "low-effort", + "medium": "medium-effort", + "high": "high-effort", + "xhigh": "xhigh-effort", + "max": "max-effort" + }, + "input": [ + "text", + "image" + ], + "cost": { + "input": 0.11, + "output": 0.22, + "cacheRead": 0.033, + "cacheWrite": 0.044, + "tiers": [ + { + "inputTokensAbove": 1000.5, + "input": 0.55, + "output": 0.66, + "cacheRead": 0.077, + "cacheWrite": 0.088 + } + ] + }, + "contextWindow": 128000.5, + "maxTokens": 16384.25, + "headers": { + "x-model-field": "model-value" + }, + "compat": { + "supportsStore": true, + "supportsDeveloperRole": true, + "supportsReasoningEffort": true, + "supportsUsageInStreaming": true, + "maxTokensField": "max_completion_tokens", + "requiresToolResultName": true, + "requiresAssistantAfterToolResult": true, + "requiresThinkingAsText": true, + "requiresReasoningContentOnAssistantMessages": true, + "thinkingFormat": "chat-template", + "chatTemplateKwargs": { + "stringValue": "literal", + "numberValue": 1.25, + "booleanValue": true, + "nullValue": null, + "variableValue": { + "$var": "thinking.effort", + "omitWhenOff": true + } + }, + "cacheControlFormat": "anthropic", + "openRouterRouting": { + "allow_fallbacks": true, + "require_parameters": true, + "data_collection": "deny", + "zdr": true, + "enforce_distillable_text": true, + "order": [ + "provider-a", + "provider-b" + ], + "only": [ + "provider-a" + ], + "ignore": [ + "provider-z" + ], + "quantizations": [ + "fp8" + ], + "sort": { + "by": "price", + "partition": null + }, + "max_price": { + "prompt": 1.1, + "completion": "2.2", + "image": 3.3, + "audio": "4.4", + "request": 5.5 + }, + "preferred_min_throughput": { + "p50": 10.5, + "p75": 9.5, + "p90": 8.5, + "p99": 7.5 + }, + "preferred_max_latency": { + "p50": 100.5, + "p75": 200.5, + "p90": 300.5, + "p99": 400.5 + } + }, + "vercelGatewayRouting": { + "only": [ + "provider-a" + ], + "order": [ + "provider-a", + "provider-b" + ] + }, + "supportsOpenAIGrammarTools": true, + "supportsStrictMode": true, + "sendSessionAffinityHeaders": true, + "deferredToolsMode": "kimi", + "sessionAffinityFormat": "openrouter", + "supportsLongCacheRetention": true, + "supportsToolSearch": true, + "supportsEagerToolInputStreaming": true, + "supportsCacheControlOnTools": true, + "supportsTemperature": true, + "forceAdaptiveThinking": true, + "allowEmptySignature": true, + "supportsStrictTools": true, + "supportsToolReferences": true + } + } + ], + "modelOverrides": { + "all-fields-model": { + "name": "All Fields Override", + "reasoning": false, + "thinkingLevelMap": { + "off": null, + "minimal": "minimal-effort", + "low": "low-effort", + "medium": "medium-effort", + "high": "high-effort", + "xhigh": "xhigh-effort", + "max": "max-effort" + }, + "input": [ + "image", + "text" + ], + "cost": { + "input": 0.11, + "output": 0.22, + "cacheRead": 0.033, + "cacheWrite": 0.044, + "tiers": [ + { + "inputTokensAbove": 1000.5, + "input": 0.55, + "output": 0.66, + "cacheRead": 0.077, + "cacheWrite": 0.088 + } + ] + }, + "contextWindow": 256000.75, + "maxTokens": 32768.5, + "headers": { + "x-override-field": "override-value" + }, + "compat": { + "supportsStore": true, + "supportsDeveloperRole": true, + "supportsReasoningEffort": true, + "supportsUsageInStreaming": true, + "maxTokensField": "max_completion_tokens", + "requiresToolResultName": true, + "requiresAssistantAfterToolResult": true, + "requiresThinkingAsText": true, + "requiresReasoningContentOnAssistantMessages": true, + "thinkingFormat": "chat-template", + "chatTemplateKwargs": { + "stringValue": "literal", + "numberValue": 1.25, + "booleanValue": true, + "nullValue": null, + "variableValue": { + "$var": "thinking.effort", + "omitWhenOff": true + } + }, + "cacheControlFormat": "anthropic", + "openRouterRouting": { + "allow_fallbacks": true, + "require_parameters": true, + "data_collection": "deny", + "zdr": true, + "enforce_distillable_text": true, + "order": [ + "provider-a", + "provider-b" + ], + "only": [ + "provider-a" + ], + "ignore": [ + "provider-z" + ], + "quantizations": [ + "fp8" + ], + "sort": { + "by": "price", + "partition": null + }, + "max_price": { + "prompt": 1.1, + "completion": "2.2", + "image": 3.3, + "audio": "4.4", + "request": 5.5 + }, + "preferred_min_throughput": { + "p50": 10.5, + "p75": 9.5, + "p90": 8.5, + "p99": 7.5 + }, + "preferred_max_latency": { + "p50": 100.5, + "p75": 200.5, + "p90": 300.5, + "p99": 400.5 + } + }, + "vercelGatewayRouting": { + "only": [ + "provider-a" + ], + "order": [ + "provider-a", + "provider-b" + ] + }, + "supportsOpenAIGrammarTools": true, + "supportsStrictMode": true, + "sendSessionAffinityHeaders": true, + "deferredToolsMode": "kimi", + "sessionAffinityFormat": "openrouter", + "supportsLongCacheRetention": true, + "supportsToolSearch": true, + "supportsEagerToolInputStreaming": true, + "supportsCacheControlOnTools": true, + "supportsTemperature": true, + "forceAdaptiveThinking": true, + "allowEmptySignature": true, + "supportsStrictTools": true, + "supportsToolReferences": true + } + } + } + }, + "execution": { + "status": "success", + "entryFunctions": [ + "composeModelProvider", + "Provider.getModels", + "resolveCompatibilityRequestConfig", + "Provider.auth.apiKey.resolve" + ] + }, + "authExecution": { + "status": "success", + "entryFunction": "Provider.auth.apiKey.resolve", + "result": { + "auth": { + "apiKey": "literal-all-fields-key", + "headers": { + "x-provider-field": "provider-value", + "Authorization": "Bearer literal-all-fields-key" + } + }, + "source": "configured API key" + } + }, + "expected": { + "provider": { + "id": "custom-all-fields", + "name": "All Fields Provider", + "baseUrl": "https://all-fields.example/v1" + }, + "models": [ + { + "id": "all-fields-model", + "name": "All Fields Override", + "api": "anthropic-messages", + "provider": "custom-all-fields", + "baseUrl": "https://all-fields-model.example/v1", + "reasoning": false, + "thinkingLevelMap": { + "off": null, + "minimal": "minimal-effort", + "low": "low-effort", + "medium": "medium-effort", + "high": "high-effort", + "xhigh": "xhigh-effort", + "max": "max-effort" + }, + "input": [ + "image", + "text" + ], + "cost": { + "input": 0.11, + "output": 0.22, + "cacheRead": 0.033, + "cacheWrite": 0.044, + "tiers": [ + { + "inputTokensAbove": 1000.5, + "input": 0.55, + "output": 0.66, + "cacheRead": 0.077, + "cacheWrite": 0.088 + } + ] + }, + "contextWindow": 256000.75, + "maxTokens": 32768.5, + "compat": { + "supportsStore": true, + "supportsDeveloperRole": true, + "supportsReasoningEffort": true, + "supportsUsageInStreaming": true, + "maxTokensField": "max_completion_tokens", + "requiresToolResultName": true, + "requiresAssistantAfterToolResult": true, + "requiresThinkingAsText": true, + "requiresReasoningContentOnAssistantMessages": true, + "thinkingFormat": "chat-template", + "chatTemplateKwargs": { + "stringValue": "literal", + "numberValue": 1.25, + "booleanValue": true, + "nullValue": null, + "variableValue": { + "$var": "thinking.effort", + "omitWhenOff": true + } + }, + "cacheControlFormat": "anthropic", + "openRouterRouting": { + "allow_fallbacks": true, + "require_parameters": true, + "data_collection": "deny", + "zdr": true, + "enforce_distillable_text": true, + "order": [ + "provider-a", + "provider-b" + ], + "only": [ + "provider-a" + ], + "ignore": [ + "provider-z" + ], + "quantizations": [ + "fp8" + ], + "sort": { + "by": "price", + "partition": null + }, + "max_price": { + "prompt": 1.1, + "completion": "2.2", + "image": 3.3, + "audio": "4.4", + "request": 5.5 + }, + "preferred_min_throughput": { + "p50": 10.5, + "p75": 9.5, + "p90": 8.5, + "p99": 7.5 + }, + "preferred_max_latency": { + "p50": 100.5, + "p75": 200.5, + "p90": 300.5, + "p99": 400.5 + } + }, + "vercelGatewayRouting": { + "only": [ + "provider-a" + ], + "order": [ + "provider-a", + "provider-b" + ] + }, + "supportsOpenAIGrammarTools": true, + "supportsStrictMode": true, + "sendSessionAffinityHeaders": true, + "deferredToolsMode": "kimi", + "sessionAffinityFormat": "openrouter", + "supportsLongCacheRetention": true, + "supportsToolSearch": true, + "supportsEagerToolInputStreaming": true, + "supportsCacheControlOnTools": true, + "supportsTemperature": true, + "forceAdaptiveThinking": true, + "allowEmptySignature": true, + "supportsStrictTools": true, + "supportsToolReferences": true + }, + "headers": { + "x-provider-field": "provider-value", + "x-override-field": "override-value", + "x-model-field": "model-value" + }, + "authHeader": true + } + ], + "ignoredOverrideKeys": [] + } + }, + { + "id": "provider-fields-inherited", + "providerId": "custom-provider-fields", + "input": { + "name": "Provider Layer Name", + "baseUrl": "https://provider-fields.example/v1", + "apiKey": "provider-layer-key", + "api": "openai-responses", + "oauth": "radius", + "headers": { + "x-provider-field": "provider-layer-value" + }, + "compat": { + "supportsStore": true, + "supportsDeveloperRole": true, + "supportsReasoningEffort": true, + "supportsUsageInStreaming": true, + "maxTokensField": "max_completion_tokens", + "requiresToolResultName": true, + "requiresAssistantAfterToolResult": true, + "requiresThinkingAsText": true, + "requiresReasoningContentOnAssistantMessages": true, + "thinkingFormat": "chat-template", + "chatTemplateKwargs": { + "stringValue": "literal", + "numberValue": 1.25, + "booleanValue": true, + "nullValue": null, + "variableValue": { + "$var": "thinking.effort", + "omitWhenOff": true + } + }, + "cacheControlFormat": "anthropic", + "openRouterRouting": { + "allow_fallbacks": true, + "require_parameters": true, + "data_collection": "deny", + "zdr": true, + "enforce_distillable_text": true, + "order": [ + "provider-a", + "provider-b" + ], + "only": [ + "provider-a" + ], + "ignore": [ + "provider-z" + ], + "quantizations": [ + "fp8" + ], + "sort": { + "by": "price", + "partition": null + }, + "max_price": { + "prompt": 1.1, + "completion": "2.2", + "image": 3.3, + "audio": "4.4", + "request": 5.5 + }, + "preferred_min_throughput": { + "p50": 10.5, + "p75": 9.5, + "p90": 8.5, + "p99": 7.5 + }, + "preferred_max_latency": { + "p50": 100.5, + "p75": 200.5, + "p90": 300.5, + "p99": 400.5 + } + }, + "vercelGatewayRouting": { + "only": [ + "provider-a" + ], + "order": [ + "provider-a", + "provider-b" + ] + }, + "supportsOpenAIGrammarTools": true, + "supportsStrictMode": true, + "sendSessionAffinityHeaders": true, + "deferredToolsMode": "kimi", + "sessionAffinityFormat": "openrouter", + "supportsLongCacheRetention": true, + "supportsToolSearch": true, + "supportsEagerToolInputStreaming": true, + "supportsCacheControlOnTools": true, + "supportsTemperature": true, + "forceAdaptiveThinking": true, + "allowEmptySignature": true, + "supportsStrictTools": true, + "supportsToolReferences": true + }, + "authHeader": true, + "models": [ + { + "id": "provider-inherited-model" + } + ] + }, + "execution": { + "status": "success", + "entryFunctions": [ + "composeModelProvider", + "Provider.getModels", + "resolveCompatibilityRequestConfig", + "Provider.auth.apiKey.resolve" + ] + }, + "authExecution": { + "status": "success", + "entryFunction": "Provider.auth.apiKey.resolve", + "result": { + "auth": { + "apiKey": "provider-layer-key", + "headers": { + "x-provider-field": "provider-layer-value", + "Authorization": "Bearer provider-layer-key" + } + }, + "source": "configured API key" + } + }, + "expected": { + "provider": { + "id": "custom-provider-fields", + "name": "Provider Layer Name", + "baseUrl": "https://provider-fields.example/v1" + }, + "models": [ + { + "id": "provider-inherited-model", + "name": "provider-inherited-model", + "api": "openai-responses", + "provider": "custom-provider-fields", + "baseUrl": "https://provider-fields.example/v1", + "reasoning": false, + "input": [ + "text" + ], + "cost": { + "input": 0, + "output": 0, + "cacheRead": 0, + "cacheWrite": 0 + }, + "contextWindow": 128000, + "maxTokens": 16384, + "compat": { + "supportsStore": true, + "supportsDeveloperRole": true, + "supportsReasoningEffort": true, + "supportsUsageInStreaming": true, + "maxTokensField": "max_completion_tokens", + "requiresToolResultName": true, + "requiresAssistantAfterToolResult": true, + "requiresThinkingAsText": true, + "requiresReasoningContentOnAssistantMessages": true, + "thinkingFormat": "chat-template", + "chatTemplateKwargs": { + "stringValue": "literal", + "numberValue": 1.25, + "booleanValue": true, + "nullValue": null, + "variableValue": { + "$var": "thinking.effort", + "omitWhenOff": true + } + }, + "cacheControlFormat": "anthropic", + "openRouterRouting": { + "allow_fallbacks": true, + "require_parameters": true, + "data_collection": "deny", + "zdr": true, + "enforce_distillable_text": true, + "order": [ + "provider-a", + "provider-b" + ], + "only": [ + "provider-a" + ], + "ignore": [ + "provider-z" + ], + "quantizations": [ + "fp8" + ], + "sort": { + "by": "price", + "partition": null + }, + "max_price": { + "prompt": 1.1, + "completion": "2.2", + "image": 3.3, + "audio": "4.4", + "request": 5.5 + }, + "preferred_min_throughput": { + "p50": 10.5, + "p75": 9.5, + "p90": 8.5, + "p99": 7.5 + }, + "preferred_max_latency": { + "p50": 100.5, + "p75": 200.5, + "p90": 300.5, + "p99": 400.5 + } + }, + "vercelGatewayRouting": { + "only": [ + "provider-a" + ], + "order": [ + "provider-a", + "provider-b" + ] + }, + "supportsOpenAIGrammarTools": true, + "supportsStrictMode": true, + "sendSessionAffinityHeaders": true, + "deferredToolsMode": "kimi", + "sessionAffinityFormat": "openrouter", + "supportsLongCacheRetention": true, + "supportsToolSearch": true, + "supportsEagerToolInputStreaming": true, + "supportsCacheControlOnTools": true, + "supportsTemperature": true, + "forceAdaptiveThinking": true, + "allowEmptySignature": true, + "supportsStrictTools": true, + "supportsToolReferences": true + }, + "headers": { + "x-provider-field": "provider-layer-value" + }, + "authHeader": true + } + ], + "ignoredOverrideKeys": [] + } + }, + { + "id": "model-fields-executed", + "providerId": "custom-model-fields", + "input": { + "apiKey": "model-layer-key", + "models": [ + { + "id": "model-layer-id", + "name": "Model Layer Name", + "api": "anthropic-messages", + "baseUrl": "https://model-fields.example/v1", + "reasoning": true, + "thinkingLevelMap": { + "off": null, + "minimal": "minimal-effort", + "low": "low-effort", + "medium": "medium-effort", + "high": "high-effort", + "xhigh": "xhigh-effort", + "max": "max-effort" + }, + "input": [ + "text", + "image" + ], + "cost": { + "input": 0.11, + "output": 0.22, + "cacheRead": 0.033, + "cacheWrite": 0.044, + "tiers": [ + { + "inputTokensAbove": 1000.5, + "input": 0.55, + "output": 0.66, + "cacheRead": 0.077, + "cacheWrite": 0.088 + } + ] + }, + "contextWindow": 64000.5, + "maxTokens": 8192.25, + "headers": { + "x-model-field": "model-layer-value" + }, + "compat": { + "supportsStore": true, + "supportsDeveloperRole": true, + "supportsReasoningEffort": true, + "supportsUsageInStreaming": true, + "maxTokensField": "max_completion_tokens", + "requiresToolResultName": true, + "requiresAssistantAfterToolResult": true, + "requiresThinkingAsText": true, + "requiresReasoningContentOnAssistantMessages": true, + "thinkingFormat": "chat-template", + "chatTemplateKwargs": { + "stringValue": "literal", + "numberValue": 1.25, + "booleanValue": true, + "nullValue": null, + "variableValue": { + "$var": "thinking.effort", + "omitWhenOff": true + } + }, + "cacheControlFormat": "anthropic", + "openRouterRouting": { + "allow_fallbacks": true, + "require_parameters": true, + "data_collection": "deny", + "zdr": true, + "enforce_distillable_text": true, + "order": [ + "provider-a", + "provider-b" + ], + "only": [ + "provider-a" + ], + "ignore": [ + "provider-z" + ], + "quantizations": [ + "fp8" + ], + "sort": { + "by": "price", + "partition": null + }, + "max_price": { + "prompt": 1.1, + "completion": "2.2", + "image": 3.3, + "audio": "4.4", + "request": 5.5 + }, + "preferred_min_throughput": { + "p50": 10.5, + "p75": 9.5, + "p90": 8.5, + "p99": 7.5 + }, + "preferred_max_latency": { + "p50": 100.5, + "p75": 200.5, + "p90": 300.5, + "p99": 400.5 + } + }, + "vercelGatewayRouting": { + "only": [ + "provider-a" + ], + "order": [ + "provider-a", + "provider-b" + ] + }, + "supportsOpenAIGrammarTools": true, + "supportsStrictMode": true, + "sendSessionAffinityHeaders": true, + "deferredToolsMode": "kimi", + "sessionAffinityFormat": "openrouter", + "supportsLongCacheRetention": true, + "supportsToolSearch": true, + "supportsEagerToolInputStreaming": true, + "supportsCacheControlOnTools": true, + "supportsTemperature": true, + "forceAdaptiveThinking": true, + "allowEmptySignature": true, + "supportsStrictTools": true, + "supportsToolReferences": true + } + } + ] + }, + "execution": { + "status": "success", + "entryFunctions": [ + "composeModelProvider", + "Provider.getModels", + "resolveCompatibilityRequestConfig", + "Provider.auth.apiKey.resolve" + ] + }, + "authExecution": { + "status": "success", + "entryFunction": "Provider.auth.apiKey.resolve", + "result": { + "auth": { + "apiKey": "model-layer-key" + }, + "source": "configured API key" + } + }, + "expected": { + "provider": { + "id": "custom-model-fields", + "name": "custom-model-fields" + }, + "models": [ + { + "id": "model-layer-id", + "name": "Model Layer Name", + "api": "anthropic-messages", + "provider": "custom-model-fields", + "baseUrl": "https://model-fields.example/v1", + "reasoning": true, + "thinkingLevelMap": { + "off": null, + "minimal": "minimal-effort", + "low": "low-effort", + "medium": "medium-effort", + "high": "high-effort", + "xhigh": "xhigh-effort", + "max": "max-effort" + }, + "input": [ + "text", + "image" + ], + "cost": { + "input": 0.11, + "output": 0.22, + "cacheRead": 0.033, + "cacheWrite": 0.044, + "tiers": [ + { + "inputTokensAbove": 1000.5, + "input": 0.55, + "output": 0.66, + "cacheRead": 0.077, + "cacheWrite": 0.088 + } + ] + }, + "contextWindow": 64000.5, + "maxTokens": 8192.25, + "compat": { + "supportsStore": true, + "supportsDeveloperRole": true, + "supportsReasoningEffort": true, + "supportsUsageInStreaming": true, + "maxTokensField": "max_completion_tokens", + "requiresToolResultName": true, + "requiresAssistantAfterToolResult": true, + "requiresThinkingAsText": true, + "requiresReasoningContentOnAssistantMessages": true, + "thinkingFormat": "chat-template", + "chatTemplateKwargs": { + "stringValue": "literal", + "numberValue": 1.25, + "booleanValue": true, + "nullValue": null, + "variableValue": { + "$var": "thinking.effort", + "omitWhenOff": true + } + }, + "cacheControlFormat": "anthropic", + "openRouterRouting": { + "allow_fallbacks": true, + "require_parameters": true, + "data_collection": "deny", + "zdr": true, + "enforce_distillable_text": true, + "order": [ + "provider-a", + "provider-b" + ], + "only": [ + "provider-a" + ], + "ignore": [ + "provider-z" + ], + "quantizations": [ + "fp8" + ], + "sort": { + "by": "price", + "partition": null + }, + "max_price": { + "prompt": 1.1, + "completion": "2.2", + "image": 3.3, + "audio": "4.4", + "request": 5.5 + }, + "preferred_min_throughput": { + "p50": 10.5, + "p75": 9.5, + "p90": 8.5, + "p99": 7.5 + }, + "preferred_max_latency": { + "p50": 100.5, + "p75": 200.5, + "p90": 300.5, + "p99": 400.5 + } + }, + "vercelGatewayRouting": { + "only": [ + "provider-a" + ], + "order": [ + "provider-a", + "provider-b" + ] + }, + "supportsOpenAIGrammarTools": true, + "supportsStrictMode": true, + "sendSessionAffinityHeaders": true, + "deferredToolsMode": "kimi", + "sessionAffinityFormat": "openrouter", + "supportsLongCacheRetention": true, + "supportsToolSearch": true, + "supportsEagerToolInputStreaming": true, + "supportsCacheControlOnTools": true, + "supportsTemperature": true, + "forceAdaptiveThinking": true, + "allowEmptySignature": true, + "supportsStrictTools": true, + "supportsToolReferences": true + }, + "headers": { + "x-model-field": "model-layer-value" + }, + "authHeader": false + } + ], + "ignoredOverrideKeys": [] + } + }, + { + "id": "override-fields-executed", + "providerId": "custom-override-fields", + "input": { + "apiKey": "override-layer-key", + "api": "openai-completions", + "baseUrl": "https://override-provider.example/v1", + "models": [ + { + "id": "override-target", + "name": "Definition Before Override", + "reasoning": true + } + ], + "modelOverrides": { + "override-target": { + "name": "Override Layer Name", + "reasoning": false, + "thinkingLevelMap": { + "off": null, + "minimal": "minimal-effort", + "low": "low-effort", + "medium": "medium-effort", + "high": "high-effort", + "xhigh": "xhigh-effort", + "max": "max-effort" + }, + "input": [ + "image", + "text" + ], + "cost": { + "input": 0.11, + "output": 0.22, + "cacheRead": 0.033, + "cacheWrite": 0.044, + "tiers": [ + { + "inputTokensAbove": 1000.5, + "input": 0.55, + "output": 0.66, + "cacheRead": 0.077, + "cacheWrite": 0.088 + } + ] + }, + "contextWindow": 96000.75, + "maxTokens": 12288.5, + "headers": { + "x-override-field": "override-layer-value" + }, + "compat": { + "supportsStore": true, + "supportsDeveloperRole": true, + "supportsReasoningEffort": true, + "supportsUsageInStreaming": true, + "maxTokensField": "max_completion_tokens", + "requiresToolResultName": true, + "requiresAssistantAfterToolResult": true, + "requiresThinkingAsText": true, + "requiresReasoningContentOnAssistantMessages": true, + "thinkingFormat": "chat-template", + "chatTemplateKwargs": { + "stringValue": "literal", + "numberValue": 1.25, + "booleanValue": true, + "nullValue": null, + "variableValue": { + "$var": "thinking.effort", + "omitWhenOff": true + } + }, + "cacheControlFormat": "anthropic", + "openRouterRouting": { + "allow_fallbacks": true, + "require_parameters": true, + "data_collection": "deny", + "zdr": true, + "enforce_distillable_text": true, + "order": [ + "provider-a", + "provider-b" + ], + "only": [ + "provider-a" + ], + "ignore": [ + "provider-z" + ], + "quantizations": [ + "fp8" + ], + "sort": { + "by": "price", + "partition": null + }, + "max_price": { + "prompt": 1.1, + "completion": "2.2", + "image": 3.3, + "audio": "4.4", + "request": 5.5 + }, + "preferred_min_throughput": { + "p50": 10.5, + "p75": 9.5, + "p90": 8.5, + "p99": 7.5 + }, + "preferred_max_latency": { + "p50": 100.5, + "p75": 200.5, + "p90": 300.5, + "p99": 400.5 + } + }, + "vercelGatewayRouting": { + "only": [ + "provider-a" + ], + "order": [ + "provider-a", + "provider-b" + ] + }, + "supportsOpenAIGrammarTools": true, + "supportsStrictMode": true, + "sendSessionAffinityHeaders": true, + "deferredToolsMode": "kimi", + "sessionAffinityFormat": "openrouter", + "supportsLongCacheRetention": true, + "supportsToolSearch": true, + "supportsEagerToolInputStreaming": true, + "supportsCacheControlOnTools": true, + "supportsTemperature": true, + "forceAdaptiveThinking": true, + "allowEmptySignature": true, + "supportsStrictTools": true, + "supportsToolReferences": true + } + } + } + }, + "execution": { + "status": "success", + "entryFunctions": [ + "composeModelProvider", + "Provider.getModels", + "resolveCompatibilityRequestConfig", + "Provider.auth.apiKey.resolve" + ] + }, + "authExecution": { + "status": "success", + "entryFunction": "Provider.auth.apiKey.resolve", + "result": { + "auth": { + "apiKey": "override-layer-key" + }, + "source": "configured API key" + } + }, + "expected": { + "provider": { + "id": "custom-override-fields", + "name": "custom-override-fields", + "baseUrl": "https://override-provider.example/v1" + }, + "models": [ + { + "id": "override-target", + "name": "Override Layer Name", + "api": "openai-completions", + "provider": "custom-override-fields", + "baseUrl": "https://override-provider.example/v1", + "reasoning": false, + "thinkingLevelMap": { + "off": null, + "minimal": "minimal-effort", + "low": "low-effort", + "medium": "medium-effort", + "high": "high-effort", + "xhigh": "xhigh-effort", + "max": "max-effort" + }, + "input": [ + "image", + "text" + ], + "cost": { + "input": 0.11, + "output": 0.22, + "cacheRead": 0.033, + "cacheWrite": 0.044, + "tiers": [ + { + "inputTokensAbove": 1000.5, + "input": 0.55, + "output": 0.66, + "cacheRead": 0.077, + "cacheWrite": 0.088 + } + ] + }, + "contextWindow": 96000.75, + "maxTokens": 12288.5, + "compat": { + "supportsStore": true, + "supportsDeveloperRole": true, + "supportsReasoningEffort": true, + "supportsUsageInStreaming": true, + "maxTokensField": "max_completion_tokens", + "requiresToolResultName": true, + "requiresAssistantAfterToolResult": true, + "requiresThinkingAsText": true, + "requiresReasoningContentOnAssistantMessages": true, + "thinkingFormat": "chat-template", + "chatTemplateKwargs": { + "stringValue": "literal", + "numberValue": 1.25, + "booleanValue": true, + "nullValue": null, + "variableValue": { + "$var": "thinking.effort", + "omitWhenOff": true + } + }, + "cacheControlFormat": "anthropic", + "openRouterRouting": { + "allow_fallbacks": true, + "require_parameters": true, + "data_collection": "deny", + "zdr": true, + "enforce_distillable_text": true, + "order": [ + "provider-a", + "provider-b" + ], + "only": [ + "provider-a" + ], + "ignore": [ + "provider-z" + ], + "quantizations": [ + "fp8" + ], + "sort": { + "by": "price", + "partition": null + }, + "max_price": { + "prompt": 1.1, + "completion": "2.2", + "image": 3.3, + "audio": "4.4", + "request": 5.5 + }, + "preferred_min_throughput": { + "p50": 10.5, + "p75": 9.5, + "p90": 8.5, + "p99": 7.5 + }, + "preferred_max_latency": { + "p50": 100.5, + "p75": 200.5, + "p90": 300.5, + "p99": 400.5 + } + }, + "vercelGatewayRouting": { + "only": [ + "provider-a" + ], + "order": [ + "provider-a", + "provider-b" + ] + }, + "supportsOpenAIGrammarTools": true, + "supportsStrictMode": true, + "sendSessionAffinityHeaders": true, + "deferredToolsMode": "kimi", + "sessionAffinityFormat": "openrouter", + "supportsLongCacheRetention": true, + "supportsToolSearch": true, + "supportsEagerToolInputStreaming": true, + "supportsCacheControlOnTools": true, + "supportsTemperature": true, + "forceAdaptiveThinking": true, + "allowEmptySignature": true, + "supportsStrictTools": true, + "supportsToolReferences": true + }, + "headers": { + "x-override-field": "override-layer-value" + }, + "authHeader": false + } + ], + "ignoredOverrideKeys": [] + } + }, + { + "id": "radius-oauth-requires-provider-base-url", + "providerId": "custom-radius-missing-base", + "input": { + "apiKey": "radius-key", + "oauth": "radius", + "models": [ + { + "id": "radius-model", + "api": "openai-responses", + "baseUrl": "https://model-only.example/v1" + } + ] + }, + "execution": { + "status": "error", + "entryFunctions": [ + "composeModelProvider" + ] + }, + "expectedError": "Provider custom-radius-missing-base: \"baseUrl\" is required when \"oauth\" is set." + }, + { + "id": "official-defaults", + "providerId": "custom-defaults", + "input": { + "api": "openai-responses", + "baseUrl": "https://default.example/v1", + "apiKey": "literal-secret", + "models": [ + { + "id": "default-model" + } + ] + }, + "execution": { + "status": "success", + "entryFunctions": [ + "composeModelProvider", + "Provider.getModels", + "resolveCompatibilityRequestConfig", + "Provider.auth.apiKey.resolve" + ] + }, + "authExecution": { + "status": "success", + "entryFunction": "Provider.auth.apiKey.resolve", + "result": { + "auth": { + "apiKey": "literal-secret" + }, + "source": "configured API key" + } + }, + "expected": { + "provider": { + "id": "custom-defaults", + "name": "custom-defaults", + "baseUrl": "https://default.example/v1" + }, + "models": [ + { + "id": "default-model", + "name": "default-model", + "api": "openai-responses", + "provider": "custom-defaults", + "baseUrl": "https://default.example/v1", + "reasoning": false, + "input": [ + "text" + ], + "cost": { + "input": 0, + "output": 0, + "cacheRead": 0, + "cacheWrite": 0 + }, + "contextWindow": 128000, + "maxTokens": 16384, + "authHeader": false + } + ], + "ignoredOverrideKeys": [] + } + }, + { + "id": "later-model-first-model-fallback", + "providerId": "custom-fallback", + "input": { + "apiKey": "literal-secret", + "models": [ + { + "id": "first", + "api": "anthropic-messages", + "baseUrl": "https://first.example/v1" + }, + { + "id": "second" + } + ] + }, + "execution": { + "status": "success", + "entryFunctions": [ + "composeModelProvider", + "Provider.getModels", + "resolveCompatibilityRequestConfig", + "Provider.auth.apiKey.resolve" + ] + }, + "authExecution": { + "status": "success", + "entryFunction": "Provider.auth.apiKey.resolve", + "result": { + "auth": { + "apiKey": "literal-secret" + }, + "source": "configured API key" + } + }, + "expected": { + "provider": { + "id": "custom-fallback", + "name": "custom-fallback" + }, + "models": [ + { + "id": "first", + "name": "first", + "api": "anthropic-messages", + "provider": "custom-fallback", + "baseUrl": "https://first.example/v1", + "reasoning": false, + "input": [ + "text" + ], + "cost": { + "input": 0, + "output": 0, + "cacheRead": 0, + "cacheWrite": 0 + }, + "contextWindow": 128000, + "maxTokens": 16384, + "authHeader": false + }, + { + "id": "second", + "name": "second", + "api": "anthropic-messages", + "provider": "custom-fallback", + "baseUrl": "https://first.example/v1", + "reasoning": false, + "input": [ + "text" + ], + "cost": { + "input": 0, + "output": 0, + "cacheRead": 0, + "cacheWrite": 0 + }, + "contextWindow": 128000, + "maxTokens": 16384, + "authHeader": false + } + ], + "ignoredOverrideKeys": [] + } + }, + { + "id": "duplicate-model-replaces-existing-slot", + "providerId": "custom-duplicate", + "input": { + "apiKey": "literal-secret", + "models": [ + { + "id": "same", + "name": "First", + "api": "openai-completions", + "baseUrl": "https://first.example/v1" + }, + { + "id": "same", + "name": "Second", + "api": "openai-responses", + "baseUrl": "https://second.example/v1" + } + ] + }, + "execution": { + "status": "success", + "entryFunctions": [ + "composeModelProvider", + "Provider.getModels", + "resolveCompatibilityRequestConfig", + "Provider.auth.apiKey.resolve" + ] + }, + "authExecution": { + "status": "success", + "entryFunction": "Provider.auth.apiKey.resolve", + "result": { + "auth": { + "apiKey": "literal-secret" + }, + "source": "configured API key" + } + }, + "expected": { + "provider": { + "id": "custom-duplicate", + "name": "custom-duplicate" + }, + "models": [ + { + "id": "same", + "name": "Second", + "api": "openai-responses", + "provider": "custom-duplicate", + "baseUrl": "https://second.example/v1", + "reasoning": false, + "input": [ + "text" + ], + "cost": { + "input": 0, + "output": 0, + "cacheRead": 0, + "cacheWrite": 0 + }, + "contextWindow": 128000, + "maxTokens": 16384, + "authHeader": false + } + ], + "ignoredOverrideKeys": [] + } + }, + { + "id": "provider-model-override-precedence", + "providerId": "custom-precedence", + "input": { + "api": "openai-completions", + "baseUrl": "https://provider.example/v1", + "apiKey": "literal-secret", + "headers": { + "layer": "provider", + "providerOnly": "yes" + }, + "compat": { + "supportsDeveloperRole": true, + "openRouterRouting": { + "zdr": true + } + }, + "models": [ + { + "id": "precedence", + "name": "Definition", + "api": "openai-responses", + "baseUrl": "https://model.example/v1", + "reasoning": false, + "thinkingLevelMap": { + "high": "model-high", + "future": "model-future" + }, + "contextWindow": 1000.5, + "maxTokens": 100.25, + "headers": { + "layer": "definition", + "definitionOnly": "yes" + }, + "compat": { + "supportsStore": true, + "openRouterRouting": { + "only": [ + "definition" + ], + "zdr": false + } + }, + "cost": { + "input": 1, + "output": 2, + "cacheRead": 0.1, + "cacheWrite": 0.2 + } + } + ], + "modelOverrides": { + "precedence": { + "name": "Override", + "reasoning": true, + "thinkingLevelMap": { + "high": "override-high", + "futureOverride": "preserve" + }, + "contextWindow": 2000.75, + "headers": { + "layer": "override", + "overrideOnly": "yes" + }, + "compat": { + "supportsStore": false, + "openRouterRouting": { + "order": [ + "override" + ] + } + }, + "cost": { + "output": 3.5 + } + } + } + }, + "execution": { + "status": "success", + "entryFunctions": [ + "composeModelProvider", + "Provider.getModels", + "resolveCompatibilityRequestConfig", + "Provider.auth.apiKey.resolve" + ] + }, + "authExecution": { + "status": "success", + "entryFunction": "Provider.auth.apiKey.resolve", + "result": { + "auth": { + "apiKey": "literal-secret", + "headers": { + "layer": "provider", + "providerOnly": "yes" + } + }, + "source": "configured API key" + } + }, + "expected": { + "provider": { + "id": "custom-precedence", + "name": "custom-precedence", + "baseUrl": "https://provider.example/v1" + }, + "models": [ + { + "id": "precedence", + "name": "Override", + "api": "openai-responses", + "provider": "custom-precedence", + "baseUrl": "https://model.example/v1", + "reasoning": true, + "thinkingLevelMap": { + "high": "override-high", + "future": "model-future", + "futureOverride": "preserve" + }, + "input": [ + "text" + ], + "cost": { + "input": 1, + "output": 3.5, + "cacheRead": 0.1, + "cacheWrite": 0.2 + }, + "contextWindow": 2000.75, + "maxTokens": 100.25, + "compat": { + "supportsDeveloperRole": true, + "openRouterRouting": { + "zdr": false, + "only": [ + "definition" + ], + "order": [ + "override" + ] + }, + "supportsStore": false + }, + "headers": { + "layer": "definition", + "providerOnly": "yes", + "overrideOnly": "yes", + "definitionOnly": "yes" + }, + "authHeader": false + } + ], + "ignoredOverrideKeys": [] + } + }, + { + "id": "fractional-cost-and-limits", + "providerId": "custom-fractional", + "input": { + "api": "google-generative-ai", + "baseUrl": "https://fractional.example/v1", + "apiKey": "literal-secret", + "models": [ + { + "id": "fractional", + "contextWindow": 128000.5, + "maxTokens": 16384.25, + "cost": { + "input": 0.125, + "output": 0.375, + "cacheRead": 0.0625, + "cacheWrite": 0.1875 + } + } + ] + }, + "execution": { + "status": "success", + "entryFunctions": [ + "composeModelProvider", + "Provider.getModels", + "resolveCompatibilityRequestConfig", + "Provider.auth.apiKey.resolve" + ] + }, + "authExecution": { + "status": "success", + "entryFunction": "Provider.auth.apiKey.resolve", + "result": { + "auth": { + "apiKey": "literal-secret" + }, + "source": "configured API key" + } + }, + "expected": { + "provider": { + "id": "custom-fractional", + "name": "custom-fractional", + "baseUrl": "https://fractional.example/v1" + }, + "models": [ + { + "id": "fractional", + "name": "fractional", + "api": "google-generative-ai", + "provider": "custom-fractional", + "baseUrl": "https://fractional.example/v1", + "reasoning": false, + "input": [ + "text" + ], + "cost": { + "input": 0.125, + "output": 0.375, + "cacheRead": 0.0625, + "cacheWrite": 0.1875 + }, + "contextWindow": 128000.5, + "maxTokens": 16384.25, + "authHeader": false + } + ], + "ignoredOverrideKeys": [] + } + }, + { + "id": "unmatched-override-is-ignored-by-composer", + "providerId": "custom-unmatched", + "input": { + "api": "google-generative-ai", + "baseUrl": "https://google.example/v1", + "apiKey": "literal-secret", + "models": [ + { + "id": "known" + } + ], + "modelOverrides": { + "missing": { + "maxTokens": 7.5, + "headers": { + "ignored": "yes" + } + } + } + }, + "execution": { + "status": "success", + "entryFunctions": [ + "composeModelProvider", + "Provider.getModels", + "resolveCompatibilityRequestConfig", + "Provider.auth.apiKey.resolve" + ] + }, + "authExecution": { + "status": "success", + "entryFunction": "Provider.auth.apiKey.resolve", + "result": { + "auth": { + "apiKey": "literal-secret" + }, + "source": "configured API key" + } + }, + "expected": { + "provider": { + "id": "custom-unmatched", + "name": "custom-unmatched", + "baseUrl": "https://google.example/v1" + }, + "models": [ + { + "id": "known", + "name": "known", + "api": "google-generative-ai", + "provider": "custom-unmatched", + "baseUrl": "https://google.example/v1", + "reasoning": false, + "input": [ + "text" + ], + "cost": { + "input": 0, + "output": 0, + "cacheRead": 0, + "cacheWrite": 0 + }, + "contextWindow": 128000, + "maxTokens": 16384, + "authHeader": false + } + ], + "ignoredOverrideKeys": [ + "missing" + ] + } + }, + { + "id": "invalid-nonempty-url-is-composed", + "providerId": "custom-invalid-url", + "input": { + "api": "openai-responses", + "baseUrl": "not a URL", + "apiKey": "literal-secret", + "models": [ + { + "id": "model" + } + ] + }, + "execution": { + "status": "success", + "entryFunctions": [ + "composeModelProvider", + "Provider.getModels", + "resolveCompatibilityRequestConfig", + "Provider.auth.apiKey.resolve" + ] + }, + "authExecution": { + "status": "success", + "entryFunction": "Provider.auth.apiKey.resolve", + "result": { + "auth": { + "apiKey": "literal-secret" + }, + "source": "configured API key" + } + }, + "expected": { + "provider": { + "id": "custom-invalid-url", + "name": "custom-invalid-url", + "baseUrl": "not a URL" + }, + "models": [ + { + "id": "model", + "name": "model", + "api": "openai-responses", + "provider": "custom-invalid-url", + "baseUrl": "not a URL", + "reasoning": false, + "input": [ + "text" + ], + "cost": { + "input": 0, + "output": 0, + "cacheRead": 0, + "cacheWrite": 0 + }, + "contextWindow": 128000, + "maxTokens": 16384, + "authHeader": false + } + ], + "ignoredOverrideKeys": [] + } + }, + { + "id": "unknown-api-is-composed", + "providerId": "custom-future-api", + "input": { + "api": "future-wire-v9", + "baseUrl": "https://future.example/v9", + "apiKey": "literal-secret", + "models": [ + { + "id": "future-model" + } + ] + }, + "execution": { + "status": "success", + "entryFunctions": [ + "composeModelProvider", + "Provider.getModels", + "resolveCompatibilityRequestConfig", + "Provider.auth.apiKey.resolve" + ] + }, + "authExecution": { + "status": "success", + "entryFunction": "Provider.auth.apiKey.resolve", + "result": { + "auth": { + "apiKey": "literal-secret" + }, + "source": "configured API key" + } + }, + "expected": { + "provider": { + "id": "custom-future-api", + "name": "custom-future-api", + "baseUrl": "https://future.example/v9" + }, + "models": [ + { + "id": "future-model", + "name": "future-model", + "api": "future-wire-v9", + "provider": "custom-future-api", + "baseUrl": "https://future.example/v9", + "reasoning": false, + "input": [ + "text" + ], + "cost": { + "input": 0, + "output": 0, + "cacheRead": 0, + "cacheWrite": 0 + }, + "contextWindow": 128000, + "maxTokens": 16384, + "authHeader": false + } + ], + "ignoredOverrideKeys": [] + } + }, + { + "id": "unknown-thinking-fields-are-preserved", + "providerId": "custom-thinking", + "input": { + "api": "anthropic-messages", + "baseUrl": "https://thinking.example/v1", + "apiKey": "literal-secret", + "models": [ + { + "id": "thinking", + "thinkingLevelMap": { + "high": "future-high", + "future": { + "nested": [ + "opaque" + ] + } + } + } + ] + }, + "execution": { + "status": "success", + "entryFunctions": [ + "composeModelProvider", + "Provider.getModels", + "resolveCompatibilityRequestConfig", + "Provider.auth.apiKey.resolve" + ] + }, + "authExecution": { + "status": "success", + "entryFunction": "Provider.auth.apiKey.resolve", + "result": { + "auth": { + "apiKey": "literal-secret" + }, + "source": "configured API key" + } + }, + "expected": { + "provider": { + "id": "custom-thinking", + "name": "custom-thinking", + "baseUrl": "https://thinking.example/v1" + }, + "models": [ + { + "id": "thinking", + "name": "thinking", + "api": "anthropic-messages", + "provider": "custom-thinking", + "baseUrl": "https://thinking.example/v1", + "reasoning": false, + "thinkingLevelMap": { + "high": "future-high", + "future": { + "nested": [ + "opaque" + ] + } + }, + "input": [ + "text" + ], + "cost": { + "input": 0, + "output": 0, + "cacheRead": 0, + "cacheWrite": 0 + }, + "contextWindow": 128000, + "maxTokens": 16384, + "authHeader": false + } + ], + "ignoredOverrideKeys": [] + } + }, + { + "id": "empty-url-fails-upstream-composition", + "providerId": "custom-empty-url", + "input": { + "api": "openai-responses", + "baseUrl": "", + "apiKey": "literal-secret", + "models": [ + { + "id": "model" + } + ] + }, + "execution": { + "status": "error", + "entryFunctions": [ + "composeModelProvider" + ] + }, + "expectedError": "Provider custom-empty-url: \"baseUrl\" is required when defining custom models." + } + ], + "failClosedCases": [ + { + "id": "builtin-overlay-without-pinned-base-catalog", + "kind": "built_in_overlay", + "unavailableContext": "pinned built-in Provider instance and model catalog", + "rustExpectedStatus": "unknown", + "reasonCode": "catalog_required" + }, + { + "id": "extension-overlay-without-extension-registration", + "kind": "extension_overlay", + "unavailableContext": "extension ProviderConfigInput and registered model catalog", + "rustExpectedStatus": "unknown", + "reasonCode": "catalog_required" + } + ] +} diff --git a/tests/fixtures/pi/native-oracle/field-coverage-v1.json b/tests/fixtures/pi/native-oracle/field-coverage-v1.json new file mode 100644 index 000000000..a68ba9583 --- /dev/null +++ b/tests/fixtures/pi/native-oracle/field-coverage-v1.json @@ -0,0 +1,3000 @@ +{ + "version": 1, + "piCommit": "ab366ebe94cacd419d986be454f12b1b9913aaca", + "schemaTarget": "ModelsConfigSchema.properties.providers.patternProperties['^.*$']", + "assertion": "every schema field is present in an actual TypeBox Value.Check vector and in a pinned composer execution dedicated to its provider, model, or override layer", + "fields": [ + { + "fieldPath": "/api", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "provider-fields-inherited", + "override-fields-executed", + "official-defaults", + "provider-model-override-precedence", + "fractional-cost-and-limits", + "unmatched-override-is-ignored-by-composer", + "invalid-nonempty-url-is-composed", + "unknown-api-is-composed", + "unknown-thinking-fields-are-preserved", + "empty-url-fails-upstream-composition" + ], + "composerBehaviorCase": "provider-fields-inherited" + }, + { + "fieldPath": "/apiKey", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "provider-fields-inherited", + "model-fields-executed", + "override-fields-executed", + "radius-oauth-requires-provider-base-url", + "official-defaults", + "later-model-first-model-fallback", + "duplicate-model-replaces-existing-slot", + "provider-model-override-precedence", + "fractional-cost-and-limits", + "unmatched-override-is-ignored-by-composer", + "invalid-nonempty-url-is-composed", + "unknown-api-is-composed", + "unknown-thinking-fields-are-preserved", + "empty-url-fails-upstream-composition" + ], + "composerBehaviorCase": "provider-fields-inherited" + }, + { + "fieldPath": "/authHeader", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "provider-fields-inherited" + ], + "composerBehaviorCase": "provider-fields-inherited" + }, + { + "fieldPath": "/baseUrl", + "rawOracleCases": [ + "all-schema-fields-valid", + "empty-present-base-url", + "non-url-base-url-is-raw-string" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "provider-fields-inherited", + "override-fields-executed", + "official-defaults", + "provider-model-override-precedence", + "fractional-cost-and-limits", + "unmatched-override-is-ignored-by-composer", + "invalid-nonempty-url-is-composed", + "unknown-api-is-composed", + "unknown-thinking-fields-are-preserved", + "empty-url-fails-upstream-composition" + ], + "composerBehaviorCase": "provider-fields-inherited" + }, + { + "fieldPath": "/compat", + "rawOracleCases": [ + "all-schema-fields-valid", + "compat-openrouter-recursive-valid", + "compat-nested-union-invalid-in-every-branch", + "compat-union-additional-field", + "compat-null", + "compat-sort-string-union-branch", + "compat-sort-object-union-branch", + "compat-sort-invalid-union-boundary" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "provider-fields-inherited", + "provider-model-override-precedence" + ], + "composerBehaviorCase": "provider-fields-inherited" + }, + { + "fieldPath": "/compat/allowEmptySignature", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "provider-fields-inherited" + ], + "composerBehaviorCase": "provider-fields-inherited" + }, + { + "fieldPath": "/compat/cacheControlFormat", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "provider-fields-inherited" + ], + "composerBehaviorCase": "provider-fields-inherited" + }, + { + "fieldPath": "/compat/chatTemplateKwargs", + "rawOracleCases": [ + "all-schema-fields-valid", + "compat-openrouter-recursive-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "provider-fields-inherited" + ], + "composerBehaviorCase": "provider-fields-inherited" + }, + { + "fieldPath": "/compat/chatTemplateKwargs/*", + "rawOracleCases": [ + "all-schema-fields-valid", + "compat-openrouter-recursive-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "provider-fields-inherited" + ], + "composerBehaviorCase": "provider-fields-inherited" + }, + { + "fieldPath": "/compat/chatTemplateKwargs/*/$var", + "rawOracleCases": [ + "all-schema-fields-valid", + "compat-openrouter-recursive-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "provider-fields-inherited" + ], + "composerBehaviorCase": "provider-fields-inherited" + }, + { + "fieldPath": "/compat/chatTemplateKwargs/*/omitWhenOff", + "rawOracleCases": [ + "all-schema-fields-valid", + "compat-openrouter-recursive-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "provider-fields-inherited" + ], + "composerBehaviorCase": "provider-fields-inherited" + }, + { + "fieldPath": "/compat/deferredToolsMode", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "provider-fields-inherited" + ], + "composerBehaviorCase": "provider-fields-inherited" + }, + { + "fieldPath": "/compat/forceAdaptiveThinking", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "provider-fields-inherited" + ], + "composerBehaviorCase": "provider-fields-inherited" + }, + { + "fieldPath": "/compat/maxTokensField", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "provider-fields-inherited" + ], + "composerBehaviorCase": "provider-fields-inherited" + }, + { + "fieldPath": "/compat/openRouterRouting", + "rawOracleCases": [ + "all-schema-fields-valid", + "compat-openrouter-recursive-valid", + "compat-nested-union-invalid-in-every-branch", + "compat-sort-string-union-branch", + "compat-sort-object-union-branch", + "compat-sort-invalid-union-boundary" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "provider-fields-inherited", + "provider-model-override-precedence" + ], + "composerBehaviorCase": "provider-fields-inherited" + }, + { + "fieldPath": "/compat/openRouterRouting/allow_fallbacks", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "provider-fields-inherited" + ], + "composerBehaviorCase": "provider-fields-inherited" + }, + { + "fieldPath": "/compat/openRouterRouting/data_collection", + "rawOracleCases": [ + "all-schema-fields-valid", + "compat-openrouter-recursive-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "provider-fields-inherited" + ], + "composerBehaviorCase": "provider-fields-inherited" + }, + { + "fieldPath": "/compat/openRouterRouting/enforce_distillable_text", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "provider-fields-inherited" + ], + "composerBehaviorCase": "provider-fields-inherited" + }, + { + "fieldPath": "/compat/openRouterRouting/ignore", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "provider-fields-inherited" + ], + "composerBehaviorCase": "provider-fields-inherited" + }, + { + "fieldPath": "/compat/openRouterRouting/max_price", + "rawOracleCases": [ + "all-schema-fields-valid", + "compat-openrouter-recursive-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "provider-fields-inherited" + ], + "composerBehaviorCase": "provider-fields-inherited" + }, + { + "fieldPath": "/compat/openRouterRouting/max_price/audio", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "provider-fields-inherited" + ], + "composerBehaviorCase": "provider-fields-inherited" + }, + { + "fieldPath": "/compat/openRouterRouting/max_price/completion", + "rawOracleCases": [ + "all-schema-fields-valid", + "compat-openrouter-recursive-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "provider-fields-inherited" + ], + "composerBehaviorCase": "provider-fields-inherited" + }, + { + "fieldPath": "/compat/openRouterRouting/max_price/image", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "provider-fields-inherited" + ], + "composerBehaviorCase": "provider-fields-inherited" + }, + { + "fieldPath": "/compat/openRouterRouting/max_price/prompt", + "rawOracleCases": [ + "all-schema-fields-valid", + "compat-openrouter-recursive-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "provider-fields-inherited" + ], + "composerBehaviorCase": "provider-fields-inherited" + }, + { + "fieldPath": "/compat/openRouterRouting/max_price/request", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "provider-fields-inherited" + ], + "composerBehaviorCase": "provider-fields-inherited" + }, + { + "fieldPath": "/compat/openRouterRouting/only", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "provider-fields-inherited" + ], + "composerBehaviorCase": "provider-fields-inherited" + }, + { + "fieldPath": "/compat/openRouterRouting/order", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "provider-fields-inherited" + ], + "composerBehaviorCase": "provider-fields-inherited" + }, + { + "fieldPath": "/compat/openRouterRouting/preferred_max_latency", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "provider-fields-inherited" + ], + "composerBehaviorCase": "provider-fields-inherited" + }, + { + "fieldPath": "/compat/openRouterRouting/preferred_max_latency/p50", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "provider-fields-inherited" + ], + "composerBehaviorCase": "provider-fields-inherited" + }, + { + "fieldPath": "/compat/openRouterRouting/preferred_max_latency/p75", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "provider-fields-inherited" + ], + "composerBehaviorCase": "provider-fields-inherited" + }, + { + "fieldPath": "/compat/openRouterRouting/preferred_max_latency/p90", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "provider-fields-inherited" + ], + "composerBehaviorCase": "provider-fields-inherited" + }, + { + "fieldPath": "/compat/openRouterRouting/preferred_max_latency/p99", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "provider-fields-inherited" + ], + "composerBehaviorCase": "provider-fields-inherited" + }, + { + "fieldPath": "/compat/openRouterRouting/preferred_min_throughput", + "rawOracleCases": [ + "all-schema-fields-valid", + "compat-openrouter-recursive-valid", + "compat-nested-union-invalid-in-every-branch" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "provider-fields-inherited" + ], + "composerBehaviorCase": "provider-fields-inherited" + }, + { + "fieldPath": "/compat/openRouterRouting/preferred_min_throughput/p50", + "rawOracleCases": [ + "all-schema-fields-valid", + "compat-openrouter-recursive-valid", + "compat-nested-union-invalid-in-every-branch" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "provider-fields-inherited" + ], + "composerBehaviorCase": "provider-fields-inherited" + }, + { + "fieldPath": "/compat/openRouterRouting/preferred_min_throughput/p75", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "provider-fields-inherited" + ], + "composerBehaviorCase": "provider-fields-inherited" + }, + { + "fieldPath": "/compat/openRouterRouting/preferred_min_throughput/p90", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "provider-fields-inherited" + ], + "composerBehaviorCase": "provider-fields-inherited" + }, + { + "fieldPath": "/compat/openRouterRouting/preferred_min_throughput/p99", + "rawOracleCases": [ + "all-schema-fields-valid", + "compat-openrouter-recursive-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "provider-fields-inherited" + ], + "composerBehaviorCase": "provider-fields-inherited" + }, + { + "fieldPath": "/compat/openRouterRouting/quantizations", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "provider-fields-inherited" + ], + "composerBehaviorCase": "provider-fields-inherited" + }, + { + "fieldPath": "/compat/openRouterRouting/require_parameters", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "provider-fields-inherited" + ], + "composerBehaviorCase": "provider-fields-inherited" + }, + { + "fieldPath": "/compat/openRouterRouting/sort", + "rawOracleCases": [ + "all-schema-fields-valid", + "compat-openrouter-recursive-valid", + "compat-sort-string-union-branch", + "compat-sort-object-union-branch", + "compat-sort-invalid-union-boundary" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "provider-fields-inherited" + ], + "composerBehaviorCase": "provider-fields-inherited" + }, + { + "fieldPath": "/compat/openRouterRouting/sort/by", + "rawOracleCases": [ + "all-schema-fields-valid", + "compat-openrouter-recursive-valid", + "compat-sort-object-union-branch" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "provider-fields-inherited" + ], + "composerBehaviorCase": "provider-fields-inherited" + }, + { + "fieldPath": "/compat/openRouterRouting/sort/partition", + "rawOracleCases": [ + "all-schema-fields-valid", + "compat-openrouter-recursive-valid", + "compat-sort-object-union-branch" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "provider-fields-inherited" + ], + "composerBehaviorCase": "provider-fields-inherited" + }, + { + "fieldPath": "/compat/openRouterRouting/zdr", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "provider-fields-inherited", + "provider-model-override-precedence" + ], + "composerBehaviorCase": "provider-fields-inherited" + }, + { + "fieldPath": "/compat/requiresAssistantAfterToolResult", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "provider-fields-inherited" + ], + "composerBehaviorCase": "provider-fields-inherited" + }, + { + "fieldPath": "/compat/requiresReasoningContentOnAssistantMessages", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "provider-fields-inherited" + ], + "composerBehaviorCase": "provider-fields-inherited" + }, + { + "fieldPath": "/compat/requiresThinkingAsText", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "provider-fields-inherited" + ], + "composerBehaviorCase": "provider-fields-inherited" + }, + { + "fieldPath": "/compat/requiresToolResultName", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "provider-fields-inherited" + ], + "composerBehaviorCase": "provider-fields-inherited" + }, + { + "fieldPath": "/compat/sendSessionAffinityHeaders", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "provider-fields-inherited" + ], + "composerBehaviorCase": "provider-fields-inherited" + }, + { + "fieldPath": "/compat/sessionAffinityFormat", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "provider-fields-inherited" + ], + "composerBehaviorCase": "provider-fields-inherited" + }, + { + "fieldPath": "/compat/supportsCacheControlOnTools", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "provider-fields-inherited" + ], + "composerBehaviorCase": "provider-fields-inherited" + }, + { + "fieldPath": "/compat/supportsDeveloperRole", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "provider-fields-inherited", + "provider-model-override-precedence" + ], + "composerBehaviorCase": "provider-fields-inherited" + }, + { + "fieldPath": "/compat/supportsEagerToolInputStreaming", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "provider-fields-inherited" + ], + "composerBehaviorCase": "provider-fields-inherited" + }, + { + "fieldPath": "/compat/supportsLongCacheRetention", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "provider-fields-inherited" + ], + "composerBehaviorCase": "provider-fields-inherited" + }, + { + "fieldPath": "/compat/supportsOpenAIGrammarTools", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "provider-fields-inherited" + ], + "composerBehaviorCase": "provider-fields-inherited" + }, + { + "fieldPath": "/compat/supportsReasoningEffort", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "provider-fields-inherited" + ], + "composerBehaviorCase": "provider-fields-inherited" + }, + { + "fieldPath": "/compat/supportsStore", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "provider-fields-inherited" + ], + "composerBehaviorCase": "provider-fields-inherited" + }, + { + "fieldPath": "/compat/supportsStrictMode", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "provider-fields-inherited" + ], + "composerBehaviorCase": "provider-fields-inherited" + }, + { + "fieldPath": "/compat/supportsStrictTools", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "provider-fields-inherited" + ], + "composerBehaviorCase": "provider-fields-inherited" + }, + { + "fieldPath": "/compat/supportsTemperature", + "rawOracleCases": [ + "all-schema-fields-valid", + "compat-nested-union-invalid-in-every-branch" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "provider-fields-inherited" + ], + "composerBehaviorCase": "provider-fields-inherited" + }, + { + "fieldPath": "/compat/supportsToolReferences", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "provider-fields-inherited" + ], + "composerBehaviorCase": "provider-fields-inherited" + }, + { + "fieldPath": "/compat/supportsToolSearch", + "rawOracleCases": [ + "all-schema-fields-valid", + "compat-nested-union-invalid-in-every-branch" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "provider-fields-inherited" + ], + "composerBehaviorCase": "provider-fields-inherited" + }, + { + "fieldPath": "/compat/supportsUsageInStreaming", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "provider-fields-inherited" + ], + "composerBehaviorCase": "provider-fields-inherited" + }, + { + "fieldPath": "/compat/thinkingFormat", + "rawOracleCases": [ + "all-schema-fields-valid", + "compat-openrouter-recursive-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "provider-fields-inherited" + ], + "composerBehaviorCase": "provider-fields-inherited" + }, + { + "fieldPath": "/compat/vercelGatewayRouting", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "provider-fields-inherited" + ], + "composerBehaviorCase": "provider-fields-inherited" + }, + { + "fieldPath": "/compat/vercelGatewayRouting/only", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "provider-fields-inherited" + ], + "composerBehaviorCase": "provider-fields-inherited" + }, + { + "fieldPath": "/compat/vercelGatewayRouting/order", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "provider-fields-inherited" + ], + "composerBehaviorCase": "provider-fields-inherited" + }, + { + "fieldPath": "/headers", + "rawOracleCases": [ + "all-schema-fields-valid", + "headers-record-valid", + "headers-record-invalid-value" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "provider-fields-inherited", + "provider-model-override-precedence" + ], + "composerBehaviorCase": "provider-fields-inherited" + }, + { + "fieldPath": "/headers/*", + "rawOracleCases": [ + "all-schema-fields-valid", + "headers-record-valid", + "headers-record-invalid-value" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "provider-fields-inherited", + "provider-model-override-precedence" + ], + "composerBehaviorCase": "provider-fields-inherited" + }, + { + "fieldPath": "/modelOverrides", + "rawOracleCases": [ + "all-schema-fields-valid", + "model-override-fractional-and-extra" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "override-fields-executed", + "provider-model-override-precedence", + "unmatched-override-is-ignored-by-composer" + ], + "composerBehaviorCase": "override-fields-executed" + }, + { + "fieldPath": "/modelOverrides/*", + "rawOracleCases": [ + "all-schema-fields-valid", + "model-override-fractional-and-extra" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "override-fields-executed", + "provider-model-override-precedence", + "unmatched-override-is-ignored-by-composer" + ], + "composerBehaviorCase": "override-fields-executed" + }, + { + "fieldPath": "/modelOverrides/*/compat", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "override-fields-executed", + "provider-model-override-precedence" + ], + "composerBehaviorCase": "override-fields-executed" + }, + { + "fieldPath": "/modelOverrides/*/compat/allowEmptySignature", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "override-fields-executed" + ], + "composerBehaviorCase": "override-fields-executed" + }, + { + "fieldPath": "/modelOverrides/*/compat/cacheControlFormat", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "override-fields-executed" + ], + "composerBehaviorCase": "override-fields-executed" + }, + { + "fieldPath": "/modelOverrides/*/compat/chatTemplateKwargs", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "override-fields-executed" + ], + "composerBehaviorCase": "override-fields-executed" + }, + { + "fieldPath": "/modelOverrides/*/compat/chatTemplateKwargs/*", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "override-fields-executed" + ], + "composerBehaviorCase": "override-fields-executed" + }, + { + "fieldPath": "/modelOverrides/*/compat/chatTemplateKwargs/*/$var", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "override-fields-executed" + ], + "composerBehaviorCase": "override-fields-executed" + }, + { + "fieldPath": "/modelOverrides/*/compat/chatTemplateKwargs/*/omitWhenOff", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "override-fields-executed" + ], + "composerBehaviorCase": "override-fields-executed" + }, + { + "fieldPath": "/modelOverrides/*/compat/deferredToolsMode", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "override-fields-executed" + ], + "composerBehaviorCase": "override-fields-executed" + }, + { + "fieldPath": "/modelOverrides/*/compat/forceAdaptiveThinking", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "override-fields-executed" + ], + "composerBehaviorCase": "override-fields-executed" + }, + { + "fieldPath": "/modelOverrides/*/compat/maxTokensField", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "override-fields-executed" + ], + "composerBehaviorCase": "override-fields-executed" + }, + { + "fieldPath": "/modelOverrides/*/compat/openRouterRouting", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "override-fields-executed", + "provider-model-override-precedence" + ], + "composerBehaviorCase": "override-fields-executed" + }, + { + "fieldPath": "/modelOverrides/*/compat/openRouterRouting/allow_fallbacks", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "override-fields-executed" + ], + "composerBehaviorCase": "override-fields-executed" + }, + { + "fieldPath": "/modelOverrides/*/compat/openRouterRouting/data_collection", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "override-fields-executed" + ], + "composerBehaviorCase": "override-fields-executed" + }, + { + "fieldPath": "/modelOverrides/*/compat/openRouterRouting/enforce_distillable_text", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "override-fields-executed" + ], + "composerBehaviorCase": "override-fields-executed" + }, + { + "fieldPath": "/modelOverrides/*/compat/openRouterRouting/ignore", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "override-fields-executed" + ], + "composerBehaviorCase": "override-fields-executed" + }, + { + "fieldPath": "/modelOverrides/*/compat/openRouterRouting/max_price", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "override-fields-executed" + ], + "composerBehaviorCase": "override-fields-executed" + }, + { + "fieldPath": "/modelOverrides/*/compat/openRouterRouting/max_price/audio", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "override-fields-executed" + ], + "composerBehaviorCase": "override-fields-executed" + }, + { + "fieldPath": "/modelOverrides/*/compat/openRouterRouting/max_price/completion", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "override-fields-executed" + ], + "composerBehaviorCase": "override-fields-executed" + }, + { + "fieldPath": "/modelOverrides/*/compat/openRouterRouting/max_price/image", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "override-fields-executed" + ], + "composerBehaviorCase": "override-fields-executed" + }, + { + "fieldPath": "/modelOverrides/*/compat/openRouterRouting/max_price/prompt", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "override-fields-executed" + ], + "composerBehaviorCase": "override-fields-executed" + }, + { + "fieldPath": "/modelOverrides/*/compat/openRouterRouting/max_price/request", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "override-fields-executed" + ], + "composerBehaviorCase": "override-fields-executed" + }, + { + "fieldPath": "/modelOverrides/*/compat/openRouterRouting/only", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "override-fields-executed" + ], + "composerBehaviorCase": "override-fields-executed" + }, + { + "fieldPath": "/modelOverrides/*/compat/openRouterRouting/order", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "override-fields-executed", + "provider-model-override-precedence" + ], + "composerBehaviorCase": "override-fields-executed" + }, + { + "fieldPath": "/modelOverrides/*/compat/openRouterRouting/preferred_max_latency", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "override-fields-executed" + ], + "composerBehaviorCase": "override-fields-executed" + }, + { + "fieldPath": "/modelOverrides/*/compat/openRouterRouting/preferred_max_latency/p50", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "override-fields-executed" + ], + "composerBehaviorCase": "override-fields-executed" + }, + { + "fieldPath": "/modelOverrides/*/compat/openRouterRouting/preferred_max_latency/p75", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "override-fields-executed" + ], + "composerBehaviorCase": "override-fields-executed" + }, + { + "fieldPath": "/modelOverrides/*/compat/openRouterRouting/preferred_max_latency/p90", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "override-fields-executed" + ], + "composerBehaviorCase": "override-fields-executed" + }, + { + "fieldPath": "/modelOverrides/*/compat/openRouterRouting/preferred_max_latency/p99", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "override-fields-executed" + ], + "composerBehaviorCase": "override-fields-executed" + }, + { + "fieldPath": "/modelOverrides/*/compat/openRouterRouting/preferred_min_throughput", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "override-fields-executed" + ], + "composerBehaviorCase": "override-fields-executed" + }, + { + "fieldPath": "/modelOverrides/*/compat/openRouterRouting/preferred_min_throughput/p50", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "override-fields-executed" + ], + "composerBehaviorCase": "override-fields-executed" + }, + { + "fieldPath": "/modelOverrides/*/compat/openRouterRouting/preferred_min_throughput/p75", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "override-fields-executed" + ], + "composerBehaviorCase": "override-fields-executed" + }, + { + "fieldPath": "/modelOverrides/*/compat/openRouterRouting/preferred_min_throughput/p90", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "override-fields-executed" + ], + "composerBehaviorCase": "override-fields-executed" + }, + { + "fieldPath": "/modelOverrides/*/compat/openRouterRouting/preferred_min_throughput/p99", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "override-fields-executed" + ], + "composerBehaviorCase": "override-fields-executed" + }, + { + "fieldPath": "/modelOverrides/*/compat/openRouterRouting/quantizations", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "override-fields-executed" + ], + "composerBehaviorCase": "override-fields-executed" + }, + { + "fieldPath": "/modelOverrides/*/compat/openRouterRouting/require_parameters", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "override-fields-executed" + ], + "composerBehaviorCase": "override-fields-executed" + }, + { + "fieldPath": "/modelOverrides/*/compat/openRouterRouting/sort", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "override-fields-executed" + ], + "composerBehaviorCase": "override-fields-executed" + }, + { + "fieldPath": "/modelOverrides/*/compat/openRouterRouting/sort/by", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "override-fields-executed" + ], + "composerBehaviorCase": "override-fields-executed" + }, + { + "fieldPath": "/modelOverrides/*/compat/openRouterRouting/sort/partition", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "override-fields-executed" + ], + "composerBehaviorCase": "override-fields-executed" + }, + { + "fieldPath": "/modelOverrides/*/compat/openRouterRouting/zdr", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "override-fields-executed" + ], + "composerBehaviorCase": "override-fields-executed" + }, + { + "fieldPath": "/modelOverrides/*/compat/requiresAssistantAfterToolResult", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "override-fields-executed" + ], + "composerBehaviorCase": "override-fields-executed" + }, + { + "fieldPath": "/modelOverrides/*/compat/requiresReasoningContentOnAssistantMessages", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "override-fields-executed" + ], + "composerBehaviorCase": "override-fields-executed" + }, + { + "fieldPath": "/modelOverrides/*/compat/requiresThinkingAsText", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "override-fields-executed" + ], + "composerBehaviorCase": "override-fields-executed" + }, + { + "fieldPath": "/modelOverrides/*/compat/requiresToolResultName", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "override-fields-executed" + ], + "composerBehaviorCase": "override-fields-executed" + }, + { + "fieldPath": "/modelOverrides/*/compat/sendSessionAffinityHeaders", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "override-fields-executed" + ], + "composerBehaviorCase": "override-fields-executed" + }, + { + "fieldPath": "/modelOverrides/*/compat/sessionAffinityFormat", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "override-fields-executed" + ], + "composerBehaviorCase": "override-fields-executed" + }, + { + "fieldPath": "/modelOverrides/*/compat/supportsCacheControlOnTools", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "override-fields-executed" + ], + "composerBehaviorCase": "override-fields-executed" + }, + { + "fieldPath": "/modelOverrides/*/compat/supportsDeveloperRole", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "override-fields-executed" + ], + "composerBehaviorCase": "override-fields-executed" + }, + { + "fieldPath": "/modelOverrides/*/compat/supportsEagerToolInputStreaming", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "override-fields-executed" + ], + "composerBehaviorCase": "override-fields-executed" + }, + { + "fieldPath": "/modelOverrides/*/compat/supportsLongCacheRetention", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "override-fields-executed" + ], + "composerBehaviorCase": "override-fields-executed" + }, + { + "fieldPath": "/modelOverrides/*/compat/supportsOpenAIGrammarTools", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "override-fields-executed" + ], + "composerBehaviorCase": "override-fields-executed" + }, + { + "fieldPath": "/modelOverrides/*/compat/supportsReasoningEffort", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "override-fields-executed" + ], + "composerBehaviorCase": "override-fields-executed" + }, + { + "fieldPath": "/modelOverrides/*/compat/supportsStore", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "override-fields-executed", + "provider-model-override-precedence" + ], + "composerBehaviorCase": "override-fields-executed" + }, + { + "fieldPath": "/modelOverrides/*/compat/supportsStrictMode", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "override-fields-executed" + ], + "composerBehaviorCase": "override-fields-executed" + }, + { + "fieldPath": "/modelOverrides/*/compat/supportsStrictTools", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "override-fields-executed" + ], + "composerBehaviorCase": "override-fields-executed" + }, + { + "fieldPath": "/modelOverrides/*/compat/supportsTemperature", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "override-fields-executed" + ], + "composerBehaviorCase": "override-fields-executed" + }, + { + "fieldPath": "/modelOverrides/*/compat/supportsToolReferences", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "override-fields-executed" + ], + "composerBehaviorCase": "override-fields-executed" + }, + { + "fieldPath": "/modelOverrides/*/compat/supportsToolSearch", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "override-fields-executed" + ], + "composerBehaviorCase": "override-fields-executed" + }, + { + "fieldPath": "/modelOverrides/*/compat/supportsUsageInStreaming", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "override-fields-executed" + ], + "composerBehaviorCase": "override-fields-executed" + }, + { + "fieldPath": "/modelOverrides/*/compat/thinkingFormat", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "override-fields-executed" + ], + "composerBehaviorCase": "override-fields-executed" + }, + { + "fieldPath": "/modelOverrides/*/compat/vercelGatewayRouting", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "override-fields-executed" + ], + "composerBehaviorCase": "override-fields-executed" + }, + { + "fieldPath": "/modelOverrides/*/compat/vercelGatewayRouting/only", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "override-fields-executed" + ], + "composerBehaviorCase": "override-fields-executed" + }, + { + "fieldPath": "/modelOverrides/*/compat/vercelGatewayRouting/order", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "override-fields-executed" + ], + "composerBehaviorCase": "override-fields-executed" + }, + { + "fieldPath": "/modelOverrides/*/contextWindow", + "rawOracleCases": [ + "all-schema-fields-valid", + "model-override-fractional-and-extra" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "override-fields-executed", + "provider-model-override-precedence" + ], + "composerBehaviorCase": "override-fields-executed" + }, + { + "fieldPath": "/modelOverrides/*/cost", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "override-fields-executed", + "provider-model-override-precedence" + ], + "composerBehaviorCase": "override-fields-executed" + }, + { + "fieldPath": "/modelOverrides/*/cost/cacheRead", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "override-fields-executed" + ], + "composerBehaviorCase": "override-fields-executed" + }, + { + "fieldPath": "/modelOverrides/*/cost/cacheWrite", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "override-fields-executed" + ], + "composerBehaviorCase": "override-fields-executed" + }, + { + "fieldPath": "/modelOverrides/*/cost/input", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "override-fields-executed" + ], + "composerBehaviorCase": "override-fields-executed" + }, + { + "fieldPath": "/modelOverrides/*/cost/output", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "override-fields-executed", + "provider-model-override-precedence" + ], + "composerBehaviorCase": "override-fields-executed" + }, + { + "fieldPath": "/modelOverrides/*/cost/tiers", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "override-fields-executed" + ], + "composerBehaviorCase": "override-fields-executed" + }, + { + "fieldPath": "/modelOverrides/*/cost/tiers/*/cacheRead", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "override-fields-executed" + ], + "composerBehaviorCase": "override-fields-executed" + }, + { + "fieldPath": "/modelOverrides/*/cost/tiers/*/cacheWrite", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "override-fields-executed" + ], + "composerBehaviorCase": "override-fields-executed" + }, + { + "fieldPath": "/modelOverrides/*/cost/tiers/*/input", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "override-fields-executed" + ], + "composerBehaviorCase": "override-fields-executed" + }, + { + "fieldPath": "/modelOverrides/*/cost/tiers/*/inputTokensAbove", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "override-fields-executed" + ], + "composerBehaviorCase": "override-fields-executed" + }, + { + "fieldPath": "/modelOverrides/*/cost/tiers/*/output", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "override-fields-executed" + ], + "composerBehaviorCase": "override-fields-executed" + }, + { + "fieldPath": "/modelOverrides/*/headers", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "override-fields-executed", + "provider-model-override-precedence", + "unmatched-override-is-ignored-by-composer" + ], + "composerBehaviorCase": "override-fields-executed" + }, + { + "fieldPath": "/modelOverrides/*/headers/*", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "override-fields-executed", + "provider-model-override-precedence", + "unmatched-override-is-ignored-by-composer" + ], + "composerBehaviorCase": "override-fields-executed" + }, + { + "fieldPath": "/modelOverrides/*/input", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "override-fields-executed" + ], + "composerBehaviorCase": "override-fields-executed" + }, + { + "fieldPath": "/modelOverrides/*/maxTokens", + "rawOracleCases": [ + "all-schema-fields-valid", + "model-override-fractional-and-extra" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "override-fields-executed", + "unmatched-override-is-ignored-by-composer" + ], + "composerBehaviorCase": "override-fields-executed" + }, + { + "fieldPath": "/modelOverrides/*/name", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "override-fields-executed", + "provider-model-override-precedence" + ], + "composerBehaviorCase": "override-fields-executed" + }, + { + "fieldPath": "/modelOverrides/*/reasoning", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "override-fields-executed", + "provider-model-override-precedence" + ], + "composerBehaviorCase": "override-fields-executed" + }, + { + "fieldPath": "/modelOverrides/*/thinkingLevelMap", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "override-fields-executed", + "provider-model-override-precedence" + ], + "composerBehaviorCase": "override-fields-executed" + }, + { + "fieldPath": "/modelOverrides/*/thinkingLevelMap/high", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "override-fields-executed", + "provider-model-override-precedence" + ], + "composerBehaviorCase": "override-fields-executed" + }, + { + "fieldPath": "/modelOverrides/*/thinkingLevelMap/low", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "override-fields-executed" + ], + "composerBehaviorCase": "override-fields-executed" + }, + { + "fieldPath": "/modelOverrides/*/thinkingLevelMap/max", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "override-fields-executed" + ], + "composerBehaviorCase": "override-fields-executed" + }, + { + "fieldPath": "/modelOverrides/*/thinkingLevelMap/medium", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "override-fields-executed" + ], + "composerBehaviorCase": "override-fields-executed" + }, + { + "fieldPath": "/modelOverrides/*/thinkingLevelMap/minimal", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "override-fields-executed" + ], + "composerBehaviorCase": "override-fields-executed" + }, + { + "fieldPath": "/modelOverrides/*/thinkingLevelMap/off", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "override-fields-executed" + ], + "composerBehaviorCase": "override-fields-executed" + }, + { + "fieldPath": "/modelOverrides/*/thinkingLevelMap/xhigh", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "override-fields-executed" + ], + "composerBehaviorCase": "override-fields-executed" + }, + { + "fieldPath": "/models", + "rawOracleCases": [ + "all-schema-fields-valid", + "models-null", + "model-missing-id", + "model-empty-id", + "integer-model-numbers", + "fractional-model-numbers", + "negative-number-is-still-typebox-number", + "string-is-not-number", + "complete-cost-with-fractions", + "incomplete-model-cost", + "thinking-map-unknown-key-and-string-value", + "thinking-map-known-key-invalid-value", + "input-union-invalid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "provider-fields-inherited", + "model-fields-executed", + "override-fields-executed", + "radius-oauth-requires-provider-base-url", + "official-defaults", + "later-model-first-model-fallback", + "duplicate-model-replaces-existing-slot", + "provider-model-override-precedence", + "fractional-cost-and-limits", + "unmatched-override-is-ignored-by-composer", + "invalid-nonempty-url-is-composed", + "unknown-api-is-composed", + "unknown-thinking-fields-are-preserved", + "empty-url-fails-upstream-composition" + ], + "composerBehaviorCase": "model-fields-executed" + }, + { + "fieldPath": "/models/*/api", + "rawOracleCases": [ + "all-schema-fields-valid", + "model-missing-id" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "model-fields-executed", + "radius-oauth-requires-provider-base-url", + "later-model-first-model-fallback", + "duplicate-model-replaces-existing-slot", + "provider-model-override-precedence" + ], + "composerBehaviorCase": "model-fields-executed" + }, + { + "fieldPath": "/models/*/baseUrl", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "model-fields-executed", + "radius-oauth-requires-provider-base-url", + "later-model-first-model-fallback", + "duplicate-model-replaces-existing-slot", + "provider-model-override-precedence" + ], + "composerBehaviorCase": "model-fields-executed" + }, + { + "fieldPath": "/models/*/compat", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "model-fields-executed", + "provider-model-override-precedence" + ], + "composerBehaviorCase": "model-fields-executed" + }, + { + "fieldPath": "/models/*/compat/allowEmptySignature", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "model-fields-executed" + ], + "composerBehaviorCase": "model-fields-executed" + }, + { + "fieldPath": "/models/*/compat/cacheControlFormat", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "model-fields-executed" + ], + "composerBehaviorCase": "model-fields-executed" + }, + { + "fieldPath": "/models/*/compat/chatTemplateKwargs", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "model-fields-executed" + ], + "composerBehaviorCase": "model-fields-executed" + }, + { + "fieldPath": "/models/*/compat/chatTemplateKwargs/*", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "model-fields-executed" + ], + "composerBehaviorCase": "model-fields-executed" + }, + { + "fieldPath": "/models/*/compat/chatTemplateKwargs/*/$var", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "model-fields-executed" + ], + "composerBehaviorCase": "model-fields-executed" + }, + { + "fieldPath": "/models/*/compat/chatTemplateKwargs/*/omitWhenOff", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "model-fields-executed" + ], + "composerBehaviorCase": "model-fields-executed" + }, + { + "fieldPath": "/models/*/compat/deferredToolsMode", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "model-fields-executed" + ], + "composerBehaviorCase": "model-fields-executed" + }, + { + "fieldPath": "/models/*/compat/forceAdaptiveThinking", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "model-fields-executed" + ], + "composerBehaviorCase": "model-fields-executed" + }, + { + "fieldPath": "/models/*/compat/maxTokensField", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "model-fields-executed" + ], + "composerBehaviorCase": "model-fields-executed" + }, + { + "fieldPath": "/models/*/compat/openRouterRouting", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "model-fields-executed", + "provider-model-override-precedence" + ], + "composerBehaviorCase": "model-fields-executed" + }, + { + "fieldPath": "/models/*/compat/openRouterRouting/allow_fallbacks", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "model-fields-executed" + ], + "composerBehaviorCase": "model-fields-executed" + }, + { + "fieldPath": "/models/*/compat/openRouterRouting/data_collection", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "model-fields-executed" + ], + "composerBehaviorCase": "model-fields-executed" + }, + { + "fieldPath": "/models/*/compat/openRouterRouting/enforce_distillable_text", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "model-fields-executed" + ], + "composerBehaviorCase": "model-fields-executed" + }, + { + "fieldPath": "/models/*/compat/openRouterRouting/ignore", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "model-fields-executed" + ], + "composerBehaviorCase": "model-fields-executed" + }, + { + "fieldPath": "/models/*/compat/openRouterRouting/max_price", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "model-fields-executed" + ], + "composerBehaviorCase": "model-fields-executed" + }, + { + "fieldPath": "/models/*/compat/openRouterRouting/max_price/audio", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "model-fields-executed" + ], + "composerBehaviorCase": "model-fields-executed" + }, + { + "fieldPath": "/models/*/compat/openRouterRouting/max_price/completion", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "model-fields-executed" + ], + "composerBehaviorCase": "model-fields-executed" + }, + { + "fieldPath": "/models/*/compat/openRouterRouting/max_price/image", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "model-fields-executed" + ], + "composerBehaviorCase": "model-fields-executed" + }, + { + "fieldPath": "/models/*/compat/openRouterRouting/max_price/prompt", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "model-fields-executed" + ], + "composerBehaviorCase": "model-fields-executed" + }, + { + "fieldPath": "/models/*/compat/openRouterRouting/max_price/request", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "model-fields-executed" + ], + "composerBehaviorCase": "model-fields-executed" + }, + { + "fieldPath": "/models/*/compat/openRouterRouting/only", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "model-fields-executed", + "provider-model-override-precedence" + ], + "composerBehaviorCase": "model-fields-executed" + }, + { + "fieldPath": "/models/*/compat/openRouterRouting/order", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "model-fields-executed" + ], + "composerBehaviorCase": "model-fields-executed" + }, + { + "fieldPath": "/models/*/compat/openRouterRouting/preferred_max_latency", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "model-fields-executed" + ], + "composerBehaviorCase": "model-fields-executed" + }, + { + "fieldPath": "/models/*/compat/openRouterRouting/preferred_max_latency/p50", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "model-fields-executed" + ], + "composerBehaviorCase": "model-fields-executed" + }, + { + "fieldPath": "/models/*/compat/openRouterRouting/preferred_max_latency/p75", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "model-fields-executed" + ], + "composerBehaviorCase": "model-fields-executed" + }, + { + "fieldPath": "/models/*/compat/openRouterRouting/preferred_max_latency/p90", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "model-fields-executed" + ], + "composerBehaviorCase": "model-fields-executed" + }, + { + "fieldPath": "/models/*/compat/openRouterRouting/preferred_max_latency/p99", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "model-fields-executed" + ], + "composerBehaviorCase": "model-fields-executed" + }, + { + "fieldPath": "/models/*/compat/openRouterRouting/preferred_min_throughput", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "model-fields-executed" + ], + "composerBehaviorCase": "model-fields-executed" + }, + { + "fieldPath": "/models/*/compat/openRouterRouting/preferred_min_throughput/p50", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "model-fields-executed" + ], + "composerBehaviorCase": "model-fields-executed" + }, + { + "fieldPath": "/models/*/compat/openRouterRouting/preferred_min_throughput/p75", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "model-fields-executed" + ], + "composerBehaviorCase": "model-fields-executed" + }, + { + "fieldPath": "/models/*/compat/openRouterRouting/preferred_min_throughput/p90", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "model-fields-executed" + ], + "composerBehaviorCase": "model-fields-executed" + }, + { + "fieldPath": "/models/*/compat/openRouterRouting/preferred_min_throughput/p99", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "model-fields-executed" + ], + "composerBehaviorCase": "model-fields-executed" + }, + { + "fieldPath": "/models/*/compat/openRouterRouting/quantizations", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "model-fields-executed" + ], + "composerBehaviorCase": "model-fields-executed" + }, + { + "fieldPath": "/models/*/compat/openRouterRouting/require_parameters", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "model-fields-executed" + ], + "composerBehaviorCase": "model-fields-executed" + }, + { + "fieldPath": "/models/*/compat/openRouterRouting/sort", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "model-fields-executed" + ], + "composerBehaviorCase": "model-fields-executed" + }, + { + "fieldPath": "/models/*/compat/openRouterRouting/sort/by", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "model-fields-executed" + ], + "composerBehaviorCase": "model-fields-executed" + }, + { + "fieldPath": "/models/*/compat/openRouterRouting/sort/partition", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "model-fields-executed" + ], + "composerBehaviorCase": "model-fields-executed" + }, + { + "fieldPath": "/models/*/compat/openRouterRouting/zdr", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "model-fields-executed", + "provider-model-override-precedence" + ], + "composerBehaviorCase": "model-fields-executed" + }, + { + "fieldPath": "/models/*/compat/requiresAssistantAfterToolResult", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "model-fields-executed" + ], + "composerBehaviorCase": "model-fields-executed" + }, + { + "fieldPath": "/models/*/compat/requiresReasoningContentOnAssistantMessages", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "model-fields-executed" + ], + "composerBehaviorCase": "model-fields-executed" + }, + { + "fieldPath": "/models/*/compat/requiresThinkingAsText", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "model-fields-executed" + ], + "composerBehaviorCase": "model-fields-executed" + }, + { + "fieldPath": "/models/*/compat/requiresToolResultName", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "model-fields-executed" + ], + "composerBehaviorCase": "model-fields-executed" + }, + { + "fieldPath": "/models/*/compat/sendSessionAffinityHeaders", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "model-fields-executed" + ], + "composerBehaviorCase": "model-fields-executed" + }, + { + "fieldPath": "/models/*/compat/sessionAffinityFormat", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "model-fields-executed" + ], + "composerBehaviorCase": "model-fields-executed" + }, + { + "fieldPath": "/models/*/compat/supportsCacheControlOnTools", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "model-fields-executed" + ], + "composerBehaviorCase": "model-fields-executed" + }, + { + "fieldPath": "/models/*/compat/supportsDeveloperRole", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "model-fields-executed" + ], + "composerBehaviorCase": "model-fields-executed" + }, + { + "fieldPath": "/models/*/compat/supportsEagerToolInputStreaming", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "model-fields-executed" + ], + "composerBehaviorCase": "model-fields-executed" + }, + { + "fieldPath": "/models/*/compat/supportsLongCacheRetention", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "model-fields-executed" + ], + "composerBehaviorCase": "model-fields-executed" + }, + { + "fieldPath": "/models/*/compat/supportsOpenAIGrammarTools", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "model-fields-executed" + ], + "composerBehaviorCase": "model-fields-executed" + }, + { + "fieldPath": "/models/*/compat/supportsReasoningEffort", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "model-fields-executed" + ], + "composerBehaviorCase": "model-fields-executed" + }, + { + "fieldPath": "/models/*/compat/supportsStore", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "model-fields-executed", + "provider-model-override-precedence" + ], + "composerBehaviorCase": "model-fields-executed" + }, + { + "fieldPath": "/models/*/compat/supportsStrictMode", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "model-fields-executed" + ], + "composerBehaviorCase": "model-fields-executed" + }, + { + "fieldPath": "/models/*/compat/supportsStrictTools", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "model-fields-executed" + ], + "composerBehaviorCase": "model-fields-executed" + }, + { + "fieldPath": "/models/*/compat/supportsTemperature", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "model-fields-executed" + ], + "composerBehaviorCase": "model-fields-executed" + }, + { + "fieldPath": "/models/*/compat/supportsToolReferences", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "model-fields-executed" + ], + "composerBehaviorCase": "model-fields-executed" + }, + { + "fieldPath": "/models/*/compat/supportsToolSearch", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "model-fields-executed" + ], + "composerBehaviorCase": "model-fields-executed" + }, + { + "fieldPath": "/models/*/compat/supportsUsageInStreaming", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "model-fields-executed" + ], + "composerBehaviorCase": "model-fields-executed" + }, + { + "fieldPath": "/models/*/compat/thinkingFormat", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "model-fields-executed" + ], + "composerBehaviorCase": "model-fields-executed" + }, + { + "fieldPath": "/models/*/compat/vercelGatewayRouting", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "model-fields-executed" + ], + "composerBehaviorCase": "model-fields-executed" + }, + { + "fieldPath": "/models/*/compat/vercelGatewayRouting/only", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "model-fields-executed" + ], + "composerBehaviorCase": "model-fields-executed" + }, + { + "fieldPath": "/models/*/compat/vercelGatewayRouting/order", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "model-fields-executed" + ], + "composerBehaviorCase": "model-fields-executed" + }, + { + "fieldPath": "/models/*/contextWindow", + "rawOracleCases": [ + "all-schema-fields-valid", + "integer-model-numbers", + "fractional-model-numbers" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "model-fields-executed", + "provider-model-override-precedence", + "fractional-cost-and-limits" + ], + "composerBehaviorCase": "model-fields-executed" + }, + { + "fieldPath": "/models/*/cost", + "rawOracleCases": [ + "all-schema-fields-valid", + "complete-cost-with-fractions", + "incomplete-model-cost" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "model-fields-executed", + "provider-model-override-precedence", + "fractional-cost-and-limits" + ], + "composerBehaviorCase": "model-fields-executed" + }, + { + "fieldPath": "/models/*/cost/cacheRead", + "rawOracleCases": [ + "all-schema-fields-valid", + "complete-cost-with-fractions", + "incomplete-model-cost" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "model-fields-executed", + "provider-model-override-precedence", + "fractional-cost-and-limits" + ], + "composerBehaviorCase": "model-fields-executed" + }, + { + "fieldPath": "/models/*/cost/cacheWrite", + "rawOracleCases": [ + "all-schema-fields-valid", + "complete-cost-with-fractions" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "model-fields-executed", + "provider-model-override-precedence", + "fractional-cost-and-limits" + ], + "composerBehaviorCase": "model-fields-executed" + }, + { + "fieldPath": "/models/*/cost/input", + "rawOracleCases": [ + "all-schema-fields-valid", + "complete-cost-with-fractions", + "incomplete-model-cost" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "model-fields-executed", + "provider-model-override-precedence", + "fractional-cost-and-limits" + ], + "composerBehaviorCase": "model-fields-executed" + }, + { + "fieldPath": "/models/*/cost/output", + "rawOracleCases": [ + "all-schema-fields-valid", + "complete-cost-with-fractions", + "incomplete-model-cost" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "model-fields-executed", + "provider-model-override-precedence", + "fractional-cost-and-limits" + ], + "composerBehaviorCase": "model-fields-executed" + }, + { + "fieldPath": "/models/*/cost/tiers", + "rawOracleCases": [ + "all-schema-fields-valid", + "complete-cost-with-fractions" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "model-fields-executed" + ], + "composerBehaviorCase": "model-fields-executed" + }, + { + "fieldPath": "/models/*/cost/tiers/*/cacheRead", + "rawOracleCases": [ + "all-schema-fields-valid", + "complete-cost-with-fractions" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "model-fields-executed" + ], + "composerBehaviorCase": "model-fields-executed" + }, + { + "fieldPath": "/models/*/cost/tiers/*/cacheWrite", + "rawOracleCases": [ + "all-schema-fields-valid", + "complete-cost-with-fractions" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "model-fields-executed" + ], + "composerBehaviorCase": "model-fields-executed" + }, + { + "fieldPath": "/models/*/cost/tiers/*/input", + "rawOracleCases": [ + "all-schema-fields-valid", + "complete-cost-with-fractions" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "model-fields-executed" + ], + "composerBehaviorCase": "model-fields-executed" + }, + { + "fieldPath": "/models/*/cost/tiers/*/inputTokensAbove", + "rawOracleCases": [ + "all-schema-fields-valid", + "complete-cost-with-fractions" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "model-fields-executed" + ], + "composerBehaviorCase": "model-fields-executed" + }, + { + "fieldPath": "/models/*/cost/tiers/*/output", + "rawOracleCases": [ + "all-schema-fields-valid", + "complete-cost-with-fractions" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "model-fields-executed" + ], + "composerBehaviorCase": "model-fields-executed" + }, + { + "fieldPath": "/models/*/headers", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "model-fields-executed", + "provider-model-override-precedence" + ], + "composerBehaviorCase": "model-fields-executed" + }, + { + "fieldPath": "/models/*/headers/*", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "model-fields-executed", + "provider-model-override-precedence" + ], + "composerBehaviorCase": "model-fields-executed" + }, + { + "fieldPath": "/models/*/id", + "rawOracleCases": [ + "all-schema-fields-valid", + "model-empty-id", + "integer-model-numbers", + "fractional-model-numbers", + "negative-number-is-still-typebox-number", + "string-is-not-number", + "complete-cost-with-fractions", + "incomplete-model-cost", + "thinking-map-unknown-key-and-string-value", + "thinking-map-known-key-invalid-value", + "input-union-invalid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "provider-fields-inherited", + "model-fields-executed", + "override-fields-executed", + "radius-oauth-requires-provider-base-url", + "official-defaults", + "later-model-first-model-fallback", + "duplicate-model-replaces-existing-slot", + "provider-model-override-precedence", + "fractional-cost-and-limits", + "unmatched-override-is-ignored-by-composer", + "invalid-nonempty-url-is-composed", + "unknown-api-is-composed", + "unknown-thinking-fields-are-preserved", + "empty-url-fails-upstream-composition" + ], + "composerBehaviorCase": "model-fields-executed" + }, + { + "fieldPath": "/models/*/input", + "rawOracleCases": [ + "all-schema-fields-valid", + "input-union-invalid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "model-fields-executed" + ], + "composerBehaviorCase": "model-fields-executed" + }, + { + "fieldPath": "/models/*/maxTokens", + "rawOracleCases": [ + "all-schema-fields-valid", + "integer-model-numbers", + "fractional-model-numbers", + "negative-number-is-still-typebox-number", + "string-is-not-number" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "model-fields-executed", + "provider-model-override-precedence", + "fractional-cost-and-limits" + ], + "composerBehaviorCase": "model-fields-executed" + }, + { + "fieldPath": "/models/*/name", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "model-fields-executed", + "override-fields-executed", + "duplicate-model-replaces-existing-slot", + "provider-model-override-precedence" + ], + "composerBehaviorCase": "model-fields-executed" + }, + { + "fieldPath": "/models/*/reasoning", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "model-fields-executed", + "override-fields-executed", + "provider-model-override-precedence" + ], + "composerBehaviorCase": "model-fields-executed" + }, + { + "fieldPath": "/models/*/thinkingLevelMap", + "rawOracleCases": [ + "all-schema-fields-valid", + "thinking-map-unknown-key-and-string-value", + "thinking-map-known-key-invalid-value" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "model-fields-executed", + "provider-model-override-precedence", + "unknown-thinking-fields-are-preserved" + ], + "composerBehaviorCase": "model-fields-executed" + }, + { + "fieldPath": "/models/*/thinkingLevelMap/high", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "model-fields-executed", + "provider-model-override-precedence", + "unknown-thinking-fields-are-preserved" + ], + "composerBehaviorCase": "model-fields-executed" + }, + { + "fieldPath": "/models/*/thinkingLevelMap/low", + "rawOracleCases": [ + "all-schema-fields-valid", + "thinking-map-unknown-key-and-string-value", + "thinking-map-known-key-invalid-value" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "model-fields-executed" + ], + "composerBehaviorCase": "model-fields-executed" + }, + { + "fieldPath": "/models/*/thinkingLevelMap/max", + "rawOracleCases": [ + "all-schema-fields-valid", + "thinking-map-unknown-key-and-string-value" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "model-fields-executed" + ], + "composerBehaviorCase": "model-fields-executed" + }, + { + "fieldPath": "/models/*/thinkingLevelMap/medium", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "model-fields-executed" + ], + "composerBehaviorCase": "model-fields-executed" + }, + { + "fieldPath": "/models/*/thinkingLevelMap/minimal", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "model-fields-executed" + ], + "composerBehaviorCase": "model-fields-executed" + }, + { + "fieldPath": "/models/*/thinkingLevelMap/off", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "model-fields-executed" + ], + "composerBehaviorCase": "model-fields-executed" + }, + { + "fieldPath": "/models/*/thinkingLevelMap/xhigh", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "model-fields-executed" + ], + "composerBehaviorCase": "model-fields-executed" + }, + { + "fieldPath": "/name", + "rawOracleCases": [ + "all-schema-fields-valid" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "provider-fields-inherited" + ], + "composerBehaviorCase": "provider-fields-inherited" + }, + { + "fieldPath": "/oauth", + "rawOracleCases": [ + "all-schema-fields-valid", + "radius-oauth", + "unknown-oauth-literal" + ], + "composerOracleCases": [ + "combined-all-fields-precedence", + "provider-fields-inherited", + "radius-oauth-requires-provider-base-url" + ], + "composerBehaviorCase": "provider-fields-inherited" + } + ] +} diff --git a/tests/fixtures/pi/native-oracle/provenance-v1.json b/tests/fixtures/pi/native-oracle/provenance-v1.json new file mode 100644 index 000000000..6098e5579 --- /dev/null +++ b/tests/fixtures/pi/native-oracle/provenance-v1.json @@ -0,0 +1,96 @@ +{ + "version": 1, + "pi": { + "repository": "https://github.com/earendil-works/pi.git", + "commit": "ab366ebe94cacd419d986be454f12b1b9913aaca" + }, + "typeboxVersion": "1.3.7", + "sources": { + "modelConfig": { + "path": "packages/coding-agent/src/core/model-config.ts", + "sha256": "62141770d675ad6357a72e07354355f0eda29281c0e5be1b48d2360f341c7360" + }, + "providerComposer": { + "path": "packages/coding-agent/src/core/provider-composer.ts", + "sha256": "17308a4179b330526eabf6c917fa13e9dbd9ece90d1555b870e87d39b5b60d9d" + }, + "resolveConfigValue": { + "path": "packages/coding-agent/src/core/resolve-config-value.ts", + "sha256": "0f53dad47fe5d5d8837c022b7951ccd3bd5a9b577bd662f0986272110e83bcc7" + } + }, + "artifacts": { + "provider-schema.snapshot.json": "e498c9f1b344eee1bd3c3ba74d1b648dcb835378cfad92800ec80078b825745c", + "raw-oracle-v1.json": "5aaa37160f96a0fe50867d900ca38c73f13aba769e156a883324368d9dbeeb9a", + "composer-oracle-v1.json": "f7e54bb84e5fd6d50e5762dc304834410fa73ef608c2f9c42475c5983f8e0cf5", + "transport-oracle-v1.json": "b2c816e53b60da5cd6352d2c23934939e9f6dd0077971488fe9dd36fa723e855", + "field-coverage-v1.json": "b8b85e611cf1dbef86c611df185ba8ac2d64160087d0c6e47747f838a0fafe42" + }, + "harness": { + "path": "scripts/generate-pi-native-oracle.mjs", + "sha256": "f7a138831284b48ef655ef500a63313f0fc89cf08d319094895027ba4777cc20", + "upstreamEntry": "packages/coding-agent/src/core/provider-composer.ts", + "entryFunctions": [ + "composeModelProvider", + "Provider.getModels", + "resolveCompatibilityRequestConfig", + "Provider.auth.apiKey.resolve" + ], + "bundler": "esbuild@0.28.1", + "transportShims": { + "pi-ai": "debdf80a40ad2086467c7c3ed6fe43d6167caa85e5bff86e10f28cc36d8fcc53", + "pi-ai/compat": "533cd8f8a242bb25b42db8249abcb539daf30889ea748b5114c177e44c3849d2" + }, + "assertion": "expected composer outputs were captured by executing the pinned upstream entry functions; transport shims are unreachable during credential-blind model composition", + "transportResolver": { + "upstreamEntry": "packages/coding-agent/src/core/resolve-config-value.ts", + "entryFunctions": [ + "resolveConfigValueOrThrow", + "resolveHeadersOrThrow" + ], + "bundler": "esbuild@0.28.1", + "platform": "linux" + } + }, + "uncoveredSemantics": [ + { + "id": "builtin-overlay-without-pinned-base-catalog", + "unavailableContext": "pinned built-in Provider instance and model catalog", + "rustExpectedStatus": "unknown", + "reasonCode": "catalog_required" + }, + { + "id": "extension-overlay-without-extension-registration", + "unavailableContext": "extension ProviderConfigInput and registered model catalog", + "rustExpectedStatus": "unknown", + "reasonCode": "catalog_required" + }, + { + "id": "radius-oauth-credential-lifecycle", + "unavailableContext": "interactive Radius login, refresh credentials, and network exchange", + "rustExpectedStatus": "direct_only", + "reasonCode": "missing_gateway_credential" + } + ], + "schemaOperatorInventory": [ + "anyOf", + "const", + "items", + "minLength", + "patternProperties", + "properties", + "required", + "type" + ], + "evaluatorOperatorAllowlist": [ + "additionalProperties", + "anyOf", + "const", + "items", + "minLength", + "patternProperties", + "properties", + "required", + "type" + ] +} diff --git a/tests/fixtures/pi/native-oracle/provider-schema.snapshot.json b/tests/fixtures/pi/native-oracle/provider-schema.snapshot.json new file mode 100644 index 000000000..493e5eccc --- /dev/null +++ b/tests/fixtures/pi/native-oracle/provider-schema.snapshot.json @@ -0,0 +1,1708 @@ +{ + "type": "object", + "properties": { + "name": { + "type": "string", + "minLength": 1 + }, + "baseUrl": { + "type": "string", + "minLength": 1 + }, + "apiKey": { + "type": "string", + "minLength": 1 + }, + "api": { + "type": "string", + "minLength": 1 + }, + "oauth": { + "type": "string", + "const": "radius" + }, + "headers": { + "type": "object", + "patternProperties": { + "^.*$": { + "type": "string" + } + } + }, + "compat": { + "anyOf": [ + { + "type": "object", + "properties": { + "supportsStore": { + "type": "boolean" + }, + "supportsDeveloperRole": { + "type": "boolean" + }, + "supportsReasoningEffort": { + "type": "boolean" + }, + "supportsUsageInStreaming": { + "type": "boolean" + }, + "maxTokensField": { + "anyOf": [ + { + "type": "string", + "const": "max_completion_tokens" + }, + { + "type": "string", + "const": "max_tokens" + } + ] + }, + "requiresToolResultName": { + "type": "boolean" + }, + "requiresAssistantAfterToolResult": { + "type": "boolean" + }, + "requiresThinkingAsText": { + "type": "boolean" + }, + "requiresReasoningContentOnAssistantMessages": { + "type": "boolean" + }, + "thinkingFormat": { + "anyOf": [ + { + "type": "string", + "const": "openai" + }, + { + "type": "string", + "const": "openrouter" + }, + { + "type": "string", + "const": "together" + }, + { + "type": "string", + "const": "deepseek" + }, + { + "type": "string", + "const": "zai" + }, + { + "type": "string", + "const": "qwen" + }, + { + "type": "string", + "const": "chat-template" + }, + { + "type": "string", + "const": "qwen-chat-template" + }, + { + "type": "string", + "const": "string-thinking" + }, + { + "type": "string", + "const": "ant-ling" + } + ] + }, + "chatTemplateKwargs": { + "type": "object", + "patternProperties": { + "^.*$": { + "anyOf": [ + { + "anyOf": [ + { + "type": "string" + }, + { + "type": "number" + }, + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + { + "type": "object", + "required": [ + "$var" + ], + "properties": { + "$var": { + "anyOf": [ + { + "type": "string", + "const": "thinking.enabled" + }, + { + "type": "string", + "const": "thinking.effort" + } + ] + }, + "omitWhenOff": { + "type": "boolean" + } + } + } + ] + } + } + }, + "cacheControlFormat": { + "type": "string", + "const": "anthropic" + }, + "openRouterRouting": { + "type": "object", + "properties": { + "allow_fallbacks": { + "type": "boolean" + }, + "require_parameters": { + "type": "boolean" + }, + "data_collection": { + "anyOf": [ + { + "type": "string", + "const": "deny" + }, + { + "type": "string", + "const": "allow" + } + ] + }, + "zdr": { + "type": "boolean" + }, + "enforce_distillable_text": { + "type": "boolean" + }, + "order": { + "type": "array", + "items": { + "type": "string" + } + }, + "only": { + "type": "array", + "items": { + "type": "string" + } + }, + "ignore": { + "type": "array", + "items": { + "type": "string" + } + }, + "quantizations": { + "type": "array", + "items": { + "type": "string" + } + }, + "sort": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "by": { + "type": "string" + }, + "partition": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + } + } + ] + }, + "max_price": { + "type": "object", + "properties": { + "prompt": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string" + } + ] + }, + "completion": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string" + } + ] + }, + "image": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string" + } + ] + }, + "audio": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string" + } + ] + }, + "request": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string" + } + ] + } + } + }, + "preferred_min_throughput": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "object", + "properties": { + "p50": { + "type": "number" + }, + "p75": { + "type": "number" + }, + "p90": { + "type": "number" + }, + "p99": { + "type": "number" + } + } + } + ] + }, + "preferred_max_latency": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "object", + "properties": { + "p50": { + "type": "number" + }, + "p75": { + "type": "number" + }, + "p90": { + "type": "number" + }, + "p99": { + "type": "number" + } + } + } + ] + } + } + }, + "vercelGatewayRouting": { + "type": "object", + "properties": { + "only": { + "type": "array", + "items": { + "type": "string" + } + }, + "order": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "supportsOpenAIGrammarTools": { + "type": "boolean" + }, + "supportsStrictMode": { + "type": "boolean" + }, + "sendSessionAffinityHeaders": { + "type": "boolean" + }, + "deferredToolsMode": { + "type": "string", + "const": "kimi" + }, + "sessionAffinityFormat": { + "anyOf": [ + { + "type": "string", + "const": "openai" + }, + { + "type": "string", + "const": "openai-nosession" + }, + { + "type": "string", + "const": "openrouter" + } + ] + }, + "supportsLongCacheRetention": { + "type": "boolean" + } + } + }, + { + "type": "object", + "properties": { + "supportsDeveloperRole": { + "type": "boolean" + }, + "sessionAffinityFormat": { + "anyOf": [ + { + "type": "string", + "const": "openai" + }, + { + "type": "string", + "const": "openai-nosession" + }, + { + "type": "string", + "const": "openrouter" + } + ] + }, + "supportsLongCacheRetention": { + "type": "boolean" + }, + "supportsStrictMode": { + "type": "boolean" + }, + "supportsOpenAIGrammarTools": { + "type": "boolean" + }, + "supportsToolSearch": { + "type": "boolean" + } + } + }, + { + "type": "object", + "properties": { + "supportsEagerToolInputStreaming": { + "type": "boolean" + }, + "supportsLongCacheRetention": { + "type": "boolean" + }, + "sendSessionAffinityHeaders": { + "type": "boolean" + }, + "supportsCacheControlOnTools": { + "type": "boolean" + }, + "supportsTemperature": { + "type": "boolean" + }, + "forceAdaptiveThinking": { + "type": "boolean" + }, + "allowEmptySignature": { + "type": "boolean" + }, + "supportsStrictTools": { + "type": "boolean" + }, + "supportsToolReferences": { + "type": "boolean" + } + } + } + ] + }, + "authHeader": { + "type": "boolean" + }, + "models": { + "type": "array", + "items": { + "type": "object", + "required": [ + "id" + ], + "properties": { + "id": { + "type": "string", + "minLength": 1 + }, + "name": { + "type": "string", + "minLength": 1 + }, + "api": { + "type": "string", + "minLength": 1 + }, + "baseUrl": { + "type": "string", + "minLength": 1 + }, + "reasoning": { + "type": "boolean" + }, + "thinkingLevelMap": { + "type": "object", + "properties": { + "off": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "minimal": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "low": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "medium": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "high": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "xhigh": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "max": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + } + }, + "input": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string", + "const": "text" + }, + { + "type": "string", + "const": "image" + } + ] + } + }, + "cost": { + "type": "object", + "required": [ + "input", + "output", + "cacheRead", + "cacheWrite" + ], + "properties": { + "input": { + "type": "number" + }, + "output": { + "type": "number" + }, + "cacheRead": { + "type": "number" + }, + "cacheWrite": { + "type": "number" + }, + "tiers": { + "type": "array", + "items": { + "type": "object", + "required": [ + "inputTokensAbove", + "input", + "output", + "cacheRead", + "cacheWrite" + ], + "properties": { + "inputTokensAbove": { + "type": "number" + }, + "input": { + "type": "number" + }, + "output": { + "type": "number" + }, + "cacheRead": { + "type": "number" + }, + "cacheWrite": { + "type": "number" + } + } + } + } + } + }, + "contextWindow": { + "type": "number" + }, + "maxTokens": { + "type": "number" + }, + "headers": { + "type": "object", + "patternProperties": { + "^.*$": { + "type": "string" + } + } + }, + "compat": { + "anyOf": [ + { + "type": "object", + "properties": { + "supportsStore": { + "type": "boolean" + }, + "supportsDeveloperRole": { + "type": "boolean" + }, + "supportsReasoningEffort": { + "type": "boolean" + }, + "supportsUsageInStreaming": { + "type": "boolean" + }, + "maxTokensField": { + "anyOf": [ + { + "type": "string", + "const": "max_completion_tokens" + }, + { + "type": "string", + "const": "max_tokens" + } + ] + }, + "requiresToolResultName": { + "type": "boolean" + }, + "requiresAssistantAfterToolResult": { + "type": "boolean" + }, + "requiresThinkingAsText": { + "type": "boolean" + }, + "requiresReasoningContentOnAssistantMessages": { + "type": "boolean" + }, + "thinkingFormat": { + "anyOf": [ + { + "type": "string", + "const": "openai" + }, + { + "type": "string", + "const": "openrouter" + }, + { + "type": "string", + "const": "together" + }, + { + "type": "string", + "const": "deepseek" + }, + { + "type": "string", + "const": "zai" + }, + { + "type": "string", + "const": "qwen" + }, + { + "type": "string", + "const": "chat-template" + }, + { + "type": "string", + "const": "qwen-chat-template" + }, + { + "type": "string", + "const": "string-thinking" + }, + { + "type": "string", + "const": "ant-ling" + } + ] + }, + "chatTemplateKwargs": { + "type": "object", + "patternProperties": { + "^.*$": { + "anyOf": [ + { + "anyOf": [ + { + "type": "string" + }, + { + "type": "number" + }, + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + { + "type": "object", + "required": [ + "$var" + ], + "properties": { + "$var": { + "anyOf": [ + { + "type": "string", + "const": "thinking.enabled" + }, + { + "type": "string", + "const": "thinking.effort" + } + ] + }, + "omitWhenOff": { + "type": "boolean" + } + } + } + ] + } + } + }, + "cacheControlFormat": { + "type": "string", + "const": "anthropic" + }, + "openRouterRouting": { + "type": "object", + "properties": { + "allow_fallbacks": { + "type": "boolean" + }, + "require_parameters": { + "type": "boolean" + }, + "data_collection": { + "anyOf": [ + { + "type": "string", + "const": "deny" + }, + { + "type": "string", + "const": "allow" + } + ] + }, + "zdr": { + "type": "boolean" + }, + "enforce_distillable_text": { + "type": "boolean" + }, + "order": { + "type": "array", + "items": { + "type": "string" + } + }, + "only": { + "type": "array", + "items": { + "type": "string" + } + }, + "ignore": { + "type": "array", + "items": { + "type": "string" + } + }, + "quantizations": { + "type": "array", + "items": { + "type": "string" + } + }, + "sort": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "by": { + "type": "string" + }, + "partition": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + } + } + ] + }, + "max_price": { + "type": "object", + "properties": { + "prompt": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string" + } + ] + }, + "completion": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string" + } + ] + }, + "image": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string" + } + ] + }, + "audio": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string" + } + ] + }, + "request": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string" + } + ] + } + } + }, + "preferred_min_throughput": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "object", + "properties": { + "p50": { + "type": "number" + }, + "p75": { + "type": "number" + }, + "p90": { + "type": "number" + }, + "p99": { + "type": "number" + } + } + } + ] + }, + "preferred_max_latency": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "object", + "properties": { + "p50": { + "type": "number" + }, + "p75": { + "type": "number" + }, + "p90": { + "type": "number" + }, + "p99": { + "type": "number" + } + } + } + ] + } + } + }, + "vercelGatewayRouting": { + "type": "object", + "properties": { + "only": { + "type": "array", + "items": { + "type": "string" + } + }, + "order": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "supportsOpenAIGrammarTools": { + "type": "boolean" + }, + "supportsStrictMode": { + "type": "boolean" + }, + "sendSessionAffinityHeaders": { + "type": "boolean" + }, + "deferredToolsMode": { + "type": "string", + "const": "kimi" + }, + "sessionAffinityFormat": { + "anyOf": [ + { + "type": "string", + "const": "openai" + }, + { + "type": "string", + "const": "openai-nosession" + }, + { + "type": "string", + "const": "openrouter" + } + ] + }, + "supportsLongCacheRetention": { + "type": "boolean" + } + } + }, + { + "type": "object", + "properties": { + "supportsDeveloperRole": { + "type": "boolean" + }, + "sessionAffinityFormat": { + "anyOf": [ + { + "type": "string", + "const": "openai" + }, + { + "type": "string", + "const": "openai-nosession" + }, + { + "type": "string", + "const": "openrouter" + } + ] + }, + "supportsLongCacheRetention": { + "type": "boolean" + }, + "supportsStrictMode": { + "type": "boolean" + }, + "supportsOpenAIGrammarTools": { + "type": "boolean" + }, + "supportsToolSearch": { + "type": "boolean" + } + } + }, + { + "type": "object", + "properties": { + "supportsEagerToolInputStreaming": { + "type": "boolean" + }, + "supportsLongCacheRetention": { + "type": "boolean" + }, + "sendSessionAffinityHeaders": { + "type": "boolean" + }, + "supportsCacheControlOnTools": { + "type": "boolean" + }, + "supportsTemperature": { + "type": "boolean" + }, + "forceAdaptiveThinking": { + "type": "boolean" + }, + "allowEmptySignature": { + "type": "boolean" + }, + "supportsStrictTools": { + "type": "boolean" + }, + "supportsToolReferences": { + "type": "boolean" + } + } + } + ] + } + } + } + }, + "modelOverrides": { + "type": "object", + "patternProperties": { + "^.*$": { + "type": "object", + "properties": { + "name": { + "type": "string", + "minLength": 1 + }, + "reasoning": { + "type": "boolean" + }, + "thinkingLevelMap": { + "type": "object", + "properties": { + "off": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "minimal": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "low": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "medium": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "high": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "xhigh": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "max": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + } + }, + "input": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string", + "const": "text" + }, + { + "type": "string", + "const": "image" + } + ] + } + }, + "cost": { + "type": "object", + "properties": { + "input": { + "type": "number" + }, + "output": { + "type": "number" + }, + "cacheRead": { + "type": "number" + }, + "cacheWrite": { + "type": "number" + }, + "tiers": { + "type": "array", + "items": { + "type": "object", + "required": [ + "inputTokensAbove", + "input", + "output", + "cacheRead", + "cacheWrite" + ], + "properties": { + "inputTokensAbove": { + "type": "number" + }, + "input": { + "type": "number" + }, + "output": { + "type": "number" + }, + "cacheRead": { + "type": "number" + }, + "cacheWrite": { + "type": "number" + } + } + } + } + } + }, + "contextWindow": { + "type": "number" + }, + "maxTokens": { + "type": "number" + }, + "headers": { + "type": "object", + "patternProperties": { + "^.*$": { + "type": "string" + } + } + }, + "compat": { + "anyOf": [ + { + "type": "object", + "properties": { + "supportsStore": { + "type": "boolean" + }, + "supportsDeveloperRole": { + "type": "boolean" + }, + "supportsReasoningEffort": { + "type": "boolean" + }, + "supportsUsageInStreaming": { + "type": "boolean" + }, + "maxTokensField": { + "anyOf": [ + { + "type": "string", + "const": "max_completion_tokens" + }, + { + "type": "string", + "const": "max_tokens" + } + ] + }, + "requiresToolResultName": { + "type": "boolean" + }, + "requiresAssistantAfterToolResult": { + "type": "boolean" + }, + "requiresThinkingAsText": { + "type": "boolean" + }, + "requiresReasoningContentOnAssistantMessages": { + "type": "boolean" + }, + "thinkingFormat": { + "anyOf": [ + { + "type": "string", + "const": "openai" + }, + { + "type": "string", + "const": "openrouter" + }, + { + "type": "string", + "const": "together" + }, + { + "type": "string", + "const": "deepseek" + }, + { + "type": "string", + "const": "zai" + }, + { + "type": "string", + "const": "qwen" + }, + { + "type": "string", + "const": "chat-template" + }, + { + "type": "string", + "const": "qwen-chat-template" + }, + { + "type": "string", + "const": "string-thinking" + }, + { + "type": "string", + "const": "ant-ling" + } + ] + }, + "chatTemplateKwargs": { + "type": "object", + "patternProperties": { + "^.*$": { + "anyOf": [ + { + "anyOf": [ + { + "type": "string" + }, + { + "type": "number" + }, + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + { + "type": "object", + "required": [ + "$var" + ], + "properties": { + "$var": { + "anyOf": [ + { + "type": "string", + "const": "thinking.enabled" + }, + { + "type": "string", + "const": "thinking.effort" + } + ] + }, + "omitWhenOff": { + "type": "boolean" + } + } + } + ] + } + } + }, + "cacheControlFormat": { + "type": "string", + "const": "anthropic" + }, + "openRouterRouting": { + "type": "object", + "properties": { + "allow_fallbacks": { + "type": "boolean" + }, + "require_parameters": { + "type": "boolean" + }, + "data_collection": { + "anyOf": [ + { + "type": "string", + "const": "deny" + }, + { + "type": "string", + "const": "allow" + } + ] + }, + "zdr": { + "type": "boolean" + }, + "enforce_distillable_text": { + "type": "boolean" + }, + "order": { + "type": "array", + "items": { + "type": "string" + } + }, + "only": { + "type": "array", + "items": { + "type": "string" + } + }, + "ignore": { + "type": "array", + "items": { + "type": "string" + } + }, + "quantizations": { + "type": "array", + "items": { + "type": "string" + } + }, + "sort": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "by": { + "type": "string" + }, + "partition": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + } + } + ] + }, + "max_price": { + "type": "object", + "properties": { + "prompt": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string" + } + ] + }, + "completion": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string" + } + ] + }, + "image": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string" + } + ] + }, + "audio": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string" + } + ] + }, + "request": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string" + } + ] + } + } + }, + "preferred_min_throughput": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "object", + "properties": { + "p50": { + "type": "number" + }, + "p75": { + "type": "number" + }, + "p90": { + "type": "number" + }, + "p99": { + "type": "number" + } + } + } + ] + }, + "preferred_max_latency": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "object", + "properties": { + "p50": { + "type": "number" + }, + "p75": { + "type": "number" + }, + "p90": { + "type": "number" + }, + "p99": { + "type": "number" + } + } + } + ] + } + } + }, + "vercelGatewayRouting": { + "type": "object", + "properties": { + "only": { + "type": "array", + "items": { + "type": "string" + } + }, + "order": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "supportsOpenAIGrammarTools": { + "type": "boolean" + }, + "supportsStrictMode": { + "type": "boolean" + }, + "sendSessionAffinityHeaders": { + "type": "boolean" + }, + "deferredToolsMode": { + "type": "string", + "const": "kimi" + }, + "sessionAffinityFormat": { + "anyOf": [ + { + "type": "string", + "const": "openai" + }, + { + "type": "string", + "const": "openai-nosession" + }, + { + "type": "string", + "const": "openrouter" + } + ] + }, + "supportsLongCacheRetention": { + "type": "boolean" + } + } + }, + { + "type": "object", + "properties": { + "supportsDeveloperRole": { + "type": "boolean" + }, + "sessionAffinityFormat": { + "anyOf": [ + { + "type": "string", + "const": "openai" + }, + { + "type": "string", + "const": "openai-nosession" + }, + { + "type": "string", + "const": "openrouter" + } + ] + }, + "supportsLongCacheRetention": { + "type": "boolean" + }, + "supportsStrictMode": { + "type": "boolean" + }, + "supportsOpenAIGrammarTools": { + "type": "boolean" + }, + "supportsToolSearch": { + "type": "boolean" + } + } + }, + { + "type": "object", + "properties": { + "supportsEagerToolInputStreaming": { + "type": "boolean" + }, + "supportsLongCacheRetention": { + "type": "boolean" + }, + "sendSessionAffinityHeaders": { + "type": "boolean" + }, + "supportsCacheControlOnTools": { + "type": "boolean" + }, + "supportsTemperature": { + "type": "boolean" + }, + "forceAdaptiveThinking": { + "type": "boolean" + }, + "allowEmptySignature": { + "type": "boolean" + }, + "supportsStrictTools": { + "type": "boolean" + }, + "supportsToolReferences": { + "type": "boolean" + } + } + } + ] + } + } + } + } + } + } +} diff --git a/tests/fixtures/pi/native-oracle/raw-oracle-v1.json b/tests/fixtures/pi/native-oracle/raw-oracle-v1.json new file mode 100644 index 000000000..0ae5d2738 --- /dev/null +++ b/tests/fixtures/pi/native-oracle/raw-oracle-v1.json @@ -0,0 +1,723 @@ +{ + "version": 1, + "execution": { + "engine": "typebox Value.Check", + "typeboxVersion": "1.3.7", + "schemaTarget": "ModelsConfigSchema.properties.providers.patternProperties['^.*$']" + }, + "cases": [ + { + "id": "all-schema-fields-valid", + "input": { + "name": "All Fields Provider", + "baseUrl": "https://all-fields.example/v1", + "apiKey": "literal-all-fields-key", + "api": "openai-responses", + "oauth": "radius", + "headers": { + "x-provider-field": "provider-value" + }, + "compat": { + "supportsStore": true, + "supportsDeveloperRole": true, + "supportsReasoningEffort": true, + "supportsUsageInStreaming": true, + "maxTokensField": "max_completion_tokens", + "requiresToolResultName": true, + "requiresAssistantAfterToolResult": true, + "requiresThinkingAsText": true, + "requiresReasoningContentOnAssistantMessages": true, + "thinkingFormat": "chat-template", + "chatTemplateKwargs": { + "stringValue": "literal", + "numberValue": 1.25, + "booleanValue": true, + "nullValue": null, + "variableValue": { + "$var": "thinking.effort", + "omitWhenOff": true + } + }, + "cacheControlFormat": "anthropic", + "openRouterRouting": { + "allow_fallbacks": true, + "require_parameters": true, + "data_collection": "deny", + "zdr": true, + "enforce_distillable_text": true, + "order": [ + "provider-a", + "provider-b" + ], + "only": [ + "provider-a" + ], + "ignore": [ + "provider-z" + ], + "quantizations": [ + "fp8" + ], + "sort": { + "by": "price", + "partition": null + }, + "max_price": { + "prompt": 1.1, + "completion": "2.2", + "image": 3.3, + "audio": "4.4", + "request": 5.5 + }, + "preferred_min_throughput": { + "p50": 10.5, + "p75": 9.5, + "p90": 8.5, + "p99": 7.5 + }, + "preferred_max_latency": { + "p50": 100.5, + "p75": 200.5, + "p90": 300.5, + "p99": 400.5 + } + }, + "vercelGatewayRouting": { + "only": [ + "provider-a" + ], + "order": [ + "provider-a", + "provider-b" + ] + }, + "supportsOpenAIGrammarTools": true, + "supportsStrictMode": true, + "sendSessionAffinityHeaders": true, + "deferredToolsMode": "kimi", + "sessionAffinityFormat": "openrouter", + "supportsLongCacheRetention": true, + "supportsToolSearch": true, + "supportsEagerToolInputStreaming": true, + "supportsCacheControlOnTools": true, + "supportsTemperature": true, + "forceAdaptiveThinking": true, + "allowEmptySignature": true, + "supportsStrictTools": true, + "supportsToolReferences": true + }, + "authHeader": true, + "models": [ + { + "id": "all-fields-model", + "name": "All Fields Model", + "api": "anthropic-messages", + "baseUrl": "https://all-fields-model.example/v1", + "reasoning": true, + "thinkingLevelMap": { + "off": null, + "minimal": "minimal-effort", + "low": "low-effort", + "medium": "medium-effort", + "high": "high-effort", + "xhigh": "xhigh-effort", + "max": "max-effort" + }, + "input": [ + "text", + "image" + ], + "cost": { + "input": 0.11, + "output": 0.22, + "cacheRead": 0.033, + "cacheWrite": 0.044, + "tiers": [ + { + "inputTokensAbove": 1000.5, + "input": 0.55, + "output": 0.66, + "cacheRead": 0.077, + "cacheWrite": 0.088 + } + ] + }, + "contextWindow": 128000.5, + "maxTokens": 16384.25, + "headers": { + "x-model-field": "model-value" + }, + "compat": { + "supportsStore": true, + "supportsDeveloperRole": true, + "supportsReasoningEffort": true, + "supportsUsageInStreaming": true, + "maxTokensField": "max_completion_tokens", + "requiresToolResultName": true, + "requiresAssistantAfterToolResult": true, + "requiresThinkingAsText": true, + "requiresReasoningContentOnAssistantMessages": true, + "thinkingFormat": "chat-template", + "chatTemplateKwargs": { + "stringValue": "literal", + "numberValue": 1.25, + "booleanValue": true, + "nullValue": null, + "variableValue": { + "$var": "thinking.effort", + "omitWhenOff": true + } + }, + "cacheControlFormat": "anthropic", + "openRouterRouting": { + "allow_fallbacks": true, + "require_parameters": true, + "data_collection": "deny", + "zdr": true, + "enforce_distillable_text": true, + "order": [ + "provider-a", + "provider-b" + ], + "only": [ + "provider-a" + ], + "ignore": [ + "provider-z" + ], + "quantizations": [ + "fp8" + ], + "sort": { + "by": "price", + "partition": null + }, + "max_price": { + "prompt": 1.1, + "completion": "2.2", + "image": 3.3, + "audio": "4.4", + "request": 5.5 + }, + "preferred_min_throughput": { + "p50": 10.5, + "p75": 9.5, + "p90": 8.5, + "p99": 7.5 + }, + "preferred_max_latency": { + "p50": 100.5, + "p75": 200.5, + "p90": 300.5, + "p99": 400.5 + } + }, + "vercelGatewayRouting": { + "only": [ + "provider-a" + ], + "order": [ + "provider-a", + "provider-b" + ] + }, + "supportsOpenAIGrammarTools": true, + "supportsStrictMode": true, + "sendSessionAffinityHeaders": true, + "deferredToolsMode": "kimi", + "sessionAffinityFormat": "openrouter", + "supportsLongCacheRetention": true, + "supportsToolSearch": true, + "supportsEagerToolInputStreaming": true, + "supportsCacheControlOnTools": true, + "supportsTemperature": true, + "forceAdaptiveThinking": true, + "allowEmptySignature": true, + "supportsStrictTools": true, + "supportsToolReferences": true + } + } + ], + "modelOverrides": { + "all-fields-model": { + "name": "All Fields Override", + "reasoning": false, + "thinkingLevelMap": { + "off": null, + "minimal": "minimal-effort", + "low": "low-effort", + "medium": "medium-effort", + "high": "high-effort", + "xhigh": "xhigh-effort", + "max": "max-effort" + }, + "input": [ + "image", + "text" + ], + "cost": { + "input": 0.11, + "output": 0.22, + "cacheRead": 0.033, + "cacheWrite": 0.044, + "tiers": [ + { + "inputTokensAbove": 1000.5, + "input": 0.55, + "output": 0.66, + "cacheRead": 0.077, + "cacheWrite": 0.088 + } + ] + }, + "contextWindow": 256000.75, + "maxTokens": 32768.5, + "headers": { + "x-override-field": "override-value" + }, + "compat": { + "supportsStore": true, + "supportsDeveloperRole": true, + "supportsReasoningEffort": true, + "supportsUsageInStreaming": true, + "maxTokensField": "max_completion_tokens", + "requiresToolResultName": true, + "requiresAssistantAfterToolResult": true, + "requiresThinkingAsText": true, + "requiresReasoningContentOnAssistantMessages": true, + "thinkingFormat": "chat-template", + "chatTemplateKwargs": { + "stringValue": "literal", + "numberValue": 1.25, + "booleanValue": true, + "nullValue": null, + "variableValue": { + "$var": "thinking.effort", + "omitWhenOff": true + } + }, + "cacheControlFormat": "anthropic", + "openRouterRouting": { + "allow_fallbacks": true, + "require_parameters": true, + "data_collection": "deny", + "zdr": true, + "enforce_distillable_text": true, + "order": [ + "provider-a", + "provider-b" + ], + "only": [ + "provider-a" + ], + "ignore": [ + "provider-z" + ], + "quantizations": [ + "fp8" + ], + "sort": { + "by": "price", + "partition": null + }, + "max_price": { + "prompt": 1.1, + "completion": "2.2", + "image": 3.3, + "audio": "4.4", + "request": 5.5 + }, + "preferred_min_throughput": { + "p50": 10.5, + "p75": 9.5, + "p90": 8.5, + "p99": 7.5 + }, + "preferred_max_latency": { + "p50": 100.5, + "p75": 200.5, + "p90": 300.5, + "p99": 400.5 + } + }, + "vercelGatewayRouting": { + "only": [ + "provider-a" + ], + "order": [ + "provider-a", + "provider-b" + ] + }, + "supportsOpenAIGrammarTools": true, + "supportsStrictMode": true, + "sendSessionAffinityHeaders": true, + "deferredToolsMode": "kimi", + "sessionAffinityFormat": "openrouter", + "supportsLongCacheRetention": true, + "supportsToolSearch": true, + "supportsEagerToolInputStreaming": true, + "supportsCacheControlOnTools": true, + "supportsTemperature": true, + "forceAdaptiveThinking": true, + "allowEmptySignature": true, + "supportsStrictTools": true, + "supportsToolReferences": true + } + } + } + }, + "expectedValid": true + }, + { + "id": "empty-provider-object", + "input": {}, + "expectedValid": true + }, + { + "id": "null-provider", + "input": null, + "expectedValid": false + }, + { + "id": "additional-provider-property", + "input": { + "futureProviderField": { + "nested": true + } + }, + "expectedValid": true + }, + { + "id": "empty-present-base-url", + "input": { + "baseUrl": "" + }, + "expectedValid": false + }, + { + "id": "non-url-base-url-is-raw-string", + "input": { + "baseUrl": "not a URL" + }, + "expectedValid": true + }, + { + "id": "radius-oauth", + "input": { + "oauth": "radius" + }, + "expectedValid": true + }, + { + "id": "unknown-oauth-literal", + "input": { + "oauth": "other" + }, + "expectedValid": false + }, + { + "id": "models-null", + "input": { + "models": null + }, + "expectedValid": false + }, + { + "id": "model-missing-id", + "input": { + "models": [ + { + "api": "openai-responses" + } + ] + }, + "expectedValid": false + }, + { + "id": "model-empty-id", + "input": { + "models": [ + { + "id": "" + } + ] + }, + "expectedValid": false + }, + { + "id": "integer-model-numbers", + "input": { + "models": [ + { + "id": "integer", + "contextWindow": 128000, + "maxTokens": 16384 + } + ] + }, + "expectedValid": true + }, + { + "id": "fractional-model-numbers", + "input": { + "models": [ + { + "id": "fractional", + "contextWindow": 128000.5, + "maxTokens": 16384.25 + } + ] + }, + "expectedValid": true + }, + { + "id": "negative-number-is-still-typebox-number", + "input": { + "models": [ + { + "id": "negative", + "maxTokens": -1.5 + } + ] + }, + "expectedValid": true + }, + { + "id": "string-is-not-number", + "input": { + "models": [ + { + "id": "string-limit", + "maxTokens": "16384" + } + ] + }, + "expectedValid": false + }, + { + "id": "complete-cost-with-fractions", + "input": { + "models": [ + { + "id": "priced", + "cost": { + "input": 0.1, + "output": 0.2, + "cacheRead": 0.03, + "cacheWrite": 0.04, + "tiers": [ + { + "inputTokensAbove": 1000.5, + "input": 0.5, + "output": 0.6, + "cacheRead": 0.07, + "cacheWrite": 0.08 + } + ] + } + } + ] + }, + "expectedValid": true + }, + { + "id": "incomplete-model-cost", + "input": { + "models": [ + { + "id": "priced", + "cost": { + "input": 0.1, + "output": 0.2, + "cacheRead": 0.03 + } + } + ] + }, + "expectedValid": false + }, + { + "id": "compat-openrouter-recursive-valid", + "input": { + "compat": { + "thinkingFormat": "chat-template", + "chatTemplateKwargs": { + "temperature": 0.25, + "thinking": { + "$var": "thinking.effort", + "omitWhenOff": true + }, + "nullable": null + }, + "openRouterRouting": { + "data_collection": "deny", + "sort": { + "by": "price", + "partition": null + }, + "max_price": { + "prompt": 1.5, + "completion": "2.0" + }, + "preferred_min_throughput": { + "p50": 10.5, + "p99": 2 + } + } + } + }, + "expectedValid": true + }, + { + "id": "compat-nested-union-invalid-in-every-branch", + "input": { + "compat": { + "openRouterRouting": { + "preferred_min_throughput": { + "p50": "fast" + } + }, + "supportsToolSearch": "yes", + "supportsTemperature": 1 + } + }, + "expectedValid": false + }, + { + "id": "compat-union-additional-field", + "input": { + "compat": { + "futureCompatField": [ + 1, + 2, + 3 + ] + } + }, + "expectedValid": true + }, + { + "id": "compat-null", + "input": { + "compat": null + }, + "expectedValid": false + }, + { + "id": "thinking-map-unknown-key-and-string-value", + "input": { + "models": [ + { + "id": "thinking", + "thinkingLevelMap": { + "low": null, + "max": "future-effort", + "future": { + "nested": true + } + } + } + ] + }, + "expectedValid": true + }, + { + "id": "thinking-map-known-key-invalid-value", + "input": { + "models": [ + { + "id": "thinking", + "thinkingLevelMap": { + "low": 2 + } + } + ] + }, + "expectedValid": false + }, + { + "id": "compat-sort-string-union-branch", + "input": { + "compat": { + "openRouterRouting": { + "sort": "price" + } + } + }, + "expectedValid": true + }, + { + "id": "compat-sort-object-union-branch", + "input": { + "compat": { + "openRouterRouting": { + "sort": { + "by": "price", + "partition": null + } + } + } + }, + "expectedValid": true + }, + { + "id": "compat-sort-invalid-union-boundary", + "input": { + "compat": { + "openRouterRouting": { + "sort": false + } + } + }, + "expectedValid": true + }, + { + "id": "headers-record-valid", + "input": { + "headers": { + "Authorization": "Bearer literal", + "x-count": "2" + } + }, + "expectedValid": true + }, + { + "id": "headers-record-invalid-value", + "input": { + "headers": { + "x-count": 2 + } + }, + "expectedValid": false + }, + { + "id": "model-override-fractional-and-extra", + "input": { + "modelOverrides": { + "model/a": { + "contextWindow": 10.5, + "maxTokens": 2.25, + "futureOverrideField": true + } + } + }, + "expectedValid": true + }, + { + "id": "input-union-invalid", + "input": { + "models": [ + { + "id": "input", + "input": [ + "text", + "audio" + ] + } + ] + }, + "expectedValid": false + } + ] +} diff --git a/tests/fixtures/pi/native-oracle/transport-oracle-v1.json b/tests/fixtures/pi/native-oracle/transport-oracle-v1.json new file mode 100644 index 000000000..5ee017932 --- /dev/null +++ b/tests/fixtures/pi/native-oracle/transport-oracle-v1.json @@ -0,0 +1,82 @@ +{ + "version": 1, + "piCommit": "ab366ebe94cacd419d986be454f12b1b9913aaca", + "engine": "pinned upstream TypeScript", + "bundler": "esbuild@0.28.1", + "upstreamEntry": "packages/coding-agent/src/core/resolve-config-value.ts", + "platform": "linux", + "cases": [ + { + "id": "literal-value", + "input": "literal-secret", + "environment": {}, + "execution": { + "status": "success", + "entryFunction": "resolveConfigValueOrThrow" + }, + "expected": "literal-secret" + }, + { + "id": "environment-template", + "input": "prefix-${PI_ORACLE_VALUE}-suffix", + "environment": { + "PI_ORACLE_VALUE": "environment-secret" + }, + "execution": { + "status": "success", + "entryFunction": "resolveConfigValueOrThrow" + }, + "expected": "prefix-environment-secret-suffix" + }, + { + "id": "escaped-dollar-and-bang", + "input": "$$literal-$!bang", + "environment": {}, + "execution": { + "status": "success", + "entryFunction": "resolveConfigValueOrThrow" + }, + "expected": "$literal-!bang" + }, + { + "id": "shell-command", + "input": "!printf pi-command-value", + "environment": {}, + "execution": { + "status": "success", + "entryFunction": "resolveConfigValueOrThrow" + }, + "expected": "pi-command-value" + }, + { + "id": "missing-environment", + "input": "${PI_ORACLE_MISSING}", + "environment": {}, + "execution": { + "status": "error", + "entryFunction": "resolveConfigValueOrThrow" + }, + "expectedError": "Failed to resolve oracle missing-environment from environment variable: PI_ORACLE_MISSING" + } + ], + "headerCase": { + "id": "provider-header-materialization", + "input": { + "x-literal": "literal-header", + "x-environment": "${PI_ORACLE_HEADER}", + "x-command": "!printf pi-command-header" + }, + "environment": { + "PI_ORACLE_HEADER": "environment-header" + }, + "execution": { + "status": "success", + "entryFunction": "resolveHeadersOrThrow" + }, + "expected": { + "x-literal": "literal-header", + "x-environment": "environment-header", + "x-command": "pi-command-header" + } + } +}