Catalog-only. This file is how to add and maintain models and providers. Nothing else.
bun validateRun this after every catalog change. It must pass before a PR is mergeable.
| Lab model metadata | Provider model | |
|---|---|---|
| What | Provider-agnostic facts about a model the lab built | How a specific API host serves that model |
| Where | models/<lab-id>/<model-id>.toml |
providers/<provider-id>/models/.../<id>.toml |
| Examples | models/anthropic/claude-opus-4-6.toml, models/openai/gpt-5.4.toml |
providers/openrouter/models/anthropic/claude-opus-4.6.toml |
| Contains | name, description, capabilities, modalities, limits, weights, … | cost, reasoning_options, status, request shape, and only real overrides |
- Labs create models (Anthropic, OpenAI, Google, DeepSeek, Alibaba, …).
- Providers host or relay them (the lab’s own API, OpenRouter, Bedrock, a random OpenAI-compatible gateway, …).
Filename (minus .toml) is the model id. Never put an id field in the TOML. Schema is strict — unknown keys fail validation.
If the provider did not create the model, the provider entry must use base_model.
- Identify the underlying lab model.
- If
models/<lab>/<model>.tomlis missing, add it under the lab that made the model, then pointbase_modelat it. - Provider file stays override-only (see below).
base_model = "anthropic/claude-opus-4-6"
[cost]
input = 5.00
output = 25.00Use a full standalone provider model TOML only when:
- The provider is the lab (first-party host of its own model), or
- The model is unique to that host — private beta alias, custom/fine-tune, or something with no sensible shared lab identity elsewhere.
If you can name the lab model, it belongs in models/ and the host uses base_model. Do not skip creating models/ just because the file did not exist yet.
After base_model = "…", write only provider-specific fields or values that differ from the base. Never restate identical data.
Do not copy from base when unchanged: name, description, family, release_date, knowledge, open_weights, attachment, reasoning, tool_call, temperature, structured_output, matching [modalities] / [limit], etc.
Usually provider-authored: cost, reasoning_options, interleaved, status, provider, experimental, plus real deltas (smaller context, PDF-only input, different display name).
Optional:
base_model_omit = ["limit.input"] # drop inherited keys after merge- Plain objects (
[limit],[modalities], …) → deep-merge - Arrays and primitives → child replaces parent
- Omitted fields → inherited from
models/ base_model/base_model_omitare parse-time only — they do not appear in generated JSON- Missing
base_modeltarget → validation error
providers/<provider-id>/
provider.toml
logo.svg # required
models/.../*.toml
name = "Example"
npm = "@ai-sdk/openai-compatible" # or the native AI SDK package
env = ["EXAMPLE_API_KEY"]
api = "https://api.example.com/v1" # required for openai-compatible
doc = "https://example.com/docs"- Path:
providers/<provider-id>/logo.svg - Use
currentColorfor fills/strokes — no hardcoded colors, no fixed width/height - Prefer square
viewBox(e.g.0 0 24 24)
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">
<!-- paths -->
</svg>If the provider has a rich catalog API that can populate model data or authoritatively remove models it no longer serves, add a sync module (see sync.md). Thin endpoints stay hand-authored.
| Field | Notes |
|---|---|
name, description |
Schema-required |
release_date, last_updated |
Required on new lab entries (hosts inherit these) |
attachment, reasoning, tool_call, open_weights |
Required on new lab entries |
limit, modalities |
Required on new lab entries — providers must resolve limit.context + limit.output |
When you create models/<lab>/<model>.toml so a third-party host can base_model it, author a complete lab file (all rows above). Do not ship name/description-only lab stubs and expect an “override-only” host of just cost + reasoning_options to validate — missing inherited required fields fail bun validate.
After base_model merge (or full inline), the provider model must have:
| Field | Notes |
|---|---|
name, description |
From base or local |
attachment, reasoning, tool_call, open_weights |
Booleans |
release_date, last_updated |
Dates |
modalities, limit |
limit.context + limit.output required on providers |
cost |
Provider-side (unless intentionally request-only / no public price) |
reasoning_options |
Required when reasoning = true |
With base_model, do not restate fields already correct on the lab entry. Still author cost and (if reasoning) reasoning_options on the provider file.
| Field | Notes |
|---|---|
family |
Model family slug — set when known |
knowledge |
Knowledge cutoff (YYYY-MM or YYYY-MM-DD) |
temperature |
Whether temperature is respected |
structured_output |
Whether structured/JSON output is supported |
license, links, weights, benchmarks |
Enrichment |
| Field | Notes |
|---|---|
cost, reasoning_options |
Host pricing and API controls |
interleaved |
Reasoning side channel on this API (reasoning_content / reasoning_details, or true) |
status |
Lifecycle on this host: alpha / beta / deprecated |
provider, experimental |
Request-shape overrides / experimental modes |
- All
costvalues are USD per million tokens. Never publish EUR, CNY, CHF, etc. as if they were USD. - Convert other currencies and note rate/date in a top-of-file comment.
- Optional keys on cost:
reasoning,cache_read,cache_write,input_audio,output_audio. - Context-based pricing →
[[cost.tiers]], notcontext_over_200k.
[cost]
input = 2.50
output = 15.00
[[cost.tiers]]
tier = { type = "context", size = 200_000 }
input = 5.00
output = 22.50cost.context_over_200kis legacy output-only. Do not author it in TOML (schema rejects it on write). The generator may emit it for old consumers when a single 200k-style tier exists; always author tiers.- Tier
sizeis the context threshold where that band starts. No duplicate sizes.
Sync re-serializes many provider files and drops every comment except a leading header block. Put sources/rationale above the first key. Short comments next to a reasoning option for exact API syntax are fine when the file is not sync-owned.
Any provider model with reasoning = true must set reasoning_options for this host’s API. Details: .opencode/skills/audit-reasoning-options/SKILL.md.
| Host kind | Who | How to pick options |
|---|---|---|
| First-party lab | Provider is the lab (OpenAI, Anthropic, DeepSeek, Alibaba, Google, …) | Match that lab’s real API and existing providers/<lab>/ entries for the same generation. |
| Multi-model relay / gateway | Hosts many labs’ models (OpenRouter, Bedrock-as-relay, random OpenAI-compat aggregators, …) | Copy the underlying model’s controls from the lab entry + established same-surface peers. |
npm = "@ai-sdk/openai-compatible" does not mean “gateway.” DeepSeek and Alibaba are first-party labs that use that package with lab-specific fields (thinking.type, enable_thinking, thinking_budget, …). Classify by who runs the API, not by the AI SDK package name.
Do not invent a universal low/medium/high for every reasoner.
- Open
providers/<lab>/models/…for the underlying model (and 1–2 solid peers on the same kind of host). - Author that effort list (and toggle/budget if those entries have them and this host exposes the same kind of control).
- Common cases:
- GPT-style on relays → often
low/medium/high(addnone/xhighonly if native/peers have them) - DeepSeek V4 →
toggle+high/max(not L/M/H; lab maps low/medium→high) - Always-on / no control →
[]
- GPT-style on relays → often
- On relays: do not use
[]just because you could not re-test this host. Empty means no caller control, not uncertainty. - Never invent
budget_tokensunless this host (or the lab API it clearly proxies) has a real reasoning budget field. Notmax_tokens.
Same model ID, on and off, via a known request field. Separate -thinking / instruct IDs are not a toggle.
| Host control | Author |
|---|---|
Effort includes none and other graded levels |
Only effort with none in values — no toggle |
Separate on/off control and graded effort (no none in effort) |
toggle + effort with the actual levels |
| Binary on/off only | toggle alone |
Every toggle needs a leading top-of-file comment with the exact wire path (sync strips mid-file comments).
# Toggle: thinking.type = enabled|disabled
# Effort: reasoning_effort = high|max
name = "DeepSeek V4 Pro"
reasoning_options = [
{ type = "toggle" },
{ type = "effort", values = ["high", "max"] },
]# Toggle: enable_thinking true|false
# Budget: thinking_budget (integer reasoning tokens)
name = "Qwen3.5 Plus"
reasoning_options = [
{ type = "toggle" },
{ type = "budget_tokens" },
]# Off is effort=none; graded levels — no toggle
base_model = "openai/gpt-5.4"
reasoning_options = [{ type = "effort", values = ["none", "low", "medium", "high", "xhigh"] }]- Dated:
-v1:0suffix (anthropic.claude-3-5-sonnet-20241022-v1:0.toml) - Latest/undated: bare
-v1(anthropic.claude-opus-4-6-v1.toml) - Region prefixes:
us.,eu.,global.(default has no prefix)
- Dated:
@YYYYMMDD(claude-opus-4-5@20251101.toml) - Latest/undated:
@default(claude-opus-4-6@default.toml)
- New provider has compliant
logo.svg - Non-lab hosts use
base_model; missing lab metadata was added undermodels/when needed (complete lab file, not a stub) - Provider
base_modelfiles are override-only (no duplicated identical fields; no provider-only keys undermodels/) -
reasoning = true⇒reasoning_optionsset per policy above - Costs are USD/MTok
-
bun validatepasses
- PR body cites pricing/docs/API for data changes
- Sync module if the provider catalog is rich enough (
sync.md) - Leading TOML comment for sources on hand-authored files