Compare commits

...

86 Commits

Author SHA1 Message Date
github-actions[bot] d0b3056e3c fix: New OpenAI Terra & Luna Pricing 2026-07-30 23:15:20 +00:00
rakshith1928 664671e31f feat(perplexity-agent): add Moonshot Kimi K3 and K2.7 Code (#3875) 2026-07-30 16:08:03 -05:00
Matthew Feroz b288869bbb fix(merge-gateway): tolerate evolving modality values (#3874)
Co-authored-by: Matthew Feroz <matt.feroz@merge.dev>
2026-07-30 16:07:34 -05:00
github-actions[bot] bfe9e932a2 chore(sync): update Hugging Face model catalog (#3864)
* chore(sync): update Hugging Face model catalog

* fix(huggingface): add reasoning options for new models

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Aiden Cline <aidenpcline@gmail.com>
2026-07-30 13:03:57 -05:00
github-actions[bot] 1766ee634b chore(sync): update Vercel AI Gateway model catalog (#3867)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-07-30 12:50:50 -05:00
github-actions[bot] 8a8763408f chore(sync): update OpenRouter model catalog (#3862)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-07-30 12:50:31 -05:00
Aiden Cline bcb11b7701 fix(inkling): add output token limit (#3852) 2026-07-30 10:21:14 -05:00
Oskar 8853cb4de1 feat(hyper): relax base model in autosync (#3854)
* relax base model for hyper

* refresh charm models
2026-07-30 10:20:47 -05:00
navyblueglove 1268c4d86d fix(scaleway): remove support of deprecated models (#3855)
Co-authored-by: Reda Maizate <rmaizate@scaleway.com>
2026-07-30 10:20:21 -05:00
Barnyard 2957c49c50 Update The Grid models: update 9 models (#3857) 2026-07-30 10:18:37 -05:00
KiKaraage 459813bd25 feat(crof): add kimi-k3-eco (cheaper variant) (#3858)
* feat(crof): add kimi-k3-eco (cheaper variant)

* fix(crof): missing display name on Kimi K3 Eco
2026-07-30 10:18:20 -05:00
github-actions[bot] 161d7b235c chore(sync): update LLM Gateway model catalog (#3840)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-07-30 10:17:34 -05:00
github-actions[bot] df835625cc chore(sync): update OpenRouter model catalog (#3842)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-07-30 10:13:45 -05:00
github-actions[bot] 126f5f27ce chore(sync): update EmpirioLabs AI model catalog (#3844)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-07-30 10:13:30 -05:00
github-actions[bot] e59d9aa532 chore(sync): update Pioneer model catalog (#3846)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-07-30 10:13:20 -05:00
github-actions[bot] 475e5df1de chore(sync): update Vercel AI Gateway model catalog (#3847)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-07-30 10:13:12 -05:00
github-actions[bot] 14471959be chore(sync): update Charm Hyper model catalog (#3860)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-07-30 10:12:45 -05:00
JD b328dfe06b feat(neuralwatt): add deepseek-v4-flash and gemma-4-31b (#3824) 2026-07-30 10:12:18 -05:00
Aiden Cline c837f4d34e fix: add OpenCode models domain (#3849)
* fix: add OpenCode models domain

* fix: remove computed custom domain field
2026-07-29 22:51:31 -05:00
Asmae_ELAZRAK 762d7feef9 feat: add kimi K3 to cortecs (#3835)
* feat: add kimi K3 to cortecs

* fix: correct Cortecs Kimi K3 metadata

---------

Co-authored-by: Aiden Cline <aidenpcline@gmail.com>
2026-07-29 22:09:54 -05:00
Cas Burggraaf b424381291 Add GreenPT provider (#3726)
* Add GreenPT provider (26 models)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Add GreenPT provider logo

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Use base_model for Mistral Small 3.2 / Medium 3.5 and Green L (review)

Reference existing models/ metadata via base_model instead of
re-declaring provider-agnostic facts inline, per review feedback.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Add required description field to full-def models

Upstream schema now requires a non-empty description on models;
base_model entries inherit it, so add it to the self-contained ones.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Refresh GreenPT catalog against production

Reconciles every GreenPT entry with the live production catalog and addresses
the data-accuracy review:

- Context limits: add provider-specific limit.context where GreenPT serves a
  smaller window than the base metadata (gemma-3-27b-it 40k, devstral-2 200k,
  llama-3.3-70b 100k, qwen3-coder-30b 128k).
- Speech-to-text: reprice green-s / green-s-pro to the current EUR 0.12/hour
  pre-recorded rate, with the standard EUR 0.23/hour noted inline.
- Modalities: override attachment and modalities.input so each entry advertises
  exactly what GreenPT serves. Adds image input to gpt-oss-120b, green-r,
  green-r-raw, green-l, green-l-raw and mistral-small-3.2; drops the inherited
  video/audio modalities from qwen3.6-35b-a3b, qwen3.5-397b-a17b and the Kimi
  entries.
- Reasoning controls: reasoning_options now lists the full accepted effort set
  (none, minimal, low, medium, high) on every reasoning model.
- Token costs: refresh prices, including glm-5.2, glm-5.1, minimax-m2.5 and the
  three Kimi entries.

* Rename gemma-4-26b-a4b-it to gemma4

The GreenPT API serves this model under the id `gemma4`; the previous filename
did not resolve against the live endpoint. The upstream weights are still
referenced through base_model.

* Address automated review feedback

- Add the required top-of-file cost-conversion comment (rate 1.14 USD/EUR,
  captured 2026-07-24, with sources) to every EUR-sourced file, per the
  AGENTS.md cost schema rule.
- Scope reasoning_options to the GreenPT-hosted models whose reasoning control
  is documented first-party (gemma4, green-r, green-r-raw). The third-party
  pass-through endpoints forward reasoning_effort upstream unchanged and their
  per-model accepted values are not verified, so they now declare [] rather
  than an assumed effort enum.
- Publish the standard EUR 0.23/hour speech-to-text rate (USD 0.00437/minute)
  instead of the temporary promotional rate, so the catalog stays correct after
  the promotion ends on 2026-08-31. The promotion is documented in the header.

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-29 22:02:09 -05:00
Matthew Feroz e6d37bdcae feat(sync): add Merge Gateway model sync (#3249)
* feat(sync): add Merge Gateway model sync

* fix(merge-gateway): document reasoning controls

* fix(sync): preserve partial Merge Gateway metadata

* fix(merge-gateway): align route metadata sync

* fix(merge-gateway): treat supports_reasoning as a positive-only signal

The public /v1/models schema does not document supports_reasoning, and the
live catalog populates it inconsistently across vendor routes: the same
model reports true on one route and false on another (claude-opus-4-6 is
false via anthropic, true via bedrock), and reasoning-only models such as
deepseek-r1 report false on their sole route. Flipping reasoning = false
from that field erased curated reasoning metadata on 42 models.

- only confirm reasoning when an available route reports
  supports_reasoning = true (always accompanied by route reasoning
  metadata), defaulting reasoning_options to [] when none are curated
- preserve curated reasoning metadata when routes report false or omit
  the field
- restore the 42 erased reasoning entries (claude, deepseek-r1, gpt-oss,
  gemma, qwen, glm, nemotron, fugu) from curated values
- re-sync against the live catalog: gemini-embedding-001 added, route
  cache_read prices and display names ingested, qwen3.5-27b limits and
  modalities updated

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* chore(merge-gateway): refresh model catalog

* fix(merge-gateway): align synced model metadata

* docs(sync): trim Merge Gateway notes

* fix(merge-gateway): remove stale Qwen aliases

* test(merge-gateway): document sync coverage

* fix(merge-gateway): mark chat models as non-reasoning

---------

Co-authored-by: Matthew Feroz <matt.feroz@merge.dev>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Aiden Cline <aidenpcline@gmail.com>
2026-07-29 21:58:24 -05:00
Kassie Povinelli bebd608155 feat(llmgateway): add reasoning effort levels for kimi-k3 (#3843)
* feat(llmgateway): add reasoning effort levels for kimi-k3

The kimi-k3 entry declared no reasoning options. Verified against the
live gateway that reasoning_effort accepts
minimal|low|medium|high|xhigh|max and returns thinking traces in
message.reasoning, with depth scaling low < medium < high ~= max.
There is no working off switch ('none', reasoning.enabled=false,
thinking.type=disabled, and reasoning.exclude=true all still reason),
so 'none' is omitted and no toggle is declared.

* refactor(llmgateway): move kimi-k3 API mapping note into header comment

Inline comments on TOML entries are dropped by the sync re-serializer;
keep the reasoning_effort/reasoning.effort mapping note in the leading
comment block per repo convention.
2026-07-29 12:58:18 -05:00
github-actions[bot] 6a308dfbf7 chore(sync): update Venice model catalog (#3827)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-07-29 11:17:35 -05:00
github-actions[bot] 6455db8f76 chore(sync): update Chutes model catalog (#3802)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-07-29 11:17:25 -05:00
github-actions[bot] ebcf1c5136 chore(sync): update Baseten model catalog (#3821)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-07-29 11:09:49 -05:00
github-actions[bot] 6f4163d814 chore(sync): update CrossModel model catalog (#3819)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-07-29 11:09:42 -05:00
github-actions[bot] 05c55247fa chore(sync): update EmpirioLabs AI model catalog (#3803)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-07-29 11:09:34 -05:00
github-actions[bot] 83b4abd291 chore(sync): update OpenRouter model catalog (#3796)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-07-29 11:09:13 -05:00
github-actions[bot] 5a66940016 chore(sync): update Deep Infra model catalog (#3825)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-07-29 11:08:57 -05:00
github-actions[bot] 3516638e90 chore(sync): update Vercel AI Gateway model catalog (#3828)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-07-29 11:08:47 -05:00
github-actions[bot] 214e4198af chore(sync): update Weights & Biases model catalog (#3829)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-07-29 11:00:50 -05:00
github-actions[bot] f4ecada627 chore(sync): update LLM Gateway model catalog (#3838)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-07-29 10:27:48 -05:00
github-actions[bot] fe06f6b0b8 chore(sync): update Charm Hyper model catalog (#3837)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-07-29 10:27:37 -05:00
huggix bddb089b85 feat(sync): add safe NanoGPT model catalog sync (#3342)
* Add safe NanoGPT model sync provider

* Address NanoGPT canonical model review

* Fix remaining NanoGPT canonical variants

* Harden NanoGPT canonical model sync

* Preserve NanoGPT overrides during factoring
2026-07-29 10:24:57 -05:00
github-actions[bot] 2605c54574 fix: dev CI failure (#3839)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-07-29 11:24:03 -04:00
Dax Raad f412635d8c Add models.opencode.ai domain 2026-07-29 11:13:33 -04:00
Deven Navani b6a79f21e5 Add Modal as an inference provider (#3760)
* Add Modal as an inference provider

* Use Modal inference gateway
2026-07-28 17:29:14 -05:00
Aiden Cline 814f7e04e0 fix(openrouter): temporarily skip :batch model routes (#3822)
Batch endpoints are not catalog targets; filter them out during sync.
2026-07-28 13:54:24 -05:00
Fenil Modi 3e74f55316 fix(aiand): fix logo.svg not rendering in provider catalog (#3800)
* fix(aiand): rescale logo.svg to 24x24 icon format

The previous logo used a 1280x1280 viewBox with a translate(0 430)
transform, causing it to render blank/broken at small icon sizes in
OpenCode's provider catalog. Rescaled to 24x24 following the convention
used by fireworks-ai, nebius, and other providers.

* fix(aiand): fix logo.svg rendering at icon sizes

Crop viewBox to the actual content bounding box (0 471 1280 430)
and add explicit width/height="24" so the logo renders correctly
at small icon sizes in OpenCode's provider catalog.
Original paths are unchanged.

* fix(aiand): fix logo.svg not rendering in provider catalog

Add width/height="24" and crop viewBox to "0 471 1280 430" —
the exact bounding box of the logo content after translate(0 430).
No path data changed.
2026-07-28 13:42:28 -05:00
github-actions[bot] 185a4f4cc1 chore(sync): update Vercel AI Gateway model catalog (#3807)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-07-28 12:02:06 -05:00
github-actions[bot] b209b33ce1 chore(sync): update Charm Hyper model catalog (#3801)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-07-28 12:01:55 -05:00
Fenil Modi 3e72fc6374 fix(aiand): remove glm-5.1 and kimi-k2.6 — not in live catalog (#3806)
* fix(aiand): remove glm-5.1 — not in live catalog (superseded by glm-5.2)

* fix(aiand): remove kimi-k2.6 — not in live catalog (superseded by kimi-k2.7-code and kimi-k3)
2026-07-28 12:01:34 -05:00
Suat-B adfe923c2c Add Xpersona premium model lineup (#3817)
* Add Xpersona premium model lineup

* Fix GPT-5.4 Mini limit inheritance

* Fix GPT-5.4 and GPT-5.5 input limit inheritance

* Align Xpersona serving limits and reasoning metadata

* Restore inherited context field for GPT-5.4 Mini
2026-07-28 11:59:50 -05:00
Christian Landgren 7343d8b35c feat(berget): add Kimi K3 (#3810)
* feat(berget): add Kimi K3

Moonshot AI's 2.8T-parameter open-weights model, served on Berget AI's
Swedish infrastructure (NVIDIA B300, SGLang with DSpark speculative
decoding).

- reasoning_effort none/low/medium/high/max mapped to K3's native
  low/high/max; reasoning returned in message.reasoning_content
- 320k context window, 32k max output
- Multimodal input (text + image)
- Pricing: $3 input / $15 output per 1M tokens, $0.30 cache read

* fix(berget): drop cache_read price, tidy reasoning comment

- Remove cache_read: no separate cache-read price on Berget
- Move reasoning comment to file top and drop xhigh mention
  (Copilot review)

* fix(berget): Kimi K3 reasoning_effort to native low/high/max

K3 only has three native reasoning levels (low/high/max, default max) and
cannot disable thinking. The previous list (none/low/medium/high/max) mixed
in clamped OpenAI-compat values and implied a granularity the model does not
have — and 'none' is misleading since K3 always thinks. The Berget API still
accepts the full OpenAI effort set and clamps it, but only the three distinct
levels are advertised. Matches the 'distinct functional levels' convention
used by our other models.

---------

Co-authored-by: berget-code <noreply@berget.ai>
Co-authored-by: berget <dev@berget.ai>
2026-07-28 11:59:23 -05:00
github-actions[bot] 69a5617db0 chore(sync): update Venice model catalog (#3818)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-07-28 11:57:33 -05:00
David Knaack 42f0d9ff3f chore(sap-ai-core): add gemini-embedding (version: 001/latest) (#3811) 2026-07-28 11:57:24 -05:00
Billy Cao 52d5045ee7 feat(synthetic): Add Kimi K3 model (#3794)
Deploy / deploy (push) Has been cancelled
* Add Synthetic's Kimi K3 offering

* Update cache read price

* fix(synthetic): declare effort-only reasoning for Kimi K3 per Synthetic API docs

Synthetic's OpenAI-compatible chat completions API documents reasoning_effort
with values low | medium | high and no reasoning on/off toggle, so drop the
toggle option and align effort values with the documented surface (matching
the existing Synthetic Kimi K2.6 / K2.7-Code entries).

https://dev.synthetic.new/docs/openai/chat-completions

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* Remove unnecessary comment

* Retrigger transient actions failure

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-27 23:36:15 -05:00
github-actions[bot] 4faf76317a chore(sync): update Venice model catalog (#3791)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-07-27 23:36:01 -05:00
github-actions[bot] efb5d8ea0d chore(sync): update Baseten model catalog (#3798)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-07-27 23:35:53 -05:00
Fenil Modi 0273194a65 feat(aiand): add Kimi K3 (#3799)
* feat(aiand): add Kimi K3

* fix(aiand): remove pdf from kimi-k3 modalities, text+image only

* fix(aiand): restore pdf modality for kimi-k3 based on /v1/models evidence

PDF was removed to match fireworks/ollama-cloud, but that is not valid
evidence for aiand. Sibling aiand entries (kimi-k2.6, kimi-k2.7-code)
keep pdf after catalog/probe evidence. Restoring pdf per original
GET /v1/models data which showed document support.
2026-07-27 23:35:42 -05:00
Abliteration AI a034112075 Add abliterated-model-large (#3793)
* Add abliterated-model-large

* Fix reasoning abliterated-model-large.toml

* fix provider

* Update abliterated-model-large.toml

* removed interleaved reasoning

* fixed docs and effort

* Address review: verified reasoning controls, citations, provider docs

- abliterated-model: reasoning = true with effort ladder and toggle,
  per docs.abliteration.ai/capabilities/thinking
- abliterated-model-large: replace unverified effort values with the
  documented ladder (none..max via reasoning_effort) plus thinking
  toggle; add API-syntax comments; move all source citations into the
  leading header block; align max output with docs (999,990)
- provider.toml: restore reasoning notes with the current verified
  per-endpoint request fields

* Narrow abliterated-model-large effort values to distinct modes

The API maps minimal-high -> high and xhigh-max -> max, so only none,
high, and max are distinct outcomes. Alias mapping kept as a comment.
2026-07-27 23:27:01 -05:00
Oskar b91080aa0e feat(hyper): add Charm Hyper provider and sync module (#3352)
* feat(hyper): add Charm Hyper provider and sync module

* feat: resync models

* fix: remove references to /provider endpoint

* feat: simplify model resolution

* fix logo

* feat: add base model resolution

* update models

* feat: add reasoning_options with base model fallback

* fix: undo env relaxation

* feat: round prices

* fix(hyper): sync modalities from vision

* .

* fix(hyper): remove base model reasoning fallback
2026-07-27 23:23:43 -05:00
Aiden Cline 6fda2e07c2 fix(nvidia): add missing NIM chat models and correct API ids (#3744)
* fix(nvidia): add missing NIM chat models and correct API ids

Add high-demand NVIDIA NIM models used by OpenCode (Nemotron Super/Ultra/Nano,
Inkling, Laguna XS, Mistral Medium 3.5, Ministral 14B, Gemma 3, Cosmos Reason2)
and rename catalog ids that used underscores so they match integrate.api.nvidia.com.

Fixes anomalyco/opencode#38865

* fix(nvidia): audit NIM reasoning_options against infer docs

Keep only verified controls (mistral-medium-3.5-128b reasoning_effort
none|high). Set reasoning_options=[] and drop interleaved where NIM OpenAPI
does not document a control. Narrow inkling modalities to text+image and cite
max_tokens bounds for Super/Laguna output limits.

* fix(nvidia): restore verified Nemotron prompt toggles and Inkling audio

First-party NIM model cards document reasoning ON/OFF via system prompts for
Super v1/v1.5, Ultra 253B, and Nano 8B. NVIDIA's Inkling card lists text/image/audio
inputs. Keep empty reasoning_options only where no control is documented (Laguna,
VL models). Align max_tokens with infer OpenAPI bounds.
2026-07-27 20:45:13 -05:00
github-actions[bot] 24b7a2aa4c chore(sync): update OpenRouter model catalog (#3758)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-07-27 20:44:47 -05:00
Vladimir Glafirov d1d08289ac feat: add gitlab duo-chat-opus-5 model (#3765) 2026-07-27 20:44:27 -05:00
rakshith1928 9875219078 feat(kimi-k3): add Kimi K3 model configuration with pricing and modalities (#3789) 2026-07-27 20:43:33 -05:00
amrrs ec23529c0c fix(nebius): fix Kimi K3 reasoning_options for Nebius Token Factory (#3792)
* fix(nebius): curate Kimi K3 reasoning_options from verified API behavior

PR #3780 merged Kimi K3 for Nebius with reasoning_options = [] (no
verified control surface). Live testing against
api.tokenfactory.nebius.com/v1/chat/completions shows reasoning_effort
is a real, validated parameter: invalid values 422, and valid values
visibly change reasoning_content length. Curate the accepted literal
list instead of leaving it empty.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* fix(nebius): narrow Kimi K3 reasoning_options to backend-verified values

Live testing invoking each literal (not just triggering the generic
gateway validator) shows the sglang model backend itself rejects
"minimal" and "xhigh" with a 400: "Input should be 'none', 'low',
'medium', 'high' or 'max'". Those two were only accepted by the
gateway's shared schema, not by this model. Narrow the list to the
5 values that actually work end-to-end.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-27 20:43:18 -05:00
KiKaraage 62ef55a446 feat(crof): add Kimi K3 (#3795)
* feat(crof): add Kimi K3

* fix(crof): change reasoning levels to low-high-max

* fix(crof): add "none" reasoning back for Kimi K3
2026-07-27 20:42:40 -05:00
github-actions[bot] 6eaf975918 chore(sync): update Venice model catalog (#3786)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-07-27 16:06:11 -05:00
rakshith1928 f8ac4b4fb1 feat(ollama-cloud): add kimi k3 model (#3787)
* feat(ollama-cloud): add kimi k3 model

* update ollama reasoning

* Revise Kimi K3 model documentation and sources
2026-07-27 16:06:00 -05:00
github-actions[bot] 03e2178662 chore(sync): update Baseten model catalog (#3770)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-07-27 14:50:15 -05:00
github-actions[bot] 03e495d946 chore(sync): update Ambient model catalog (#3771)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-07-27 14:46:40 -05:00
github-actions[bot] 1f5a03df40 chore(sync): update Vercel AI Gateway model catalog (#3772)
* chore(sync): update Vercel AI Gateway model catalog

* fix(vercel): set Kimi K3 Fast reasoning effort options

K3 always reasons; expose verified low/high/max via reasoning_effort.

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Aiden Cline <aidenpcline@gmail.com>
2026-07-27 14:46:30 -05:00
github-actions[bot] 0968fea09f chore(sync): update LLM Gateway model catalog (#3779)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-07-27 14:43:34 -05:00
github-actions[bot] 1fb770040a chore(sync): update Deep Infra model catalog (#3782)
* chore(sync): update Deep Infra model catalog

* fix(deepinfra): set Kimi-K3 reasoning effort options

K3 always reasons; expose verified low/high/max via reasoning_effort.

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Aiden Cline <aidenpcline@gmail.com>
2026-07-27 14:43:20 -05:00
Kevin 1efc768ae5 Add Kimi K3 to Nebius Token Factory (#3780)
Register moonshotai/Kimi-K3 with Nebius pricing and limits from
https://tokenfactory.nebius.com/api/public/models_info.
2026-07-27 14:42:37 -05:00
github-actions[bot] c3aab14477 chore(sync): update Hugging Face model catalog (#3784)
* chore(sync): update Hugging Face model catalog

* fix(huggingface): set Kimi-K3 reasoning effort options

K3 always reasons; expose verified low/high/max via reasoning_effort.

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Aiden Cline <aidenpcline@gmail.com>
2026-07-27 14:42:20 -05:00
Zain Hasan 236d2dd99a add kimi k3 (#3783) 2026-07-27 14:37:56 -05:00
github-actions[bot] ad211c8f8f chore(sync): update Venice model catalog (#3781)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-07-27 14:37:42 -05:00
Jack 38ccccc20d add kimi k3 to Zen 2026-07-28 01:08:00 +08:00
github-actions[bot] cce20188e5 chore(sync): update Venice model catalog (#3775)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-07-27 11:33:49 -05:00
Ahmad Shahzad 2acddd4818 feat(fireworks-ai): add Kimi K3 and Kimi K3 Fast (#3777) 2026-07-27 11:33:33 -05:00
Ahmad Shahzad c67dbc2e02 fix(fireworks-ai): remove deprecated GLM 5.1 and GLM 5.1 Fast (#3730)
Deploy / deploy (push) Has been cancelled
Fireworks AI will decommission GLM 5.1 and GLM 5.1 Fast serverless
endpoints on 2026-07-26, with GLM 5.2 and GLM 5.2 Fast serving as
their recommended replacements:

  GLM 5.1      -> GLM 5.2      (accounts/fireworks/models/glm-5p2)
  GLM 5.1 Fast -> GLM 5.2 Fast (accounts/fireworks/routers/glm-5p2-fast)

Remove the two model files ahead of the decommission date. Dedicated
deployments are unaffected.
2026-07-26 22:48:41 -05:00
github-actions[bot] 790e5cb842 chore(sync): update Ambient model catalog (#3747)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-07-26 22:48:00 -05:00
github-actions[bot] 73160c42bd chore(sync): update OpenRouter model catalog (#3748)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-07-26 22:47:50 -05:00
Carlo Francisco ff9bc91921 fix(thinkingmachines): name 256K variant "Inkling (256K)" (#3755)
Both Tinker Inkling tiers rendered with the same display name "Inkling"
because the :peft:262144 variant inherits it via base_model. Downstream
consumers (e.g. opencode) show two indistinguishable entries despite
different context windows and pricing. Override the name to match
the "Inkling (256K)" label used on Tinker's pricing page.
2026-07-26 22:47:35 -05:00
Nathan Nguyen 9c249c78cb feat(cloudflare-ai-gateway): add Claude Opus 5 (#3736) 2026-07-26 15:33:34 -05:00
github-actions[bot] c40d2ae925 chore(sync): update OpenRouter model catalog (#3733)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-07-25 22:57:04 -05:00
github-actions[bot] 71b3ca345d chore(sync): update Vercel AI Gateway model catalog (#3732)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-07-25 22:56:55 -05:00
github-actions[bot] 0b0414d78e chore(sync): update Weights & Biases model catalog (#3731)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-07-25 22:56:49 -05:00
github-actions[bot] f5edd52931 chore(sync): update Ambient model catalog (#3745)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-07-25 22:34:31 -05:00
opencode-agent[bot] d53651e3d9 fix(reviewer): compare reasoning options by API surface (#3746)
Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com>
2026-07-25 22:34:20 -05:00
Aiden Cline 2e25bad01c chore(azure): remove retired models, mark deprecated still-serving (#3729)
* chore(azure): remove retired models, mark deprecated still-serving

Delete Foundry models past retirement (chat snapshots, Phi-3, old GPT-4,
retired Meta/Cohere/DeepSeek/Mistral/xAI/Moonshot entries). Clean broken
azure-cognitive-services symlinks that pointed at deleted azure models.

Mark still-serving Deprecated/Legacy models with status = "deprecated"
(gpt-4.1*, gpt-4o*, o1/o3-mini/o4-mini, codex-mini, gpt-image-1,
deepseek-r1, claude-opus-4-1).

Sources:
- https://learn.microsoft.com/en-us/azure/foundry/openai/concepts/model-retirement-schedule
- https://learn.microsoft.com/en-us/azure/foundry/openai/concepts/retired-models

* fix(azure): address review — Preview status + Nov-2025 cohort

- Remove status=deprecated from gpt-image-1 and claude-opus-4-1
  (official lifecycle is Preview, not Deprecated)
- Delete remaining Nov-2025 OpenAI cohort for consistency with o1-mini:
  gpt-3.5-turbo-0125/1106/instruct, gpt-4-turbo, gpt-4-turbo-vision
- Drop broken azure-cognitive-services symlinks

* fix(azure): restore Nov-2025 OpenAI cohort as deprecated

Azure schedule/retired-models pages do not list gpt-4-turbo or
gpt-3.5-turbo-0125/1106/instruct as Retired. OpenAI still serves the
turbo family (catalog marks deprecated). Restore these IDs with
status=deprecated instead of deleting, matching OpenAI catalog policy.

Keep o1-mini deleted (long shut down on OpenAI API).
2026-07-25 15:04:01 -05:00
github-actions[bot] fcf16dcf64 chore(sync): update Ambient model catalog (#3727)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-07-25 13:52:39 -05:00
github-actions[bot] 8a61715de2 chore(sync): update CrossModel model catalog (#3743)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-07-25 13:14:58 -05:00
477 changed files with 5731 additions and 1577 deletions
+1
View File
@@ -74,6 +74,7 @@ jobs:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
VENICE_API_KEY: ${{ secrets.VENICE_API_KEY }}
LLMGATEWAY_API_KEY: ${{ secrets.LLMGATEWAY_API_KEY }}
MERGE_GATEWAY_API_KEY: ${{ secrets.MERGE_GATEWAY_API_KEY }}
KILO_API_KEY: ${{ secrets.KILO_API_KEY }}
GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY }}
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
+2
View File
@@ -38,6 +38,8 @@ For model catalog changes, enforce these review rules:
- Treat a missing compliant logo for a new provider as a merge blocker. The SVG must use `currentColor`, have no fixed size or hardcoded color, and preferably use a square `viewBox`.
- Treat duplicated provider-agnostic metadata as a merge blocker when a matching `models/<provider>/<model>.toml` exists; the provider entry must use `base_model` and retain only provider-specific fields and overrides.
- Treat missing `reasoning_options` on `reasoning = true` provider models as a merge blocker. Options describe controls exposed by that inference provider, not merely by the upstream model. An empty array is correct when reasoning exists but no caller control is verified.
- Before reporting a `reasoning_options` problem, compare the proposed model with existing entries for the same underlying model that use a comparable request surface. Determine that surface from the effective `npm`, provider API shape, and any model-level provider override—not from the model family alone. Prefer native-provider examples when the target uses the native SDK (for example, an Anthropic model through `@ai-sdk/anthropic` should be compared with the Anthropic provider). Prefer established OpenAI-compatible gateway examples when the target uses an OpenAI-compatible chat-completions surface (for example, Cloudflare AI Gateway may be usefully compared with OpenRouter). Do not compare a native Anthropic route with an OpenAI-compatible gateway as though their controls were interchangeable.
- Use those peer entries as required review context, not as values to copy mechanically or as standalone proof. Consistent same-model, same-surface examples make a proposed option more plausible and help identify likely omissions or contradictions; target-provider documentation, endpoint metadata, adapter behavior, or reproduced requests still override peer precedent. A lack of bespoke provider documentation is not by itself an action item when the target surface and strong peer examples support the proposal and the diff contains no concrete contradictory evidence. Conversely, do not accept or reject `reasoning_options = []` mechanically: explain the specific mismatch with the target API shape or comparable providers before requesting a change.
- Do not treat absence of a sync module as a blocker. Recommend one only when a context-rich provider API can authoritatively populate model data or delete models no longer served.
- Data-changing PRs should cite direct provider pricing, model documentation, or API references in the PR body. Missing citations are not by themselves a merge blocker, but should be reported as a low-severity request for evidence when material factual changes otherwise cannot be reviewed. Prefer first-party sources and require each citation to state what it supports.
- You cannot fetch citation URLs. Assess whether citations are present, direct, and mapped to claims, but never claim you opened a URL or verified its contents. A URL or PR assertion alone does not prove a disputed value.
+1
View File
@@ -17,6 +17,7 @@ license = "Apache-2.0"
[limit]
context = 1_048_576
output = 1_048_576
[modalities]
input = ["text", "image", "audio"]
+2
View File
@@ -28,6 +28,8 @@
"huggingface:sync": "bun ./packages/core/script/sync-models.ts huggingface",
"kilo:sync": "bun ./packages/core/script/sync-models.ts kilo",
"llmgateway:sync": "bun ./packages/core/script/sync-models.ts llmgateway",
"merge-gateway:sync": "bun ./packages/core/script/sync-models.ts merge-gateway",
"nano-gpt:sync": "bun ./packages/core/script/sync-models.ts nano-gpt",
"venice:sync": "bun ./packages/core/script/sync-models.ts venice",
"vercel:generate": "bun ./packages/core/script/sync-models.ts vercel",
"wandb:generate": "bun ./packages/core/script/sync-models.ts wandb",
+33 -12
View File
@@ -15,9 +15,12 @@ import { deepinfra } from "./providers/deepinfra.js";
import { digitalocean } from "./providers/digitalocean.js";
import { empiriolabs } from "./providers/empiriolabs.js";
import { google } from "./providers/google.js";
import { hyper } from "./providers/hyper.js";
import { huggingface } from "./providers/huggingface.js";
import { kilo } from "./providers/kilo.js";
import { llmgateway } from "./providers/llmgateway.js";
import { mergeGateway } from "./providers/merge-gateway.js";
import { nanoGpt } from "./providers/nano-gpt.js";
import { openai } from "./providers/openai.js";
import { openrouter } from "./providers/openrouter.js";
import { ovhcloud } from "./providers/ovhcloud.js";
@@ -72,6 +75,7 @@ export interface SyncProvider<SourceModel> {
deleteMissing?: boolean;
preserveSymlinks?: boolean;
preserveBaseModels?: boolean;
preserveDescriptions?: boolean;
sameModel?(current: ExistingModel, desired: SyncedModel): boolean;
missingNotice?(paths: string[]): string[];
/**
@@ -114,9 +118,12 @@ export const providers: {
digitalocean: SyncProvider<any>;
empiriolabs: SyncProvider<any>;
google: SyncProvider<any>;
kilo: SyncProvider<any>;
hyper: SyncProvider<any>;
huggingface: SyncProvider<any>;
kilo: SyncProvider<any>;
llmgateway: SyncProvider<any>;
"merge-gateway": SyncProvider<any>;
"nano-gpt": SyncProvider<any>;
openai: SyncProvider<any>;
openrouter: SyncProvider<any>;
ovhcloud: SyncProvider<any>;
@@ -136,9 +143,12 @@ export const providers: {
digitalocean,
empiriolabs,
google,
kilo,
hyper,
huggingface,
kilo,
llmgateway,
"merge-gateway": mergeGateway,
"nano-gpt": nanoGpt,
openai,
openrouter,
ovhcloud,
@@ -150,9 +160,19 @@ export const providers: {
};
export const groups = {
aggregators: ["crossmodel", "empiriolabs", "huggingface", "kilo", "llmgateway", "openrouter", "vercel"],
aggregators: [
"crossmodel",
"empiriolabs",
"huggingface",
"kilo",
"llmgateway",
"merge-gateway",
"nano-gpt",
"openrouter",
"vercel",
],
cloudflare: ["cloudflare-workers-ai"],
direct: ["ambient", "anthropic", "baseten", "chutes", "deepinfra", "digitalocean", "google", "openai", "ovhcloud", "pioneer", "venice", "wandb", "xai"],
direct: ["ambient", "anthropic", "baseten", "chutes", "deepinfra", "digitalocean", "google", "hyper", "openai", "ovhcloud", "pioneer", "venice", "wandb", "xai"],
} as const;
type ProviderID = keyof typeof providers;
@@ -239,16 +259,17 @@ export async function syncProvider<SourceModel>(
} else {
resolvedReasoning = existing.get(relativePath)?.toml.reasoning;
}
const withReasoningOptions = preserveReasoningOptions(
translatedModel,
existing.get(relativePath)?.authored,
resolvedReasoning,
);
const withDescription = provider.preserveDescriptions === false
? withReasoningOptions
: preserveDescription(withReasoningOptions, existing.get(relativePath)?.authored);
const parsed = SyncedAuthoredModel.safeParse(stripUndefined({
id: translated.id,
...preserveDescription(
preserveReasoningOptions(
translatedModel,
existing.get(relativePath)?.authored,
resolvedReasoning,
),
existing.get(relativePath)?.authored,
),
...withDescription,
}));
if (!parsed.success) {
parsed.error.cause = { provider: provider.id, path: relativePath };
+197
View File
@@ -0,0 +1,197 @@
import { existsSync } from "node:fs";
import path from "node:path";
import { z } from "zod";
import { describeModel } from "../../describe.js";
import type { ExistingModel, SyncProvider, SyncedFullModel, SyncedModel } from "../index.js";
import { factorBaseModel, resolveModelMetadataBaseModel } from "./openrouter.js";
const API_ENDPOINT = "https://hyper.charm.land/v1/models";
const MODELS_DIR = path.join(import.meta.dirname, "..", "..", "..", "..", "..", "models");
function baseModelExists(modelID: string) {
return existsSync(path.join(MODELS_DIR, `${modelID}.toml`));
}
function resolveHyperBaseModel(modelID: string, existingBase: string | undefined) {
if (existingBase !== undefined && baseModelExists(existingBase)) return existingBase;
const resolved = resolveModelMetadataBaseModel(modelID);
return resolved !== undefined && baseModelExists(resolved) ? resolved : undefined;
}
const ReasoningEffort = z.enum([
"default",
"max",
"low",
"high",
"none",
"medium",
"minimal",
"xhigh",
]);
export const HyperModel = z.object({
id: z.string(),
created: z.number(),
display_name: z.string(),
context_window: z.number(),
max_output_tokens: z.number(),
capabilities: z.object({
vision: z.boolean().optional(),
}).optional(),
reasoning: z.object({
effort_levels: z.array(z.object({
value: z.string(),
display: z.string().optional(),
})).optional(),
}).optional(),
pricing: z.object({
input: z.number().optional(),
output: z.number().optional(),
cache_hit: z.number().optional(),
cache_create: z.number().optional(),
}).optional(),
}).passthrough();
const HyperResponse = z.object({
data: z.array(HyperModel),
}).passthrough();
export type HyperModel = z.infer<typeof HyperModel>;
export const hyper = {
id: "hyper",
name: "Charm Hyper",
modelsDir: "providers/hyper/models",
preserveBaseModels: false,
async fetchModels() {
const key = process.env.HYPER_API_KEY;
const response = await fetch(API_ENDPOINT, key
? { headers: { Authorization: `Bearer ${key}` } }
: undefined);
if (!response.ok) {
throw new Error(`Hyper models request failed: ${response.status} ${response.statusText}`);
}
return response.json();
},
parseModels(raw) {
return HyperResponse.parse(raw).data;
},
translateModel(model, context) {
const existing = context.existing(model.id);
return {
id: model.id,
model: buildHyperModel(model, existing),
};
},
} satisfies SyncProvider<HyperModel>;
function dateFromTimestamp(timestamp: number) {
return new Date(timestamp * 1000).toISOString().slice(0, 10);
}
function reasoningOptions(model: HyperModel) {
const effortLevels = model.reasoning?.effort_levels?.map((level) => level.value) ?? [];
if (effortLevels.length === 0) return [];
const values = effortLevels.filter(isReasoningEffort);
if (values.length === 0) return [{ type: "toggle" as const }];
return [{ type: "effort" as const, values }];
}
function isReasoningEffort(value: string): value is z.infer<typeof ReasoningEffort> {
return ReasoningEffort.safeParse(value).success;
}
function price(value: number) {
return Math.round(value * 1_000_000) / 1_000_000;
}
function positivePrice(value: number | undefined) {
return value !== undefined && value > 0 ? price(value) : undefined;
}
function buildCost(model: HyperModel, existing: ExistingModel["cost"] | undefined) {
const pricing = model.pricing;
if (pricing?.input === undefined || pricing.output === undefined) return existing;
return {
input: price(pricing.input),
output: price(pricing.output),
cache_read: positivePrice(pricing.cache_hit)
?? (pricing.cache_hit === undefined ? existing?.cache_read : undefined),
cache_write: positivePrice(pricing.cache_create)
?? (pricing.cache_create === undefined ? existing?.cache_write : undefined),
reasoning: existing?.reasoning,
};
}
function hyperModalities(vision: boolean) {
const input = vision ? ["text" as const, "image" as const] : ["text" as const];
return {
input,
output: ["text" as const],
};
}
export function buildHyperModel(
model: HyperModel,
existing: ExistingModel | undefined,
baseModel = existing?.base_model,
today = new Date().toISOString().slice(0, 10),
): SyncedModel {
const limit = {
context: model.context_window,
input: existing?.limit?.input,
output: model.max_output_tokens,
};
const modalities = hyperModalities(model.capabilities?.vision ?? false);
const reasoning = model.reasoning != null;
const releaseDate = existing?.release_date ?? dateFromTimestamp(model.created);
const values: Partial<SyncedFullModel> = {
attachment: modalities.input.some((value) => value !== "text"),
modalities,
reasoning,
release_date: releaseDate,
last_updated: existing?.last_updated ?? today,
interleaved: existing?.interleaved,
cost: buildCost(model, existing?.cost),
limit,
};
if (reasoning) values.reasoning_options = reasoningOptions(model);
const resolvedBase = resolveHyperBaseModel(model.id, baseModel);
if (resolvedBase !== undefined) {
return factorBaseModel(
resolvedBase,
values,
limit,
existing?.base_model === resolvedBase ? existing.base_model_omit : undefined,
);
}
const name = existing?.name ?? model.display_name;
return {
name,
description: existing?.description ?? describeModel({
id: model.id,
name,
family: existing?.family,
reasoning,
tool_call: existing?.tool_call ?? true,
structured_output: existing?.structured_output,
open_weights: existing?.open_weights ?? false,
limit,
modalities,
}),
family: existing?.family,
...values,
temperature: existing?.temperature,
tool_call: existing?.tool_call ?? true,
structured_output: existing?.structured_output,
knowledge: existing?.knowledge,
open_weights: existing?.open_weights ?? false,
status: existing?.status,
provider: existing?.provider,
experimental: existing?.experimental,
};
}
@@ -0,0 +1,339 @@
import { z } from "zod";
import { describeModel } from "../../describe.js";
import type { ExistingModel, SyncProvider, SyncedFullModel, SyncedModel } from "../index.js";
import { factorBaseModel, resolveCanonicalBaseModel } from "./openrouter.js";
const API_ENDPOINT = "https://api-gateway.merge.dev/v1/models";
const AvailabilityStatus = z.enum(["available", "deprecated"]);
const VendorReasoning = z.object({
configurable: z.boolean().optional(),
disable_supported: z.boolean().optional(),
default_enabled: z.boolean().optional(),
controls: z.array(z.string()).optional(),
output_style: z.string().nullable().optional(),
}).passthrough();
const VendorCapabilities = z.object({
// Keep the API boundary forward-compatible; `modalities()` filters the
// evolving Gateway vocabulary to values supported by models.dev.
input: z.array(z.string()),
output: z.array(z.string()),
supports_tool_calling: z.boolean(),
supports_tool_choice: z.boolean().default(false),
supports_structured_outputs: z.boolean(),
supports_reasoning: z.boolean().optional(),
reasoning: VendorReasoning.nullable().optional(),
streaming: z.boolean(),
}).passthrough();
const PromptCaching = z.object({
mode: z.enum(["automatic", "explicit", "none"]).optional(),
cache_read_cost_per_million: z.number().nonnegative().nullable().optional(),
cache_write_cost_per_million: z.number().nonnegative().nullable().optional(),
}).passthrough();
const VendorInfo = z.object({
launch_date: z.string().nullable().optional(),
context_window: z.number().int().nonnegative(),
max_output_tokens: z.number().int().nonnegative(),
availability_status: AvailabilityStatus,
capabilities: VendorCapabilities,
pricing: z.object({
currency: z.literal("USD").default("USD"),
input_per_million: z.number().nonnegative(),
output_per_million: z.number().nonnegative(),
cache_read_per_million: z.number().nonnegative().nullable().optional(),
cache_write_per_million: z.number().nonnegative().nullable().optional(),
}).passthrough(),
prompt_caching: PromptCaching.nullable().optional(),
}).passthrough();
export const MergeGatewayModel = z.object({
model: z.string().min(1),
provider: z.string().min(1),
display_name: z.string().min(1),
vendors: z.record(VendorInfo),
availability_status: AvailabilityStatus,
created_at: z.string().nullable().optional(),
updated_at: z.string().nullable().optional(),
}).passthrough().superRefine((model, context) => {
const namespace = model.model.split("/")[0];
if (namespace !== model.provider) {
context.addIssue({
code: z.ZodIssueCode.custom,
path: ["provider"],
message: `Model namespace ${namespace} does not match provider ${model.provider}`,
});
}
});
export const MergeGatewayResponse = z.object({
object: z.literal("list").default("list"),
data: z.array(MergeGatewayModel),
has_more: z.boolean().default(false),
next_cursor: z.string().nullable().optional(),
}).passthrough();
export type MergeGatewayModel = z.infer<typeof MergeGatewayModel>;
export type MergeGatewayVendor = z.infer<typeof VendorInfo>;
export async function fetchMergeGatewayModels(
fetcher: typeof fetch = fetch,
apiKey = process.env.MERGE_GATEWAY_API_KEY,
) {
if (!apiKey) throw new Error("MERGE_GATEWAY_API_KEY is required to sync Merge Gateway models");
const models = new Map<string, MergeGatewayModel>();
const cursors = new Set<string>();
let cursor: string | undefined;
do {
const url = new URL(API_ENDPOINT);
url.searchParams.set("limit", "500");
if (cursor !== undefined) url.searchParams.set("cursor", cursor);
const response = await fetcher(url, {
headers: { Authorization: `Bearer ${apiKey}` },
});
if (!response.ok) {
throw new Error(`Merge Gateway request failed: ${response.status} ${response.statusText}`);
}
const page = MergeGatewayResponse.parse(await response.json());
for (const model of page.data) {
if (models.has(model.model)) {
throw new Error(`Merge Gateway returned duplicate model ID: ${model.model}`);
}
models.set(model.model, model);
}
if (!page.has_more) break;
if (!page.next_cursor) throw new Error("Merge Gateway returned has_more=true without next_cursor");
if (cursors.has(page.next_cursor)) throw new Error(`Merge Gateway repeated cursor: ${page.next_cursor}`);
cursors.add(page.next_cursor);
cursor = page.next_cursor;
} while (true);
return {
object: "list" as const,
data: [...models.values()],
has_more: false,
next_cursor: null,
};
}
export const mergeGateway = {
id: "merge-gateway",
name: "Merge Gateway",
modelsDir: "providers/merge-gateway/models",
// API-key policy can affect catalog visibility. Retain missing local models
// until Merge exposes an account-independent catalog response.
deleteMissing: false,
sourceID(model) {
return model.model;
},
skippedNotice(ids) {
if (ids.length === 0) return [];
return [
`${ids.length} Merge Gateway models were skipped because they are not text models or lack canonical metadata.`,
`Skipped remote IDs: ${ids.map((id) => `\`${id}\``).join(", ")}`,
];
},
missingNotice(paths) {
if (paths.length === 0) return [];
return [
`${paths.length} local Merge Gateway models were absent from the API response and retained for manual lifecycle review.`,
`Retained local paths: ${paths.map((item) => `\`${item}\``).join(", ")}`,
];
},
fetchModels() {
return fetchMergeGatewayModels();
},
parseModels(raw) {
return MergeGatewayResponse.parse(raw).data;
},
translateModel(model, context) {
const existing = context.existing(model.model);
const translated = buildMergeGatewayModel(model, existing, context.authored(model.model));
return translated === undefined ? undefined : { id: model.model, model: translated };
},
} satisfies SyncProvider<MergeGatewayModel>;
export function selectMergeGatewayVendor(model: MergeGatewayModel) {
const canonical = model.vendors[model.provider];
if (canonical?.availability_status === "available") {
return { id: model.provider, info: canonical };
}
// Match Gateway's default resolver: when the model author's native route is
// unavailable, use the cheapest active route by combined input + output
// price. Object order is preserved for equal prices; the public API emits
// vendors in CMS-priority order, which is Gateway's own tiebreaker.
const available = Object.entries(model.vendors)
.filter(([, info]) => info.availability_status === "available");
const selected = available.reduce<typeof available[number] | undefined>((best, candidate) => {
if (best === undefined) return candidate;
const bestCost = best[1].pricing.input_per_million + best[1].pricing.output_per_million;
const candidateCost = candidate[1].pricing.input_per_million + candidate[1].pricing.output_per_million;
return candidateCost < bestCost ? candidate : best;
}, undefined);
if (selected !== undefined) return { id: selected[0], info: selected[1] };
if (canonical !== undefined) return { id: model.provider, info: canonical };
const fallback = Object.entries(model.vendors)[0];
return fallback === undefined ? undefined : { id: fallback[0], info: fallback[1] };
}
export function buildMergeGatewayModel(
model: MergeGatewayModel,
existing: ExistingModel | undefined,
authored: ExistingModel | undefined = existing,
): SyncedModel | undefined {
const selected = selectMergeGatewayVendor(model);
if (selected === undefined || !selected.info.capabilities.output.includes("text")) return undefined;
const input = modalities(selected.info.capabilities.input);
const output = modalities(selected.info.capabilities.output);
const limit = {
context: selected.info.context_window || existing?.limit?.context || 0,
// Preserve only a provider-authored input cap. `existing` is resolved
// against base-model metadata, so using its inherited input value here
// can keep an impossible cap when the gateway reports a smaller context.
input: authored?.limit?.input,
output: selected.info.max_output_tokens || existing?.limit?.output || selected.info.context_window,
};
const cachePricing = mergeGatewayCachePricing(selected.info, existing);
const cost = {
input: selected.info.pricing.input_per_million,
output: selected.info.pricing.output_per_million,
reasoning: existing?.cost?.reasoning,
cache_read: cachePricing.read,
cache_write: cachePricing.write,
input_audio: existing?.cost?.input_audio,
output_audio: existing?.cost?.output_audio,
tiers: existing?.cost?.tiers,
};
const status = model.availability_status === "deprecated" || selected.info.availability_status === "deprecated"
? "deprecated" as const
: undefined;
const baseModel = existing?.base_model ?? resolveCanonicalBaseModel(model.model);
// `supports_reasoning` is not part of the documented public schema
// (PublicVendorModelCapabilities) and is inconsistently populated across
// vendor routes: the same model can report `true` on one route and `false`
// on another (e.g. anthropic/claude-opus-4-6 reports `false` via `anthropic`
// and `true` via `bedrock`), and reasoning-only models such as
// deepseek/deepseek-r1 report `false` on their sole route. Treat it as a
// positive-only signal: `true` (always accompanied by route `reasoning`
// metadata) confirms the model reasons on the gateway, while `false`/absent
// means unknown and preserves curated reasoning metadata.
const routeConfirmsReasoning = Object.values(model.vendors).some(
(vendor) => vendor.availability_status === "available" && vendor.capabilities.supports_reasoning === true,
);
const reasoning = routeConfirmsReasoning ? true : existing?.reasoning;
const existingReasoningOptions = existing?.reasoning_options ?? [];
const reasoningOptions = reasoning === true && existingReasoningOptions.length === 0
&& selected.info.capabilities.reasoning?.disable_supported === true
? [{ type: "toggle" as const }]
: reasoning === true
? existingReasoningOptions
: existing?.reasoning_options;
const modelSlug = model.model.split("/").at(-1)?.toLowerCase();
const displayNameIsID = model.display_name.includes("/")
|| model.display_name.toLowerCase() === modelSlug;
const authoritative = {
// Some catalog rows use an upstream org/model ID as display_name. Let
// canonical metadata provide the human-readable name for factored models.
name: baseModel !== undefined && displayNameIsID ? undefined : model.display_name,
attachment: input.some((value) => value !== "text"),
tool_call: selected.info.capabilities.supports_tool_calling,
structured_output: selected.info.capabilities.supports_structured_outputs,
status,
cost,
limit,
modalities: { input, output },
};
if (baseModel !== undefined) {
return factorBaseModel(
baseModel,
{
...authoritative,
description: existing?.description,
reasoning,
reasoning_options: reasoningOptions,
temperature: existing?.temperature,
interleaved: existing?.interleaved,
provider: existing?.provider,
experimental: existing?.experimental,
},
limit,
existing?.base_model_omit,
);
}
if (existing === undefined) return undefined;
const releaseDate = selected.info.launch_date
?? model.created_at?.slice(0, 10)
?? existing.release_date;
if (releaseDate === undefined) return undefined;
const lastUpdated = model.updated_at?.slice(0, 10)
?? existing.last_updated
?? releaseDate;
return {
...authoritative,
description: existing.description ?? describeModel({
id: model.model,
name: model.display_name,
family: existing.family,
reasoning,
tool_call: selected.info.capabilities.supports_tool_calling,
structured_output: selected.info.capabilities.supports_structured_outputs,
open_weights: existing.open_weights,
limit,
modalities: { input, output },
}),
family: existing.family,
release_date: releaseDate,
last_updated: lastUpdated,
reasoning: reasoning ?? false,
reasoning_options: reasoningOptions,
temperature: existing.temperature,
knowledge: existing.knowledge,
open_weights: existing.open_weights ?? false,
interleaved: existing.interleaved,
provider: existing.provider,
experimental: existing.experimental,
} satisfies SyncedFullModel;
}
function mergeGatewayCachePricing(
vendor: MergeGatewayVendor,
existing: ExistingModel | undefined,
) {
const promptCaching = vendor.prompt_caching;
const pricing = vendor.pricing;
if (promptCaching?.mode === "none") {
return { read: undefined, write: undefined };
}
return {
read: promptCaching?.cache_read_cost_per_million
?? pricing.cache_read_per_million
?? existing?.cost?.cache_read,
write: promptCaching?.cache_write_cost_per_million
?? pricing.cache_write_per_million
?? existing?.cost?.cache_write,
};
}
type Modality = "text" | "audio" | "image" | "video" | "pdf";
function modalities(values: string[]): Modality[] {
const allowed = new Set<Modality>(["text", "audio", "image", "video", "pdf"]);
return [...new Set(values
.map((value) => value === "document" ? "pdf" : value)
.filter((value): value is Modality => allowed.has(value as Modality))
)];
}
@@ -0,0 +1,348 @@
import { z } from "zod";
import { inferKimiFamily, ModelFamilyValues } from "../../family.js";
import type { ExistingModel, SyncProvider, SyncedFullModel, SyncedModel } from "../index.js";
import { factorBaseModel, resolveModelMetadataBaseModel } from "./openrouter.js";
const API_ENDPOINT = "https://nano-gpt.com/api/v1/models?detailed=true";
// NanoGPT accepts these exact request values, including `max`:
// https://github.com/Nano-GPT-com/nanogpt/blob/073b25b07e9af619333c679e694de664bf1ceb30/lib/utils/reasoningInput.ts#L12-L28
const ReasoningEffort = z.enum(["none", "minimal", "low", "medium", "high", "xhigh", "max"]);
const Pricing = z.object({
prompt: z.number().nullish(),
completion: z.number().nullish(),
input: z.number().nullish(),
output: z.number().nullish(),
cacheReadInputPer1kTokens: z.number().nullish(),
cacheWriteInputPer1kTokens: z.number().nullish(),
note: z.string().optional(),
}).passthrough();
const Architecture = z.object({
input_modalities: z.array(z.string()).optional(),
output_modalities: z.array(z.string()).optional(),
}).passthrough();
const Capabilities = z.object({
vision: z.boolean().optional(),
video_input: z.boolean().optional(),
audio_input: z.boolean().optional(),
reasoning: z.boolean().optional(),
tool_calling: z.boolean().optional(),
structured_output: z.boolean().optional(),
pdf_upload: z.boolean().optional(),
}).passthrough();
export const NanoGptModel = z.object({
id: z.string().min(1),
name: z.string().nullish(),
description: z.string().nullish(),
created: z.number().nullish(),
owned_by: z.string().nullish(),
context_length: z.number().int().nonnegative().nullish(),
max_output_tokens: z.number().int().nonnegative().nullish(),
architecture: Architecture.optional(),
capabilities: Capabilities.optional(),
reasoning_efforts: z.array(ReasoningEffort).nullish(),
open_weights: z.boolean().nullish(),
pricing: Pricing.optional(),
}).passthrough();
export const NanoGptResponse = z.object({
data: z.array(NanoGptModel),
}).passthrough();
export type NanoGptModel = z.infer<typeof NanoGptModel>;
type Modality = "text" | "audio" | "image" | "video" | "pdf";
export const nanoGpt = {
id: "nano-gpt",
name: "NanoGPT",
modelsDir: "providers/nano-gpt/models",
preserveDescriptions: false,
async fetchModels() {
const response = await fetch(process.env.NANO_GPT_MODELS_URL ?? API_ENDPOINT);
if (!response.ok) {
throw new Error(`NanoGPT models request failed: ${response.status} ${response.statusText}`);
}
return response.json();
},
parseModels(raw) {
return NanoGptResponse.parse(raw).data;
},
translateModel(model, context) {
const id = normalizeModelID(model.id);
const existing = context.existing(id);
const baseModel = existing?.base_model ?? resolveNanoGptBaseModel(model.id);
const translated = buildNanoGptModel(model, existing, baseModel);
if (translated === undefined) return undefined;
return {
id,
model: translated,
};
},
} satisfies SyncProvider<NanoGptModel>;
const ORG_ID_NORMALIZATION: Record<string, string | undefined> = {
nousresearch: "NousResearch",
qwen: "qwen",
thedrummer: "TheDrummer",
};
const BASE_MODEL_ALIASES: Record<string, string | undefined> = {
"claude-opus-4": "anthropic/claude-opus-4-0",
"claude-sonnet-4": "anthropic/claude-sonnet-4-0",
"cohere/north-mini-code": "cohere/north-mini-code-1-0",
};
const NANO_GPT_VARIANT_SUFFIX = /(?::(?:thinking|none|minimal|low|medium|high|xhigh|max|\d+)|-thinking)$/i;
const KNOWN_OPEN_WEIGHT_IDS = new Set([
"nex-agi/nex-n2-pro",
]);
export function buildNanoGptModel(
model: NanoGptModel,
existing: ExistingModel | undefined,
baseModel = existing?.base_model ?? resolveNanoGptBaseModel(model.id),
): SyncedModel | undefined {
const capabilities = model.capabilities ?? {};
const explicitInputModalities = model.architecture?.input_modalities;
const hasInputCapabilityMetadata = capabilities.vision !== undefined
|| capabilities.audio_input !== undefined
|| capabilities.video_input !== undefined
|| capabilities.pdf_upload !== undefined;
const addedInputModalities = [
...(capabilities.vision ? ["image"] : []),
...(capabilities.audio_input ? ["audio"] : []),
...(capabilities.video_input ? ["video"] : []),
...(capabilities.pdf_upload ? ["pdf"] : []),
];
const hasInputMetadata = explicitInputModalities !== undefined || hasInputCapabilityMetadata;
const hasOutputMetadata = model.architecture?.output_modalities !== undefined;
const input = normalizeModalities([
...explicitInputModalities
?? (hasInputCapabilityMetadata ? ["text"] : existing?.modalities?.input)
?? ["text"],
...addedInputModalities,
]);
const output = normalizeModalities(
model.architecture?.output_modalities ?? existing?.modalities?.output ?? ["text"],
);
const sourceContext = positive(model.context_length);
const sourceOutputLimit = positive(model.max_output_tokens);
const context = sourceContext ?? existing?.limit?.context;
const inputLimit = sourceContext ?? existing?.limit?.input;
const outputLimit = sourceOutputLimit ?? existing?.limit?.output;
const releaseDate = dateFromTimestamp(model.created) ?? existing?.release_date;
const inferredSourceReasoning = capabilities.reasoning
?? (model.reasoning_efforts != null ? true : undefined);
const reasoning = inferredSourceReasoning ?? existing?.reasoning ?? false;
const cost = buildCost(model.pricing, existing);
if (baseModel !== undefined) {
const existingAlreadyFactored = existing?.base_model === baseModel;
const factoredModalities = {
input: hasInputMetadata || existing !== undefined ? input : undefined,
output: hasOutputMetadata || existing !== undefined ? output : undefined,
};
const factoredLimit = {
context: sourceContext ?? existing?.limit?.context,
input: sourceContext ?? existing?.limit?.input,
output: sourceOutputLimit ?? existing?.limit?.output,
};
const sourceReasoning = inferredSourceReasoning;
const sourceReasoningOptions = reasoningOptions(model, sourceReasoning, existing?.reasoning_options);
return factorBaseModel(
baseModel,
{
name: existing?.name ?? model.name ?? undefined,
description: existingAlreadyFactored ? existing?.description : undefined,
family: existingAlreadyFactored ? existing?.family : undefined,
release_date: existingAlreadyFactored ? existing?.release_date : undefined,
last_updated: existingAlreadyFactored ? existing?.last_updated : undefined,
attachment: hasInputMetadata
? input.some((value) => value !== "text")
: existing?.attachment,
reasoning: sourceReasoning ?? existing?.reasoning,
reasoning_options: sourceReasoningOptions,
temperature: existing?.temperature,
tool_call: capabilities.tool_calling ?? existing?.tool_call,
structured_output: capabilities.structured_output ?? existing?.structured_output,
knowledge: existing?.knowledge,
status: existing?.status,
interleaved: existing?.interleaved,
provider: existing?.provider,
experimental: existing?.experimental,
cost,
limit: factoredLimit,
modalities: factoredModalities,
},
factoredLimit,
existingAlreadyFactored ? existing?.base_model_omit : undefined,
);
}
if (context === undefined || outputLimit === undefined || releaseDate === undefined) {
return undefined;
}
const values = {
name: existing?.name ?? model.name ?? humanizeModelName(model.id),
description: existing?.description ?? model.description ?? `${model.name ?? humanizeModelName(model.id)} on NanoGPT.`,
family: existing?.family ?? inferFamily(model.id, model.name ?? ""),
release_date: releaseDate,
last_updated: existing?.last_updated ?? releaseDate,
attachment: input.some((value) => value !== "text"),
reasoning,
reasoning_options: reasoningOptions(model, reasoning, existing?.reasoning_options),
temperature: existing?.temperature,
tool_call: capabilities.tool_calling ?? existing?.tool_call ?? false,
structured_output: capabilities.structured_output ?? existing?.structured_output,
knowledge: existing?.knowledge,
status: existing?.status,
interleaved: existing?.interleaved,
provider: existing?.provider,
experimental: existing?.experimental,
cost,
limit: { context, input: inputLimit ?? context, output: outputLimit },
modalities: { input, output },
};
return {
...values,
open_weights: model.open_weights
?? (KNOWN_OPEN_WEIGHT_IDS.has(model.id.toLowerCase()) ? true : existing?.open_weights)
?? false,
} satisfies SyncedFullModel;
}
function buildCost(
pricing: NanoGptModel["pricing"],
existing: ExistingModel | undefined,
): SyncedFullModel["cost"] {
if (pricing === undefined) return existing?.cost;
if (pricing.note === "varies_by_modality") return existing?.cost;
const input = pricing.input ?? pricing.prompt;
const output = pricing.output ?? pricing.completion;
if (!validPrice(input) || !validPrice(output)) return existing?.cost;
return {
input: price(input),
output: price(output),
reasoning: existing?.cost?.reasoning,
cache_read: !validPrice(pricing.cacheReadInputPer1kTokens)
? existing?.cost?.cache_read
: price(pricing.cacheReadInputPer1kTokens * 1_000),
cache_write: !validPrice(pricing.cacheWriteInputPer1kTokens)
? existing?.cost?.cache_write
: price(pricing.cacheWriteInputPer1kTokens * 1_000),
input_audio: existing?.cost?.input_audio,
output_audio: existing?.cost?.output_audio,
tiers: existing?.cost?.tiers,
};
}
function reasoningOptions(
model: NanoGptModel,
reasoning: boolean | undefined,
existing: SyncedFullModel["reasoning_options"],
): SyncedFullModel["reasoning_options"] {
if (reasoning === false) return undefined;
if (reasoning === undefined) return existing;
if (model.reasoning_efforts == null) return existing ?? [];
if (model.reasoning_efforts.length === 0) return [];
return [{ type: "effort", values: [...model.reasoning_efforts] }];
}
export function resolveNanoGptBaseModel(modelID: string) {
let normalized = normalizeModelID(modelID);
if (normalized.toLowerCase().startsWith("tee/")) {
normalized = normalizeModelID(normalized.slice("TEE/".length));
}
const exact = resolveNanoGptCanonicalCandidate(normalized);
if (exact !== undefined) return exact;
const stripped = stripNanoGptVariantSuffixes(normalized);
return stripped === normalized ? undefined : resolveNanoGptCanonicalCandidate(stripped);
}
function resolveNanoGptCanonicalCandidate(modelID: string) {
return BASE_MODEL_ALIASES[modelID.toLowerCase()] ?? resolveModelMetadataBaseModel(modelID);
}
function stripNanoGptVariantSuffixes(modelID: string) {
let normalized = modelID;
while (true) {
const stripped = normalized.replace(NANO_GPT_VARIANT_SUFFIX, "");
if (stripped === normalized) return normalized;
normalized = stripped;
}
}
function normalizeModalities(values: string[]): Modality[] {
const allowed = new Set<Modality>(["text", "audio", "image", "video", "pdf"]);
const result = values
.map((value) => normalizeModality(value))
.filter((value): value is Modality => allowed.has(value as Modality));
return [...new Set(result.length > 0 ? result : ["text"] as Modality[])];
}
function normalizeModality(value: string) {
const lower = value.toLowerCase();
if (lower === "images") return "image";
if (lower === "videos") return "video";
if (lower === "audios") return "audio";
if (lower === "documents") return "pdf";
return lower;
}
function normalizeModelID(modelId: string) {
const [org, ...parts] = modelId.split("/");
if (org === undefined || parts.length === 0) return modelId;
const normalizedOrg = ORG_ID_NORMALIZATION[org.toLowerCase()];
return normalizedOrg === undefined ? modelId : `${normalizedOrg}/${parts.join("/")}`;
}
function inferFamily(id: string, name: string) {
const kimiFamily = inferKimiFamily(id, name);
if (kimiFamily !== undefined) return kimiFamily;
const target = `${id} ${name}`.toLowerCase();
return [...ModelFamilyValues]
.sort((a, b) => b.length - a.length)
.find((family) => {
const value = family.toLowerCase().replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
if (family === "o") return new RegExp(`(^|[^a-z0-9])${value}(?=\\d)`).test(target);
return new RegExp(`(^|[^a-z0-9])${value}(?=$|[^a-z0-9])`).test(target);
});
}
function humanizeModelName(modelId: string) {
const modelPart = modelId.split("/").at(-1) ?? modelId;
return modelPart
.replace(/[:/_-]+/g, " ")
.replace(/\b\w/g, (value) => value.toUpperCase());
}
function dateFromTimestamp(timestamp: number | null | undefined) {
if (timestamp == null || timestamp <= 0) return undefined;
return new Date(timestamp * 1_000).toISOString().slice(0, 10);
}
function positive(value: number | null | undefined) {
return value == null || value <= 0 ? undefined : value;
}
function price(value: number) {
return Math.round(value * 1_000_000) / 1_000_000;
}
function validPrice(value: number | null | undefined): value is number {
return value !== null && value !== undefined && value >= 0;
}
+70 -11
View File
@@ -10,6 +10,7 @@ const API_ENDPOINT = "https://openrouter.ai/api/v1/models";
const MODELS_DIR = path.join(import.meta.dirname, "..", "..", "..", "..", "..", "models");
const modelMetadataByID = new Map<string, Record<string, unknown>>();
const modelMetadataFilesByProvider = new Map<string, Set<string>>();
let allModelMetadataIDs: string[] | undefined;
const CANONICAL_BASE_MODEL_OVERRIDES = {
"openai/gpt-5.6-luna-pro": "openai/gpt-5.6-luna",
@@ -29,11 +30,14 @@ const CANONICAL_PROVIDER_PREFIXES = {
"meta-llama": { provider: "llama", metadata: "meta" },
minimax: { provider: "minimax", metadata: "minimax" },
mistralai: { provider: "mistral", metadata: "mistral" },
moonshot: { provider: "moonshotai", metadata: "moonshotai" },
moonshotai: { provider: "moonshotai", metadata: "moonshotai" },
openai: { provider: "openai", metadata: "openai" },
nvidia: { provider: "nvidia", metadata: "nvidia" },
qwen: { provider: "alibaba", metadata: "alibaba" },
sakana: { provider: "sakana", metadata: "sakana" },
stepfun: { provider: "stepfun", metadata: "stepfun" },
"stepfun-ai": { provider: "stepfun", metadata: "stepfun" },
tencent: { provider: "tencent", metadata: "tencent" },
thinkingmachines: { provider: "thinkingmachines", metadata: "thinkingmachines" },
"x-ai": { provider: "xai", metadata: "xai" },
@@ -41,6 +45,7 @@ const CANONICAL_PROVIDER_PREFIXES = {
xiaomi: { provider: "xiaomi", metadata: "xiaomi" },
zai: { provider: "zai", metadata: "zhipuai" },
"z-ai": { provider: "zai", metadata: "zhipuai" },
"zai-org": { provider: "zai", metadata: "zhipuai" },
} as const;
export const OpenRouterModel = z.object({
@@ -100,7 +105,8 @@ export const openrouter = {
return response.json();
},
parseModels(raw) {
return OpenRouterResponse.parse(raw).data;
// Temporarily skip batch routes (`*:batch`) — they are not catalog targets.
return OpenRouterResponse.parse(raw).data.filter((model) => !model.id.endsWith(":batch"));
},
translateModel(model, context) {
// OpenRouter serves deprecated/unavailable routes as degraded stubs:
@@ -309,19 +315,39 @@ export function resolveCanonicalBaseModel(openrouterID: string) {
if (prefix === undefined || modelParts.length === 0) return undefined;
if (openrouterID.startsWith("~/") || prefix.startsWith("~")) return undefined;
const canonical = CANONICAL_PROVIDER_PREFIXES[prefix as keyof typeof CANONICAL_PROVIDER_PREFIXES];
const canonical = CANONICAL_PROVIDER_PREFIXES[
prefix.toLowerCase() as keyof typeof CANONICAL_PROVIDER_PREFIXES
];
if (canonical === undefined) return undefined;
const modelID = modelParts.join("/").replace(/:free$/, "");
const candidates = canonicalCandidates(canonical.provider, modelID);
const match = candidates.find((candidate) => {
return modelMetadataExists(canonical.metadata, candidate);
});
const match = matchingModelMetadataFile(canonical.metadata, candidates);
return match === undefined ? undefined : `${canonical.metadata}/${match}`;
}
function modelMetadataExists(provider: string, modelID: string) {
/**
* Resolve provider IDs that are not OpenRouter-shaped against the same canonical
* metadata tree. Exact paths win; bare IDs only resolve when their filename is
* unique across every metadata provider.
*/
export function resolveModelMetadataBaseModel(modelID: string) {
const routed = resolveCanonicalBaseModel(modelID);
if (routed !== undefined) return routed;
const normalized = modelID.replace(/:free$/, "");
const ids = modelMetadataIDs();
const exact = ids.find((candidate) => candidate.toLowerCase() === normalized.toLowerCase());
if (exact !== undefined) return exact;
if (normalized.includes("/")) return undefined;
const lower = normalized.toLowerCase();
const matches = ids.filter((candidate) => candidate.split("/").at(-1)?.toLowerCase() === lower);
return matches.length === 1 ? matches[0] : undefined;
}
function matchingModelMetadataFile(provider: string, candidates: string[]) {
let files = modelMetadataFilesByProvider.get(provider);
if (files === undefined) {
try {
@@ -331,7 +357,30 @@ function modelMetadataExists(provider: string, modelID: string) {
}
modelMetadataFilesByProvider.set(provider, files);
}
return files.has(`${modelID}.toml`);
for (const candidate of candidates) {
const expected = `${candidate}.toml`.toLowerCase();
const match = [...files].find((file) => file.toLowerCase() === expected);
if (match !== undefined) return match.slice(0, -".toml".length);
}
return undefined;
}
function modelMetadataIDs() {
if (allModelMetadataIDs !== undefined) return allModelMetadataIDs;
try {
allModelMetadataIDs = readdirSync(MODELS_DIR, { withFileTypes: true })
.filter((entry) => entry.isDirectory())
.flatMap((entry) => {
return readdirSync(path.join(MODELS_DIR, entry.name))
.filter((file) => file.endsWith(".toml"))
.map((file) => `${entry.name}/${file.slice(0, -".toml".length)}`);
});
} catch {
allModelMetadataIDs = [];
}
return allModelMetadataIDs;
}
function canonicalBaseModelOverride(openrouterID: string) {
@@ -358,10 +407,18 @@ function normalizeModelSlug(value: string) {
return value.toLowerCase().replaceAll(/[^a-z0-9]/g, "");
}
type BaseModelOverrides = Omit<Partial<SyncedFullModel>, "limit" | "modalities"> & {
limit?: Partial<SyncedFullModel["limit"]>;
modalities?: {
input?: SyncedFullModel["modalities"]["input"];
output?: SyncedFullModel["modalities"]["output"];
};
};
export function factorBaseModel(
modelID: string,
values: Partial<SyncedFullModel>,
limit: SyncedFullModel["limit"],
values: BaseModelOverrides,
limit?: Partial<SyncedFullModel["limit"]>,
existingOmit?: string[],
): SyncedModel {
return {
@@ -373,14 +430,16 @@ export function factorBaseModel(
function baseModelOmit(
modelID: string,
limit: SyncedFullModel["limit"],
limit: Partial<SyncedFullModel["limit"]> | undefined,
) {
if (limit === undefined) return undefined;
const metadata = modelMetadata(modelID);
const omit: string[] = [];
const baseLimit = metadata.limit;
if (
isPlainObject(baseLimit) &&
baseLimit.input !== undefined &&
limit.context !== undefined &&
limit.input === undefined &&
baseLimit.context !== limit.context
) {
@@ -392,7 +451,7 @@ function baseModelOmit(
function baseModelOverrides(
modelID: string,
values: Partial<SyncedFullModel>,
values: BaseModelOverrides,
) {
const metadata = modelMetadata(modelID);
const result: Record<string, unknown> = {};
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,42 @@
# Sources (accessed 2026-07-28):
# - Model card, limits (1M context, 999,990 max output), capabilities, GLM-5.2 base:
# https://docs.abliteration.ai/models
# - Reasoning effort ladder and per-endpoint request fields:
# https://docs.abliteration.ai/capabilities/thinking
# - Pricing ($5 per 1M tokens, flat input + output):
# https://docs.abliteration.ai/pricing
# https://abliteration.ai/pricing#api-pricing
# - Launch announcement (base model, fine-tuning, benchmarks):
# https://abliteration.ai/blog/introducing-abliterated-model-large
name = "Abliterated Model Large"
description = "GLM-5.2 model abliterated and finetuned for cyber, ML red teaming, and agent testing"
release_date = "2026-07-25"
last_updated = "2026-07-28"
attachment = false
reasoning = true
tool_call = true
structured_output = true
temperature = true
open_weights = false
# Two distinct reasoning depths: high and max. The API also accepts the other
# ladder values as aliases, mapping minimal-high -> high and xhigh-max -> max.
[[reasoning_options]]
type = "effort" # API: {"reasoning_effort": "<value>"} on /v1/chat/completions
values = ["none", "high", "max"]
[[reasoning_options]]
type = "toggle" # API: {"thinking": false} on /v1/messages (legacy alias on /v1/chat/completions)
[cost]
input = 5.00
output = 5.00
[limit]
context = 1_000_000
input = 1_000_000
output = 999_990
[modalities]
input = ["text"]
output = ["text"]
@@ -1,20 +1,27 @@
# Sources (accessed 2026-07-28):
# - Model card and capabilities: https://docs.abliteration.ai/models
# - Reasoning effort ladder and per-endpoint request fields (reasoning on by
# default; "none" disables; top-level thinking:false disables on
# /v1/messages and as a legacy alias on /v1/chat/completions):
# https://docs.abliteration.ai/capabilities/thinking
name = "Abliterated Model"
description = "Multimodal model for analyzing text, images, documents, and rich media"
# Reasoning HTTP format (accessed 2026-06-25):
# This model thinks by default. On POST /v1/chat/completions or /v1/messages,
# top-level `thinking: false` skips thinking; omission keeps it enabled.
# Sources:
# https://docs.abliteration.ai/models
# https://docs.abliteration.ai/capabilities/thinking
release_date = "2026-01-06"
last_updated = "2026-01-06"
last_updated = "2026-07-28"
attachment = true
reasoning = false
reasoning = true
tool_call = true
structured_output = false
temperature = true
open_weights = true
[[reasoning_options]]
type = "effort" # API: {"reasoning_effort": "<value>"} on /v1/chat/completions
values = ["none", "minimal", "low", "medium", "high", "xhigh", "max"]
[[reasoning_options]]
type = "toggle" # API: {"thinking": false} on /v1/messages (legacy alias on /v1/chat/completions)
[cost]
input = 3.00
output = 3.00
+11 -7
View File
@@ -1,12 +1,16 @@
# Reasoning controls for the whole API surface (verified 2026-07-28):
# https://docs.abliteration.ai/capabilities/thinking
# Both models reason by default. Effort ladder, least to most:
# none (disables reasoning), minimal, low, medium, high, xhigh, max.
# Request field per endpoint:
# - POST /v1/chat/completions: top-level "reasoning_effort": "<level>"
# (legacy top-level "thinking": false also disables reasoning)
# - POST /v1/responses: "reasoning": { "effort": "<level>" }
# - POST /v1/messages: "output_config": { "effort": "<level>" } or
# "thinking": { "type": "enabled", "budget_tokens": <n> };
# "thinking": false disables reasoning
name = "abliteration.ai"
env = ["ABLIT_KEY"]
npm = "@ai-sdk/openai-compatible"
# Reasoning HTTP format (accessed 2026-06-25):
# POST /v1/chat/completions and POST /v1/messages: top-level `thinking` is true
# by default; false skips thinking. POST /v1/responses has no thinking toggle.
# No effort or numeric reasoning-budget request field is documented.
# Sources:
# https://docs.abliteration.ai/capabilities/thinking
# https://docs.abliteration.ai/compatibility-matrix
api = "https://api.abliteration.ai/v1"
doc = "https://docs.abliteration.ai/models"
+1 -1
View File
@@ -1,4 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1280 1280" fill="currentColor">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 471 1280 430" width="24" height="24" fill="currentColor">
<g transform="translate(0 430)">
<path d="m1036.05 174.114 102.7-59.232 27.43 45.579c-140.14 75.45-215.743 92.363-192.311 166.512 35.811 65.478 124.001 31.082 146.991 2.463l-70.6-67.493 42.37-23.43c16.5 16.189 56.74 54.391 62.04 59.247 3.54-1.734 16.73-9.813 20-31.494h58.22c-14.86 33.303-28.14 42.07-41.99 63.027l78.56 80.071h-68.68l-41.87-41.008c-102.57 84.123-227.969 43.61-244.725-27.506-20.512-87.072 40.084-129.654 72.947-140.061l-60.566-59.407 40.229-30.788z"/><path d="m1036.63 168.825 103.63-59.764 31.92 53.045-3.97 2.133c-35.1 18.898-66.18 34.144-92.56 47.473-26.46 13.367-48.07 24.753-64.54 36.034-16.493 11.29-27.372 22.169-32.878 34.311-5.393 11.892-5.894 25.599-.394 43.228 16.822 30.395 45.632 37.773 73.752 33.954 14.16-1.922 27.99-6.69 39.67-12.719 9.99-5.159 18.2-11.133 23.65-16.829l-71.83-68.678 50.27-27.802 2.28 2.239c14.96 14.668 49.44 47.446 59.63 56.991 4.51-3.073 12.75-10.777 15.16-26.813l.55-3.653h68.53l-2.69 6.039c-7.59 17.006-14.81 27.813-21.89 37.013-6.49 8.43-12.38 14.952-18.5 23.766l83.27 84.862h-80.66l-40.4-39.564c-51.46 40.852-108.42 51.515-155.04 42.947-47.298-8.693-84.683-37.436-93.581-75.205-10.543-44.75-.266-78.449 17.17-102.295 15.196-20.783 35.684-33.899 52.071-40.471l-59.221-58.087L967.044 105zm-103.529-27.043 62.132 60.942-6.805 2.155c-15.704 4.973-38.249 17.742-54.321 39.723-15.95 21.815-25.715 52.943-15.745 95.263 7.857 33.348 41.551 60.42 86.778 68.732 45.01 8.271 100.72-2.284 151.05-43.558l2.97-2.441 43.37 42.475h56.71l-73.83-75.252 1.91-2.894c7.03-10.63 14.15-18.506 20.8-27.136 5.86-7.615 11.81-16.351 18.04-29.234h-47.91c-4.39 20.767-17.52 29.015-21.69 31.055l-2.62 1.289-2.16-1.976c-5.12-4.687-42.19-39.874-59.87-57.113l-34.46 19.062 69.22 66.18-2.47 3.07c-6.24 7.773-16.62 15.627-29.01 22.022-12.44 6.423-27.2 11.528-42.44 13.597-30.56 4.149-63.629-3.94-82.646-38.711l-.2-.367-.127-.398c-6.084-19.255-5.892-35.353.639-49.755 6.458-14.241 18.845-26.21 35.844-37.848 17.01-11.647 39.12-23.271 65.52-36.612 25.47-12.872 55.05-27.396 88.4-45.244l-22.93-38.105-101.78 58.7-68.924-63.215zM859.42 416.999h-71.842V118.77h71.842z"/><ellipse cx="823.5" cy="41" rx="45.5" ry="41"/><path d="M562.718 418q-37.308 0-61.977-12.41-24.672-12.409-36.705-33.091C452.003 351.817 452 343.743 452 327.59q0-28.363 14.442-49.044 15.044-20.683 43.925-31.909 28.885-11.82 71.004-11.819h76.422q0-23.636-6.619-39-6.62-15.954-20.459-23.636-13.841-7.682-35.502-7.682-24.673 0-42.121 11.227-17.453 10.636-21.663 33.091h-71.006q3.613-31.91 21.663-54.363 18.053-23.046 47.537-35.455 30.087-13 65.59-13 42.723 0 72.807 14.773 30.087 14.181 45.734 41.363Q730 188.728 730 226.546V410.91h-61.377l-7.22-47.864q-6.018 11.819-15.646 21.863-9.024 10.046-21.061 17.727-12.035 7.091-27.679 11.227-15.044 4.138-34.299 4.137m16.246-56.136q17.453 0 30.689-5.91 13.84-6.5 23.468-17.727 10.229-11.817 15.646-25.999 5.415-14.183 7.22-30.137v-1.182h-66.793q-21.06 0-34.899 5.317-13.84 4.73-19.859 14.182-6.015 9.456-6.016 21.865 0 12.409 6.016 21.272 6.02 8.865 17.452 13.592t27.076 4.727M347.361 144.73c11.758-9.086 28.639-.54 28.639 14.498v240.577c0 10.049-7.991 18.195-17.852 18.195H347.19a4.16 4.16 0 0 1-3.016-1.303l-69.965-73.545c-5.775-6.07-15.036-6.935-21.798-2.034l-104.986 76.09a4.15 4.15 0 0 1-2.434.792H46.885c-17.106-.002-24.416-22.157-10.786-32.693z"/><path d="M348.028 5.818c9.296-7.073 22.68.092 21.943 12.195l-4.377 71.729c-.254 4.158-2.288 7.974-5.534 10.441L21.965 357.192C12.975 364.026 0 357.569 0 345.873V277.68c0-4.473 2.072-8.662 5.557-11.315z"/>
</g>

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

@@ -1,20 +0,0 @@
# Source: https://docs.aiand.com/models/catalog/ (USD list prices; accessed
# 2026-07-24). The catalog lists this model with vision + document but not
# video, so modalities are overridden from the shared base model's
# text+image+video. Not runtime-verified: model access is org-scoped and not
# enabled for the contributing org; kimi-k2.7-code (same capability set) was
# verified by direct probe — image and PDF accepted, video rejected.
# reasoning_options is left empty rather than assumed: the gpt-oss-120b
# deployment proved the gateway-wide documented effort set is not guaranteed
# per model, and this model could not be probed.
base_model = "moonshotai/kimi-k2.6"
reasoning_options = []
[cost]
input = 0.85
output = 3.50
[modalities]
input = ["text", "image", "pdf"]
output = ["text"]
@@ -0,0 +1,24 @@
# Source: GET https://api.aiand.com/v1/models (USD list prices; accessed
# 2026-07-28). Pricing is aiand-specific: $3.00 input, $0.50 cache_read,
# $12.50 output (differs from Moonshot official rates).
# Modalities: text + image + pdf accepted per GET /v1/models; video rejected
# ("does not support video input") — overridden from shared base
# text+image+video. PDF kept: sibling aiand Moonshot entries (kimi-k2.6,
# kimi-k2.7-code) include pdf after catalog/probe evidence; aiand treats
# PDF as a provider-level Files API modality.
# reasoning_effort verified live: gateway schema accepts
# none/minimal/low/medium/high/xhigh/max, but the K3 backend only accepts
# none/low/high/max (minimal/medium/xhigh rejected). Invalid values rejected
# with 400 (negative control).
base_model = "moonshotai/kimi-k3"
reasoning_options = [{ type = "effort", values = ["none", "low", "high", "max"] }]
[cost]
input = 3.00
cache_read = 0.50
output = 12.50
[modalities]
input = ["text", "image", "pdf"]
output = ["text"]
@@ -1,17 +0,0 @@
# Source: https://docs.aiand.com/models/catalog/ (USD list prices; accessed
# 2026-07-24). Not runtime-verifiable: model access is org-scoped and not
# enabled for the contributing org (it does not appear in our GET /v1/models),
# so per-model reasoning_effort support could not be probed — the gpt-oss-120b
# deployment proved the gateway-wide documented set is not guaranteed per
# model, so reasoning_options is left empty rather than assumed.
# The catalog shows a rounded context of "203K"; the exact context_window is
# only visible via GET /v1/models for orgs with access, so context is left
# inherited from the shared base model (200_000) rather than guessed — it may
# understate the live window by ~3K.
base_model = "zhipuai/glm-5.1"
reasoning_options = []
[cost]
input = 1.40
output = 4.40
+26
View File
@@ -0,0 +1,26 @@
# AIHubMix model page documents Kimi K3 pricing, supported input modalities
# (text, vision, video), and a 1M-token context window.
# Source accessed 2026-07-27:
# https://aihubmix.com/model/kimi-k3
base_model = "moonshotai/kimi-k3"
last_updated = "2026-07-27"
[interleaved]
field = "reasoning_content"
[[reasoning_options]]
type = "toggle"
[[reasoning_options]]
type = "effort"
values = ["low", "high", "max"]
[cost]
input = 3.00
output = 15.00
cache_read = 0.30
[modalities]
input = ["text", "image", "video"]
output = ["text"]
@@ -5,7 +5,7 @@ temperature = true
reasoning_options = []
[cost]
input = 0.82
output = 3.75
input = 0.73
output = 3.5
cache_read = 0.15
cache_write = 0
@@ -1 +0,0 @@
../../azure/models/cohere-command-r-08-2024.toml
@@ -1 +0,0 @@
../../azure/models/cohere-command-r-plus-08-2024.toml
@@ -1 +0,0 @@
../../azure/models/deepseek-r1-0528.toml
@@ -1 +0,0 @@
../../azure/models/deepseek-v3-0324.toml
@@ -1 +0,0 @@
../../azure/models/deepseek-v3.1.toml
@@ -1 +0,0 @@
../../azure/models/gpt-3.5-turbo-0301.toml
@@ -1 +0,0 @@
../../azure/models/gpt-3.5-turbo-0613.toml
@@ -1 +0,0 @@
../../azure/models/gpt-4-32k.toml
@@ -1 +0,0 @@
../../azure/models/gpt-4.toml
@@ -1 +0,0 @@
../../azure/models/gpt-5-chat.toml
@@ -1 +0,0 @@
../../azure/models/gpt-5.1-chat.toml
@@ -1 +0,0 @@
../../azure/models/gpt-5.2-chat.toml
@@ -1 +0,0 @@
../../azure/models/grok-4-fast-reasoning.toml
@@ -1 +0,0 @@
../../azure/models/kimi-k2-thinking.toml
@@ -1 +0,0 @@
../../azure/models/llama-3.2-11b-vision-instruct.toml
@@ -1 +0,0 @@
../../azure/models/llama-3.2-90b-vision-instruct.toml
@@ -1 +0,0 @@
../../azure/models/meta-llama-3-70b-instruct.toml
@@ -1 +0,0 @@
../../azure/models/meta-llama-3-8b-instruct.toml
@@ -1 +0,0 @@
../../azure/models/meta-llama-3.1-405b-instruct.toml
@@ -1 +0,0 @@
../../azure/models/meta-llama-3.1-70b-instruct.toml
@@ -1 +0,0 @@
../../azure/models/meta-llama-3.1-8b-instruct.toml
@@ -1 +0,0 @@
../../azure/models/mistral-large-2411.toml
@@ -1 +0,0 @@
../../azure/models/mistral-nemo.toml
@@ -1 +0,0 @@
../../azure/models/o1-mini.toml
@@ -1 +0,0 @@
../../azure/models/phi-3-medium-128k-instruct.toml
@@ -1 +0,0 @@
../../azure/models/phi-3-medium-4k-instruct.toml
@@ -1 +0,0 @@
../../azure/models/phi-3-mini-128k-instruct.toml
@@ -1 +0,0 @@
../../azure/models/phi-3-mini-4k-instruct.toml
@@ -1 +0,0 @@
../../azure/models/phi-3-small-128k-instruct.toml
@@ -1 +0,0 @@
../../azure/models/phi-3-small-8k-instruct.toml
@@ -1 +0,0 @@
../../azure/models/phi-3.5-mini-instruct.toml
@@ -1 +0,0 @@
../../azure/models/phi-3.5-moe-instruct.toml
+1
View File
@@ -10,6 +10,7 @@ temperature = false
knowledge = "2024-04"
tool_call = true
open_weights = false
status = "deprecated"
[cost]
input = 1.50
@@ -1,5 +0,0 @@
base_model = "cohere/command-r-08-2024"
[cost]
input = 0.15
output = 0.6
@@ -1,5 +0,0 @@
base_model = "cohere/command-r-plus-08-2024"
[cost]
input = 2.5
output = 10
@@ -1,24 +0,0 @@
name = "DeepSeek-R1-0528"
description = "DeepSeek reasoning model for multi-step analysis, math, coding, and tools"
family = "deepseek-thinking"
release_date = "2025-05-28"
last_updated = "2025-05-28"
attachment = false
reasoning = true
reasoning_options = []
temperature = true
knowledge = "2024-07"
tool_call = true
open_weights = true
[cost]
input = 1.35
output = 5.4
[limit]
context = 163_840
output = 163_840
[modalities]
input = ["text"]
output = ["text"]
+1
View File
@@ -10,6 +10,7 @@ temperature = true
knowledge = "2024-07"
tool_call = false
open_weights = true
status = "deprecated"
[cost]
input = 1.35
@@ -1,23 +0,0 @@
name = "DeepSeek-V3-0324"
description = "DeepSeek chat model for instruction following, coding, and analysis"
family = "deepseek"
release_date = "2025-03-24"
last_updated = "2025-03-24"
attachment = false
reasoning = false
temperature = true
knowledge = "2024-07"
tool_call = true
open_weights = true
[cost]
input = 1.14
output = 4.56
[limit]
context = 131_072
output = 131_072
[modalities]
input = ["text"]
output = ["text"]
-24
View File
@@ -1,24 +0,0 @@
name = "DeepSeek-V3.1"
description = "DeepSeek chat model for instruction following, coding, and analysis"
family = "deepseek"
release_date = "2025-08-21"
last_updated = "2025-08-21"
attachment = false
reasoning = true
reasoning_options = []
temperature = true
knowledge = "2024-07"
tool_call = true
open_weights = true
[cost]
input = 0.56
output = 1.68
[limit]
context = 131_072
output = 131_072
[modalities]
input = ["text"]
output = ["text"]
@@ -9,6 +9,7 @@ temperature = true
knowledge = "2021-08"
tool_call = false
open_weights = false
status = "deprecated"
[cost]
input = 0.50
@@ -1,23 +0,0 @@
name = "GPT-3.5 Turbo 0301"
description = "Compact GPT model for low-latency assistance and high-volume workloads"
family = "gpt"
release_date = "2023-03-01"
last_updated = "2023-03-01"
attachment = false
reasoning = false
temperature = true
knowledge = "2021-08"
tool_call = false
open_weights = false
[cost]
input = 1.50
output = 2.00
[limit]
context = 4096
output = 4096
[modalities]
input = ["text"]
output = ["text"]
@@ -1,23 +0,0 @@
name = "GPT-3.5 Turbo 0613"
description = "Compact GPT model for low-latency assistance and high-volume workloads"
family = "gpt"
release_date = "2023-06-13"
last_updated = "2023-06-13"
attachment = false
reasoning = false
temperature = true
knowledge = "2021-08"
tool_call = false
open_weights = false
[cost]
input = 3.00
output = 4.00
[limit]
context = 16384
output = 16384
[modalities]
input = ["text"]
output = ["text"]
@@ -9,6 +9,7 @@ temperature = true
knowledge = "2021-08"
tool_call = false
open_weights = false
status = "deprecated"
[cost]
input = 1.00
@@ -9,6 +9,7 @@ temperature = true
knowledge = "2021-08"
tool_call = false
open_weights = false
status = "deprecated"
[cost]
input = 1.50
-23
View File
@@ -1,23 +0,0 @@
name = "GPT-4 32K"
description = "GPT model for general reasoning, writing, coding, and tool-assisted tasks"
family = "gpt"
release_date = "2023-03-14"
last_updated = "2023-03-14"
attachment = false
reasoning = false
temperature = true
knowledge = "2023-11"
tool_call = true
open_weights = false
[cost]
input = 60.00
output = 120.00
[limit]
context = 32768
output = 32768
[modalities]
input = ["text"]
output = ["text"]
@@ -9,6 +9,7 @@ temperature = true
knowledge = "2023-11"
tool_call = true
open_weights = false
status = "deprecated"
[cost]
input = 10.00
+1
View File
@@ -1,4 +1,5 @@
base_model = "openai/gpt-4-turbo"
status = "deprecated"
base_model_omit = ["structured_output"]
[cost]
+1
View File
@@ -1,4 +1,5 @@
base_model = "openai/gpt-4.1-mini"
status = "deprecated"
base_model_omit = ["structured_output"]
[cost]
+1
View File
@@ -1,4 +1,5 @@
base_model = "openai/gpt-4.1-nano"
status = "deprecated"
base_model_omit = ["structured_output"]
[cost]
+1
View File
@@ -1,4 +1,5 @@
base_model = "openai/gpt-4.1"
status = "deprecated"
base_model_omit = ["structured_output"]
[cost]
-23
View File
@@ -1,23 +0,0 @@
name = "GPT-4"
description = "GPT model for general reasoning, writing, coding, and tool-assisted tasks"
family = "gpt"
release_date = "2023-03-14"
last_updated = "2023-03-14"
attachment = false
reasoning = false
temperature = true
knowledge = "2023-11"
tool_call = true
open_weights = false
[cost]
input = 60.00
output = 120.00
[limit]
context = 8192
output = 8192
[modalities]
input = ["text"]
output = ["text"]
+1
View File
@@ -1,4 +1,5 @@
base_model = "openai/gpt-4o-mini"
status = "deprecated"
base_model_omit = ["structured_output"]
[cost]
+1
View File
@@ -1,4 +1,5 @@
base_model = "openai/gpt-4o"
status = "deprecated"
base_model_omit = ["structured_output"]
[cost]
-25
View File
@@ -1,25 +0,0 @@
name = "GPT-5 Chat"
description = "Chat-tuned GPT model for conversational assistance, writing, and tool workflows"
family = "gpt-codex"
release_date = "2025-08-07"
last_updated = "2025-08-07"
attachment = true
reasoning = true
reasoning_options = []
temperature = false
knowledge = "2024-10-24"
tool_call = false
open_weights = false
[cost]
input = 1.25
output = 10.00
cache_read = 0.13
[limit]
context = 128_000
output = 16_384
[modalities]
input = ["text", "image"]
output = ["text"]
-26
View File
@@ -1,26 +0,0 @@
name = "GPT-5.1 Chat"
description = "Speech generation model for controllable voice, narration, and audio delivery"
family = "gpt-codex"
release_date = "2025-11-14"
last_updated = "2025-11-14"
attachment = true
reasoning = true
reasoning_options = [{ type = "effort", values = ["medium"] }]
temperature = false
knowledge = "2024-09-30"
tool_call = true
structured_output = true
open_weights = false
[cost]
input = 1.25
output = 10.00
cache_read = 0.125
[limit]
context = 128_000
output = 16_384
[modalities]
input = ["text", "image", "audio"]
output = ["text", "image", "audio"]
-26
View File
@@ -1,26 +0,0 @@
name = "GPT-5.2 Chat"
description = "Chat-tuned GPT model for conversational assistance, writing, and tool workflows"
family = "gpt-codex"
release_date = "2025-12-11"
last_updated = "2025-12-11"
attachment = true
reasoning = true
reasoning_options = [{ type = "effort", values = ["medium"] }]
temperature = false
knowledge = "2025-08-31"
tool_call = true
structured_output = true
open_weights = false
[cost]
input = 1.75
output = 14.00
cache_read = 0.175
[limit]
context = 128_000
output = 16_384
[modalities]
input = ["text", "image"]
output = ["text"]
-26
View File
@@ -1,26 +0,0 @@
name = "GPT-5.3 Chat"
description = "Chat-tuned GPT model for conversational assistance, writing, and tool workflows"
family = "gpt-codex"
release_date = "2026-03-03"
last_updated = "2026-03-03"
attachment = true
reasoning = true
reasoning_options = [{ type = "effort", values = ["medium"] }]
temperature = false
knowledge = "2025-08-31"
tool_call = true
structured_output = true
open_weights = false
[cost]
input = 1.75
output = 14.00
cache_read = 0.175
[limit]
context = 128_000
output = 16_384
[modalities]
input = ["text", "image"]
output = ["text"]
@@ -1,25 +0,0 @@
name = "Grok 4 Fast (Reasoning)"
description = "Fast Grok model for responsive chat, reasoning, and tool-assisted work"
family = "grok"
release_date = "2025-09-19"
last_updated = "2025-09-19"
attachment = true
reasoning = true
reasoning_options = []
temperature = true
knowledge = "2025-07"
tool_call = true
open_weights = false
[cost]
input = 0.20
output = 0.50
cache_read = 0.05
[limit]
context = 2_000_000
output = 30_000
[modalities]
input = ["text", "image"]
output = ["text"]
@@ -1,26 +0,0 @@
name = "Kimi K2 Thinking"
description = "Kimi reasoning model for long-horizon research, planning, and tool use"
family = "kimi-thinking"
release_date = "2025-11-06"
last_updated = "2025-12-02"
attachment = false
reasoning = true
reasoning_options = []
temperature = true
knowledge = "2024-08"
tool_call = true
interleaved = true
open_weights = true
[cost]
input = 0.60
output = 2.50
cache_read = 0.15
[limit]
context = 262_144
output = 262_144
[modalities]
input = ["text"]
output = ["text"]
@@ -1,23 +0,0 @@
name = "Llama-3.2-11B-Vision-Instruct"
description = "Open Llama multimodal model for image understanding and text reasoning"
family = "llama"
release_date = "2024-09-25"
last_updated = "2024-09-25"
attachment = true
reasoning = false
temperature = true
knowledge = "2023-12"
tool_call = true
open_weights = true
[cost]
input = 0.37
output = 0.37
[limit]
context = 128_000
output = 8_192
[modalities]
input = ["text", "image"]
output = ["text"]
@@ -1,23 +0,0 @@
name = "Llama-3.2-90B-Vision-Instruct"
description = "Open Llama multimodal model for image understanding and text reasoning"
family = "llama"
release_date = "2024-09-25"
last_updated = "2024-09-25"
attachment = true
reasoning = false
temperature = true
knowledge = "2023-12"
tool_call = true
open_weights = true
[cost]
input = 2.04
output = 2.04
[limit]
context = 128_000
output = 8_192
[modalities]
input = ["text", "image"]
output = ["text"]
@@ -1,23 +0,0 @@
name = "Meta-Llama-3-70B-Instruct"
description = "Open Llama instruction model for multilingual chat, reasoning, and coding"
family = "llama"
release_date = "2024-04-18"
last_updated = "2024-04-18"
attachment = false
reasoning = false
temperature = true
knowledge = "2023-12"
tool_call = false
open_weights = true
[cost]
input = 2.68
output = 3.54
[limit]
context = 8_192
output = 2_048
[modalities]
input = ["text"]
output = ["text"]
@@ -1,23 +0,0 @@
name = "Meta-Llama-3-8B-Instruct"
description = "Open Llama instruction model for multilingual chat, reasoning, and coding"
family = "llama"
release_date = "2024-04-18"
last_updated = "2024-04-18"
attachment = false
reasoning = false
temperature = true
knowledge = "2023-12"
tool_call = false
open_weights = true
[cost]
input = 0.30
output = 0.61
[limit]
context = 8_192
output = 2_048
[modalities]
input = ["text"]
output = ["text"]
@@ -1,23 +0,0 @@
name = "Meta-Llama-3.1-405B-Instruct"
description = "Open Llama instruction model for multilingual chat, reasoning, and coding"
family = "llama"
release_date = "2024-07-23"
last_updated = "2024-07-23"
attachment = false
reasoning = false
temperature = true
knowledge = "2023-12"
tool_call = true
open_weights = true
[cost]
input = 5.33
output = 16.00
[limit]
context = 128_000
output = 32_768
[modalities]
input = ["text"]
output = ["text"]
@@ -1,23 +0,0 @@
name = "Meta-Llama-3.1-70B-Instruct"
description = "Open Llama instruction model for multilingual chat, reasoning, and coding"
family = "llama"
release_date = "2024-07-23"
last_updated = "2024-07-23"
attachment = false
reasoning = false
temperature = true
knowledge = "2023-12"
tool_call = true
open_weights = true
[cost]
input = 2.68
output = 3.54
[limit]
context = 128_000
output = 32_768
[modalities]
input = ["text"]
output = ["text"]
@@ -1,23 +0,0 @@
name = "Meta-Llama-3.1-8B-Instruct"
description = "Open Llama instruction model for multilingual chat, reasoning, and coding"
family = "llama"
release_date = "2024-07-23"
last_updated = "2024-07-23"
attachment = false
reasoning = false
temperature = true
knowledge = "2023-12"
tool_call = true
open_weights = true
[cost]
input = 0.30
output = 0.61
[limit]
context = 128_000
output = 32_768
[modalities]
input = ["text"]
output = ["text"]
@@ -1,23 +0,0 @@
name = "Mistral Large 24.11"
description = "Flagship Mistral model for advanced reasoning, coding, and multilingual work"
family = "mistral-large"
release_date = "2024-11-01"
last_updated = "2024-11-01"
attachment = false
reasoning = false
temperature = true
knowledge = "2024-09"
tool_call = true
open_weights = false
[cost]
input = 2.00
output = 6.00
[limit]
context = 128_000
output = 32_768
[modalities]
input = ["text"]
output = ["text"]
-23
View File
@@ -1,23 +0,0 @@
name = "Mistral Nemo"
description = "Mistral model for multilingual chat, reasoning, and tool-assisted workflows"
family = "mistral-nemo"
release_date = "2024-07-18"
last_updated = "2024-07-18"
attachment = false
reasoning = false
temperature = true
knowledge = "2024-07"
tool_call = true
open_weights = true
[cost]
input = 0.15
output = 0.15
[limit]
context = 128_000
output = 128_000
[modalities]
input = ["text"]
output = ["text"]
-25
View File
@@ -1,25 +0,0 @@
name = "o1-mini"
description = "O-series reasoning model for hard analysis, math, coding, and planning"
family = "o-mini"
release_date = "2024-09-12"
last_updated = "2024-09-12"
attachment = false
reasoning = true
reasoning_options = []
temperature = false
knowledge = "2023-09"
tool_call = true
open_weights = false
[cost]
input = 1.10
output = 4.40
cache_read = 0.55
[limit]
context = 128_000
output = 65_536
[modalities]
input = ["text"]
output = ["text"]
+1
View File
@@ -10,6 +10,7 @@ temperature = false
knowledge = "2023-09"
tool_call = true
open_weights = false
status = "deprecated"
[cost]
input = 15.00
+1
View File
@@ -1,4 +1,5 @@
base_model = "openai/o3-mini"
status = "deprecated"
reasoning_options = [{ type = "effort", values = ["low", "medium", "high"] }]
base_model_omit = ["structured_output"]
+1
View File
@@ -1,4 +1,5 @@
base_model = "openai/o4-mini"
status = "deprecated"
reasoning_options = [{ type = "effort", values = ["low", "medium", "high"] }]
base_model_omit = ["structured_output"]
@@ -1,23 +0,0 @@
name = "Phi-3-medium-instruct (128k)"
description = "Open-weight instruction model for adaptable chat and self-hosted production workloads"
family = "phi"
release_date = "2024-04-23"
last_updated = "2024-04-23"
attachment = false
reasoning = false
temperature = true
knowledge = "2023-10"
tool_call = false
open_weights = true
[cost]
input = 0.17
output = 0.68
[limit]
context = 128_000
output = 4_096
[modalities]
input = ["text"]
output = ["text"]
@@ -1,23 +0,0 @@
name = "Phi-3-medium-instruct (4k)"
description = "Open-weight instruction model for adaptable chat and self-hosted production workloads"
family = "phi"
release_date = "2024-04-23"
last_updated = "2024-04-23"
attachment = false
reasoning = false
temperature = true
knowledge = "2023-10"
tool_call = false
open_weights = true
[cost]
input = 0.17
output = 0.68
[limit]
context = 4_096
output = 1_024
[modalities]
input = ["text"]
output = ["text"]
@@ -1,23 +0,0 @@
name = "Phi-3-mini-instruct (128k)"
description = "Efficient model for low-latency assistance, extraction, and routine automation"
family = "phi"
release_date = "2024-04-23"
last_updated = "2024-04-23"
attachment = false
reasoning = false
temperature = true
knowledge = "2023-10"
tool_call = false
open_weights = true
[cost]
input = 0.13
output = 0.52
[limit]
context = 128_000
output = 4_096
[modalities]
input = ["text"]
output = ["text"]
@@ -1,23 +0,0 @@
name = "Phi-3-mini-instruct (4k)"
description = "Efficient model for low-latency assistance, extraction, and routine automation"
family = "phi"
release_date = "2024-04-23"
last_updated = "2024-04-23"
attachment = false
reasoning = false
temperature = true
knowledge = "2023-10"
tool_call = false
open_weights = true
[cost]
input = 0.13
output = 0.52
[limit]
context = 4_096
output = 1_024
[modalities]
input = ["text"]
output = ["text"]
@@ -1,23 +0,0 @@
name = "Phi-3-small-instruct (128k)"
description = "Efficient model for low-latency assistance, extraction, and routine automation"
family = "phi"
release_date = "2024-04-23"
last_updated = "2024-04-23"
attachment = false
reasoning = false
temperature = true
knowledge = "2023-10"
tool_call = false
open_weights = true
[cost]
input = 0.15
output = 0.60
[limit]
context = 128_000
output = 4_096
[modalities]
input = ["text"]
output = ["text"]
@@ -1,23 +0,0 @@
name = "Phi-3-small-instruct (8k)"
description = "Efficient model for low-latency assistance, extraction, and routine automation"
family = "phi"
release_date = "2024-04-23"
last_updated = "2024-04-23"
attachment = false
reasoning = false
temperature = true
knowledge = "2023-10"
tool_call = false
open_weights = true
[cost]
input = 0.15
output = 0.60
[limit]
context = 8_192
output = 2_048
[modalities]
input = ["text"]
output = ["text"]
@@ -1,23 +0,0 @@
name = "Phi-3.5-mini-instruct"
description = "Efficient model for low-latency assistance, extraction, and routine automation"
family = "phi"
release_date = "2024-08-20"
last_updated = "2024-08-20"
attachment = false
reasoning = false
temperature = true
knowledge = "2023-10"
tool_call = false
open_weights = true
[cost]
input = 0.13
output = 0.52
[limit]
context = 128_000
output = 4_096
[modalities]
input = ["text"]
output = ["text"]
@@ -1,23 +0,0 @@
name = "Phi-3.5-MoE-instruct"
description = "Open-weight instruction model for adaptable chat and self-hosted production workloads"
family = "phi"
release_date = "2024-08-20"
last_updated = "2024-08-20"
attachment = false
reasoning = false
temperature = true
knowledge = "2023-10"
tool_call = false
open_weights = true
[cost]
input = 0.16
output = 0.64
[limit]
context = 128_000
output = 4_096
[modalities]
input = ["text"]
output = ["text"]
@@ -0,0 +1,14 @@
base_model = "moonshotai/kimi-k3"
description = "Kimi multimodal agent model for visual understanding, coding, and planning"
temperature = true
reasoning_options = []
[cost]
input = 3
output = 15
[limit]
output = 262_144
[modalities]
input = ["text", "image"]

Some files were not shown because too many files have changed in this diff Show More