import { refreshOpenAI } from './openai-refresh'; import { SHIPPED_CATALOGUE } from './shipped'; import { getCatalogue, mergeCatalogue, writeCatalogue } from './store'; import type { Catalogue } from './types'; /** * Bringing the catalogue up to date — on demand, never on a timer. * * There was a daily schedule here and it is gone. A refresh only matters at one moment — you are * choosing a provider and want to know its models are current — and at that moment a button answers * the question while a nightly job can only have already answered it, or not, invisibly. A timer also * buys a standing obligation: something that hits a vendor from every install forever, whether anyone * is looking at Atelier or not. * * One refresher per provider, each returning what it could establish. Adding a second provider is a * second function and a line in `REFRESHERS` — the same restraint hono.ts keeps on its mounts. */ const REFRESHERS: Record Promise<{ catalogue: Catalogue | null; listedIds: string[] | null }>> = { openai: refreshOpenAI, }; export const refreshableProviders = () => Object.keys(REFRESHERS); /** * Refresh one provider, or every provider we have a refresher for. * * Retiring happens HERE rather than in a refresher, because it is the one judgement that needs the * whole picture: a model we ship and the vendor no longer lists is deprecated, but only when the * listing actually succeeded. "The endpoint returned three models" and "the request failed after * three" are indistinguishable inside the refresher and completely different here. * * The merge starts from the catalogue as it stands, not from the shipped file, so refreshing OpenAI * cannot discard what a previous refresh established about anything else. */ export async function refreshCatalogue(providerName?: string): Promise { const names = providerName ? [providerName] : Object.keys(REFRESHERS); const unknown = names.filter((name) => !REFRESHERS[name]); if (unknown.length > 0) throw new Error(`No refresher for provider "${unknown.join(', ')}".`); let merged = mergeCatalogue(SHIPPED_CATALOGUE, await getCatalogue()); for (const name of names) { const { catalogue, listedIds } = await REFRESHERS[name]!(); if (!catalogue) continue; merged = mergeCatalogue(merged, catalogue); if (listedIds) { const listed = new Set(listedIds); for (const provider of merged.providers) { if (provider.name !== name) continue; for (const model of provider.models) { // Deprecated, not deleted: the gallery labels historical rows from this list, and 88 images // name the model that made them. model.status = listed.has(model.id) ? 'current' : 'deprecated'; } } } } const written = { ...merged, updatedAt: new Date().toISOString() }; await writeCatalogue(written); const count = written.providers.reduce((sum, provider) => sum + provider.models.length, 0); console.log(`[atelier:catalogue] refreshed ${names.join(', ')} — ${count} model(s) across ${written.providers.length} provider(s)`); return written; } export { getCatalogue };