Files
dev/third_party/pi/packages/ai/scripts/hydrate-model-catalog.ts
alexion 6081e3e325 chore(vendor): import pi 1.3.0
Imported as a linear vendor commit so forge rebases do not replay the upstream root tree.

git-subtree-dir: third_party/pi

git-subtree-split: 71023aec1546d6d677a65669c5665c0927ce0dd0
2026-10-15 17:58:48 -04:00

87 lines
3.4 KiB
JavaScript

#!/usr/bin/env node
import { mkdirSync, mkdtempSync, readFileSync, renameSync, rmSync, writeFileSync } from "node:fs";
import { dirname, join, resolve } from "node:path";
import { fileURLToPath, pathToFileURL } from "node:url";
import {
createModelDataManifest,
groupProviderModelData,
MODEL_DATA_MANIFEST_FILE,
type ModelCatalogEntry,
type ModelDataStructure,
readModelDataProviderIds,
validateModelDataDirectory,
} from "./model-data.ts";
type CatalogEntry = ModelCatalogEntry & Record<string, unknown>;
function isRecord(value: unknown): value is Record<string, unknown> {
return typeof value === "object" && value !== null && !Array.isArray(value);
}
function isCatalogEntry(value: unknown): value is CatalogEntry {
return (
isRecord(value) &&
typeof value.type === "string" &&
typeof value.id === "string" &&
typeof value.api === "string" &&
value.api.length > 0
);
}
/**
* Hydrate the checkout's provider data files from a published typed catalog
* (`models.all.json`) without network access. With `validateOnly`, stage and
* validate the data without replacing the checkout's current data.
*/
export function hydrateModelCatalog(
packageRoot: string,
catalogPath: string,
options: { validateOnly?: boolean } = {},
): void {
const catalog: unknown = JSON.parse(readFileSync(catalogPath, "utf8"));
if (!isRecord(catalog)) throw new Error("Model catalog must be an object");
const files: Record<string, string> = {};
const structure: ModelDataStructure = {};
for (const provider of readModelDataProviderIds(packageRoot)) {
const models = catalog[provider];
if (models === undefined) throw new Error(`Model catalog is missing provider: ${provider}`);
if (!Array.isArray(models) || models.length === 0) {
throw new Error(`Model catalog has no typed model list for provider: ${provider}`);
}
const entries: CatalogEntry[] = [];
for (const model of models) {
if (!isCatalogEntry(model)) throw new Error(`Model catalog has an invalid entry for provider ${provider}`);
entries.push(model);
}
const grouped = groupProviderModelData(provider, entries);
structure[provider] = grouped.structure;
files[`${provider}.json`] = `${JSON.stringify(grouped.groups)}\n`;
}
// Public catalogs have no generation timestamp. Use a fixed stamp so hydration
// produces identical bytes for identical input, regardless of build time.
const manifest = createModelDataManifest(structure, files, "1970-01-01T00:00:00.000Z");
const providersDir = join(packageRoot, "src", "providers");
const stagingRoot = mkdtempSync(join(providersDir, ".model-hydration-"));
const stagedData = join(stagingRoot, "data");
try {
mkdirSync(stagedData);
for (const [name, content] of Object.entries(files)) writeFileSync(join(stagedData, name), content);
writeFileSync(join(stagedData, MODEL_DATA_MANIFEST_FILE), `${JSON.stringify(manifest)}\n`);
validateModelDataDirectory(structure, stagedData);
if (options.validateOnly) return;
const dataDir = join(providersDir, "data");
rmSync(dataDir, { recursive: true, force: true });
renameSync(stagedData, dataDir);
} finally {
rmSync(stagingRoot, { recursive: true, force: true });
}
}
if (process.argv[1] && import.meta.url === pathToFileURL(resolve(process.argv[1])).href) {
if (process.argv.length !== 3) throw new Error("Usage: node hydrate-model-catalog.ts <models.all.json>");
hydrateModelCatalog(join(dirname(fileURLToPath(import.meta.url)), ".."), resolve(process.argv[2]));
}