feat(catalogue): a curated file of providers and models, refreshed daily
The five connections in the database were never configured by anyone — a migration minted one per model from the adapter's own list, so the table held five decisions nobody made. What that list actually is, is a CATALOGUE: what you can choose from, which is not the same kind of thing as what you chose. So it is a file. `sidecar/catalogue/shipped.ts` is the curated baseline, in git, reviewed; `$OFFICER_ROOT/data/atelier/catalogue.json` is what a refresh writes, and it wins where it has something to say. Data dir, not the plugin dir — a plugin directory is a clone an update replaces, which is the rule env.ts already states for images. Refresh runs in the sidecar, daily, and only on boot when the file is actually stale — this process has restarted twenty times in an afternoon and should not hit OpenAI twenty times for it. Two sources, unequal, and the code says so. `GET /v1/models` is authoritative and thin: ids and nothing else, so a model matching /^(gpt-image|dall-e)/ appears the day it ships without a release. Prices come off an HTML page because there is no pricing endpoint for any plan, and that parser is written to give up rather than guess — a guess gets written to disk and then believed. When it gives up it says so in the log; the durable replacement is handing the page to a model for structured output, which Officer can already do locally. Nothing a refresh cannot establish overwrites a curated value: fields merge one by one, not objects wholesale, so an id-only listing cannot wipe the sizes and prices that were written by hand. Retiring is decided by the merge, which can tell a complete listing from a request that failed halfway — and it marks `deprecated` rather than deleting, because the gallery labels 88 historical rows from this list. Per-model sizes, qualities and transparency now travel with each entry, which is what will let the form stop offering dall-e-2 a transparent background it cannot do. The catalogue is advisory throughout: validate.ts still refuses to enforce per-model rules, exactly as before.
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
import { CATALOGUE_REVISION, type Catalogue } from './types';
|
||||
|
||||
/**
|
||||
* The catalogue as it ships with the plugin — the floor a refresh builds on, and the whole of it when
|
||||
* no refresh has ever run or every refresh has failed.
|
||||
*
|
||||
* Written by hand and reviewed in git, because none of it is fetchable: `GET /v1/models` returns ids,
|
||||
* an owner and a timestamp, and says nothing about price, sizes, transparency, or whether a model is
|
||||
* one you would still choose. That is the entire reason this file exists rather than a nightly fetch.
|
||||
*
|
||||
* The prices are USD per million tokens, from OpenAI's published pricing, and three of the five models
|
||||
* have none — deliberately. `PRICING` in providers/openai.ts had the same gap and the same reason: a
|
||||
* guessed price produces a number that looks authoritative and sums into a wrong total.
|
||||
*/
|
||||
export const SHIPPED_CATALOGUE: Catalogue = {
|
||||
revision: CATALOGUE_REVISION,
|
||||
providers: [
|
||||
{
|
||||
name: 'openai',
|
||||
label: 'OpenAI',
|
||||
envVar: 'OPENAI_API_KEY',
|
||||
defaultModel: 'gpt-image-1',
|
||||
models: [
|
||||
{
|
||||
id: 'gpt-image-1.5',
|
||||
label: 'GPT Image 1.5',
|
||||
kind: 'image',
|
||||
sizes: ['auto', '1024x1024', '1536x1024', '1024x1536'],
|
||||
qualities: ['auto', 'low', 'medium', 'high'],
|
||||
transparency: true,
|
||||
edit: true,
|
||||
pricing: { unit: 'token', textInput: 5, imageInput: 8, imageOutput: 32 },
|
||||
status: 'current',
|
||||
source: 'shipped',
|
||||
},
|
||||
{
|
||||
id: 'gpt-image-1',
|
||||
label: 'GPT Image 1',
|
||||
kind: 'image',
|
||||
sizes: ['auto', '1024x1024', '1536x1024', '1024x1536'],
|
||||
qualities: ['auto', 'low', 'medium', 'high'],
|
||||
transparency: true,
|
||||
edit: true,
|
||||
pricing: { unit: 'token', textInput: 5, imageInput: 10, imageOutput: 40 },
|
||||
status: 'current',
|
||||
source: 'shipped',
|
||||
},
|
||||
{
|
||||
id: 'gpt-image-1-mini',
|
||||
label: 'GPT Image 1 mini',
|
||||
kind: 'image',
|
||||
sizes: ['auto', '1024x1024', '1536x1024', '1024x1536'],
|
||||
qualities: ['auto', 'low', 'medium', 'high'],
|
||||
transparency: true,
|
||||
edit: true,
|
||||
pricing: { unit: 'token', textInput: 2, imageInput: 2.5, imageOutput: 8 },
|
||||
status: 'current',
|
||||
source: 'shipped',
|
||||
},
|
||||
{
|
||||
// No transparency and a different size set — which is exactly what the form was getting
|
||||
// wrong by offering one hard-coded list of sizes and qualities for every model.
|
||||
id: 'dall-e-3',
|
||||
label: 'DALL·E 3',
|
||||
kind: 'image',
|
||||
sizes: ['1024x1024', '1792x1024', '1024x1792'],
|
||||
qualities: ['standard', 'hd'],
|
||||
transparency: false,
|
||||
edit: false,
|
||||
status: 'current',
|
||||
source: 'shipped',
|
||||
},
|
||||
{
|
||||
id: 'dall-e-2',
|
||||
label: 'DALL·E 2',
|
||||
kind: 'image',
|
||||
sizes: ['256x256', '512x512', '1024x1024'],
|
||||
qualities: ['standard'],
|
||||
transparency: false,
|
||||
edit: true,
|
||||
status: 'current',
|
||||
source: 'shipped',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
Reference in New Issue
Block a user