@code-yeongyu/senpi 2026.9.23-2 → 2026.9.23-3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +24 -0
- package/dist/bundle/chunks/{app-server-command-UO3ODP6D.js → app-server-command-U5Y46LHO.js} +1 -1
- package/dist/bundle/chunks/{chunk-OLJ7LQJY.js → chunk-BBOQ5H4W.js} +1 -1
- package/dist/bundle/chunks/{chunk-7EH64T6E.js → chunk-ORCF262V.js} +2 -2
- package/dist/bundle/chunks/{chunk-BPHXJ4N2.js → chunk-R6F525XB.js} +1 -1
- package/dist/bundle/chunks/{chunk-LMO3WYKH.js → chunk-TH4PERSI.js} +131 -124
- package/dist/bundle/chunks/{chunk-IBHBWRC4.js → chunk-TUVSARCZ.js} +2 -2
- package/dist/bundle/chunks/{chunk-WJ2BGYFK.js → chunk-XPDV6SIX.js} +1 -1
- package/dist/bundle/chunks/{chunk-IDJUNOOR.js → chunk-Y4AJLTFT.js} +2 -2
- package/dist/bundle/chunks/{cli-main-LUQZADLN.js → cli-main-65WMMIRT.js} +1 -1
- package/dist/bundle/chunks/{interactive-host-runtime-A6PM52FT.js → interactive-host-runtime-DUNYT24Q.js} +1 -1
- package/dist/bundle/chunks/{interactive-mode-VR7QYMAE.js → interactive-mode-SCG5PQZ6.js} +1 -1
- package/dist/bundle/chunks/{list-tips-VGOIMT6W.js → list-tips-Z62FXPOJ.js} +1 -1
- package/dist/bundle/chunks/{multi-session-host-LYOVVA75.js → multi-session-host-QC6BZZNH.js} +1 -1
- package/dist/bundle/chunks/{package-manager-cli-OMEJACX6.js → package-manager-cli-23GULCVL.js} +1 -1
- package/dist/bundle/chunks/{rpc-mode-AKGJA73P.js → rpc-mode-ZQ4E6FFA.js} +1 -1
- package/dist/bundle/chunks/{session-picker-UE5LOM6W.js → session-picker-CO2ZK3EC.js} +1 -1
- package/dist/bundle/chunks/session-worker.js +202 -195
- package/dist/bundle/cli.js +1 -1
- package/dist/bundle/index.js +1 -1
- package/dist/bundle/rpc-entry.js +1 -1
- package/dist/core/extensions/builtin/anthropic-subscription/executable-model-support.d.ts +19 -0
- package/dist/core/extensions/builtin/anthropic-subscription/executable-model-support.d.ts.map +1 -0
- package/dist/core/extensions/builtin/anthropic-subscription/executable-model-support.js +76 -0
- package/dist/core/extensions/builtin/anthropic-subscription/executable-model-support.js.map +1 -0
- package/dist/core/extensions/builtin/anthropic-subscription/executable-path-lookup.d.ts +7 -2
- package/dist/core/extensions/builtin/anthropic-subscription/executable-path-lookup.d.ts.map +1 -1
- package/dist/core/extensions/builtin/anthropic-subscription/executable-path-lookup.js +28 -3
- package/dist/core/extensions/builtin/anthropic-subscription/executable-path-lookup.js.map +1 -1
- package/dist/core/extensions/builtin/anthropic-subscription/executable.d.ts +11 -0
- package/dist/core/extensions/builtin/anthropic-subscription/executable.d.ts.map +1 -1
- package/dist/core/extensions/builtin/anthropic-subscription/executable.js +31 -10
- package/dist/core/extensions/builtin/anthropic-subscription/executable.js.map +1 -1
- package/dist/core/extensions/builtin/anthropic-subscription/guidance.d.ts +2 -1
- package/dist/core/extensions/builtin/anthropic-subscription/guidance.d.ts.map +1 -1
- package/dist/core/extensions/builtin/anthropic-subscription/guidance.js +18 -2
- package/dist/core/extensions/builtin/anthropic-subscription/guidance.js.map +1 -1
- package/dist/core/extensions/builtin/anthropic-subscription/stream-guidance.d.ts +2 -1
- package/dist/core/extensions/builtin/anthropic-subscription/stream-guidance.d.ts.map +1 -1
- package/dist/core/extensions/builtin/anthropic-subscription/stream-guidance.js +2 -2
- package/dist/core/extensions/builtin/anthropic-subscription/stream-guidance.js.map +1 -1
- package/dist/core/extensions/builtin/anthropic-subscription/stream.d.ts.map +1 -1
- package/dist/core/extensions/builtin/anthropic-subscription/stream.js +5 -3
- package/dist/core/extensions/builtin/anthropic-subscription/stream.js.map +1 -1
- package/dist/core/extensions/builtin/goal/cache-warm-renderer.d.ts +6 -0
- package/dist/core/extensions/builtin/goal/cache-warm-renderer.d.ts.map +1 -1
- package/dist/core/extensions/builtin/goal/cache-warm-renderer.js +8 -0
- package/dist/core/extensions/builtin/goal/cache-warm-renderer.js.map +1 -1
- package/dist/core/extensions/builtin/goal/index.d.ts.map +1 -1
- package/dist/core/extensions/builtin/goal/index.js +4 -2
- package/dist/core/extensions/builtin/goal/index.js.map +1 -1
- package/dist/core/extensions/builtin/goal/monitor-continuation.d.ts +3 -1
- package/dist/core/extensions/builtin/goal/monitor-continuation.d.ts.map +1 -1
- package/dist/core/extensions/builtin/goal/monitor-continuation.js +32 -10
- package/dist/core/extensions/builtin/goal/monitor-continuation.js.map +1 -1
- package/dist/core/extensions/builtin/goal/parked-wait.d.ts +16 -0
- package/dist/core/extensions/builtin/goal/parked-wait.d.ts.map +1 -0
- package/dist/core/extensions/builtin/goal/parked-wait.js +54 -0
- package/dist/core/extensions/builtin/goal/parked-wait.js.map +1 -0
- package/dist/core/extensions/builtin/mcp/auth/commands-auth-dispatch.d.ts +1 -1
- package/dist/core/extensions/builtin/mcp/auth/commands-auth-dispatch.d.ts.map +1 -1
- package/dist/core/extensions/builtin/mcp/auth/commands-auth-dispatch.js +8 -2
- package/dist/core/extensions/builtin/mcp/auth/commands-auth-dispatch.js.map +1 -1
- package/dist/core/extensions/builtin/mcp/auth/commands-auth.d.ts.map +1 -1
- package/dist/core/extensions/builtin/mcp/auth/commands-auth.js +15 -4
- package/dist/core/extensions/builtin/mcp/auth/commands-auth.js.map +1 -1
- package/dist/core/extensions/builtin/mcp/commands.d.ts.map +1 -1
- package/dist/core/extensions/builtin/mcp/commands.js +2 -0
- package/dist/core/extensions/builtin/mcp/commands.js.map +1 -1
- package/dist/core/extensions/builtin/prompt-preset/gpt-5.6.d.ts +5 -5
- package/dist/core/extensions/builtin/prompt-preset/gpt-5.6.d.ts.map +1 -1
- package/dist/core/extensions/builtin/prompt-preset/gpt-5.6.js +16 -11
- package/dist/core/extensions/builtin/prompt-preset/gpt-5.6.js.map +1 -1
- package/dist/core/extensions/builtin/prompt-preset/gpt-6-astra.d.ts +5 -5
- package/dist/core/extensions/builtin/prompt-preset/gpt-6-astra.d.ts.map +1 -1
- package/dist/core/extensions/builtin/prompt-preset/gpt-6-astra.js +9 -6
- package/dist/core/extensions/builtin/prompt-preset/gpt-6-astra.js.map +1 -1
- package/dist/core/extensions/builtin/prompt-preset/test-decision.d.ts +2 -0
- package/dist/core/extensions/builtin/prompt-preset/test-decision.d.ts.map +1 -0
- package/dist/core/extensions/builtin/prompt-preset/test-decision.js +5 -0
- package/dist/core/extensions/builtin/prompt-preset/test-decision.js.map +1 -0
- package/dist/core/extensions/index.d.ts +1 -1
- package/dist/core/extensions/index.d.ts.map +1 -1
- package/dist/core/extensions/index.js.map +1 -1
- package/dist/core/extensions/loader.d.ts.map +1 -1
- package/dist/core/extensions/loader.js +6 -1
- package/dist/core/extensions/loader.js.map +1 -1
- package/dist/core/extensions/runner.d.ts +3 -1
- package/dist/core/extensions/runner.d.ts.map +1 -1
- package/dist/core/extensions/runner.js +5 -0
- package/dist/core/extensions/runner.js.map +1 -1
- package/dist/core/extensions/types.d.ts +10 -1
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/model-config.d.ts +1 -0
- package/dist/core/model-config.d.ts.map +1 -1
- package/dist/core/model-config.js +26 -20
- package/dist/core/model-config.js.map +1 -1
- package/dist/core/models-json-migration.d.ts +30 -0
- package/dist/core/models-json-migration.d.ts.map +1 -0
- package/dist/core/models-json-migration.js +257 -0
- package/dist/core/models-json-migration.js.map +1 -0
- package/dist/core/output-guard.d.ts +2 -0
- package/dist/core/output-guard.d.ts.map +1 -1
- package/dist/core/output-guard.js +31 -0
- package/dist/core/output-guard.js.map +1 -1
- package/dist/core/settings-manager.d.ts +4 -0
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +16 -6
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/settings-overrides.d.ts +8 -0
- package/dist/core/settings-overrides.d.ts.map +1 -0
- package/dist/core/settings-overrides.js +28 -0
- package/dist/core/settings-overrides.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/modes/interactive/components/assistant-message.d.ts +2 -0
- package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/assistant-message.js +4 -0
- package/dist/modes/interactive/components/assistant-message.js.map +1 -1
- package/dist/modes/interactive/components/custom-entry.d.ts +10 -1
- package/dist/modes/interactive/components/custom-entry.d.ts.map +1 -1
- package/dist/modes/interactive/components/custom-entry.js +22 -0
- package/dist/modes/interactive/components/custom-entry.js.map +1 -1
- package/dist/modes/interactive/components/exploration-call.d.ts +12 -0
- package/dist/modes/interactive/components/exploration-call.d.ts.map +1 -0
- package/dist/modes/interactive/components/exploration-call.js +41 -0
- package/dist/modes/interactive/components/exploration-call.js.map +1 -0
- package/dist/modes/interactive/components/exploration-group.d.ts +19 -0
- package/dist/modes/interactive/components/exploration-group.d.ts.map +1 -0
- package/dist/modes/interactive/components/exploration-group.js +89 -0
- package/dist/modes/interactive/components/exploration-group.js.map +1 -0
- package/dist/modes/interactive/components/exploration-transcript-container.d.ts +18 -0
- package/dist/modes/interactive/components/exploration-transcript-container.d.ts.map +1 -0
- package/dist/modes/interactive/components/exploration-transcript-container.js +67 -0
- package/dist/modes/interactive/components/exploration-transcript-container.js.map +1 -0
- package/dist/modes/interactive/components/tool-execution.d.ts +7 -1
- package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/dist/modes/interactive/components/tool-execution.js +4 -0
- package/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +24 -35
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/replay-assistant-tools.d.ts +14 -0
- package/dist/modes/interactive/replay-assistant-tools.d.ts.map +1 -0
- package/dist/modes/interactive/replay-assistant-tools.js +33 -0
- package/dist/modes/interactive/replay-assistant-tools.js.map +1 -0
- package/dist/modes/interactive/tool-progress.d.ts +2 -0
- package/dist/modes/interactive/tool-progress.d.ts.map +1 -1
- package/dist/modes/interactive/tool-progress.js +5 -2
- package/dist/modes/interactive/tool-progress.js.map +1 -1
- package/dist/modes/interactive/tui-renderer.d.ts.map +1 -1
- package/dist/modes/interactive/tui-renderer.js +6 -1
- package/dist/modes/interactive/tui-renderer.js.map +1 -1
- package/docs/extensions.md +8 -0
- package/docs/mcp.md +9 -6
- package/node_modules/@code-yeongyu/senpi-codemode/CHANGELOG.md +16 -0
- package/node_modules/@code-yeongyu/senpi-codemode/README.md +10 -8
- package/node_modules/@code-yeongyu/senpi-codemode/package.json +4 -4
- package/node_modules/@code-yeongyu/senpi-codemode/src/tool/display-code.ts +145 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/tool/eval-request.ts +7 -12
- package/node_modules/@code-yeongyu/senpi-codemode/src/tool/eval-tool.ts +2 -2
- package/node_modules/@code-yeongyu/senpi-codemode/src/tool/render.ts +34 -16
- package/node_modules/@code-yeongyu/senpi-codemode/src/tool/types.ts +1 -4
- package/node_modules/@earendil-works/pi-agent-core/package.json +3 -3
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/.manifest.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/openrouter.json +1 -1
- package/node_modules/@earendil-works/pi-ai/package.json +2 -2
- package/node_modules/@earendil-works/pi-pty/package.json +1 -1
- package/node_modules/@earendil-works/pi-telemetry/package.json +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/stderr-observer.d.ts +3 -0
- package/node_modules/@earendil-works/pi-tui/dist/stderr-observer.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-tui/dist/stderr-observer.js +14 -0
- package/node_modules/@earendil-works/pi-tui/dist/stderr-observer.js.map +1 -0
- package/node_modules/@earendil-works/pi-tui/dist/terminal.d.ts +4 -1
- package/node_modules/@earendil-works/pi-tui/dist/terminal.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/terminal.js +8 -12
- package/node_modules/@earendil-works/pi-tui/dist/terminal.js.map +1 -1
- package/node_modules/@earendil-works/pi-tui/package.json +1 -1
- package/package.json +7 -7
|
@@ -6,6 +6,7 @@ import { stripJsonComments } from "../utils/json.js";
|
|
|
6
6
|
import { normalizePath } from "../utils/paths.js";
|
|
7
7
|
import { stripBom } from "../utils/text.js";
|
|
8
8
|
import { validateModelsConfig, } from "./model-config-schema.js";
|
|
9
|
+
import { migrateModelsJsonProviderIds } from "./models-json-migration.js";
|
|
9
10
|
/*
|
|
10
11
|
p50: Type.Optional(Type.Number()),
|
|
11
12
|
p75: Type.Optional(Type.Number()),
|
|
@@ -464,16 +465,19 @@ export class ModelConfig {
|
|
|
464
465
|
if (typeof provider !== "object" || provider === null)
|
|
465
466
|
continue;
|
|
466
467
|
const record = provider;
|
|
467
|
-
|
|
468
|
+
// Shape errors are the validator's to report; only well-shaped entries are normalized here.
|
|
469
|
+
const modelEntries = [
|
|
470
|
+
...(Array.isArray(record.models) ? record.models : []),
|
|
471
|
+
...(typeof record.modelOverrides === "object" && record.modelOverrides !== null
|
|
472
|
+
? Object.values(record.modelOverrides)
|
|
473
|
+
: []),
|
|
474
|
+
].filter((model) => typeof model === "object" && model !== null);
|
|
475
|
+
for (const model of modelEntries) {
|
|
468
476
|
delete model.samplingParams;
|
|
469
477
|
}
|
|
470
|
-
const compatEntries = [
|
|
471
|
-
record.compat,
|
|
472
|
-
...(record.models ?? []).map((model) => model.compat),
|
|
473
|
-
...Object.values(record.modelOverrides ?? {}).map((model) => model.compat),
|
|
474
|
-
];
|
|
478
|
+
const compatEntries = [record.compat, ...modelEntries.map((model) => model.compat)];
|
|
475
479
|
for (const compat of compatEntries) {
|
|
476
|
-
if (
|
|
480
|
+
if (typeof compat !== "object" || compat === null)
|
|
477
481
|
continue;
|
|
478
482
|
if (compat.thinkingFormat === "baseten")
|
|
479
483
|
compat.thinkingFormat = "together";
|
|
@@ -492,25 +496,27 @@ export class ModelConfig {
|
|
|
492
496
|
const providers = new Map();
|
|
493
497
|
// Read boundary (senpi#1989): a models.json written before the rename keys
|
|
494
498
|
// its overlay by the legacy provider id. Normalize the key so the overlay
|
|
495
|
-
// still attaches
|
|
496
|
-
// never rewritten.
|
|
497
|
-
const renamed = [];
|
|
499
|
+
// still attaches; `parseAndMigrate` then rewrites the file once (senpi#2044).
|
|
498
500
|
for (const [providerId, provider] of Object.entries(config.providers)) {
|
|
499
501
|
const canonical = normalizeProviderId(providerId);
|
|
500
|
-
if (canonical !== providerId)
|
|
501
|
-
renamed.push(`${providerId} -> ${canonical}`);
|
|
502
502
|
// An explicit canonical entry wins over a legacy one that normalizes onto it.
|
|
503
503
|
if (canonical !== providerId && providers.has(canonical))
|
|
504
504
|
continue;
|
|
505
505
|
providers.set(canonical, deepFreeze(structuredClone(provider)));
|
|
506
506
|
}
|
|
507
507
|
const disabled = new Set((config.disabledProviders ?? []).map((id) => normalizeProviderId(id)));
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
508
|
+
return new ModelConfig(providers, disabled);
|
|
509
|
+
}
|
|
510
|
+
static parseAndMigrate(content, path) {
|
|
511
|
+
const config = ModelConfig.parse(content, path);
|
|
512
|
+
if (config.error !== undefined)
|
|
513
|
+
return config;
|
|
514
|
+
const migration = migrateModelsJsonProviderIds(path, content);
|
|
515
|
+
if (migration.kind !== "failed")
|
|
516
|
+
return config;
|
|
517
|
+
return new ModelConfig(config.providers, config.disabledProviders, undefined, [
|
|
518
|
+
`models.json uses renamed provider ids (${migration.renamed.join(", ")}) and could not be updated automatically: ${migration.reason}. They still work, but update the file to the new ids.`,
|
|
519
|
+
]);
|
|
514
520
|
}
|
|
515
521
|
static async load(modelsJsonPath) {
|
|
516
522
|
if (!modelsJsonPath)
|
|
@@ -525,14 +531,14 @@ export class ModelConfig {
|
|
|
525
531
|
return new ModelConfig(new Map());
|
|
526
532
|
return new ModelConfig(new Map(), new Set(), `Failed to load models.json: ${error instanceof Error ? error.message : error}\n\nFile: ${path}`);
|
|
527
533
|
}
|
|
528
|
-
return ModelConfig.
|
|
534
|
+
return ModelConfig.parseAndMigrate(content, path);
|
|
529
535
|
}
|
|
530
536
|
static loadSync(modelsJsonPath) {
|
|
531
537
|
if (!modelsJsonPath)
|
|
532
538
|
return new ModelConfig(new Map());
|
|
533
539
|
const path = normalizePath(modelsJsonPath);
|
|
534
540
|
try {
|
|
535
|
-
return ModelConfig.
|
|
541
|
+
return ModelConfig.parseAndMigrate(readFileSync(path, "utf-8"), path);
|
|
536
542
|
}
|
|
537
543
|
catch (error) {
|
|
538
544
|
if (error.code === "ENOENT")
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"model-config.js","sourceRoot":"","sources":["../../src/core/model-config.ts"],"names":[],"mappings":"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","sourcesContent":["/** Immutable, credential-blind models.json snapshot. */\n\nimport { readFileSync } from \"node:fs\";\nimport { readFile } from \"node:fs/promises\";\nimport { normalizeProviderId } from \"@earendil-works/pi-ai\";\nimport type { TLocalizedValidationError } from \"typebox/error\";\nimport { stripJsonComments } from \"../utils/json.ts\";\nimport { normalizePath } from \"../utils/paths.ts\";\nimport { stripBom } from \"../utils/text.ts\";\nimport {\n\ttype ModelsJson,\n\ttype ModelsJsonProvider,\n\ttype ModelsJsonModel as SchemaModelsJsonModel,\n\ttype ModelsJsonModelOverride as SchemaModelsJsonModelOverride,\n\tvalidateModelsConfig,\n} from \"./model-config-schema.ts\";\n\nexport type ModelsJsonModel = SchemaModelsJsonModel & { samplingParams?: Record<string, unknown> };\nexport type ModelsJsonModelOverride = SchemaModelsJsonModelOverride & { samplingParams?: Record<string, unknown> };\nexport type { ModelsJsonProvider };\n\n/*\n\tp50: Type.Optional(Type.Number()),\n\tp75: Type.Optional(Type.Number()),\n\tp90: Type.Optional(Type.Number()),\n\tp99: Type.Optional(Type.Number()),\n});\n\nconst OpenRouterRoutingSchema = Type.Object({\n\tallow_fallbacks: Type.Optional(Type.Boolean()),\n\trequire_parameters: Type.Optional(Type.Boolean()),\n\tdata_collection: Type.Optional(Type.Union([Type.Literal(\"deny\"), Type.Literal(\"allow\")])),\n\tzdr: Type.Optional(Type.Boolean()),\n\tenforce_distillable_text: Type.Optional(Type.Boolean()),\n\torder: Type.Optional(Type.Array(Type.String())),\n\tonly: Type.Optional(Type.Array(Type.String())),\n\tignore: Type.Optional(Type.Array(Type.String())),\n\tquantizations: Type.Optional(Type.Array(Type.String())),\n\tsort: Type.Optional(\n\t\tType.Union([\n\t\t\tType.String(),\n\t\t\tType.Object({\n\t\t\t\tby: Type.Optional(Type.String()),\n\t\t\t\tpartition: Type.Optional(Type.Union([Type.String(), Type.Null()])),\n\t\t\t}),\n\t\t]),\n\t),\n\tmax_price: Type.Optional(\n\t\tType.Object({\n\t\t\tprompt: Type.Optional(Type.Union([Type.Number(), Type.String()])),\n\t\t\tcompletion: Type.Optional(Type.Union([Type.Number(), Type.String()])),\n\t\t\timage: Type.Optional(Type.Union([Type.Number(), Type.String()])),\n\t\t\taudio: Type.Optional(Type.Union([Type.Number(), Type.String()])),\n\t\t\trequest: Type.Optional(Type.Union([Type.Number(), Type.String()])),\n\t\t}),\n\t),\n\tpreferred_min_throughput: Type.Optional(Type.Union([Type.Number(), PercentileCutoffsSchema])),\n\tpreferred_max_latency: Type.Optional(Type.Union([Type.Number(), PercentileCutoffsSchema])),\n});\n\nconst VercelGatewayRoutingSchema = Type.Object({\n\tonly: Type.Optional(Type.Array(Type.String())),\n\torder: Type.Optional(Type.Array(Type.String())),\n});\n\nconst ThinkingLevelMapValueSchema = Type.Union([Type.String(), Type.Null()]);\nconst ThinkingLevelMapSchema = Type.Object({\n\toff: Type.Optional(ThinkingLevelMapValueSchema),\n\tminimal: Type.Optional(ThinkingLevelMapValueSchema),\n\tlow: Type.Optional(ThinkingLevelMapValueSchema),\n\tmedium: Type.Optional(ThinkingLevelMapValueSchema),\n\thigh: Type.Optional(ThinkingLevelMapValueSchema),\n\txhigh: Type.Optional(ThinkingLevelMapValueSchema),\n\tmax: Type.Optional(ThinkingLevelMapValueSchema),\n});\n\nconst ChatTemplateKwargScalarSchema = Type.Union([Type.String(), Type.Number(), Type.Boolean(), Type.Null()]);\nconst ChatTemplateKwargVariableSchema = Type.Object({\n\t$var: Type.Union([Type.Literal(\"thinking.enabled\"), Type.Literal(\"thinking.effort\")]),\n\tomitWhenOff: Type.Optional(Type.Boolean()),\n});\nconst ChatTemplateKwargSchema = Type.Union([ChatTemplateKwargScalarSchema, ChatTemplateKwargVariableSchema]);\n\nconst OpenAICompletionsCompatSchema = Type.Object({\n\tsupportsStore: Type.Optional(Type.Boolean()),\n\tsupportsDeveloperRole: Type.Optional(Type.Boolean()),\n\tsupportsReasoningEffort: Type.Optional(Type.Boolean()),\n\tsupportsUsageInStreaming: Type.Optional(Type.Boolean()),\n\tsupportsFinishReason: Type.Optional(Type.Boolean()),\n\tmaxTokensField: Type.Optional(Type.Union([Type.Literal(\"max_completion_tokens\"), Type.Literal(\"max_tokens\")])),\n\trequiresToolResultName: Type.Optional(Type.Boolean()),\n\trequiresAssistantAfterToolResult: Type.Optional(Type.Boolean()),\n\trequiresThinkingAsText: Type.Optional(Type.Boolean()),\n\trequiresReasoningContentOnAssistantMessages: Type.Optional(Type.Boolean()),\n\tthinkingFormat: Type.Optional(\n\t\tType.Union([\n\t\t\tType.Literal(\"openai\"),\n\t\t\tType.Literal(\"openrouter\"),\n\t\t\tType.Literal(\"together\"),\n\t\t\tType.Literal(\"deepseek\"),\n\t\t\tType.Literal(\"zai\"),\n\t\t\tType.Literal(\"qwen\"),\n\t\t\tType.Literal(\"chat-template\"),\n\t\t\tType.Literal(\"qwen-chat-template\"),\n\t\t\tType.Literal(\"string-thinking\"),\n\t\t\tType.Literal(\"ant-ling\"),\n\t\t]),\n\t),\n\tchatTemplateKwargs: Type.Optional(Type.Record(Type.String(), ChatTemplateKwargSchema)),\n\tcacheControlFormat: Type.Optional(Type.Literal(\"anthropic\")),\n\topenRouterRouting: Type.Optional(OpenRouterRoutingSchema),\n\tvercelGatewayRouting: Type.Optional(VercelGatewayRoutingSchema),\n\tsupportsOpenAIGrammarTools: Type.Optional(Type.Boolean()),\n\tsupportsStrictMode: Type.Optional(Type.Boolean()),\n\tsendSessionAffinityHeaders: Type.Optional(Type.Boolean()),\n\tdeferredToolsMode: Type.Optional(Type.Literal(\"kimi\")),\n\tsessionAffinityFormat: Type.Optional(\n\t\tType.Union([Type.Literal(\"openai\"), Type.Literal(\"openai-nosession\"), Type.Literal(\"openrouter\")]),\n\t),\n\tsupportsLongCacheRetention: Type.Optional(Type.Boolean()),\n});\n\nconst OpenAIResponsesCompatSchema = Type.Object({\n\tsupportsDeveloperRole: Type.Optional(Type.Boolean()),\n\tsessionAffinityFormat: Type.Optional(\n\t\tType.Union([Type.Literal(\"openai\"), Type.Literal(\"openai-nosession\"), Type.Literal(\"openrouter\")]),\n\t),\n\tsupportsLongCacheRetention: Type.Optional(Type.Boolean()),\n\tsupportsStrictMode: Type.Optional(Type.Boolean()),\n\tsupportsOpenAIGrammarTools: Type.Optional(Type.Boolean()),\n\tsupportsToolSearch: Type.Optional(Type.Boolean()),\n});\n\nconst AnthropicMessagesCompatSchema = Type.Object({\n\tsupportsEagerToolInputStreaming: Type.Optional(Type.Boolean()),\n\tsupportsLongCacheRetention: Type.Optional(Type.Boolean()),\n\tsendSessionAffinityHeaders: Type.Optional(Type.Boolean()),\n\tsupportsCacheControlOnTools: Type.Optional(Type.Boolean()),\n\tsupportsTemperature: Type.Optional(Type.Boolean()),\n\tforceAdaptiveThinking: Type.Optional(Type.Boolean()),\n\tallowEmptySignature: Type.Optional(Type.Boolean()),\n\tsupportsStrictTools: Type.Optional(Type.Boolean()),\n\tsupportsToolReferences: Type.Optional(Type.Boolean()),\n});\n\nconst ProviderCompatSchema = Type.Union([\n\tOpenAICompletionsCompatSchema,\n\tOpenAIResponsesCompatSchema,\n\tAnthropicMessagesCompatSchema,\n]);\n\nconst ModelCostRatesSchema = {\n\tinput: Type.Number(),\n\toutput: Type.Number(),\n\tcacheRead: Type.Number(),\n\tcacheWrite: Type.Number(),\n};\nconst ModelCostTierSchema = Type.Object({\n\tinputTokensAbove: Type.Number(),\n\t...ModelCostRatesSchema,\n});\nconst ModelCostSchema = Type.Object({\n\t...ModelCostRatesSchema,\n\ttiers: Type.Optional(Type.Array(ModelCostTierSchema)),\n});\n\nconst ModelDefinitionSchema = Type.Object({\n\tid: Type.String({ minLength: 1 }),\n\tname: Type.Optional(Type.String({ minLength: 1 })),\n\tapi: Type.Optional(Type.String({ minLength: 1 })),\n\tbaseUrl: Type.Optional(Type.String({ minLength: 1 })),\n\treasoning: Type.Optional(Type.Boolean()),\n\tthinkingLevelMap: Type.Optional(ThinkingLevelMapSchema),\n\tinput: Type.Optional(Type.Array(Type.Union([Type.Literal(\"text\"), Type.Literal(\"image\")]))),\n\tcost: Type.Optional(ModelCostSchema),\n\tcontextWindow: Type.Optional(Type.Number()),\n\tmaxTokens: Type.Optional(Type.Number()),\n\theaders: Type.Optional(Type.Record(Type.String(), Type.String())),\n\tcompat: Type.Optional(ProviderCompatSchema),\n});\n\nconst ModelOverrideSchema = Type.Object({\n\tname: Type.Optional(Type.String({ minLength: 1 })),\n\treasoning: Type.Optional(Type.Boolean()),\n\tthinkingLevelMap: Type.Optional(ThinkingLevelMapSchema),\n\tinput: Type.Optional(Type.Array(Type.Union([Type.Literal(\"text\"), Type.Literal(\"image\")]))),\n\tcost: Type.Optional(\n\t\tType.Object({\n\t\t\tinput: Type.Optional(Type.Number()),\n\t\t\toutput: Type.Optional(Type.Number()),\n\t\t\tcacheRead: Type.Optional(Type.Number()),\n\t\t\tcacheWrite: Type.Optional(Type.Number()),\n\t\t\ttiers: Type.Optional(Type.Array(ModelCostTierSchema)),\n\t\t}),\n\t),\n\tcontextWindow: Type.Optional(Type.Number()),\n\tmaxTokens: Type.Optional(Type.Number()),\n\theaders: Type.Optional(Type.Record(Type.String(), Type.String())),\n\tcompat: Type.Optional(ProviderCompatSchema),\n});\n\nconst ProviderConfigSchema = Type.Object({\n\tname: Type.Optional(Type.String({ minLength: 1 })),\n\tbaseUrl: Type.Optional(Type.String({ minLength: 1 })),\n\tapiKey: Type.Optional(Type.String({ minLength: 1 })),\n\tapi: Type.Optional(Type.String({ minLength: 1 })),\n\t\theaders: Type.Optional(Type.Record(Type.String(), Type.String())),\n\tcompat: Type.Optional(ProviderCompatSchema),\n\tauthHeader: Type.Optional(Type.Boolean()),\n\tmodels: Type.Optional(Type.Array(ModelDefinitionSchema)),\n\tmodelOverrides: Type.Optional(Type.Record(Type.String(), ModelOverrideSchema)),\n});\n\nconst ModelsConfigSchema = Type.Object({\n\tproviders: Type.Record(Type.String(), ProviderConfigSchema),\n});\nconst validateModelsConfig = Compile(ModelsConfigSchema);\n\nexport type ModelsJsonModel = Static<typeof ModelDefinitionSchema>;\nexport type ModelsJsonModelOverride = Static<typeof ModelOverrideSchema>;\nexport type ModelsJsonProvider = Static<typeof ProviderConfigSchema>;\ntype LegacyModelsJson = Static<typeof ModelsConfigSchema>;\n*/\n/*\n\tp50: Type.Optional(Type.Number()),\n\tp75: Type.Optional(Type.Number()),\n\tp90: Type.Optional(Type.Number()),\n\tp99: Type.Optional(Type.Number()),\n});\n\nconst OpenRouterRoutingSchema = Type.Object({\n\tallow_fallbacks: Type.Optional(Type.Boolean()),\n\trequire_parameters: Type.Optional(Type.Boolean()),\n\tdata_collection: Type.Optional(Type.Union([Type.Literal(\"deny\"), Type.Literal(\"allow\")])),\n\tzdr: Type.Optional(Type.Boolean()),\n\tenforce_distillable_text: Type.Optional(Type.Boolean()),\n\torder: Type.Optional(Type.Array(Type.String())),\n\tonly: Type.Optional(Type.Array(Type.String())),\n\tignore: Type.Optional(Type.Array(Type.String())),\n\tquantizations: Type.Optional(Type.Array(Type.String())),\n\tsort: Type.Optional(\n\t\tType.Union([\n\t\t\tType.String(),\n\t\t\tType.Object({\n\t\t\t\tby: Type.Optional(Type.String()),\n\t\t\t\tpartition: Type.Optional(Type.Union([Type.String(), Type.Null()])),\n\t\t\t}),\n\t\t]),\n\t),\n\tmax_price: Type.Optional(\n\t\tType.Object({\n\t\t\tprompt: Type.Optional(Type.Union([Type.Number(), Type.String()])),\n\t\t\tcompletion: Type.Optional(Type.Union([Type.Number(), Type.String()])),\n\t\t\timage: Type.Optional(Type.Union([Type.Number(), Type.String()])),\n\t\t\taudio: Type.Optional(Type.Union([Type.Number(), Type.String()])),\n\t\t\trequest: Type.Optional(Type.Union([Type.Number(), Type.String()])),\n\t\t}),\n\t),\n\tpreferred_min_throughput: Type.Optional(Type.Union([Type.Number(), PercentileCutoffsSchema])),\n\tpreferred_max_latency: Type.Optional(Type.Union([Type.Number(), PercentileCutoffsSchema])),\n});\n\nconst VercelGatewayRoutingSchema = Type.Object({\n\tonly: Type.Optional(Type.Array(Type.String())),\n\torder: Type.Optional(Type.Array(Type.String())),\n});\n\nconst ThinkingLevelMapValueSchema = Type.Union([Type.String(), Type.Null()]);\nconst ThinkingLevelMapSchema = Type.Object({\n\toff: Type.Optional(ThinkingLevelMapValueSchema),\n\tminimal: Type.Optional(ThinkingLevelMapValueSchema),\n\tlow: Type.Optional(ThinkingLevelMapValueSchema),\n\tmedium: Type.Optional(ThinkingLevelMapValueSchema),\n\thigh: Type.Optional(ThinkingLevelMapValueSchema),\n\txhigh: Type.Optional(ThinkingLevelMapValueSchema),\n\tmax: Type.Optional(ThinkingLevelMapValueSchema),\n});\n\nconst ChatTemplateKwargScalarSchema = Type.Union([Type.String(), Type.Number(), Type.Boolean(), Type.Null()]);\nconst ChatTemplateKwargVariableSchema = Type.Object({\n\t$var: Type.Union([Type.Literal(\"thinking.enabled\"), Type.Literal(\"thinking.effort\")]),\n\tomitWhenOff: Type.Optional(Type.Boolean()),\n});\nconst ChatTemplateKwargSchema = Type.Union([ChatTemplateKwargScalarSchema, ChatTemplateKwargVariableSchema]);\n\nconst OpenAICompletionsCompatSchema = Type.Object({\n\tsupportsStore: Type.Optional(Type.Boolean()),\n\tsupportsDeveloperRole: Type.Optional(Type.Boolean()),\n\tsupportsReasoningEffort: Type.Optional(Type.Boolean()),\n\tsupportsUsageInStreaming: Type.Optional(Type.Boolean()),\n\tsupportsFinishReason: Type.Optional(Type.Boolean()),\n\tsupportsFinishReason: Type.Optional(Type.Boolean()),\n\tmaxTokensField: Type.Optional(Type.Union([Type.Literal(\"max_completion_tokens\"), Type.Literal(\"max_tokens\")])),\n\trequiresToolResultName: Type.Optional(Type.Boolean()),\n\trequiresAssistantAfterToolResult: Type.Optional(Type.Boolean()),\n\trequiresThinkingAsText: Type.Optional(Type.Boolean()),\n\trequiresReasoningContentOnAssistantMessages: Type.Optional(Type.Boolean()),\n\tthinkingFormat: Type.Optional(\n\t\tType.Union([\n\t\t\tType.Literal(\"openai\"),\n\t\t\tType.Literal(\"openrouter\"),\n\t\t\tType.Literal(\"together\"),\n\t\t\tType.Literal(\"baseten\"),\n\t\t\tType.Literal(\"deepseek\"),\n\t\t\tType.Literal(\"zai\"),\n\t\t\tType.Literal(\"qwen\"),\n\t\t\tType.Literal(\"chat-template\"),\n\t\t\tType.Literal(\"qwen-chat-template\"),\n\t\t\tType.Literal(\"string-thinking\"),\n\t\t\tType.Literal(\"ant-ling\"),\n\t\t]),\n\t),\n\tchatTemplateKwargs: Type.Optional(Type.Record(Type.String(), ChatTemplateKwargSchema)),\n\tchatTemplateArgs: Type.Optional(Type.Record(Type.String(), ChatTemplateKwargSchema)),\n\tcacheControlFormat: Type.Optional(Type.Literal(\"anthropic\")),\n\topenRouterRouting: Type.Optional(OpenRouterRoutingSchema),\n\tvercelGatewayRouting: Type.Optional(VercelGatewayRoutingSchema),\n\tsupportsOpenAIGrammarTools: Type.Optional(Type.Boolean()),\n\tsupportsStrictMode: Type.Optional(Type.Boolean()),\n\tsendSessionAffinityHeaders: Type.Optional(Type.Boolean()),\n\tdeferredToolsMode: Type.Optional(Type.Literal(\"kimi\")),\n\tsessionAffinityFormat: Type.Optional(\n\t\tType.Union([Type.Literal(\"openai\"), Type.Literal(\"openai-nosession\"), Type.Literal(\"openrouter\")]),\n\t),\n\tsupportsLongCacheRetention: Type.Optional(Type.Boolean()),\n\tvllmPriority: Type.Optional(Type.Number()),\n});\n\nconst OpenAIResponsesCompatSchema = Type.Object({\n\tsupportsDeveloperRole: Type.Optional(Type.Boolean()),\n\tsessionAffinityFormat: Type.Optional(\n\t\tType.Union([Type.Literal(\"openai\"), Type.Literal(\"openai-nosession\"), Type.Literal(\"openrouter\")]),\n\t),\n\tsupportsLongCacheRetention: Type.Optional(Type.Boolean()),\n\tsupportsStrictMode: Type.Optional(Type.Boolean()),\n\tsupportsOpenAIGrammarTools: Type.Optional(Type.Boolean()),\n\tsupportsAdditionalTools: Type.Optional(Type.Boolean()),\n\tsupportsToolSearch: Type.Optional(Type.Boolean()),\n\tsupportsMaxOutputTokens: Type.Optional(Type.Boolean()),\n});\n\nconst AnthropicMessagesCompatSchema = Type.Object({\n\tsupportsEagerToolInputStreaming: Type.Optional(Type.Boolean()),\n\tsupportsLongCacheRetention: Type.Optional(Type.Boolean()),\n\tsendSessionAffinityHeaders: Type.Optional(Type.Boolean()),\n\tsupportsCacheControlOnTools: Type.Optional(Type.Boolean()),\n\tsupportsTemperature: Type.Optional(Type.Boolean()),\n\tforceAdaptiveThinking: Type.Optional(Type.Boolean()),\n\tallowEmptySignature: Type.Optional(Type.Boolean()),\n\tsupportsStrictTools: Type.Optional(Type.Boolean()),\n\tsupportsMidConvoEffort: Type.Optional(Type.Boolean()),\n\tsupportsToolReferences: Type.Optional(Type.Boolean()),\n});\n\nconst ProviderCompatSchema = Type.Union([\n\tOpenAICompletionsCompatSchema,\n\tOpenAIResponsesCompatSchema,\n\tAnthropicMessagesCompatSchema,\n]);\n\nconst ModelCostRatesSchema = {\n\tinput: Type.Number(),\n\toutput: Type.Number(),\n\tcacheRead: Type.Number(),\n\tcacheWrite: Type.Number(),\n};\nconst ModelCostTierSchema = Type.Object({\n\tinputTokensAbove: Type.Number(),\n\t...ModelCostRatesSchema,\n});\nconst ModelCostSchema = Type.Object({\n\t...ModelCostRatesSchema,\n\ttiers: Type.Optional(Type.Array(ModelCostTierSchema)),\n});\n\nconst ModelDefinitionSchema = Type.Object({\n\tid: Type.String({ minLength: 1 }),\n\tname: Type.Optional(Type.String({ minLength: 1 })),\n\tapi: Type.Optional(Type.String({ minLength: 1 })),\n\tbaseUrl: Type.Optional(Type.String({ minLength: 1 })),\n\treasoning: Type.Optional(Type.Boolean()),\n\tthinkingLevelMap: Type.Optional(ThinkingLevelMapSchema),\n\tinput: Type.Optional(Type.Array(Type.Union([Type.Literal(\"text\"), Type.Literal(\"image\")]))),\n\tcost: Type.Optional(ModelCostSchema),\n\tcontextWindow: Type.Optional(Type.Number()),\n\tmaxTokens: Type.Optional(Type.Number()),\n\tsamplingParams: Type.Optional(Type.Record(Type.String(), Type.Unknown())),\n\theaders: Type.Optional(Type.Record(Type.String(), Type.String())),\n\tcompat: Type.Optional(ProviderCompatSchema),\n});\n\nconst ModelOverrideSchema = Type.Object({\n\tname: Type.Optional(Type.String({ minLength: 1 })),\n\treasoning: Type.Optional(Type.Boolean()),\n\tthinkingLevelMap: Type.Optional(ThinkingLevelMapSchema),\n\tinput: Type.Optional(Type.Array(Type.Union([Type.Literal(\"text\"), Type.Literal(\"image\")]))),\n\tcost: Type.Optional(\n\t\tType.Object({\n\t\t\tinput: Type.Optional(Type.Number()),\n\t\t\toutput: Type.Optional(Type.Number()),\n\t\t\tcacheRead: Type.Optional(Type.Number()),\n\t\t\tcacheWrite: Type.Optional(Type.Number()),\n\t\t\ttiers: Type.Optional(Type.Array(ModelCostTierSchema)),\n\t\t}),\n\t),\n\tcontextWindow: Type.Optional(Type.Number()),\n\tmaxTokens: Type.Optional(Type.Number()),\n\tsamplingParams: Type.Optional(Type.Record(Type.String(), Type.Unknown())),\n\theaders: Type.Optional(Type.Record(Type.String(), Type.String())),\n\tcompat: Type.Optional(ProviderCompatSchema),\n});\n\nconst ProviderConfigSchema = Type.Object({\n\tname: Type.Optional(Type.String({ minLength: 1 })),\n\tbaseUrl: Type.Optional(Type.String({ minLength: 1 })),\n\tapiKey: Type.Optional(Type.String({ minLength: 1 })),\n\tapi: Type.Optional(Type.String({ minLength: 1 })),\n\t\theaders: Type.Optional(Type.Record(Type.String(), Type.String())),\n\tcompat: Type.Optional(ProviderCompatSchema),\n\tauthHeader: Type.Optional(Type.Boolean()),\n\tmodels: Type.Optional(Type.Array(ModelDefinitionSchema)),\n\tmodelOverrides: Type.Optional(Type.Record(Type.String(), ModelOverrideSchema)),\n});\n\nconst ModelsConfigSchema = Type.Object({\n\tproviders: Type.Record(Type.String(), ProviderConfigSchema),\n});\nconst validateModelsConfig = Compile(ModelsConfigSchema);\n\nexport type ModelsJsonModel = Static<typeof ModelDefinitionSchema>;\nexport type ModelsJsonModelOverride = Static<typeof ModelOverrideSchema>;\nexport type ModelsJsonProvider = Static<typeof ProviderConfigSchema>;\ntype UpstreamModelsJson = Static<typeof ModelsConfigSchema>;\n*/\n\nfunction formatValidationPath(error: TLocalizedValidationError): string {\n\tif (error.keyword === \"required\") {\n\t\tconst requiredProperties = (error.params as { requiredProperties?: string[] }).requiredProperties;\n\t\tconst requiredProperty = requiredProperties?.[0];\n\t\tif (requiredProperty) {\n\t\t\tconst basePath = error.instancePath.replace(/^\\//, \"\").replace(/\\//g, \".\");\n\t\t\treturn basePath ? `${basePath}.${requiredProperty}` : requiredProperty;\n\t\t}\n\t}\n\tconst path = error.instancePath.replace(/^\\//, \"\").replace(/\\//g, \".\");\n\treturn path || \"root\";\n}\n\nfunction deepFreeze<T>(value: T): T {\n\tif (typeof value !== \"object\" || value === null || Object.isFrozen(value)) return value;\n\tfor (const child of Object.values(value)) deepFreeze(child);\n\treturn Object.freeze(value);\n}\n\n/** One immutable load of models.json. */\nexport class ModelConfig {\n\tprivate readonly providers: ReadonlyMap<string, ModelsJsonProvider>;\n\tprivate readonly disabledProviders: ReadonlySet<string>;\n\tprivate readonly error: string | undefined;\n\tprivate readonly warnings: readonly string[];\n\n\tprivate constructor(\n\t\tproviders: ReadonlyMap<string, ModelsJsonProvider>,\n\t\tdisabledProviders: ReadonlySet<string> = new Set(),\n\t\terror?: string,\n\t\twarnings: readonly string[] = [],\n\t) {\n\t\tthis.providers = providers;\n\t\tthis.disabledProviders = disabledProviders;\n\t\tthis.error = error;\n\t\tthis.warnings = warnings;\n\t}\n\n\tprivate static parse(content: string, path: string): ModelConfig {\n\t\tlet parsed: unknown;\n\t\ttry {\n\t\t\tparsed = JSON.parse(stripJsonComments(stripBom(content)));\n\t\t} catch (error) {\n\t\t\treturn new ModelConfig(\n\t\t\t\tnew Map(),\n\t\t\t\tnew Set(),\n\t\t\t\t`Failed to parse models.json: ${error instanceof Error ? error.message : error}\\n\\nFile: ${path}`,\n\t\t\t);\n\t\t}\n\n\t\t// The shared fork schema intentionally carries compatibility fields that the\n\t\t// upstream validator has not learned yet. Validate a schema-compatible clone,\n\t\t// then retain the original values in the immutable runtime snapshot.\n\t\tconst validationInput = structuredClone(parsed);\n\t\tif (typeof validationInput === \"object\" && validationInput !== null && \"providers\" in validationInput) {\n\t\t\tconst providers = (validationInput as { providers?: Record<string, unknown> }).providers;\n\t\t\tfor (const provider of Object.values(providers ?? {})) {\n\t\t\t\tif (typeof provider !== \"object\" || provider === null) continue;\n\t\t\t\tconst record = provider as {\n\t\t\t\t\tcompat?: Record<string, unknown>;\n\t\t\t\t\tmodels?: Array<{ compat?: Record<string, unknown>; samplingParams?: Record<string, unknown> }>;\n\t\t\t\t\tmodelOverrides?: Record<\n\t\t\t\t\t\tstring,\n\t\t\t\t\t\t{ compat?: Record<string, unknown>; samplingParams?: Record<string, unknown> }\n\t\t\t\t\t>;\n\t\t\t\t};\n\t\t\t\tfor (const model of [...(record.models ?? []), ...Object.values(record.modelOverrides ?? {})]) {\n\t\t\t\t\tdelete model.samplingParams;\n\t\t\t\t}\n\t\t\t\tconst compatEntries = [\n\t\t\t\t\trecord.compat,\n\t\t\t\t\t...(record.models ?? []).map((model) => model.compat),\n\t\t\t\t\t...Object.values(record.modelOverrides ?? {}).map((model) => model.compat),\n\t\t\t\t];\n\t\t\t\tfor (const compat of compatEntries) {\n\t\t\t\t\tif (!compat) continue;\n\t\t\t\t\tif (compat.thinkingFormat === \"baseten\") compat.thinkingFormat = \"together\";\n\t\t\t\t\tdelete compat.chatTemplateArgs;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (!validateModelsConfig.Check(validationInput)) {\n\t\t\tconst errors =\n\t\t\t\tvalidateModelsConfig\n\t\t\t\t\t.Errors(validationInput)\n\t\t\t\t\t.map((error) => ` - ${formatValidationPath(error)}: ${error.message}`)\n\t\t\t\t\t.join(\"\\n\") || \"Unknown schema error\";\n\t\t\treturn new ModelConfig(new Map(), new Set(), `Invalid models.json schema:\\n${errors}\\n\\nFile: ${path}`);\n\t\t}\n\n\t\tconst config = parsed as ModelsJson;\n\t\tconst providers = new Map<string, ModelsJsonProvider>();\n\t\t// Read boundary (senpi#1989): a models.json written before the rename keys\n\t\t// its overlay by the legacy provider id. Normalize the key so the overlay\n\t\t// still attaches, and tell the user ONCE which ids moved. The file itself is\n\t\t// never rewritten.\n\t\tconst renamed: string[] = [];\n\t\tfor (const [providerId, provider] of Object.entries(config.providers)) {\n\t\t\tconst canonical = normalizeProviderId(providerId);\n\t\t\tif (canonical !== providerId) renamed.push(`${providerId} -> ${canonical}`);\n\t\t\t// An explicit canonical entry wins over a legacy one that normalizes onto it.\n\t\t\tif (canonical !== providerId && providers.has(canonical)) continue;\n\t\t\tproviders.set(canonical, deepFreeze(structuredClone(provider)));\n\t\t}\n\t\tconst disabled = new Set((config.disabledProviders ?? []).map((id) => normalizeProviderId(id)));\n\t\tconst warnings =\n\t\t\trenamed.length > 0\n\t\t\t\t? [\n\t\t\t\t\t\t`models.json uses renamed provider ids (${renamed.join(\", \")}). They still work, but update the file to the new ids.`,\n\t\t\t\t\t]\n\t\t\t\t: [];\n\t\treturn new ModelConfig(providers, disabled, undefined, warnings);\n\t}\n\n\tstatic async load(modelsJsonPath: string | undefined): Promise<ModelConfig> {\n\t\tif (!modelsJsonPath) return new ModelConfig(new Map());\n\t\tconst path = normalizePath(modelsJsonPath);\n\t\tlet content: string;\n\t\ttry {\n\t\t\tcontent = await readFile(path, \"utf-8\");\n\t\t} catch (error) {\n\t\t\tif ((error as NodeJS.ErrnoException).code === \"ENOENT\") return new ModelConfig(new Map());\n\t\t\treturn new ModelConfig(\n\t\t\t\tnew Map(),\n\t\t\t\tnew Set(),\n\t\t\t\t`Failed to load models.json: ${error instanceof Error ? error.message : error}\\n\\nFile: ${path}`,\n\t\t\t);\n\t\t}\n\t\treturn ModelConfig.parse(content, path);\n\t}\n\n\tstatic loadSync(modelsJsonPath: string | undefined): ModelConfig {\n\t\tif (!modelsJsonPath) return new ModelConfig(new Map());\n\t\tconst path = normalizePath(modelsJsonPath);\n\t\ttry {\n\t\t\treturn ModelConfig.parse(readFileSync(path, \"utf-8\"), path);\n\t\t} catch (error) {\n\t\t\tif ((error as NodeJS.ErrnoException).code === \"ENOENT\") return new ModelConfig(new Map());\n\t\t\treturn new ModelConfig(\n\t\t\t\tnew Map(),\n\t\t\t\tnew Set(),\n\t\t\t\t`Failed to load models.json: ${error instanceof Error ? error.message : error}\\n\\nFile: ${path}`,\n\t\t\t);\n\t\t}\n\t}\n\n\tgetProvider(providerId: string): ModelsJsonProvider | undefined {\n\t\treturn this.providers.get(providerId);\n\t}\n\n\tgetProviderIds(): readonly string[] {\n\t\treturn [...this.providers.keys()];\n\t}\n\n\tisProviderDisabled(providerId: string): boolean {\n\t\treturn this.disabledProviders.has(providerId) || this.providers.get(providerId)?.disabled === true;\n\t}\n\n\tgetError(): string | undefined {\n\t\treturn this.error;\n\t}\n\n\t/** Non-fatal notices raised while reading models.json (e.g. renamed provider ids). */\n\tgetWarnings(): readonly string[] {\n\t\treturn this.warnings;\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"model-config.js","sourceRoot":"","sources":["../../src/core/model-config.ts"],"names":[],"mappings":"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","sourcesContent":["/** Immutable, credential-blind models.json snapshot. */\n\nimport { readFileSync } from \"node:fs\";\nimport { readFile } from \"node:fs/promises\";\nimport { normalizeProviderId } from \"@earendil-works/pi-ai\";\nimport type { TLocalizedValidationError } from \"typebox/error\";\nimport { stripJsonComments } from \"../utils/json.ts\";\nimport { normalizePath } from \"../utils/paths.ts\";\nimport { stripBom } from \"../utils/text.ts\";\nimport {\n\ttype ModelsJson,\n\ttype ModelsJsonProvider,\n\ttype ModelsJsonModel as SchemaModelsJsonModel,\n\ttype ModelsJsonModelOverride as SchemaModelsJsonModelOverride,\n\tvalidateModelsConfig,\n} from \"./model-config-schema.ts\";\nimport { migrateModelsJsonProviderIds } from \"./models-json-migration.ts\";\n\nexport type ModelsJsonModel = SchemaModelsJsonModel & { samplingParams?: Record<string, unknown> };\nexport type ModelsJsonModelOverride = SchemaModelsJsonModelOverride & { samplingParams?: Record<string, unknown> };\nexport type { ModelsJsonProvider };\n\n/*\n\tp50: Type.Optional(Type.Number()),\n\tp75: Type.Optional(Type.Number()),\n\tp90: Type.Optional(Type.Number()),\n\tp99: Type.Optional(Type.Number()),\n});\n\nconst OpenRouterRoutingSchema = Type.Object({\n\tallow_fallbacks: Type.Optional(Type.Boolean()),\n\trequire_parameters: Type.Optional(Type.Boolean()),\n\tdata_collection: Type.Optional(Type.Union([Type.Literal(\"deny\"), Type.Literal(\"allow\")])),\n\tzdr: Type.Optional(Type.Boolean()),\n\tenforce_distillable_text: Type.Optional(Type.Boolean()),\n\torder: Type.Optional(Type.Array(Type.String())),\n\tonly: Type.Optional(Type.Array(Type.String())),\n\tignore: Type.Optional(Type.Array(Type.String())),\n\tquantizations: Type.Optional(Type.Array(Type.String())),\n\tsort: Type.Optional(\n\t\tType.Union([\n\t\t\tType.String(),\n\t\t\tType.Object({\n\t\t\t\tby: Type.Optional(Type.String()),\n\t\t\t\tpartition: Type.Optional(Type.Union([Type.String(), Type.Null()])),\n\t\t\t}),\n\t\t]),\n\t),\n\tmax_price: Type.Optional(\n\t\tType.Object({\n\t\t\tprompt: Type.Optional(Type.Union([Type.Number(), Type.String()])),\n\t\t\tcompletion: Type.Optional(Type.Union([Type.Number(), Type.String()])),\n\t\t\timage: Type.Optional(Type.Union([Type.Number(), Type.String()])),\n\t\t\taudio: Type.Optional(Type.Union([Type.Number(), Type.String()])),\n\t\t\trequest: Type.Optional(Type.Union([Type.Number(), Type.String()])),\n\t\t}),\n\t),\n\tpreferred_min_throughput: Type.Optional(Type.Union([Type.Number(), PercentileCutoffsSchema])),\n\tpreferred_max_latency: Type.Optional(Type.Union([Type.Number(), PercentileCutoffsSchema])),\n});\n\nconst VercelGatewayRoutingSchema = Type.Object({\n\tonly: Type.Optional(Type.Array(Type.String())),\n\torder: Type.Optional(Type.Array(Type.String())),\n});\n\nconst ThinkingLevelMapValueSchema = Type.Union([Type.String(), Type.Null()]);\nconst ThinkingLevelMapSchema = Type.Object({\n\toff: Type.Optional(ThinkingLevelMapValueSchema),\n\tminimal: Type.Optional(ThinkingLevelMapValueSchema),\n\tlow: Type.Optional(ThinkingLevelMapValueSchema),\n\tmedium: Type.Optional(ThinkingLevelMapValueSchema),\n\thigh: Type.Optional(ThinkingLevelMapValueSchema),\n\txhigh: Type.Optional(ThinkingLevelMapValueSchema),\n\tmax: Type.Optional(ThinkingLevelMapValueSchema),\n});\n\nconst ChatTemplateKwargScalarSchema = Type.Union([Type.String(), Type.Number(), Type.Boolean(), Type.Null()]);\nconst ChatTemplateKwargVariableSchema = Type.Object({\n\t$var: Type.Union([Type.Literal(\"thinking.enabled\"), Type.Literal(\"thinking.effort\")]),\n\tomitWhenOff: Type.Optional(Type.Boolean()),\n});\nconst ChatTemplateKwargSchema = Type.Union([ChatTemplateKwargScalarSchema, ChatTemplateKwargVariableSchema]);\n\nconst OpenAICompletionsCompatSchema = Type.Object({\n\tsupportsStore: Type.Optional(Type.Boolean()),\n\tsupportsDeveloperRole: Type.Optional(Type.Boolean()),\n\tsupportsReasoningEffort: Type.Optional(Type.Boolean()),\n\tsupportsUsageInStreaming: Type.Optional(Type.Boolean()),\n\tsupportsFinishReason: Type.Optional(Type.Boolean()),\n\tmaxTokensField: Type.Optional(Type.Union([Type.Literal(\"max_completion_tokens\"), Type.Literal(\"max_tokens\")])),\n\trequiresToolResultName: Type.Optional(Type.Boolean()),\n\trequiresAssistantAfterToolResult: Type.Optional(Type.Boolean()),\n\trequiresThinkingAsText: Type.Optional(Type.Boolean()),\n\trequiresReasoningContentOnAssistantMessages: Type.Optional(Type.Boolean()),\n\tthinkingFormat: Type.Optional(\n\t\tType.Union([\n\t\t\tType.Literal(\"openai\"),\n\t\t\tType.Literal(\"openrouter\"),\n\t\t\tType.Literal(\"together\"),\n\t\t\tType.Literal(\"deepseek\"),\n\t\t\tType.Literal(\"zai\"),\n\t\t\tType.Literal(\"qwen\"),\n\t\t\tType.Literal(\"chat-template\"),\n\t\t\tType.Literal(\"qwen-chat-template\"),\n\t\t\tType.Literal(\"string-thinking\"),\n\t\t\tType.Literal(\"ant-ling\"),\n\t\t]),\n\t),\n\tchatTemplateKwargs: Type.Optional(Type.Record(Type.String(), ChatTemplateKwargSchema)),\n\tcacheControlFormat: Type.Optional(Type.Literal(\"anthropic\")),\n\topenRouterRouting: Type.Optional(OpenRouterRoutingSchema),\n\tvercelGatewayRouting: Type.Optional(VercelGatewayRoutingSchema),\n\tsupportsOpenAIGrammarTools: Type.Optional(Type.Boolean()),\n\tsupportsStrictMode: Type.Optional(Type.Boolean()),\n\tsendSessionAffinityHeaders: Type.Optional(Type.Boolean()),\n\tdeferredToolsMode: Type.Optional(Type.Literal(\"kimi\")),\n\tsessionAffinityFormat: Type.Optional(\n\t\tType.Union([Type.Literal(\"openai\"), Type.Literal(\"openai-nosession\"), Type.Literal(\"openrouter\")]),\n\t),\n\tsupportsLongCacheRetention: Type.Optional(Type.Boolean()),\n});\n\nconst OpenAIResponsesCompatSchema = Type.Object({\n\tsupportsDeveloperRole: Type.Optional(Type.Boolean()),\n\tsessionAffinityFormat: Type.Optional(\n\t\tType.Union([Type.Literal(\"openai\"), Type.Literal(\"openai-nosession\"), Type.Literal(\"openrouter\")]),\n\t),\n\tsupportsLongCacheRetention: Type.Optional(Type.Boolean()),\n\tsupportsStrictMode: Type.Optional(Type.Boolean()),\n\tsupportsOpenAIGrammarTools: Type.Optional(Type.Boolean()),\n\tsupportsToolSearch: Type.Optional(Type.Boolean()),\n});\n\nconst AnthropicMessagesCompatSchema = Type.Object({\n\tsupportsEagerToolInputStreaming: Type.Optional(Type.Boolean()),\n\tsupportsLongCacheRetention: Type.Optional(Type.Boolean()),\n\tsendSessionAffinityHeaders: Type.Optional(Type.Boolean()),\n\tsupportsCacheControlOnTools: Type.Optional(Type.Boolean()),\n\tsupportsTemperature: Type.Optional(Type.Boolean()),\n\tforceAdaptiveThinking: Type.Optional(Type.Boolean()),\n\tallowEmptySignature: Type.Optional(Type.Boolean()),\n\tsupportsStrictTools: Type.Optional(Type.Boolean()),\n\tsupportsToolReferences: Type.Optional(Type.Boolean()),\n});\n\nconst ProviderCompatSchema = Type.Union([\n\tOpenAICompletionsCompatSchema,\n\tOpenAIResponsesCompatSchema,\n\tAnthropicMessagesCompatSchema,\n]);\n\nconst ModelCostRatesSchema = {\n\tinput: Type.Number(),\n\toutput: Type.Number(),\n\tcacheRead: Type.Number(),\n\tcacheWrite: Type.Number(),\n};\nconst ModelCostTierSchema = Type.Object({\n\tinputTokensAbove: Type.Number(),\n\t...ModelCostRatesSchema,\n});\nconst ModelCostSchema = Type.Object({\n\t...ModelCostRatesSchema,\n\ttiers: Type.Optional(Type.Array(ModelCostTierSchema)),\n});\n\nconst ModelDefinitionSchema = Type.Object({\n\tid: Type.String({ minLength: 1 }),\n\tname: Type.Optional(Type.String({ minLength: 1 })),\n\tapi: Type.Optional(Type.String({ minLength: 1 })),\n\tbaseUrl: Type.Optional(Type.String({ minLength: 1 })),\n\treasoning: Type.Optional(Type.Boolean()),\n\tthinkingLevelMap: Type.Optional(ThinkingLevelMapSchema),\n\tinput: Type.Optional(Type.Array(Type.Union([Type.Literal(\"text\"), Type.Literal(\"image\")]))),\n\tcost: Type.Optional(ModelCostSchema),\n\tcontextWindow: Type.Optional(Type.Number()),\n\tmaxTokens: Type.Optional(Type.Number()),\n\theaders: Type.Optional(Type.Record(Type.String(), Type.String())),\n\tcompat: Type.Optional(ProviderCompatSchema),\n});\n\nconst ModelOverrideSchema = Type.Object({\n\tname: Type.Optional(Type.String({ minLength: 1 })),\n\treasoning: Type.Optional(Type.Boolean()),\n\tthinkingLevelMap: Type.Optional(ThinkingLevelMapSchema),\n\tinput: Type.Optional(Type.Array(Type.Union([Type.Literal(\"text\"), Type.Literal(\"image\")]))),\n\tcost: Type.Optional(\n\t\tType.Object({\n\t\t\tinput: Type.Optional(Type.Number()),\n\t\t\toutput: Type.Optional(Type.Number()),\n\t\t\tcacheRead: Type.Optional(Type.Number()),\n\t\t\tcacheWrite: Type.Optional(Type.Number()),\n\t\t\ttiers: Type.Optional(Type.Array(ModelCostTierSchema)),\n\t\t}),\n\t),\n\tcontextWindow: Type.Optional(Type.Number()),\n\tmaxTokens: Type.Optional(Type.Number()),\n\theaders: Type.Optional(Type.Record(Type.String(), Type.String())),\n\tcompat: Type.Optional(ProviderCompatSchema),\n});\n\nconst ProviderConfigSchema = Type.Object({\n\tname: Type.Optional(Type.String({ minLength: 1 })),\n\tbaseUrl: Type.Optional(Type.String({ minLength: 1 })),\n\tapiKey: Type.Optional(Type.String({ minLength: 1 })),\n\tapi: Type.Optional(Type.String({ minLength: 1 })),\n\t\theaders: Type.Optional(Type.Record(Type.String(), Type.String())),\n\tcompat: Type.Optional(ProviderCompatSchema),\n\tauthHeader: Type.Optional(Type.Boolean()),\n\tmodels: Type.Optional(Type.Array(ModelDefinitionSchema)),\n\tmodelOverrides: Type.Optional(Type.Record(Type.String(), ModelOverrideSchema)),\n});\n\nconst ModelsConfigSchema = Type.Object({\n\tproviders: Type.Record(Type.String(), ProviderConfigSchema),\n});\nconst validateModelsConfig = Compile(ModelsConfigSchema);\n\nexport type ModelsJsonModel = Static<typeof ModelDefinitionSchema>;\nexport type ModelsJsonModelOverride = Static<typeof ModelOverrideSchema>;\nexport type ModelsJsonProvider = Static<typeof ProviderConfigSchema>;\ntype LegacyModelsJson = Static<typeof ModelsConfigSchema>;\n*/\n/*\n\tp50: Type.Optional(Type.Number()),\n\tp75: Type.Optional(Type.Number()),\n\tp90: Type.Optional(Type.Number()),\n\tp99: Type.Optional(Type.Number()),\n});\n\nconst OpenRouterRoutingSchema = Type.Object({\n\tallow_fallbacks: Type.Optional(Type.Boolean()),\n\trequire_parameters: Type.Optional(Type.Boolean()),\n\tdata_collection: Type.Optional(Type.Union([Type.Literal(\"deny\"), Type.Literal(\"allow\")])),\n\tzdr: Type.Optional(Type.Boolean()),\n\tenforce_distillable_text: Type.Optional(Type.Boolean()),\n\torder: Type.Optional(Type.Array(Type.String())),\n\tonly: Type.Optional(Type.Array(Type.String())),\n\tignore: Type.Optional(Type.Array(Type.String())),\n\tquantizations: Type.Optional(Type.Array(Type.String())),\n\tsort: Type.Optional(\n\t\tType.Union([\n\t\t\tType.String(),\n\t\t\tType.Object({\n\t\t\t\tby: Type.Optional(Type.String()),\n\t\t\t\tpartition: Type.Optional(Type.Union([Type.String(), Type.Null()])),\n\t\t\t}),\n\t\t]),\n\t),\n\tmax_price: Type.Optional(\n\t\tType.Object({\n\t\t\tprompt: Type.Optional(Type.Union([Type.Number(), Type.String()])),\n\t\t\tcompletion: Type.Optional(Type.Union([Type.Number(), Type.String()])),\n\t\t\timage: Type.Optional(Type.Union([Type.Number(), Type.String()])),\n\t\t\taudio: Type.Optional(Type.Union([Type.Number(), Type.String()])),\n\t\t\trequest: Type.Optional(Type.Union([Type.Number(), Type.String()])),\n\t\t}),\n\t),\n\tpreferred_min_throughput: Type.Optional(Type.Union([Type.Number(), PercentileCutoffsSchema])),\n\tpreferred_max_latency: Type.Optional(Type.Union([Type.Number(), PercentileCutoffsSchema])),\n});\n\nconst VercelGatewayRoutingSchema = Type.Object({\n\tonly: Type.Optional(Type.Array(Type.String())),\n\torder: Type.Optional(Type.Array(Type.String())),\n});\n\nconst ThinkingLevelMapValueSchema = Type.Union([Type.String(), Type.Null()]);\nconst ThinkingLevelMapSchema = Type.Object({\n\toff: Type.Optional(ThinkingLevelMapValueSchema),\n\tminimal: Type.Optional(ThinkingLevelMapValueSchema),\n\tlow: Type.Optional(ThinkingLevelMapValueSchema),\n\tmedium: Type.Optional(ThinkingLevelMapValueSchema),\n\thigh: Type.Optional(ThinkingLevelMapValueSchema),\n\txhigh: Type.Optional(ThinkingLevelMapValueSchema),\n\tmax: Type.Optional(ThinkingLevelMapValueSchema),\n});\n\nconst ChatTemplateKwargScalarSchema = Type.Union([Type.String(), Type.Number(), Type.Boolean(), Type.Null()]);\nconst ChatTemplateKwargVariableSchema = Type.Object({\n\t$var: Type.Union([Type.Literal(\"thinking.enabled\"), Type.Literal(\"thinking.effort\")]),\n\tomitWhenOff: Type.Optional(Type.Boolean()),\n});\nconst ChatTemplateKwargSchema = Type.Union([ChatTemplateKwargScalarSchema, ChatTemplateKwargVariableSchema]);\n\nconst OpenAICompletionsCompatSchema = Type.Object({\n\tsupportsStore: Type.Optional(Type.Boolean()),\n\tsupportsDeveloperRole: Type.Optional(Type.Boolean()),\n\tsupportsReasoningEffort: Type.Optional(Type.Boolean()),\n\tsupportsUsageInStreaming: Type.Optional(Type.Boolean()),\n\tsupportsFinishReason: Type.Optional(Type.Boolean()),\n\tsupportsFinishReason: Type.Optional(Type.Boolean()),\n\tmaxTokensField: Type.Optional(Type.Union([Type.Literal(\"max_completion_tokens\"), Type.Literal(\"max_tokens\")])),\n\trequiresToolResultName: Type.Optional(Type.Boolean()),\n\trequiresAssistantAfterToolResult: Type.Optional(Type.Boolean()),\n\trequiresThinkingAsText: Type.Optional(Type.Boolean()),\n\trequiresReasoningContentOnAssistantMessages: Type.Optional(Type.Boolean()),\n\tthinkingFormat: Type.Optional(\n\t\tType.Union([\n\t\t\tType.Literal(\"openai\"),\n\t\t\tType.Literal(\"openrouter\"),\n\t\t\tType.Literal(\"together\"),\n\t\t\tType.Literal(\"baseten\"),\n\t\t\tType.Literal(\"deepseek\"),\n\t\t\tType.Literal(\"zai\"),\n\t\t\tType.Literal(\"qwen\"),\n\t\t\tType.Literal(\"chat-template\"),\n\t\t\tType.Literal(\"qwen-chat-template\"),\n\t\t\tType.Literal(\"string-thinking\"),\n\t\t\tType.Literal(\"ant-ling\"),\n\t\t]),\n\t),\n\tchatTemplateKwargs: Type.Optional(Type.Record(Type.String(), ChatTemplateKwargSchema)),\n\tchatTemplateArgs: Type.Optional(Type.Record(Type.String(), ChatTemplateKwargSchema)),\n\tcacheControlFormat: Type.Optional(Type.Literal(\"anthropic\")),\n\topenRouterRouting: Type.Optional(OpenRouterRoutingSchema),\n\tvercelGatewayRouting: Type.Optional(VercelGatewayRoutingSchema),\n\tsupportsOpenAIGrammarTools: Type.Optional(Type.Boolean()),\n\tsupportsStrictMode: Type.Optional(Type.Boolean()),\n\tsendSessionAffinityHeaders: Type.Optional(Type.Boolean()),\n\tdeferredToolsMode: Type.Optional(Type.Literal(\"kimi\")),\n\tsessionAffinityFormat: Type.Optional(\n\t\tType.Union([Type.Literal(\"openai\"), Type.Literal(\"openai-nosession\"), Type.Literal(\"openrouter\")]),\n\t),\n\tsupportsLongCacheRetention: Type.Optional(Type.Boolean()),\n\tvllmPriority: Type.Optional(Type.Number()),\n});\n\nconst OpenAIResponsesCompatSchema = Type.Object({\n\tsupportsDeveloperRole: Type.Optional(Type.Boolean()),\n\tsessionAffinityFormat: Type.Optional(\n\t\tType.Union([Type.Literal(\"openai\"), Type.Literal(\"openai-nosession\"), Type.Literal(\"openrouter\")]),\n\t),\n\tsupportsLongCacheRetention: Type.Optional(Type.Boolean()),\n\tsupportsStrictMode: Type.Optional(Type.Boolean()),\n\tsupportsOpenAIGrammarTools: Type.Optional(Type.Boolean()),\n\tsupportsAdditionalTools: Type.Optional(Type.Boolean()),\n\tsupportsToolSearch: Type.Optional(Type.Boolean()),\n\tsupportsMaxOutputTokens: Type.Optional(Type.Boolean()),\n});\n\nconst AnthropicMessagesCompatSchema = Type.Object({\n\tsupportsEagerToolInputStreaming: Type.Optional(Type.Boolean()),\n\tsupportsLongCacheRetention: Type.Optional(Type.Boolean()),\n\tsendSessionAffinityHeaders: Type.Optional(Type.Boolean()),\n\tsupportsCacheControlOnTools: Type.Optional(Type.Boolean()),\n\tsupportsTemperature: Type.Optional(Type.Boolean()),\n\tforceAdaptiveThinking: Type.Optional(Type.Boolean()),\n\tallowEmptySignature: Type.Optional(Type.Boolean()),\n\tsupportsStrictTools: Type.Optional(Type.Boolean()),\n\tsupportsMidConvoEffort: Type.Optional(Type.Boolean()),\n\tsupportsToolReferences: Type.Optional(Type.Boolean()),\n});\n\nconst ProviderCompatSchema = Type.Union([\n\tOpenAICompletionsCompatSchema,\n\tOpenAIResponsesCompatSchema,\n\tAnthropicMessagesCompatSchema,\n]);\n\nconst ModelCostRatesSchema = {\n\tinput: Type.Number(),\n\toutput: Type.Number(),\n\tcacheRead: Type.Number(),\n\tcacheWrite: Type.Number(),\n};\nconst ModelCostTierSchema = Type.Object({\n\tinputTokensAbove: Type.Number(),\n\t...ModelCostRatesSchema,\n});\nconst ModelCostSchema = Type.Object({\n\t...ModelCostRatesSchema,\n\ttiers: Type.Optional(Type.Array(ModelCostTierSchema)),\n});\n\nconst ModelDefinitionSchema = Type.Object({\n\tid: Type.String({ minLength: 1 }),\n\tname: Type.Optional(Type.String({ minLength: 1 })),\n\tapi: Type.Optional(Type.String({ minLength: 1 })),\n\tbaseUrl: Type.Optional(Type.String({ minLength: 1 })),\n\treasoning: Type.Optional(Type.Boolean()),\n\tthinkingLevelMap: Type.Optional(ThinkingLevelMapSchema),\n\tinput: Type.Optional(Type.Array(Type.Union([Type.Literal(\"text\"), Type.Literal(\"image\")]))),\n\tcost: Type.Optional(ModelCostSchema),\n\tcontextWindow: Type.Optional(Type.Number()),\n\tmaxTokens: Type.Optional(Type.Number()),\n\tsamplingParams: Type.Optional(Type.Record(Type.String(), Type.Unknown())),\n\theaders: Type.Optional(Type.Record(Type.String(), Type.String())),\n\tcompat: Type.Optional(ProviderCompatSchema),\n});\n\nconst ModelOverrideSchema = Type.Object({\n\tname: Type.Optional(Type.String({ minLength: 1 })),\n\treasoning: Type.Optional(Type.Boolean()),\n\tthinkingLevelMap: Type.Optional(ThinkingLevelMapSchema),\n\tinput: Type.Optional(Type.Array(Type.Union([Type.Literal(\"text\"), Type.Literal(\"image\")]))),\n\tcost: Type.Optional(\n\t\tType.Object({\n\t\t\tinput: Type.Optional(Type.Number()),\n\t\t\toutput: Type.Optional(Type.Number()),\n\t\t\tcacheRead: Type.Optional(Type.Number()),\n\t\t\tcacheWrite: Type.Optional(Type.Number()),\n\t\t\ttiers: Type.Optional(Type.Array(ModelCostTierSchema)),\n\t\t}),\n\t),\n\tcontextWindow: Type.Optional(Type.Number()),\n\tmaxTokens: Type.Optional(Type.Number()),\n\tsamplingParams: Type.Optional(Type.Record(Type.String(), Type.Unknown())),\n\theaders: Type.Optional(Type.Record(Type.String(), Type.String())),\n\tcompat: Type.Optional(ProviderCompatSchema),\n});\n\nconst ProviderConfigSchema = Type.Object({\n\tname: Type.Optional(Type.String({ minLength: 1 })),\n\tbaseUrl: Type.Optional(Type.String({ minLength: 1 })),\n\tapiKey: Type.Optional(Type.String({ minLength: 1 })),\n\tapi: Type.Optional(Type.String({ minLength: 1 })),\n\t\theaders: Type.Optional(Type.Record(Type.String(), Type.String())),\n\tcompat: Type.Optional(ProviderCompatSchema),\n\tauthHeader: Type.Optional(Type.Boolean()),\n\tmodels: Type.Optional(Type.Array(ModelDefinitionSchema)),\n\tmodelOverrides: Type.Optional(Type.Record(Type.String(), ModelOverrideSchema)),\n});\n\nconst ModelsConfigSchema = Type.Object({\n\tproviders: Type.Record(Type.String(), ProviderConfigSchema),\n});\nconst validateModelsConfig = Compile(ModelsConfigSchema);\n\nexport type ModelsJsonModel = Static<typeof ModelDefinitionSchema>;\nexport type ModelsJsonModelOverride = Static<typeof ModelOverrideSchema>;\nexport type ModelsJsonProvider = Static<typeof ProviderConfigSchema>;\ntype UpstreamModelsJson = Static<typeof ModelsConfigSchema>;\n*/\n\nfunction formatValidationPath(error: TLocalizedValidationError): string {\n\tif (error.keyword === \"required\") {\n\t\tconst requiredProperties = (error.params as { requiredProperties?: string[] }).requiredProperties;\n\t\tconst requiredProperty = requiredProperties?.[0];\n\t\tif (requiredProperty) {\n\t\t\tconst basePath = error.instancePath.replace(/^\\//, \"\").replace(/\\//g, \".\");\n\t\t\treturn basePath ? `${basePath}.${requiredProperty}` : requiredProperty;\n\t\t}\n\t}\n\tconst path = error.instancePath.replace(/^\\//, \"\").replace(/\\//g, \".\");\n\treturn path || \"root\";\n}\n\nfunction deepFreeze<T>(value: T): T {\n\tif (typeof value !== \"object\" || value === null || Object.isFrozen(value)) return value;\n\tfor (const child of Object.values(value)) deepFreeze(child);\n\treturn Object.freeze(value);\n}\n\n/** One immutable load of models.json. */\nexport class ModelConfig {\n\tprivate readonly providers: ReadonlyMap<string, ModelsJsonProvider>;\n\tprivate readonly disabledProviders: ReadonlySet<string>;\n\tprivate readonly error: string | undefined;\n\tprivate readonly warnings: readonly string[];\n\n\tprivate constructor(\n\t\tproviders: ReadonlyMap<string, ModelsJsonProvider>,\n\t\tdisabledProviders: ReadonlySet<string> = new Set(),\n\t\terror?: string,\n\t\twarnings: readonly string[] = [],\n\t) {\n\t\tthis.providers = providers;\n\t\tthis.disabledProviders = disabledProviders;\n\t\tthis.error = error;\n\t\tthis.warnings = warnings;\n\t}\n\n\tprivate static parse(content: string, path: string): ModelConfig {\n\t\tlet parsed: unknown;\n\t\ttry {\n\t\t\tparsed = JSON.parse(stripJsonComments(stripBom(content)));\n\t\t} catch (error) {\n\t\t\treturn new ModelConfig(\n\t\t\t\tnew Map(),\n\t\t\t\tnew Set(),\n\t\t\t\t`Failed to parse models.json: ${error instanceof Error ? error.message : error}\\n\\nFile: ${path}`,\n\t\t\t);\n\t\t}\n\n\t\t// The shared fork schema intentionally carries compatibility fields that the\n\t\t// upstream validator has not learned yet. Validate a schema-compatible clone,\n\t\t// then retain the original values in the immutable runtime snapshot.\n\t\tconst validationInput = structuredClone(parsed);\n\t\tif (typeof validationInput === \"object\" && validationInput !== null && \"providers\" in validationInput) {\n\t\t\tconst providers = (validationInput as { providers?: Record<string, unknown> }).providers;\n\t\t\tfor (const provider of Object.values(providers ?? {})) {\n\t\t\t\tif (typeof provider !== \"object\" || provider === null) continue;\n\t\t\t\tconst record = provider as {\n\t\t\t\t\tcompat?: Record<string, unknown>;\n\t\t\t\t\tmodels?: Array<{ compat?: Record<string, unknown>; samplingParams?: Record<string, unknown> }>;\n\t\t\t\t\tmodelOverrides?: Record<\n\t\t\t\t\t\tstring,\n\t\t\t\t\t\t{ compat?: Record<string, unknown>; samplingParams?: Record<string, unknown> }\n\t\t\t\t\t>;\n\t\t\t\t};\n\t\t\t\t// Shape errors are the validator's to report; only well-shaped entries are normalized here.\n\t\t\t\tconst modelEntries = [\n\t\t\t\t\t...(Array.isArray(record.models) ? record.models : []),\n\t\t\t\t\t...(typeof record.modelOverrides === \"object\" && record.modelOverrides !== null\n\t\t\t\t\t\t? Object.values(record.modelOverrides)\n\t\t\t\t\t\t: []),\n\t\t\t\t].filter((model) => typeof model === \"object\" && model !== null);\n\t\t\t\tfor (const model of modelEntries) {\n\t\t\t\t\tdelete model.samplingParams;\n\t\t\t\t}\n\t\t\t\tconst compatEntries = [record.compat, ...modelEntries.map((model) => model.compat)];\n\t\t\t\tfor (const compat of compatEntries) {\n\t\t\t\t\tif (typeof compat !== \"object\" || compat === null) continue;\n\t\t\t\t\tif (compat.thinkingFormat === \"baseten\") compat.thinkingFormat = \"together\";\n\t\t\t\t\tdelete compat.chatTemplateArgs;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (!validateModelsConfig.Check(validationInput)) {\n\t\t\tconst errors =\n\t\t\t\tvalidateModelsConfig\n\t\t\t\t\t.Errors(validationInput)\n\t\t\t\t\t.map((error) => ` - ${formatValidationPath(error)}: ${error.message}`)\n\t\t\t\t\t.join(\"\\n\") || \"Unknown schema error\";\n\t\t\treturn new ModelConfig(new Map(), new Set(), `Invalid models.json schema:\\n${errors}\\n\\nFile: ${path}`);\n\t\t}\n\n\t\tconst config = parsed as ModelsJson;\n\t\tconst providers = new Map<string, ModelsJsonProvider>();\n\t\t// Read boundary (senpi#1989): a models.json written before the rename keys\n\t\t// its overlay by the legacy provider id. Normalize the key so the overlay\n\t\t// still attaches; `parseAndMigrate` then rewrites the file once (senpi#2044).\n\t\tfor (const [providerId, provider] of Object.entries(config.providers)) {\n\t\t\tconst canonical = normalizeProviderId(providerId);\n\t\t\t// An explicit canonical entry wins over a legacy one that normalizes onto it.\n\t\t\tif (canonical !== providerId && providers.has(canonical)) continue;\n\t\t\tproviders.set(canonical, deepFreeze(structuredClone(provider)));\n\t\t}\n\t\tconst disabled = new Set((config.disabledProviders ?? []).map((id) => normalizeProviderId(id)));\n\t\treturn new ModelConfig(providers, disabled);\n\t}\n\n\tprivate static parseAndMigrate(content: string, path: string): ModelConfig {\n\t\tconst config = ModelConfig.parse(content, path);\n\t\tif (config.error !== undefined) return config;\n\t\tconst migration = migrateModelsJsonProviderIds(path, content);\n\t\tif (migration.kind !== \"failed\") return config;\n\t\treturn new ModelConfig(config.providers, config.disabledProviders, undefined, [\n\t\t\t`models.json uses renamed provider ids (${migration.renamed.join(\", \")}) and could not be updated automatically: ${migration.reason}. They still work, but update the file to the new ids.`,\n\t\t]);\n\t}\n\n\tstatic async load(modelsJsonPath: string | undefined): Promise<ModelConfig> {\n\t\tif (!modelsJsonPath) return new ModelConfig(new Map());\n\t\tconst path = normalizePath(modelsJsonPath);\n\t\tlet content: string;\n\t\ttry {\n\t\t\tcontent = await readFile(path, \"utf-8\");\n\t\t} catch (error) {\n\t\t\tif ((error as NodeJS.ErrnoException).code === \"ENOENT\") return new ModelConfig(new Map());\n\t\t\treturn new ModelConfig(\n\t\t\t\tnew Map(),\n\t\t\t\tnew Set(),\n\t\t\t\t`Failed to load models.json: ${error instanceof Error ? error.message : error}\\n\\nFile: ${path}`,\n\t\t\t);\n\t\t}\n\t\treturn ModelConfig.parseAndMigrate(content, path);\n\t}\n\n\tstatic loadSync(modelsJsonPath: string | undefined): ModelConfig {\n\t\tif (!modelsJsonPath) return new ModelConfig(new Map());\n\t\tconst path = normalizePath(modelsJsonPath);\n\t\ttry {\n\t\t\treturn ModelConfig.parseAndMigrate(readFileSync(path, \"utf-8\"), path);\n\t\t} catch (error) {\n\t\t\tif ((error as NodeJS.ErrnoException).code === \"ENOENT\") return new ModelConfig(new Map());\n\t\t\treturn new ModelConfig(\n\t\t\t\tnew Map(),\n\t\t\t\tnew Set(),\n\t\t\t\t`Failed to load models.json: ${error instanceof Error ? error.message : error}\\n\\nFile: ${path}`,\n\t\t\t);\n\t\t}\n\t}\n\n\tgetProvider(providerId: string): ModelsJsonProvider | undefined {\n\t\treturn this.providers.get(providerId);\n\t}\n\n\tgetProviderIds(): readonly string[] {\n\t\treturn [...this.providers.keys()];\n\t}\n\n\tisProviderDisabled(providerId: string): boolean {\n\t\treturn this.disabledProviders.has(providerId) || this.providers.get(providerId)?.disabled === true;\n\t}\n\n\tgetError(): string | undefined {\n\t\treturn this.error;\n\t}\n\n\t/** Non-fatal notices raised while reading models.json (e.g. renamed provider ids). */\n\tgetWarnings(): readonly string[] {\n\t\treturn this.warnings;\n\t}\n}\n"]}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One-shot on-disk migration of legacy provider ids in models.json (senpi#2044).
|
|
3
|
+
*
|
|
4
|
+
* A models.json written before the subscription rename (senpi#1989) keys its
|
|
5
|
+
* overlays and `disabledProviders` by the legacy ids. The read boundary in
|
|
6
|
+
* `model-config.ts` already normalizes them in memory; this module rewrites the
|
|
7
|
+
* file once so the notice never repeats. The file is user-authored JSONC, so
|
|
8
|
+
* only the affected key/element tokens are edited - comments and formatting
|
|
9
|
+
* survive - and the result must re-parse to exactly the migrated document
|
|
10
|
+
* before it replaces the original.
|
|
11
|
+
*/
|
|
12
|
+
export type ModelsJsonMigration = {
|
|
13
|
+
readonly kind: "unchanged";
|
|
14
|
+
} | {
|
|
15
|
+
readonly kind: "migrated";
|
|
16
|
+
readonly renamed: readonly string[];
|
|
17
|
+
readonly backupPath: string;
|
|
18
|
+
} | {
|
|
19
|
+
readonly kind: "failed";
|
|
20
|
+
readonly renamed: readonly string[];
|
|
21
|
+
readonly reason: string;
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* Rewrite `path` once when its content (already read as `content`) uses a legacy
|
|
25
|
+
* provider id. Keeps a timestamped backup of the original bytes, writes through a
|
|
26
|
+
* temp file with the original mode, and refuses to replace a file that changed
|
|
27
|
+
* since it was read. Never throws: a failure leaves the original untouched.
|
|
28
|
+
*/
|
|
29
|
+
export declare function migrateModelsJsonProviderIds(path: string, content: string): ModelsJsonMigration;
|
|
30
|
+
//# sourceMappingURL=models-json-migration.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"models-json-migration.d.ts","sourceRoot":"","sources":["../../src/core/models-json-migration.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAQH,MAAM,MAAM,mBAAmB,GAC5B;IAAE,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAA;CAAE,GAC9B;IAAE,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IAAC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;CAAE,GAC/F;IAAE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAyM7F;;;;;GAKG;AACH,wBAAgB,4BAA4B,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,mBAAmB,CAkC/F"}
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One-shot on-disk migration of legacy provider ids in models.json (senpi#2044).
|
|
3
|
+
*
|
|
4
|
+
* A models.json written before the subscription rename (senpi#1989) keys its
|
|
5
|
+
* overlays and `disabledProviders` by the legacy ids. The read boundary in
|
|
6
|
+
* `model-config.ts` already normalizes them in memory; this module rewrites the
|
|
7
|
+
* file once so the notice never repeats. The file is user-authored JSONC, so
|
|
8
|
+
* only the affected key/element tokens are edited - comments and formatting
|
|
9
|
+
* survive - and the result must re-parse to exactly the migrated document
|
|
10
|
+
* before it replaces the original.
|
|
11
|
+
*/
|
|
12
|
+
import { chmodSync, existsSync, readFileSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
13
|
+
import { isDeepStrictEqual } from "node:util";
|
|
14
|
+
import { normalizeProviderId } from "@earendil-works/pi-ai";
|
|
15
|
+
import { stripJsonComments } from "../utils/json.js";
|
|
16
|
+
import { stripBom } from "../utils/text.js";
|
|
17
|
+
const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
18
|
+
class JsoncSpanScanner {
|
|
19
|
+
constructor(text) {
|
|
20
|
+
this.text = text;
|
|
21
|
+
}
|
|
22
|
+
skipTrivia(from) {
|
|
23
|
+
let i = from;
|
|
24
|
+
while (i < this.text.length) {
|
|
25
|
+
const c = this.text[i];
|
|
26
|
+
if (c === " " || c === "\t" || c === "\n" || c === "\r" || c === "\uFEFF")
|
|
27
|
+
i++;
|
|
28
|
+
else if (this.text.startsWith("//", i))
|
|
29
|
+
i = this.lineEnd(i);
|
|
30
|
+
else if (this.text.startsWith("/*", i))
|
|
31
|
+
i = this.blockCommentEnd(i);
|
|
32
|
+
else
|
|
33
|
+
break;
|
|
34
|
+
}
|
|
35
|
+
return i;
|
|
36
|
+
}
|
|
37
|
+
value(from) {
|
|
38
|
+
const start = this.skipTrivia(from);
|
|
39
|
+
const c = this.text[start];
|
|
40
|
+
if (c === "{")
|
|
41
|
+
return this.object(start);
|
|
42
|
+
if (c === "[")
|
|
43
|
+
return this.array(start);
|
|
44
|
+
if (c === '"')
|
|
45
|
+
return { start, end: this.stringEnd(start) };
|
|
46
|
+
let end = start;
|
|
47
|
+
while (end < this.text.length && !/[\s,\]}/]/.test(this.text[end] ?? ""))
|
|
48
|
+
end++;
|
|
49
|
+
if (end === start)
|
|
50
|
+
throw new Error(`unexpected character at offset ${start}`);
|
|
51
|
+
return { start, end };
|
|
52
|
+
}
|
|
53
|
+
object(start) {
|
|
54
|
+
const members = [];
|
|
55
|
+
let i = start + 1;
|
|
56
|
+
while (true) {
|
|
57
|
+
i = this.skipTrivia(i);
|
|
58
|
+
const c = this.text[i];
|
|
59
|
+
if (c === "}")
|
|
60
|
+
return { start, end: i + 1, members };
|
|
61
|
+
if (c === ",") {
|
|
62
|
+
i++;
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
if (c !== '"')
|
|
66
|
+
throw new Error(`expected a key at offset ${i}`);
|
|
67
|
+
const keySpan = { start: i, end: this.stringEnd(i) };
|
|
68
|
+
const colon = this.skipTrivia(keySpan.end);
|
|
69
|
+
if (this.text[colon] !== ":")
|
|
70
|
+
throw new Error(`expected ':' at offset ${colon}`);
|
|
71
|
+
const value = this.value(colon + 1);
|
|
72
|
+
members.push({ key: JSON.parse(this.text.slice(keySpan.start, keySpan.end)), keySpan, value });
|
|
73
|
+
i = value.end;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
array(start) {
|
|
77
|
+
const elements = [];
|
|
78
|
+
let i = start + 1;
|
|
79
|
+
while (true) {
|
|
80
|
+
i = this.skipTrivia(i);
|
|
81
|
+
const c = this.text[i];
|
|
82
|
+
if (c === "]")
|
|
83
|
+
return { start, end: i + 1, elements };
|
|
84
|
+
if (c === ",") {
|
|
85
|
+
i++;
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
const element = this.value(i);
|
|
89
|
+
elements.push(element);
|
|
90
|
+
i = element.end;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
stringEnd(start) {
|
|
94
|
+
for (let i = start + 1; i < this.text.length; i++) {
|
|
95
|
+
if (this.text[i] === "\\")
|
|
96
|
+
i++;
|
|
97
|
+
else if (this.text[i] === '"')
|
|
98
|
+
return i + 1;
|
|
99
|
+
}
|
|
100
|
+
throw new Error("unterminated string");
|
|
101
|
+
}
|
|
102
|
+
blockCommentEnd(from) {
|
|
103
|
+
const close = this.text.indexOf("*/", from + 2);
|
|
104
|
+
if (close < 0)
|
|
105
|
+
throw new Error("unterminated comment");
|
|
106
|
+
return close + 2;
|
|
107
|
+
}
|
|
108
|
+
lineEnd(from) {
|
|
109
|
+
const newline = this.text.indexOf("\n", from);
|
|
110
|
+
return newline < 0 ? this.text.length : newline;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
const parseJsonc = (content) => JSON.parse(stripJsonComments(stripBom(content)));
|
|
114
|
+
function migratedDocument(document) {
|
|
115
|
+
const renamed = [];
|
|
116
|
+
const next = { ...document };
|
|
117
|
+
if (isRecord(document.providers)) {
|
|
118
|
+
const providers = {};
|
|
119
|
+
for (const [id, provider] of Object.entries(document.providers)) {
|
|
120
|
+
const canonical = normalizeProviderId(id);
|
|
121
|
+
if (canonical !== id)
|
|
122
|
+
renamed.push(`${id} -> ${canonical}`);
|
|
123
|
+
if (canonical !== id && canonical in document.providers)
|
|
124
|
+
continue;
|
|
125
|
+
providers[canonical] = provider;
|
|
126
|
+
}
|
|
127
|
+
next.providers = providers;
|
|
128
|
+
}
|
|
129
|
+
if (Array.isArray(document.disabledProviders)) {
|
|
130
|
+
next.disabledProviders = document.disabledProviders.map((id) => {
|
|
131
|
+
if (typeof id !== "string" || normalizeProviderId(id) === id)
|
|
132
|
+
return id;
|
|
133
|
+
renamed.push(`${id} -> ${normalizeProviderId(id)}`);
|
|
134
|
+
return normalizeProviderId(id);
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
return { next, renamed: [...new Set(renamed)] };
|
|
138
|
+
}
|
|
139
|
+
function memberRemovalSpan(text, scanner, member) {
|
|
140
|
+
const after = scanner.skipTrivia(member.value.end);
|
|
141
|
+
if (text[after] === ",") {
|
|
142
|
+
let start = member.keySpan.start;
|
|
143
|
+
while (start > 0 && (text[start - 1] === " " || text[start - 1] === "\t"))
|
|
144
|
+
start--;
|
|
145
|
+
const ownsLine = start === 0 || text[start - 1] === "\n";
|
|
146
|
+
let end = after + 1;
|
|
147
|
+
while (end < text.length && (text[end] === " " || text[end] === "\t"))
|
|
148
|
+
end++;
|
|
149
|
+
if (ownsLine && text[end] === "\r")
|
|
150
|
+
end++;
|
|
151
|
+
if (ownsLine && text[end] === "\n")
|
|
152
|
+
return { start, end: end + 1 };
|
|
153
|
+
return { start: ownsLine ? start : member.keySpan.start, end: after + 1 };
|
|
154
|
+
}
|
|
155
|
+
let comma = member.keySpan.start - 1;
|
|
156
|
+
while (comma >= 0 && /\s/.test(text[comma] ?? ""))
|
|
157
|
+
comma--;
|
|
158
|
+
return { start: text[comma] === "," ? comma : member.keySpan.start, end: member.value.end };
|
|
159
|
+
}
|
|
160
|
+
function rewriteProviderIdTokens(content) {
|
|
161
|
+
const scanner = new JsoncSpanScanner(content);
|
|
162
|
+
const root = scanner.value(0);
|
|
163
|
+
const edits = [];
|
|
164
|
+
for (const member of root.members ?? []) {
|
|
165
|
+
if (member.key === "providers" && member.value.members) {
|
|
166
|
+
const keys = new Set(member.value.members.map((provider) => provider.key));
|
|
167
|
+
for (const provider of member.value.members) {
|
|
168
|
+
const canonical = normalizeProviderId(provider.key);
|
|
169
|
+
if (canonical === provider.key)
|
|
170
|
+
continue;
|
|
171
|
+
if (keys.has(canonical))
|
|
172
|
+
edits.push({ ...memberRemovalSpan(content, scanner, provider), text: "" });
|
|
173
|
+
else
|
|
174
|
+
edits.push({ ...provider.keySpan, text: JSON.stringify(canonical) });
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
if (member.key === "disabledProviders" && member.value.elements) {
|
|
178
|
+
for (const element of member.value.elements) {
|
|
179
|
+
const raw = JSON.parse(content.slice(element.start, element.end));
|
|
180
|
+
if (typeof raw !== "string" || normalizeProviderId(raw) === raw)
|
|
181
|
+
continue;
|
|
182
|
+
edits.push({ ...element, text: JSON.stringify(normalizeProviderId(raw)) });
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
let next = content;
|
|
187
|
+
for (const edit of edits.sort((a, b) => b.start - a.start)) {
|
|
188
|
+
next = next.slice(0, edit.start) + edit.text + next.slice(edit.end);
|
|
189
|
+
}
|
|
190
|
+
return next;
|
|
191
|
+
}
|
|
192
|
+
function verifiedRewrite(content, expected) {
|
|
193
|
+
let tokenEdited;
|
|
194
|
+
try {
|
|
195
|
+
tokenEdited = rewriteProviderIdTokens(content);
|
|
196
|
+
}
|
|
197
|
+
catch {
|
|
198
|
+
tokenEdited = undefined;
|
|
199
|
+
}
|
|
200
|
+
if (tokenEdited !== undefined && isDeepStrictEqual(parseJsonc(tokenEdited), expected))
|
|
201
|
+
return tokenEdited;
|
|
202
|
+
return `${JSON.stringify(expected, null, 2)}\n`;
|
|
203
|
+
}
|
|
204
|
+
function uniqueBackupPath(path) {
|
|
205
|
+
const stamp = new Date().toISOString().replace(/[:.]/g, "-");
|
|
206
|
+
let backupPath = `${path}.backup-${stamp}`;
|
|
207
|
+
for (let attempt = 1; existsSync(backupPath); attempt++)
|
|
208
|
+
backupPath = `${path}.backup-${stamp}-${attempt}`;
|
|
209
|
+
return backupPath;
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Rewrite `path` once when its content (already read as `content`) uses a legacy
|
|
213
|
+
* provider id. Keeps a timestamped backup of the original bytes, writes through a
|
|
214
|
+
* temp file with the original mode, and refuses to replace a file that changed
|
|
215
|
+
* since it was read. Never throws: a failure leaves the original untouched.
|
|
216
|
+
*/
|
|
217
|
+
export function migrateModelsJsonProviderIds(path, content) {
|
|
218
|
+
let document;
|
|
219
|
+
try {
|
|
220
|
+
document = parseJsonc(content);
|
|
221
|
+
}
|
|
222
|
+
catch {
|
|
223
|
+
return { kind: "unchanged" };
|
|
224
|
+
}
|
|
225
|
+
if (!isRecord(document))
|
|
226
|
+
return { kind: "unchanged" };
|
|
227
|
+
const { next, renamed } = migratedDocument(document);
|
|
228
|
+
if (renamed.length === 0)
|
|
229
|
+
return { kind: "unchanged" };
|
|
230
|
+
const backupPath = uniqueBackupPath(path);
|
|
231
|
+
const temporary = `${path}.${process.pid}.tmp`;
|
|
232
|
+
const created = [];
|
|
233
|
+
try {
|
|
234
|
+
const mode = statSync(path).mode & 0o777;
|
|
235
|
+
writeFileSync(backupPath, content, { encoding: "utf-8", mode, flag: "wx" });
|
|
236
|
+
created.push(backupPath);
|
|
237
|
+
writeFileSync(temporary, verifiedRewrite(content, next), { encoding: "utf-8", mode, flag: "wx" });
|
|
238
|
+
created.push(temporary);
|
|
239
|
+
chmodSync(temporary, mode);
|
|
240
|
+
if (readFileSync(path, "utf-8") !== content)
|
|
241
|
+
throw new Error("the file changed while it was being migrated");
|
|
242
|
+
renameSync(temporary, path);
|
|
243
|
+
return { kind: "migrated", renamed, backupPath };
|
|
244
|
+
}
|
|
245
|
+
catch (error) {
|
|
246
|
+
for (const leftover of created) {
|
|
247
|
+
try {
|
|
248
|
+
rmSync(leftover, { force: true });
|
|
249
|
+
}
|
|
250
|
+
catch {
|
|
251
|
+
// A leftover backup or temp is harmless; the original file is untouched.
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
return { kind: "failed", renamed, reason: error instanceof Error ? error.message : String(error) };
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
//# sourceMappingURL=models-json-migration.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"models-json-migration.js","sourceRoot":"","sources":["../../src/core/models-json-migration.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * One-shot on-disk migration of legacy provider ids in models.json (senpi#2044).\n *\n * A models.json written before the subscription rename (senpi#1989) keys its\n * overlays and `disabledProviders` by the legacy ids. The read boundary in\n * `model-config.ts` already normalizes them in memory; this module rewrites the\n * file once so the notice never repeats. The file is user-authored JSONC, so\n * only the affected key/element tokens are edited - comments and formatting\n * survive - and the result must re-parse to exactly the migrated document\n * before it replaces the original.\n */\n\nimport { chmodSync, existsSync, readFileSync, renameSync, rmSync, statSync, writeFileSync } from \"node:fs\";\nimport { isDeepStrictEqual } from \"node:util\";\nimport { normalizeProviderId } from \"@earendil-works/pi-ai\";\nimport { stripJsonComments } from \"../utils/json.ts\";\nimport { stripBom } from \"../utils/text.ts\";\n\nexport type ModelsJsonMigration =\n\t| { readonly kind: \"unchanged\" }\n\t| { readonly kind: \"migrated\"; readonly renamed: readonly string[]; readonly backupPath: string }\n\t| { readonly kind: \"failed\"; readonly renamed: readonly string[]; readonly reason: string };\n\ninterface Span {\n\treadonly start: number;\n\treadonly end: number;\n}\ninterface Member {\n\treadonly key: string;\n\treadonly keySpan: Span;\n\treadonly value: Node;\n}\ninterface Node extends Span {\n\treadonly members?: readonly Member[];\n\treadonly elements?: readonly Node[];\n}\ninterface Edit extends Span {\n\treadonly text: string;\n}\ntype JsonRecord = Record<string, unknown>;\n\nconst isRecord = (value: unknown): value is JsonRecord =>\n\ttypeof value === \"object\" && value !== null && !Array.isArray(value);\n\nclass JsoncSpanScanner {\n\tprivate readonly text: string;\n\n\tconstructor(text: string) {\n\t\tthis.text = text;\n\t}\n\n\tskipTrivia(from: number): number {\n\t\tlet i = from;\n\t\twhile (i < this.text.length) {\n\t\t\tconst c = this.text[i];\n\t\t\tif (c === \" \" || c === \"\\t\" || c === \"\\n\" || c === \"\\r\" || c === \"\\uFEFF\") i++;\n\t\t\telse if (this.text.startsWith(\"//\", i)) i = this.lineEnd(i);\n\t\t\telse if (this.text.startsWith(\"/*\", i)) i = this.blockCommentEnd(i);\n\t\t\telse break;\n\t\t}\n\t\treturn i;\n\t}\n\n\tvalue(from: number): Node {\n\t\tconst start = this.skipTrivia(from);\n\t\tconst c = this.text[start];\n\t\tif (c === \"{\") return this.object(start);\n\t\tif (c === \"[\") return this.array(start);\n\t\tif (c === '\"') return { start, end: this.stringEnd(start) };\n\t\tlet end = start;\n\t\twhile (end < this.text.length && !/[\\s,\\]}/]/.test(this.text[end] ?? \"\")) end++;\n\t\tif (end === start) throw new Error(`unexpected character at offset ${start}`);\n\t\treturn { start, end };\n\t}\n\n\tprivate object(start: number): Node {\n\t\tconst members: Member[] = [];\n\t\tlet i = start + 1;\n\t\twhile (true) {\n\t\t\ti = this.skipTrivia(i);\n\t\t\tconst c = this.text[i];\n\t\t\tif (c === \"}\") return { start, end: i + 1, members };\n\t\t\tif (c === \",\") {\n\t\t\t\ti++;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (c !== '\"') throw new Error(`expected a key at offset ${i}`);\n\t\t\tconst keySpan = { start: i, end: this.stringEnd(i) };\n\t\t\tconst colon = this.skipTrivia(keySpan.end);\n\t\t\tif (this.text[colon] !== \":\") throw new Error(`expected ':' at offset ${colon}`);\n\t\t\tconst value = this.value(colon + 1);\n\t\t\tmembers.push({ key: JSON.parse(this.text.slice(keySpan.start, keySpan.end)), keySpan, value });\n\t\t\ti = value.end;\n\t\t}\n\t}\n\n\tprivate array(start: number): Node {\n\t\tconst elements: Node[] = [];\n\t\tlet i = start + 1;\n\t\twhile (true) {\n\t\t\ti = this.skipTrivia(i);\n\t\t\tconst c = this.text[i];\n\t\t\tif (c === \"]\") return { start, end: i + 1, elements };\n\t\t\tif (c === \",\") {\n\t\t\t\ti++;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst element = this.value(i);\n\t\t\telements.push(element);\n\t\t\ti = element.end;\n\t\t}\n\t}\n\n\tprivate stringEnd(start: number): number {\n\t\tfor (let i = start + 1; i < this.text.length; i++) {\n\t\t\tif (this.text[i] === \"\\\\\") i++;\n\t\t\telse if (this.text[i] === '\"') return i + 1;\n\t\t}\n\t\tthrow new Error(\"unterminated string\");\n\t}\n\n\tprivate blockCommentEnd(from: number): number {\n\t\tconst close = this.text.indexOf(\"*/\", from + 2);\n\t\tif (close < 0) throw new Error(\"unterminated comment\");\n\t\treturn close + 2;\n\t}\n\n\tprivate lineEnd(from: number): number {\n\t\tconst newline = this.text.indexOf(\"\\n\", from);\n\t\treturn newline < 0 ? this.text.length : newline;\n\t}\n}\n\nconst parseJsonc = (content: string): unknown => JSON.parse(stripJsonComments(stripBom(content)));\n\nfunction migratedDocument(document: JsonRecord): { next: JsonRecord; renamed: string[] } {\n\tconst renamed: string[] = [];\n\tconst next: JsonRecord = { ...document };\n\tif (isRecord(document.providers)) {\n\t\tconst providers: JsonRecord = {};\n\t\tfor (const [id, provider] of Object.entries(document.providers)) {\n\t\t\tconst canonical = normalizeProviderId(id);\n\t\t\tif (canonical !== id) renamed.push(`${id} -> ${canonical}`);\n\t\t\tif (canonical !== id && canonical in document.providers) continue;\n\t\t\tproviders[canonical] = provider;\n\t\t}\n\t\tnext.providers = providers;\n\t}\n\tif (Array.isArray(document.disabledProviders)) {\n\t\tnext.disabledProviders = document.disabledProviders.map((id: unknown) => {\n\t\t\tif (typeof id !== \"string\" || normalizeProviderId(id) === id) return id;\n\t\t\trenamed.push(`${id} -> ${normalizeProviderId(id)}`);\n\t\t\treturn normalizeProviderId(id);\n\t\t});\n\t}\n\treturn { next, renamed: [...new Set(renamed)] };\n}\n\nfunction memberRemovalSpan(text: string, scanner: JsoncSpanScanner, member: Member): Span {\n\tconst after = scanner.skipTrivia(member.value.end);\n\tif (text[after] === \",\") {\n\t\tlet start = member.keySpan.start;\n\t\twhile (start > 0 && (text[start - 1] === \" \" || text[start - 1] === \"\\t\")) start--;\n\t\tconst ownsLine = start === 0 || text[start - 1] === \"\\n\";\n\t\tlet end = after + 1;\n\t\twhile (end < text.length && (text[end] === \" \" || text[end] === \"\\t\")) end++;\n\t\tif (ownsLine && text[end] === \"\\r\") end++;\n\t\tif (ownsLine && text[end] === \"\\n\") return { start, end: end + 1 };\n\t\treturn { start: ownsLine ? start : member.keySpan.start, end: after + 1 };\n\t}\n\tlet comma = member.keySpan.start - 1;\n\twhile (comma >= 0 && /\\s/.test(text[comma] ?? \"\")) comma--;\n\treturn { start: text[comma] === \",\" ? comma : member.keySpan.start, end: member.value.end };\n}\n\nfunction rewriteProviderIdTokens(content: string): string {\n\tconst scanner = new JsoncSpanScanner(content);\n\tconst root = scanner.value(0);\n\tconst edits: Edit[] = [];\n\tfor (const member of root.members ?? []) {\n\t\tif (member.key === \"providers\" && member.value.members) {\n\t\t\tconst keys = new Set(member.value.members.map((provider) => provider.key));\n\t\t\tfor (const provider of member.value.members) {\n\t\t\t\tconst canonical = normalizeProviderId(provider.key);\n\t\t\t\tif (canonical === provider.key) continue;\n\t\t\t\tif (keys.has(canonical)) edits.push({ ...memberRemovalSpan(content, scanner, provider), text: \"\" });\n\t\t\t\telse edits.push({ ...provider.keySpan, text: JSON.stringify(canonical) });\n\t\t\t}\n\t\t}\n\t\tif (member.key === \"disabledProviders\" && member.value.elements) {\n\t\t\tfor (const element of member.value.elements) {\n\t\t\t\tconst raw: unknown = JSON.parse(content.slice(element.start, element.end));\n\t\t\t\tif (typeof raw !== \"string\" || normalizeProviderId(raw) === raw) continue;\n\t\t\t\tedits.push({ ...element, text: JSON.stringify(normalizeProviderId(raw)) });\n\t\t\t}\n\t\t}\n\t}\n\tlet next = content;\n\tfor (const edit of edits.sort((a, b) => b.start - a.start)) {\n\t\tnext = next.slice(0, edit.start) + edit.text + next.slice(edit.end);\n\t}\n\treturn next;\n}\n\nfunction verifiedRewrite(content: string, expected: JsonRecord): string {\n\tlet tokenEdited: string | undefined;\n\ttry {\n\t\ttokenEdited = rewriteProviderIdTokens(content);\n\t} catch {\n\t\ttokenEdited = undefined;\n\t}\n\tif (tokenEdited !== undefined && isDeepStrictEqual(parseJsonc(tokenEdited), expected)) return tokenEdited;\n\treturn `${JSON.stringify(expected, null, 2)}\\n`;\n}\n\nfunction uniqueBackupPath(path: string): string {\n\tconst stamp = new Date().toISOString().replace(/[:.]/g, \"-\");\n\tlet backupPath = `${path}.backup-${stamp}`;\n\tfor (let attempt = 1; existsSync(backupPath); attempt++) backupPath = `${path}.backup-${stamp}-${attempt}`;\n\treturn backupPath;\n}\n\n/**\n * Rewrite `path` once when its content (already read as `content`) uses a legacy\n * provider id. Keeps a timestamped backup of the original bytes, writes through a\n * temp file with the original mode, and refuses to replace a file that changed\n * since it was read. Never throws: a failure leaves the original untouched.\n */\nexport function migrateModelsJsonProviderIds(path: string, content: string): ModelsJsonMigration {\n\tlet document: unknown;\n\ttry {\n\t\tdocument = parseJsonc(content);\n\t} catch {\n\t\treturn { kind: \"unchanged\" };\n\t}\n\tif (!isRecord(document)) return { kind: \"unchanged\" };\n\tconst { next, renamed } = migratedDocument(document);\n\tif (renamed.length === 0) return { kind: \"unchanged\" };\n\n\tconst backupPath = uniqueBackupPath(path);\n\tconst temporary = `${path}.${process.pid}.tmp`;\n\tconst created: string[] = [];\n\ttry {\n\t\tconst mode = statSync(path).mode & 0o777;\n\t\twriteFileSync(backupPath, content, { encoding: \"utf-8\", mode, flag: \"wx\" });\n\t\tcreated.push(backupPath);\n\t\twriteFileSync(temporary, verifiedRewrite(content, next), { encoding: \"utf-8\", mode, flag: \"wx\" });\n\t\tcreated.push(temporary);\n\t\tchmodSync(temporary, mode);\n\t\tif (readFileSync(path, \"utf-8\") !== content) throw new Error(\"the file changed while it was being migrated\");\n\t\trenameSync(temporary, path);\n\t\treturn { kind: \"migrated\", renamed, backupPath };\n\t} catch (error) {\n\t\tfor (const leftover of created) {\n\t\t\ttry {\n\t\t\t\trmSync(leftover, { force: true });\n\t\t\t} catch {\n\t\t\t\t// A leftover backup or temp is harmless; the original file is untouched.\n\t\t\t}\n\t\t}\n\t\treturn { kind: \"failed\", renamed, reason: error instanceof Error ? error.message : String(error) };\n\t}\n}\n"]}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export declare function takeOverStdout(): void;
|
|
2
2
|
export declare function restoreStdout(): void;
|
|
3
|
+
/** Observe the visible sink below any diagnostic redirect, including its failure fallback. */
|
|
4
|
+
export declare function observeVisibleStderrWrites(listener: () => void): () => void;
|
|
3
5
|
export declare function takeOverStderr(onHiddenDiagnostic?: (text: string) => void, formatHiddenDiagnosticFallback?: (text: string) => string): void;
|
|
4
6
|
export declare function restoreStderr(): void;
|
|
5
7
|
export declare function isStdoutTakenOver(): boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"output-guard.d.ts","sourceRoot":"","sources":["../../src/core/output-guard.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"output-guard.d.ts","sourceRoot":"","sources":["../../src/core/output-guard.ts"],"names":[],"mappings":"AA0DA,wBAAgB,cAAc,IAAI,IAAI,CAyBrC;AAED,wBAAgB,aAAa,IAAI,IAAI,CAOpC;AAoBD,8FAA8F;AAC9F,wBAAgB,0BAA0B,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAuB3E;AAED,wBAAgB,cAAc,CAC7B,kBAAkB,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,EAC3C,8BAA8B,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,GACvD,IAAI,CA2CN;AAED,wBAAgB,aAAa,IAAI,IAAI,CAOpC;AAED,wBAAgB,iBAAiB,IAAI,OAAO,CAE3C;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAQjD;AAED,wBAAsB,4BAA4B,IAAI,OAAO,CAAC,IAAI,CAAC,CAQlE;AAED,wBAAsB,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC,CAGpD"}
|