Modell-id:n hör inte hemma i TypeScript

· 5 min läsning

Ett modell-id är det snabbast föränderliga faktumet i hela systemet. Leverantörer byter namn på dem, pensionerar dem, släpper en daterad variant, ändrar i tysthet vad det odaterade aliaset pekar på och låser hälften av dem bakom vilket konto som frågar. Kompilera in det i ett paket och du har garanterat att paketet har fel inom några veckor — inte trasigt på ett uppenbart sätt, vilket åtminstone vore en buggrapport, utan fel på just det sättet där en väljare erbjuder någon en modell som deras nyckel inte kan anropa.

Katalogen är därför en JSON-fil, och det intressanta är inte att den är JSON. Det är vad skiktningen under den medger om säkerhet.

Fem skikt, senare slår tidigare

0. pi-ai's built-in models      whatever it knows about the catalogued vendors
1. the bundled catalog.json     27 providers, 6 verified models
2. ~/.oharness/catalog.json     your edits, and what discovery found
3. a catalog path in config
4. providers / models in config
5. /models refresh              asks the vendor directly

Läs den sifferkolumnen som en rangordning efter tillförlitlighet snarare än en laddningsordning. Skikt 0 är någon annans ögonblicksbild av världen. Skikt 1 är vad vi var beredda att påstå rakt ut. Skikt 5 är leverantören som svarar om ditt konto, just nu — och det vinner, eftersom ingenting vi levererade för tre månader sedan vet mer om dina rättigheter än den endpoint du är på väg att anropa.

Det medföljande skiktet innehåller tjugosju leverantörer och sex verifierade modeller, och det förhållandet är hela designen. Fakta om leverantörer — endpointen, miljövariabeln, trådprotokollet — är stabila nog att leverera. Modell-id:n är det inte, så de enda som anges rakt ut är de som faktiskt gick att verifiera, och allt annat kommer via upptäckt som frågar varje leverantörs egen /models.

Sex är en obekväm siffra att publicera. Det är också den ärliga.

Åsidosättningar sker på fältnivå

Varför det spelar roll i det dagliga är mindre och tråkigare: att peka Kimi mot dess endpoint i Fastlandskina ska inte betyda att du anger allt annat om Kimi på nytt.

{ "providers": { "moonshot": { "baseUrl": "https://api.moonshot.cn/v1" } } }

Det är hela ändringen. Åsidosättningar av hela poster är så konfigurationsformat ruttnar — någon kopierar det levererade blocket för att ändra en URL, och två versioner senare är deras kopia det enda som står mellan dem och en rättning som alla andra redan har fått.

Där fröet tar slut

Skikt 0 förtjänar en kommentar, eftersom det är det som överraskar folk: en leverantör du aldrig har anslutit har ändå modeller att visa. De kommer från pi-ai, trådskiktet, som levererar en genererad katalog över det den känner till. De har kontextfönster, prissättning och resonemangsnivåer, och de vet ingenting alls om vad ditt konto får anropa — så både en angiven post och allt som upptäckten returnerar slår dem, och OHARNESS_NO_BUILTIN_MODELS=1 stänger av fröet helt.

Två gränser hindrar fröet från att ställa till skada.

En modell förs bara in som frö i en post vars protokoll matchar det API som pi-ai katalogiserar den mot. Mistrals modeller är listade mot pi-ai:s eget conversations-API, så de lämnas åt upptäckten i stället för att skickas till en OpenAI-kompatibel endpoint i en form den inte talar. Fel form ger ett 400-fel som du får felsöka, inte ett sämre svar du kan leva med.

Och där de två katalogerna är oense om en leverantörs id, stäms de av genom att den andra namnges i stället för genom gissning:

{ "providers": { "moonshot": { "piProvider": "moonshotai" } } }

En leverantör utan frö och ännu utan upptäckt — en lokal server, en gateway som ingen levererar namn för — visas ändå i väljaren som en enda rad som öppnar panelen där du ansluter den. Ett tomt läge som förklarar sig självt är bättre än en leverantör som helt enkelt saknas tills något osynligt villkor är uppfyllt.

Protokollet är det enda som är värt att namnge

En katalogposts protocol anger vilket format dess endpoint talar:

anthropic-messages   openai-chat      openai-responses
gemini               bedrock          vertex
openai-codex

openai-codex är den som motiverar att listan hålls explicit i stället för att härledas ur ett leverantörsnamn. Det är backend som Codex CLI talar med, och det är ett genuint annat trådformat än openai-responses, inte samma format på en annan värd: store nekas, max_output_tokens skickas inte och konto-id:t färdas i en egen header. Ett system som gissade ”det är OpenAI, använd OpenAI-formen” skulle ha fel om det på tre olika sätt.

SDK:erna bakom dessa importeras först när de behövs, så en session som bara talar med Anthropic laddar aldrig AWS- eller Google-klienterna.

Det som inte flyttade

Autentisering. Autentiseringsuppgifter löses per förfrågan av harnessens egen AuthManager, eftersom en prenumerationstoken som var giltig vid start inte är giltig en timme senare. Därför anropas pi-ai:s API-implementationer direkt och får en redan löst nyckel och headers, i stället för att gå via dess leverantörsautentisering och lagring av autentiseringsuppgifter.

Det är därför vissa poster inte levereras alls. Bedrock och Vertex fungerar som protokoll, men bara med autentiseringsuppgifter som redan finns i miljön — fullständig SigV4-signering och gcloud ADC ligger i exakt det leverantörsskikt som den här harnessen avstår från att använda för autentisering. Azure OpenAI har ingen fast bas-URL att katalogisera, eftersom endpointen är per driftsättning. Båda kan deklareras av den vars miljö redan är uppsatt; ingen av dem kommer att levereras som ett fungerande standardval som i tysthet inte är det.

Mönstret under allt detta: ange de fakta som står still, fråga om dem som inte gör det, och håll skillnaden synlig i filen i stället för begravd i en versionsanteckning.


OHarness är en agent-harness med öppen källkod, MIT-licensierad. Leverantörsreferensen dokumenterar varje skikt, varje åsidosättning och varje protokoll ovan, och leverantörsöversikten listar vad som ingår i den medföljande katalogen i dag.