UNPKG

@kontextmind/kxm

Version:

KXM local-first multi-agent orchestration and operator dashboard

573 lines (533 loc) • 24.6 kB
import { existsSync, readdirSync, readFileSync } from "node:fs"; import { join } from "node:path"; import { isAlias, isMap, isSeq, parse, parseDocument, stringify, type YAMLMap } from "yaml"; import type { PriceCatalog } from "./price-calc.ts"; import { hashPriceCatalog, parsePriceCatalog } from "./prices.ts"; import { OPENROUTER_MODELS_URL, inventoryModelFromAggregatorItem, type InventoryModel, type InventoryPrice, type ModelInventory, } from "./model-inventory.ts"; export { OPENROUTER_MODELS_URL }; const RATE_FIELDS = ["inputPerMillion", "outputPerMillion", "cacheReadPerMillion", "cacheWritePerMillion"] as const; type RateField = (typeof RATE_FIELDS)[number]; const LIST_CAP = 40; export interface CatalogReference { label: string; ids: readonly string[]; } export interface RateChange { field: RateField; before: number | null; after: number | null; /** Absolute relative change. Infinite when a numeric rate becomes null or leaves zero. */ ratio: number; } export interface FlaggedModel { id: string; kind: "disappeared" | "repriced"; detail: string; references: string[]; } export interface CatalogRefreshPlan { inventoryText: string; pricesText: string; changed: boolean; guardFailed: boolean; summary: string; added: string[]; removed: string[]; repriced: Array<{ id: string; changes: RateChange[] }>; priceRepriced: Array<{ id: string; changes: RateChange[] }>; retained: FlaggedModel[]; flaggedRepriced: FlaggedModel[]; tieredUnchanged: FlaggedModel[]; ambiguousPrices: string[]; date: string; pricedDate: string | null; } interface LoosePriceRow { id: string; provider: string; model: string; aliases?: string[]; tiers: Array<Record<string, unknown>>; } function nearlyEqual(left: number, right: number): boolean { const scale = Math.max(1, Math.abs(left), Math.abs(right)); return Math.abs(left - right) <= 1e-8 * scale; } function canonicalRate(value: number | null): number | null { if (value === null) return null; return Number(value.toFixed(10)); } function ratesNearlyEqual(left: InventoryPrice | undefined, right: InventoryPrice | undefined): boolean { return rateChanges(left, right).length === 0; } export function rateChanges(before: InventoryPrice | undefined, after: InventoryPrice | undefined): RateChange[] { const changes: RateChange[] = []; for (const field of RATE_FIELDS) { const previous = before?.[field] ?? null; const next = after?.[field] ?? null; if (previous === null && next === null) continue; if (previous !== null && next !== null && nearlyEqual(previous, next)) continue; let ratio = Infinity; if (previous !== null && next !== null) { ratio = previous === 0 ? (next === 0 ? 0 : Infinity) : Math.abs(next - previous) / Math.abs(previous); } if (ratio === 0) continue; changes.push({ field, before: previous, after: next, ratio }); } return changes; } function isSignificant(change: RateChange): boolean { if (change.before === null) return false; if (!Number.isFinite(change.ratio)) { return change.field === "inputPerMillion" || change.field === "outputPerMillion"; } return change.ratio > 0.2; } function expandIds(raw: string): string[] { const out = new Set<string>(); const addOne = (value: string): void => { const token = value.trim().toLowerCase(); if (!token || out.has(token)) return; out.add(token); if (token.startsWith("openrouter/")) addOne(token.slice("openrouter/".length)); const slash = token.indexOf("/"); if (slash <= 0) return; const vendor = token.slice(0, slash); const rest = token.slice(slash + 1); if (vendor === "xai") addOne(`x-ai/${rest}`); else if (vendor === "x-ai") addOne(`xai/${rest}`); const effort = rest.match(/^(.*)-(low|medium|high|max)$/); if (effort?.[1]) addOne(`${vendor}/${effort[1]}`); }; addOne(raw); return [...out]; } function priceRowTokens(row: LoosePriceRow): string[] { const tokens = new Set<string>(); const add = (value: string): void => { for (const id of expandIds(value)) tokens.add(id); }; add(row.id); if (row.model.includes("/")) add(row.model); else add(`${row.provider}/${row.model}`); // Aliases on native and OpenRouter rows point at the catalog id. A different // plan (qwen-token-plan, zai-coding-cn) keeps its own rate unless it shares a YAML anchor. const trackAliases = new Set(["anthropic", "openai", "google", "xai", "openrouter"]); if (trackAliases.has(row.provider)) { for (const alias of row.aliases ?? []) add(alias.includes("/") ? alias : `${row.provider}/${alias}`); } return [...tokens]; } function readLoosePrices(text: string): { date: string; rows: LoosePriceRow[] } { const parsed = parse(text) as { date?: unknown; models?: unknown }; if (!parsed || typeof parsed !== "object" || !Array.isArray(parsed.models)) { throw new Error("price catalog must contain a models array"); } if (typeof parsed.date !== "string") throw new Error("price catalog date must be a string"); const rows: LoosePriceRow[] = parsed.models.map((item, index) => { if (!item || typeof item !== "object") throw new Error(`price catalog model at index ${index} must be an object`); const row = item as Record<string, unknown>; if (typeof row.id !== "string" || typeof row.provider !== "string" || typeof row.model !== "string") { throw new Error(`price catalog model at index ${index} is missing id, provider, or model`); } const aliases = Array.isArray(row.aliases) ? row.aliases.filter((alias): alias is string => typeof alias === "string") : undefined; const tiers = Array.isArray(row.tiers) ? row.tiers.filter((tier): tier is Record<string, unknown> => Boolean(tier) && typeof tier === "object") : []; return { id: row.id, provider: row.provider, model: row.model, ...(aliases ? { aliases } : {}), tiers }; }); return { date: parsed.date, rows }; } function tierRates(tier: Record<string, unknown> | undefined): InventoryPrice | undefined { if (!tier) return undefined; const num = (value: unknown): number | null => (typeof value === "number" && Number.isFinite(value) ? value : null); const input = num(tier.inputPerMillion); const output = num(tier.outputPerMillion); if (input === null && output === null) return undefined; return { inputPerMillion: input, outputPerMillion: output, cacheReadPerMillion: num(tier.cacheReadPerMillion), cacheWritePerMillion: num(tier.cacheWritePerMillion), }; } function formatRate(value: number | null): string { if (value === null) return "none"; if (Number.isInteger(value)) return String(value); return String(Number(value.toFixed(10))); } function formatRatio(change: RateChange): string { if (!Number.isFinite(change.ratio) || change.before === null || change.after === null || change.before === 0) return ""; const signed = (change.after - change.before) / Math.abs(change.before); const pct = Math.round(signed * 1000) / 10; const text = Number.isInteger(pct) ? String(pct) : pct.toFixed(1); return ` (${pct > 0 ? "+" : ""}${text}%)`; } export function formatRateChanges(changes: readonly RateChange[]): string { return changes.map((change) => `${change.field} ${formatRate(change.before)} → ${formatRate(change.after)}${formatRatio(change)}`).join(", "); } function remember(map: Map<string, string[]>, token: string, label: string): void { const labels = map.get(token) ?? []; if (!labels.includes(label)) labels.push(label); map.set(token, labels); } function labelsFor(map: Map<string, string[]>, id: string): string[] { const labels = new Set<string>(); for (const token of expandIds(id)) { for (const label of map.get(token) ?? []) labels.add(label); } return [...labels].sort((left, right) => left.localeCompare(right)); } function indexOpenRouter(items: readonly unknown[]): Map<string, InventoryModel> { const byId = new Map<string, InventoryModel>(); for (const item of items) { const model = inventoryModelFromAggregatorItem(item, "openrouter", "standard"); if (!model) continue; byId.set(model.id.toLowerCase(), model); } if (byId.size === 0) throw new Error("OpenRouter catalog is empty"); return byId; } function assignRates(target: InventoryPrice, incoming: InventoryPrice): boolean { let changed = false; for (const field of RATE_FIELDS) { const before = target[field]; const after = canonicalRate(incoming[field]); if (before === null && after === null) continue; if (before !== null && after !== null && nearlyEqual(before, after)) continue; target[field] = after; changed = true; } return changed; } function desiredRates(matches: readonly InventoryModel[]): { rates?: InventoryPrice; ambiguous: boolean } { const priced = matches.filter((model) => model.standard); if (priced.length === 0) return { ambiguous: false }; const first = priced[0]?.standard; if (!first) return { ambiguous: false }; const ambiguous = priced.slice(1).some((model) => !ratesNearlyEqual(first, model.standard)); return ambiguous ? { ambiguous: true } : { rates: first, ambiguous: false }; } function writeTierRates(map: YAMLMap, previous: InventoryPrice, incoming: InventoryPrice): boolean { let changed = false; for (const field of RATE_FIELDS) { const before = previous[field]; const after = canonicalRate(incoming[field]); if (before === null && after === null) continue; if (before !== null && after !== null && nearlyEqual(before, after)) continue; map.set(field, after); changed = true; } return changed; } function updatePrices(text: string, updates: Map<string, InventoryPrice>, date: string): { text: string; dateChanged: boolean } { if (updates.size === 0) return { text, dateChanged: false }; const doc = parseDocument(text); const models = doc.get("models", true); if (!isSeq(models)) throw new Error("price catalog models must be a sequence"); const maps = new Map<string, YAMLMap>(); let wrote = false; for (const row of models.items) { if (!isMap(row)) continue; const tiers = row.get("tiers", true); if (!isSeq(tiers) || tiers.items.length !== 1) continue; const tier = tiers.items[0]; if (isMap(tier) && tier.anchor) maps.set(String(tier.anchor), tier); } const seen = new Set<YAMLMap>(); for (const row of models.items) { if (!isMap(row)) continue; const id = row.get("id"); if (typeof id !== "string" || !updates.has(id)) continue; const incoming = updates.get(id); if (!incoming) continue; const tiers = row.get("tiers", true); if (!isSeq(tiers) || tiers.items.length !== 1) continue; const tier = tiers.items[0]; const target = isMap(tier) ? tier : (isAlias(tier) ? maps.get(String(tier.source)) : undefined); if (!target || seen.has(target)) continue; seen.add(target); const previous = tierRates(target.toJSON() as Record<string, unknown>); if (!previous) continue; if (writeTierRates(target, previous, incoming)) wrote = true; } if (!wrote) return { text, dateChanged: false }; doc.set("date", date); const draft = doc.toString(); const body = parse(draft) as { sha256?: string; schema: "kxm.prices.v1"; date: string; currency?: string; models: LoosePriceRow[] }; const { sha256: _ignored, ...rest } = body; doc.set("sha256", hashPriceCatalog(rest as unknown as Omit<PriceCatalog, "sha256">)); const next = doc.toString(); parsePriceCatalog(next); return { text: next, dateChanged: true }; } function listLines(title: string, ids: readonly string[]): string[] { const lines = [`- ${title}: ${ids.length}`]; const shown = ids.slice(0, LIST_CAP); for (const id of shown) lines.push(` - \`${id}\``); if (ids.length > LIST_CAP) lines.push(` - … and ${ids.length - LIST_CAP} more`); return lines; } export function renderCatalogSummary(plan: Omit<CatalogRefreshPlan, "summary">): string { const lines: string[] = ["## Needs a human", ""]; if (plan.retained.length === 0 && plan.flaggedRepriced.length === 0 && plan.tieredUnchanged.length === 0) { lines.push("No admitted route or roster model disappeared or was repriced by more than 20%."); } else { for (const flag of plan.retained) { lines.push(`- **Disappeared, kept:** \`${flag.id}\` — ${flag.references.join("; ") || "referenced model"}`); } for (const flag of plan.flaggedRepriced) { lines.push(`- **Repriced more than 20%:** \`${flag.id}\` ${flag.detail}${flag.references.length ? ` — ${flag.references.join("; ")}` : ""}`); } for (const flag of plan.tieredUnchanged) { lines.push(`- **Tiered price left unchanged:** \`${flag.id}\` ${flag.detail}${flag.references.length ? ` — ${flag.references.join("; ")}` : ""}`); } } lines.push("", "## Inventory", ""); lines.push(...listLines("Added", plan.added)); lines.push(...listLines("Removed", plan.removed)); lines.push(`- Repriced: ${plan.repriced.length}`); for (const row of plan.repriced.slice(0, LIST_CAP)) lines.push(` - \`${row.id}\` ${formatRateChanges(row.changes)}`); if (plan.repriced.length > LIST_CAP) lines.push(` - … and ${plan.repriced.length - LIST_CAP} more`); lines.push("", "## Prices", ""); lines.push(`- Repriced: ${plan.priceRepriced.length}`); for (const row of plan.priceRepriced.slice(0, LIST_CAP)) lines.push(` - \`${row.id}\` ${formatRateChanges(row.changes)}`); if (plan.priceRepriced.length > LIST_CAP) lines.push(` - … and ${plan.priceRepriced.length - LIST_CAP} more`); if (plan.pricedDate) lines.push(`- Date: ${plan.pricedDate}`); if (plan.ambiguousPrices.length > 0) { lines.push(`- Ambiguous OpenRouter matches left unchanged: ${plan.ambiguousPrices.length}`); for (const id of plan.ambiguousPrices.slice(0, LIST_CAP)) lines.push(` - \`${id}\``); } lines.push(""); return lines.join("\n"); } function sortFlags(flags: FlaggedModel[]): FlaggedModel[] { return flags.sort((left, right) => left.id.localeCompare(right.id)); } /** * Applies an OpenRouter models payload to the checked-in inventory and price catalog. * The dated price field and inventory `fetchedAt` change only when the catalog content changes. * `fetchedAt` is midnight UTC on `date`, not a clock timestamp. */ export function planModelCatalogRefresh(input: { inventory: ModelInventory; inventoryText: string; pricesText: string; openRouterItems: readonly unknown[]; references: readonly CatalogReference[]; date: string; openRouterUrl?: string; }): CatalogRefreshPlan { if (!/^\d{4}-\d{2}-\d{2}$/.test(input.date)) throw new Error("date must be YYYY-MM-DD"); const feed = indexOpenRouter(input.openRouterItems); const loose = readLoosePrices(input.pricesText); const labelMap = new Map<string, string[]>(); for (const reference of input.references) { for (const id of reference.ids) { for (const token of expandIds(id)) remember(labelMap, token, reference.label); } } for (const row of loose.rows) { const tokens = priceRowTokens(row); const labels = [...new Set(tokens.flatMap((token) => labelMap.get(token) ?? []))].sort((left, right) => left.localeCompare(right)); if (labels.length === 0) continue; for (const token of tokens) { for (const label of labels) remember(labelMap, token, label); } } const referenced = (id: string): string[] => labelsFor(labelMap, id); const next = structuredClone(input.inventory) as ModelInventory; const previousRates = new Map(input.inventory.models.map((model) => [model.id, model.standard ? { ...model.standard } : undefined])); const present = new Set(feed.keys()); const added: string[] = []; const removed: string[] = []; const retained: FlaggedModel[] = []; for (const [id, incoming] of feed) { const current = next.models.find((model) => model.id.toLowerCase() === id); if (!current) { const created: InventoryModel = { id: incoming.id, ...(incoming.name ? { name: incoming.name } : {}), ...(incoming.contextLength !== undefined ? { contextLength: incoming.contextLength } : {}), capabilities: incoming.capabilities, sources: ["openrouter"], ...(incoming.standard ? { standard: incoming.standard } : {}), }; next.models.push(created); added.push(incoming.id); continue; } if (incoming.standard) { if (current.standard) assignRates(current.standard, incoming.standard); else current.standard = { ...incoming.standard }; } if (!current.sources.includes("openrouter")) current.sources.push("openrouter"); if (!current.name && incoming.name) current.name = incoming.name; if (current.contextLength === undefined && incoming.contextLength !== undefined) current.contextLength = incoming.contextLength; if (current.missingUpstream) delete current.missingUpstream; } next.models = next.models.filter((model) => { const key = model.id.toLowerCase(); if (present.has(key) || !model.sources.includes("openrouter")) return true; const references = referenced(model.id); if (references.length > 0) { if (!model.missingUpstream) { model.missingUpstream = true; retained.push({ id: model.id, kind: "disappeared", detail: "left the OpenRouter catalog; previous row kept", references }); } return true; } model.sources = model.sources.filter((source) => source !== "openrouter"); delete model.standard; if (model.sources.length === 0) { removed.push(model.id); return false; } return true; }); next.models.sort((left, right) => left.id.localeCompare(right.id)); added.sort((left, right) => left.localeCompare(right)); removed.sort((left, right) => left.localeCompare(right)); const url = input.openRouterUrl ?? OPENROUTER_MODELS_URL; const source = next.sources.openrouter ?? { url, ok: true }; if (source.url !== url || source.ok !== true || source.error) { next.sources.openrouter = { url, ok: true }; } else if (source !== next.sources.openrouter) { next.sources.openrouter = source; } const repriced: Array<{ id: string; changes: RateChange[] }> = []; const flaggedRepriced: FlaggedModel[] = []; for (const model of next.models) { if (!previousRates.has(model.id)) continue; const changes = rateChanges(previousRates.get(model.id), model.standard); if (changes.length === 0) continue; repriced.push({ id: model.id, changes }); if (!changes.some(isSignificant)) continue; const references = referenced(model.id); if (references.length === 0) continue; flaggedRepriced.push({ id: model.id, kind: "repriced", detail: formatRateChanges(changes), references }); } repriced.sort((left, right) => left.id.localeCompare(right.id)); const priceUpdates = new Map<string, InventoryPrice>(); const tieredUnchanged: FlaggedModel[] = []; const ambiguousPrices: string[] = []; const beforeTiers = new Map(loose.rows.map((row) => [row.id, tierRates(row.tiers[0])])); for (const row of loose.rows) { const matched = new Map<string, InventoryModel>(); for (const token of priceRowTokens(row)) { const hit = feed.get(token); if (hit) matched.set(hit.id.toLowerCase(), hit); } const desired = desiredRates([...matched.values()].sort((left, right) => left.id.localeCompare(right.id))); const current = beforeTiers.get(row.id); if (desired.ambiguous) { ambiguousPrices.push(row.id); continue; } if (!desired.rates || !current) continue; const canonical: InventoryPrice = { inputPerMillion: canonicalRate(desired.rates.inputPerMillion), outputPerMillion: canonicalRate(desired.rates.outputPerMillion), cacheReadPerMillion: canonicalRate(desired.rates.cacheReadPerMillion), cacheWritePerMillion: canonicalRate(desired.rates.cacheWritePerMillion), }; const changes = rateChanges(current, canonical); if (changes.length === 0) continue; const references = referenced(row.id); if (row.tiers.length !== 1) { if (references.length > 0 && changes.some(isSignificant)) { tieredUnchanged.push({ id: row.id, kind: "repriced", detail: formatRateChanges(changes), references }); } continue; } priceUpdates.set(row.id, canonical); } ambiguousPrices.sort((left, right) => left.localeCompare(right)); const priced = updatePrices(input.pricesText, priceUpdates, input.date); const priceRepriced: Array<{ id: string; changes: RateChange[] }> = []; for (const row of readLoosePrices(priced.text).rows) { const changes = rateChanges(beforeTiers.get(row.id), tierRates(row.tiers[0])); if (changes.length === 0) continue; priceRepriced.push({ id: row.id, changes }); const references = referenced(row.id); if (references.length > 0 && changes.some(isSignificant)) { flaggedRepriced.push({ id: row.id, kind: "repriced", detail: formatRateChanges(changes), references }); } } priceRepriced.sort((left, right) => left.id.localeCompare(right.id)); const inventoryChanged = JSON.stringify({ sources: next.sources, models: next.models }) !== JSON.stringify({ sources: input.inventory.sources, models: input.inventory.models }); let inventoryText = input.inventoryText; if (inventoryChanged) { next.fetchedAt = `${input.date}T00:00:00.000Z`; inventoryText = stringify(next); } const pricedDate = priced.dateChanged ? `${loose.date} → ${input.date}` : null; const plan: Omit<CatalogRefreshPlan, "summary"> = { inventoryText, pricesText: priced.text, changed: inventoryText !== input.inventoryText || priced.text !== input.pricesText, guardFailed: retained.length > 0, added, removed, repriced, priceRepriced, retained: sortFlags(retained), flaggedRepriced: sortFlags(flaggedRepriced), tieredUnchanged: sortFlags(tieredUnchanged), ambiguousPrices, date: input.date, pricedDate, }; return { ...plan, summary: renderCatalogSummary(plan) }; } export function loadCatalogReferences(root: string): CatalogReference[] { const references: CatalogReference[] = []; const routesPath = join(root, ".kxm", "routes.yaml"); if (!existsSync(routesPath)) throw new Error(`missing ${routesPath}`); const routes = parse(readFileSync(routesPath, "utf8")) as { admitted?: unknown; disabled?: unknown }; const collect = (ids: unknown, kind: "admitted" | "disabled"): void => { if (!Array.isArray(ids)) return; for (const id of ids) { if (typeof id !== "string" || !id) continue; references.push({ label: `routes.yaml ${kind} \`${id}\``, ids: [id] }); } }; collect(routes.admitted, "admitted"); collect(routes.disabled, "disabled"); const rolesDir = join(root, ".kxm", "roles"); if (!existsSync(rolesDir)) return references; for (const file of readdirSync(rolesDir).filter((name) => name.endsWith(".yaml")).sort()) { const role = parse(readFileSync(join(rolesDir, file), "utf8")) as { id?: unknown; roster?: unknown }; const roleId = typeof role.id === "string" ? role.id : file.replace(/\.yaml$/, ""); if (!Array.isArray(role.roster)) continue; for (const entry of role.roster) { if (!entry || typeof entry !== "object") continue; const route = (entry as { route?: unknown }).route; if (typeof route !== "string" || !route) continue; const ids = [route]; const modelPath = join(root, ".kxm", "models", `${route}.yaml`); if (existsSync(modelPath)) { const model = parse(readFileSync(modelPath, "utf8")) as { model?: unknown; vendor?: unknown }; if (typeof model.model === "string" && model.model) { ids.push(model.model); if (typeof model.vendor === "string" && model.vendor && !model.model.includes("/")) ids.push(`${model.vendor}/${model.model}`); } } references.push({ label: `roster ${roleId} \`${route}\``, ids }); } } return references; } export function readCatalogFiles(root: string): { inventory: ModelInventory; inventoryText: string; pricesText: string } { const inventoryText = readFileSync(join(root, ".kxm", "models", "inventory.yaml"), "utf8"); const pricesText = readFileSync(join(root, ".kxm", "prices.yaml"), "utf8"); const inventory = parse(inventoryText) as ModelInventory; if (inventory.schema !== "kxm.model-inventory.v1" || !Array.isArray(inventory.models)) { throw new Error("inventory.yaml is not a kxm.model-inventory.v1 catalog"); } return { inventory, inventoryText, pricesText }; }