sync update

This commit is contained in:
Daniel Barnes
2026-06-26 09:26:55 +09:00
parent ee9a5048b1
commit c9faae0647
4 changed files with 300 additions and 627 deletions
+3 -2
View File
@@ -26,10 +26,11 @@
"huggingface:sync": "bun ./packages/core/script/sync-models.ts huggingface",
"venice:sync": "bun ./packages/core/script/sync-models.ts venice",
"vercel:generate": "bun ./packages/core/script/sync-models.ts vercel",
"wandb:generate": "bun ./packages/core/script/generate-wandb.ts",
"wandb:generate": "bun ./packages/core/script/sync-models.ts wandb",
"digitalocean:generate": "bun ./packages/core/script/generate-digitalocean.ts",
"ambient:generate": "bun ./packages/core/script/generate-ambient.ts",
"models:sync": "bun ./packages/core/script/sync-models.ts"
"models:sync": "bun ./packages/core/script/sync-models.ts",
"sync:models": "bun ./packages/core/script/sync-models.ts"
},
"dependencies": {
"@cloudflare/workers-types": "^4.20260424.1",
+2 -624
View File
@@ -1,627 +1,5 @@
#!/usr/bin/env bun
import path from "node:path";
import { mkdir } from "node:fs/promises";
import { z } from "zod";
import { inferKimiFamily, ModelFamily, ModelFamilyValues } from "../src/family.js";
import { main } from "../src/sync/index.js";
// This endpoint already returns data in the models.dev schema, so most fields
// map straight through. Only fields the catalog can't provide (family) are
// inferred, and manually-curated fields in existing TOMLs are preserved.
const API_ENDPOINT = "https://trace.wandb.ai/inference/modelsdev/models";
const ApiCost = z
.object({
input: z.number(),
output: z.number(),
reasoning: z.number().optional(),
cache_read: z.number().optional(),
cache_write: z.number().optional(),
input_audio: z.number().optional(),
output_audio: z.number().optional(),
})
.passthrough();
const ApiLimit = z
.object({
context: z.number(),
input: z.number(),
output: z.number(),
})
.passthrough();
const ApiModalities = z
.object({
input: z.array(z.string()),
output: z.array(z.string()),
})
.passthrough();
const ApiModel = z
.object({
id: z.string(),
name: z.string(),
attachment: z.boolean(),
reasoning: z.boolean(),
tool_call: z.boolean(),
structured_output: z.boolean().optional(),
temperature: z.boolean().optional(),
knowledge: z.string().optional(),
release_date: z.string(),
last_updated: z.string(),
open_weights: z.boolean(),
status: z.string().optional(),
interleaved: z
.union([z.boolean(), z.object({ field: z.string() })])
.optional(),
cost: ApiCost.optional(),
limit: ApiLimit.optional(),
modalities: ApiModalities.optional(),
})
.passthrough();
const ApiProvider = z
.object({
id: z.string(),
name: z.string(),
npm: z.string(),
env: z.array(z.string()),
doc: z.string(),
api: z.string().optional(),
models: z.record(z.string(), ApiModel),
})
.passthrough();
// The models.dev `api.json` shape: a mapping of provider id -> provider.
const ApiResponse = z.record(z.string(), ApiProvider);
interface ExistingModel {
base_model?: string;
base_model_omit?: string[];
name?: string;
family?: string;
attachment?: boolean;
reasoning?: boolean;
tool_call?: boolean;
structured_output?: boolean;
temperature?: boolean;
knowledge?: string;
release_date?: string;
last_updated?: string;
open_weights?: boolean;
interleaved?: boolean | { field: string };
status?: string;
cost?: {
input?: number;
output?: number;
cache_read?: number;
cache_write?: number;
};
limit?: {
context?: number;
input?: number;
output?: number;
};
modalities?: {
input?: string[];
output?: string[];
};
}
interface MergedModel {
base_model?: string;
base_model_omit?: string[];
name: string;
family?: string;
attachment: boolean;
reasoning: boolean;
tool_call: boolean;
structured_output?: boolean;
temperature: boolean;
knowledge?: string;
release_date: string;
last_updated: string;
open_weights: boolean;
interleaved?: boolean | { field: string };
status?: string;
cost?: {
input: number;
output: number;
cache_read?: number;
cache_write?: number;
};
limit: {
context: number;
output: number;
};
modalities: {
input: SupportedModality[];
output: SupportedModality[];
};
}
interface Changes {
field: string;
oldValue: string;
newValue: string;
}
type SupportedModality = "text" | "audio" | "image" | "video" | "pdf";
const modalityMap: Record<string, SupportedModality | undefined> = {
text: "text",
image: "image",
audio: "audio",
video: "video",
pdf: "pdf",
file: "pdf",
files: "pdf",
};
function getTodayDate(): string {
return new Date().toISOString().slice(0, 10);
}
function formatNumber(n: number): string {
if (n >= 1000) {
return n.toString().replace(/\B(?=(\d{3})+(?!\d))/g, "_");
}
return n.toString();
}
function formatDecimal(n: number): string {
return Number(n.toFixed(6)).toString();
}
function isSubstring(target: string, family: string): boolean {
return target.toLowerCase().includes(family.toLowerCase());
}
function matchesFamily(target: string, family: string): boolean {
const targetLower = target.toLowerCase();
const familyLower = family.toLowerCase();
let familyIdx = 0;
for (
let i = 0;
i < targetLower.length && familyIdx < familyLower.length;
i++
) {
if (targetLower[i] === familyLower[familyIdx]) {
familyIdx++;
}
}
return familyIdx === familyLower.length;
}
function inferFamily(modelId: string, modelName: string): string | undefined {
const kimiFamily = inferKimiFamily(modelId, modelName);
if (kimiFamily !== undefined) return kimiFamily;
const sortedFamilies = [...ModelFamilyValues].sort(
(a, b) => b.length - a.length,
);
for (const family of sortedFamilies) {
if (isSubstring(modelId, family) || isSubstring(modelName, family)) {
return family;
}
}
for (const family of sortedFamilies) {
if (matchesFamily(modelId, family) || matchesFamily(modelName, family)) {
return family;
}
}
return undefined;
}
function isValidFamily(family: string | undefined): family is ModelFamily {
return family !== undefined && ModelFamily.safeParse(family).success;
}
function resolveFamily(
existing: ExistingModel | null,
apiModel: z.infer<typeof ApiModel>,
): ModelFamily | undefined {
// Preserve a manually-curated family only when it's a recognized enum value;
// otherwise fall back to inference so we never emit an invalid family.
if (isValidFamily(existing?.family)) {
return existing.family;
}
const inferred = inferFamily(apiModel.id, apiModel.name);
return isValidFamily(inferred) ? inferred : undefined;
}
function normalizeName(apiModel: z.infer<typeof ApiModel>): string {
const stripped = apiModel.name.replace(/^[^:]+:\s*/, "").trim();
return stripped || path.basename(apiModel.id);
}
function normalizeModalities(values: string[]): SupportedModality[] {
const normalized = values
.map((value) => modalityMap[value.toLowerCase()])
.filter((value): value is SupportedModality => value !== undefined);
return [...new Set(normalized)];
}
async function loadExistingModel(
filePath: string,
): Promise<ExistingModel | null> {
try {
const file = Bun.file(filePath);
if (!(await file.exists())) {
return null;
}
const toml = await import(filePath, { with: { type: "toml" } }).then(
(mod) => mod.default,
);
return toml as ExistingModel;
} catch (cause) {
console.warn(`Warning: Failed to parse existing file ${filePath}:`, cause);
return null;
}
}
function mergeModel(
apiModel: z.infer<typeof ApiModel>,
existing: ExistingModel | null,
): MergedModel {
const inputModalities = normalizeModalities(apiModel.modalities?.input ?? []);
const outputModalities = normalizeModalities(
apiModel.modalities?.output ?? [],
);
const merged: MergedModel = {
...(existing?.base_model ? { base_model: existing.base_model } : {}),
...(existing?.base_model_omit
? { base_model_omit: existing.base_model_omit }
: {}),
name: existing?.name ?? normalizeName(apiModel),
family: resolveFamily(existing, apiModel),
attachment: existing?.attachment ?? apiModel.attachment,
reasoning: existing?.reasoning ?? apiModel.reasoning,
tool_call: existing?.tool_call ?? apiModel.tool_call,
temperature: existing?.temperature ?? apiModel.temperature ?? true,
release_date: existing?.release_date ?? apiModel.release_date,
last_updated: getTodayDate(),
open_weights: existing?.open_weights ?? apiModel.open_weights,
...(existing?.structured_output !== undefined
? { structured_output: existing.structured_output }
: apiModel.structured_output
? { structured_output: true }
: {}),
...((existing?.knowledge ?? apiModel.knowledge)
? { knowledge: existing?.knowledge ?? apiModel.knowledge }
: {}),
...((existing?.interleaved ?? apiModel.interleaved)
? { interleaved: existing?.interleaved ?? apiModel.interleaved }
: {}),
...((existing?.status ?? apiModel.status)
? { status: existing?.status ?? apiModel.status }
: {}),
limit: {
context: apiModel.limit?.context ?? existing?.limit?.context ?? 0,
output: apiModel.limit?.output ?? existing?.limit?.output ?? 0,
},
modalities: {
input:
inputModalities.length > 0
? inputModalities
: ((existing?.modalities?.input as
| SupportedModality[]
| undefined) ?? ["text"]),
output:
outputModalities.length > 0
? outputModalities
: ((existing?.modalities?.output as
| SupportedModality[]
| undefined) ?? ["text"]),
},
};
if (apiModel.cost) {
merged.cost = {
input: apiModel.cost.input,
output: apiModel.cost.output,
...(apiModel.cost.cache_read && apiModel.cost.cache_read > 0
? { cache_read: apiModel.cost.cache_read }
: {}),
...(apiModel.cost.cache_write && apiModel.cost.cache_write > 0
? { cache_write: apiModel.cost.cache_write }
: {}),
};
} else if (
existing?.cost?.input !== undefined &&
existing.cost.output !== undefined
) {
merged.cost = {
input: existing.cost.input,
output: existing.cost.output,
...(existing.cost.cache_read !== undefined
? { cache_read: existing.cost.cache_read }
: {}),
...(existing.cost.cache_write !== undefined
? { cache_write: existing.cost.cache_write }
: {}),
};
}
return merged;
}
function formatToml(model: MergedModel): string {
const lines: string[] = [];
if (model.base_model !== undefined) {
lines.push(`base_model = "${model.base_model}"`);
}
if (model.base_model_omit !== undefined) {
lines.push(
`base_model_omit = [${model.base_model_omit.map((item) => `"${item}"`).join(", ")}]`,
);
}
lines.push(`name = "${model.name.replace(/"/g, '\\"')}"`);
if (model.family) {
lines.push(`family = "${model.family}"`);
}
lines.push(`release_date = "${model.release_date}"`);
lines.push(`last_updated = "${model.last_updated}"`);
lines.push(`attachment = ${model.attachment}`);
lines.push(`reasoning = ${model.reasoning}`);
if (model.structured_output !== undefined) {
lines.push(`structured_output = ${model.structured_output}`);
}
lines.push(`temperature = ${model.temperature}`);
lines.push(`tool_call = ${model.tool_call}`);
if (model.knowledge) {
lines.push(`knowledge = "${model.knowledge}"`);
}
lines.push(`open_weights = ${model.open_weights}`);
if (model.status) {
lines.push(`status = "${model.status}"`);
}
if (model.interleaved !== undefined) {
lines.push("");
if (model.interleaved === true) {
lines.push("interleaved = true");
} else if (model.interleaved !== false) {
lines.push("[interleaved]");
lines.push(`field = "${model.interleaved.field}"`);
}
}
if (model.cost) {
lines.push("");
lines.push("[cost]");
lines.push(`input = ${formatDecimal(model.cost.input)}`);
lines.push(`output = ${formatDecimal(model.cost.output)}`);
if (model.cost.cache_read !== undefined) {
lines.push(`cache_read = ${formatDecimal(model.cost.cache_read)}`);
}
if (model.cost.cache_write !== undefined) {
lines.push(`cache_write = ${formatDecimal(model.cost.cache_write)}`);
}
}
lines.push("");
lines.push("[limit]");
lines.push(`context = ${formatNumber(model.limit.context)}`);
lines.push(`output = ${formatNumber(model.limit.output)}`);
lines.push("");
lines.push("[modalities]");
lines.push(
`input = [${model.modalities.input.map((m) => `"${m}"`).join(", ")}]`,
);
lines.push(
`output = [${model.modalities.output.map((m) => `"${m}"`).join(", ")}]`,
);
return `${lines.join("\n")}\n`;
}
function detectChanges(
existing: ExistingModel | null,
merged: MergedModel,
): Changes[] {
if (!existing) {
return [];
}
const changes: Changes[] = [];
const epsilon = 0.001;
const formatValue = (value: unknown): string => {
if (typeof value === "number") return formatNumber(value);
if (Array.isArray(value)) return `[${value.join(", ")}]`;
if (value === undefined) return "(none)";
return String(value);
};
const compare = (field: string, oldValue: unknown, newValue: unknown) => {
const changed = field.startsWith("cost.")
? oldValue === undefined && newValue === undefined
? false
: oldValue === undefined || newValue === undefined
? true
: Math.abs((oldValue as number) - (newValue as number)) > epsilon
: JSON.stringify(oldValue) !== JSON.stringify(newValue);
if (changed) {
changes.push({
field,
oldValue: formatValue(oldValue),
newValue: formatValue(newValue),
});
}
};
compare("name", existing.name, merged.name);
compare("family", existing.family, merged.family);
compare("release_date", existing.release_date, merged.release_date);
compare("attachment", existing.attachment, merged.attachment);
compare("reasoning", existing.reasoning, merged.reasoning);
compare(
"structured_output",
existing.structured_output,
merged.structured_output,
);
compare("temperature", existing.temperature, merged.temperature);
compare("tool_call", existing.tool_call, merged.tool_call);
compare("open_weights", existing.open_weights, merged.open_weights);
compare("cost.input", existing.cost?.input, merged.cost?.input);
compare("cost.output", existing.cost?.output, merged.cost?.output);
compare(
"cost.cache_read",
existing.cost?.cache_read,
merged.cost?.cache_read,
);
compare(
"cost.cache_write",
existing.cost?.cache_write,
merged.cost?.cache_write,
);
compare("limit.context", existing.limit?.context, merged.limit.context);
compare("limit.output", existing.limit?.output, merged.limit.output);
compare(
"modalities.input",
existing.modalities?.input,
merged.modalities.input,
);
compare(
"modalities.output",
existing.modalities?.output,
merged.modalities.output,
);
return changes;
}
async function main() {
const args = process.argv.slice(2);
const dryRun = args.includes("--dry-run");
const newOnly = args.includes("--new-only");
const modelsDir = path.join(
import.meta.dirname,
"..",
"..",
"..",
"providers",
"wandb",
"models",
);
console.log(
`${dryRun ? "[DRY RUN] " : ""}${newOnly ? "[NEW ONLY] " : ""}Fetching WandB models from API...`,
);
const res = await fetch(API_ENDPOINT);
if (!res.ok) {
console.error(`Failed to fetch API: ${res.status} ${res.statusText}`);
process.exit(1);
}
const json = await res.json();
const parsed = ApiResponse.safeParse(json);
if (!parsed.success) {
console.error("Invalid API response:", parsed.error.errors);
process.exit(1);
}
// The response groups models by provider; flatten to a single list.
const apiModels = Object.values(parsed.data).flatMap((provider) =>
Object.values(provider.models),
);
const existingFiles = new Set<string>();
for await (const file of new Bun.Glob("**/*.toml").scan({
cwd: modelsDir,
absolute: false,
})) {
existingFiles.add(file);
}
console.log(
`Found ${apiModels.length} models in API, ${existingFiles.size} existing files\n`,
);
const apiModelIds = new Set<string>();
let created = 0;
let updated = 0;
let unchanged = 0;
for (const apiModel of apiModels) {
const relativePath = `${apiModel.id}.toml`;
const filePath = path.join(modelsDir, relativePath);
const dirPath = path.dirname(filePath);
apiModelIds.add(relativePath);
const existing = await loadExistingModel(filePath);
const merged = mergeModel(apiModel, existing);
const tomlContent = formatToml(merged);
if (existing === null) {
created++;
if (dryRun) {
console.log(`[DRY RUN] Would create: ${relativePath}`);
console.log(` name = "${merged.name}"`);
if (merged.family) {
console.log(` family = "${merged.family}"`);
}
console.log("");
} else {
await mkdir(dirPath, { recursive: true });
await Bun.write(filePath, tomlContent);
console.log(`Created: ${relativePath}`);
}
continue;
}
if (newOnly) {
unchanged++;
continue;
}
const changes = detectChanges(existing, merged);
if (changes.length === 0) {
unchanged++;
continue;
}
updated++;
if (dryRun) {
console.log(`[DRY RUN] Would update: ${relativePath}`);
} else {
await mkdir(dirPath, { recursive: true });
await Bun.write(filePath, tomlContent);
console.log(`Updated: ${relativePath}`);
}
for (const change of changes) {
console.log(` ${change.field}: ${change.oldValue}${change.newValue}`);
}
console.log("");
}
const orphaned = [...existingFiles].filter((file) => !apiModelIds.has(file));
for (const file of orphaned) {
console.log(`Warning: Orphaned file (not in API): ${file}`);
}
console.log("");
console.log(
dryRun
? `Summary: ${created} would be created, ${updated} would be updated, ${unchanged} unchanged, ${orphaned.length} orphaned`
: `Summary: ${created} created, ${updated} updated, ${unchanged} unchanged, ${orphaned.length} orphaned`,
);
}
await main();
await main(["wandb", ...process.argv.slice(2)]);
+4 -1
View File
@@ -12,6 +12,7 @@ import { openrouter } from "./providers/openrouter.js";
import { ovhcloud } from "./providers/ovhcloud.js";
import { vercel } from "./providers/vercel.js";
import { venice } from "./providers/venice.js";
import { wandb } from "./providers/wandb.js";
import { xai } from "./providers/xai.js";
const ExistingModelType = AuthoredModelShape.partial()
@@ -86,6 +87,7 @@ export const providers: {
ovhcloud: SyncProvider<any>;
vercel: SyncProvider<any>;
venice: SyncProvider<any>;
wandb: SyncProvider<any>;
xai: SyncProvider<any>;
} = {
baseten,
@@ -96,11 +98,12 @@ export const providers: {
ovhcloud,
vercel,
venice,
wandb,
xai,
};
export const groups = {
aggregators: ["huggingface", "openrouter", "vercel"],
aggregators: ["huggingface", "openrouter", "vercel", "wandb"],
cloudflare: ["cloudflare-workers-ai"],
direct: ["baseten", "google", "ovhcloud", "venice", "xai"],
} as const;
+291
View File
@@ -0,0 +1,291 @@
import path from "node:path";
import { z } from "zod";
import { inferKimiFamily, ModelFamily, ModelFamilyValues } from "../../family.js";
import type { ExistingModel, SyncProvider, SyncedFullModel, SyncedModel } from "../index.js";
const API_ENDPOINT = "https://trace.wandb.ai/inference/modelsdev/models";
const WandbCost = z.object({
input: z.number(),
output: z.number(),
reasoning: z.number().optional(),
cache_read: z.number().optional(),
cache_write: z.number().optional(),
input_audio: z.number().optional(),
output_audio: z.number().optional(),
}).passthrough();
const WandbLimit = z.object({
context: z.number(),
input: z.number().optional(),
output: z.number(),
}).passthrough();
const WandbModalities = z.object({
input: z.array(z.string()),
output: z.array(z.string()),
}).passthrough();
export const WandbModel = z.object({
id: z.string(),
name: z.string(),
attachment: z.boolean(),
reasoning: z.boolean(),
tool_call: z.boolean(),
structured_output: z.boolean().optional(),
temperature: z.boolean().optional(),
knowledge: z.string().optional(),
release_date: z.string(),
last_updated: z.string(),
open_weights: z.boolean(),
status: z.string().optional(),
interleaved: z.union([z.boolean(), z.object({ field: z.string() }).passthrough()]).optional(),
cost: WandbCost.optional(),
limit: WandbLimit.optional(),
modalities: WandbModalities.optional(),
}).passthrough();
const WandbProvider = z.object({
id: z.string(),
name: z.string(),
npm: z.string(),
env: z.array(z.string()),
doc: z.string(),
api: z.string().optional(),
models: z.record(z.string(), WandbModel),
}).passthrough();
const WandbResponse = z.record(z.string(), WandbProvider);
export type WandbModel = z.infer<typeof WandbModel>;
type SupportedModality = "text" | "audio" | "image" | "video" | "pdf";
type InterleavedObject = Exclude<SyncedFullModel["interleaved"], true | undefined>;
const modalityMap: Record<string, SupportedModality | undefined> = {
text: "text",
image: "image",
audio: "audio",
video: "video",
pdf: "pdf",
file: "pdf",
files: "pdf",
};
export const wandb = {
id: "wandb",
name: "Weights & Biases",
modelsDir: "providers/wandb/models",
deleteMissing: false,
sourceID(model) {
return model.id;
},
missingNotice(paths) {
if (paths.length === 0) return [];
return [
`${paths.length} local W&B models were absent from the W&B Inference catalog and were retained for manual lifecycle review.`,
`Retained local paths: ${paths.map((item) => `\`${item}\``).join(", ")}`,
];
},
async fetchModels() {
const response = await fetch(API_ENDPOINT);
if (!response.ok) {
throw new Error(`W&B Inference request failed: ${response.status} ${response.statusText}`);
}
return response.json();
},
parseModels(raw) {
return Object.values(WandbResponse.parse(raw)).flatMap((provider) => Object.values(provider.models));
},
translateModel(model, context) {
return {
id: model.id,
model: buildWandbModel(model, context.existing(model.id)),
};
},
sameModel(current, desired) {
return sameWandbModel(current, desired);
},
} satisfies SyncProvider<WandbModel>;
export function buildWandbModel(
model: WandbModel,
existing: ExistingModel | undefined,
today = new Date().toISOString().slice(0, 10),
): SyncedFullModel {
const inputModalities = normalizeModalities(model.modalities?.input ?? []);
const outputModalities = normalizeModalities(model.modalities?.output ?? []);
return {
name: existing?.name ?? normalizeName(model),
family: resolveFamily(existing, model),
attachment: existing?.attachment ?? model.attachment,
reasoning: existing?.reasoning ?? model.reasoning,
reasoning_options: existing?.reasoning_options,
temperature: existing?.temperature ?? model.temperature ?? true,
tool_call: existing?.tool_call ?? model.tool_call,
structured_output: existing?.structured_output !== undefined
? existing.structured_output
: model.structured_output === true
? true
: undefined,
knowledge: existing?.knowledge ?? model.knowledge,
release_date: existing?.release_date ?? model.release_date,
last_updated: today,
open_weights: existing?.open_weights ?? model.open_weights,
status: resolveStatus(existing, model.status),
interleaved: existing?.interleaved ?? normalizeInterleaved(model.interleaved),
cost: buildCost(model.cost, existing?.cost),
limit: {
context: model.limit?.context ?? existing?.limit?.context ?? 0,
output: model.limit?.output ?? existing?.limit?.output ?? 0,
input: existing?.limit?.input,
},
modalities: {
input: inputModalities.length > 0
? inputModalities
: existing?.modalities?.input ?? ["text"],
output: outputModalities.length > 0
? outputModalities
: existing?.modalities?.output ?? ["text"],
},
};
}
function buildCost(
cost: WandbModel["cost"],
existing: ExistingModel["cost"] | undefined,
): SyncedFullModel["cost"] | undefined {
if (cost !== undefined) {
return {
input: cost.input,
output: cost.output,
cache_read: cost.cache_read !== undefined && cost.cache_read > 0
? cost.cache_read
: undefined,
cache_write: cost.cache_write !== undefined && cost.cache_write > 0
? cost.cache_write
: undefined,
};
}
if (existing?.input === undefined || existing.output === undefined) return undefined;
return {
input: existing.input,
output: existing.output,
cache_read: existing.cache_read,
cache_write: existing.cache_write,
};
}
function normalizeName(model: WandbModel): string {
const stripped = model.name.replace(/^[^:]+:\s*/, "").trim();
return stripped || path.basename(model.id);
}
function normalizeModalities(values: string[]): SupportedModality[] {
const normalized = values
.map((value) => modalityMap[value.toLowerCase()])
.filter((value): value is SupportedModality => value !== undefined);
return [...new Set(normalized)];
}
function normalizeInterleaved(
value: WandbModel["interleaved"],
): SyncedFullModel["interleaved"] | undefined {
if (value === true) return true;
if (value !== undefined && value !== false) {
return { field: value.field as InterleavedObject["field"] };
}
return undefined;
}
function resolveStatus(
existing: ExistingModel | undefined,
status: string | undefined,
): SyncedFullModel["status"] | undefined {
return existing?.status ?? (status as SyncedFullModel["status"] | undefined);
}
function resolveFamily(
existing: ExistingModel | undefined,
model: WandbModel,
): SyncedFullModel["family"] | undefined {
if (existing?.family !== undefined) return existing.family;
const inferred = inferFamily(model.id, model.name);
return isValidFamily(inferred) ? inferred : undefined;
}
function isValidFamily(family: string | undefined): family is ModelFamily {
return family !== undefined && ModelFamily.safeParse(family).success;
}
function inferFamily(modelID: string, modelName: string): string | undefined {
const kimiFamily = inferKimiFamily(modelID, modelName);
if (kimiFamily !== undefined) return kimiFamily;
const sortedFamilies = [...ModelFamilyValues].sort((a, b) => b.length - a.length);
for (const family of sortedFamilies) {
if (includesIgnoreCase(modelID, family) || includesIgnoreCase(modelName, family)) {
return family;
}
}
for (const family of sortedFamilies) {
if (isSubsequence(modelID, family) || isSubsequence(modelName, family)) {
return family;
}
}
return undefined;
}
function includesIgnoreCase(target: string, value: string) {
return target.toLowerCase().includes(value.toLowerCase());
}
function isSubsequence(target: string, value: string) {
const targetLower = target.toLowerCase();
const valueLower = value.toLowerCase();
let valueIndex = 0;
for (const character of targetLower) {
if (character === valueLower[valueIndex]) valueIndex++;
}
return valueIndex === valueLower.length;
}
function sameWandbModel(current: ExistingModel, desired: SyncedModel) {
const desiredModel = desired as SyncedFullModel;
const fields: Array<[unknown, unknown, boolean?]> = [
[current.name, desiredModel.name],
[current.family, desiredModel.family],
[current.release_date, desiredModel.release_date],
[current.attachment, desiredModel.attachment],
[current.reasoning, desiredModel.reasoning],
[current.structured_output, desiredModel.structured_output],
[current.temperature, desiredModel.temperature],
[current.tool_call, desiredModel.tool_call],
[current.open_weights, desiredModel.open_weights],
[current.cost?.input, desiredModel.cost?.input, true],
[current.cost?.output, desiredModel.cost?.output, true],
[current.cost?.cache_read, desiredModel.cost?.cache_read, true],
[current.cost?.cache_write, desiredModel.cost?.cache_write, true],
[current.limit?.context, desiredModel.limit?.context],
[current.limit?.output, desiredModel.limit?.output],
[current.modalities?.input, desiredModel.modalities?.input],
[current.modalities?.output, desiredModel.modalities?.output],
];
return fields.every(([currentValue, desiredValue, cost]) => {
if (cost && currentValue === undefined && desiredValue === undefined) return true;
if (cost && (currentValue === undefined || desiredValue === undefined)) return false;
if (cost && typeof currentValue === "number" && typeof desiredValue === "number") {
return Math.abs(currentValue - desiredValue) <= 0.001;
}
return JSON.stringify(currentValue) === JSON.stringify(desiredValue);
});
}