* feat: split LLM Gateway into two provider catalogs
Renames the existing llmgateway provider to "DevPass (LLM Gateway)" (id
and models unchanged: the aggregated, auto-routed root-model catalog) and
adds llmgateway-providers ("LLM Gateway"): one entry per upstream
provider mapping, addressed as provider/model-id, synced from
/v1/models?mapped=true. The catalog starts empty and is populated by the
scheduled sync automation; the sync refuses to run against a deployment
without the mapped view so it fails loudly instead of syncing wrong ids.
Claude-Session: https://claude.ai/code/session_017pReWhniXJcDL9aiQHqoFQ
* fix: apply deployment data on mapped factored entries
Addresses the PR review: brand-new factored mapped entries now carry the
mapping's own capability flags (attachment/tool_call/reasoning and
structured_output) as overrides, translate the deployment's declared
reasoning_efforts into reasoning_options instead of stamping [], prefer
the gateway's served max_output over inherited/authored output limits,
and only fall back to context when the base metadata declares no output.
Adds unit tests for mapped factoring, capability overrides, max_output
preference, and the unprefixed-id refusal guard.
Claude-Session: https://claude.ai/code/session_017pReWhniXJcDL9aiQHqoFQ
* chore: seed the llmgateway-providers catalog
The dev branch now rejects providers with zero models, so the empty
.gitkeep-anchored catalog no longer validates. Seed it with a small
representative set generated by the sync (factored, full, duplicate
deployments of one model, capability deltas); the scheduled sync fills
in the rest once the gateway's mapped view is live.
Claude-Session: https://claude.ai/code/session_017pReWhniXJcDL9aiQHqoFQ
* fix: honor base and sibling reasoning data on mapped sync
Round 2 of review feedback:
- Factored resyncs no longer stamp context as limit.output when the
gateway omits max_output and the base declares an output to inherit;
the served max_output still wins whenever reported (creates and
resyncs), and reasoning_options now refresh from deployment efforts.
- A deployment whose only accepted effort is "none" is a plain on/off
switch, so it translates to a toggle (matches the lab's control).
- When a deployment declares no efforts, mapped entries reuse the
aggregated llmgateway catalog's curated reasoning_options for the
same root model instead of ending up with []; a curated [] counts as
unknown so a bad first stamp is not sticky. The runner also stops
stamping [] onto factored reasoners whose base metadata already
declares reasoning_options (it would shadow the base's controls).
- perplexity added to the canonical prefixes so Sonar models factor
against their lab metadata; the sonar-pro seed is now override-only.
Claude-Session: https://claude.ai/code/session_017pReWhniXJcDL9aiQHqoFQ
* fix: harden mapped sync guards and seed curation
Round 3 of review feedback:
- Both LLM Gateway syncs now reject an empty (or fully filtered)
response instead of authoritatively deleting the catalog through the
delete-missing pass; the every() prefix guard alone passed on [].
- A vision-less deployment also overrides modalities on factored
creates, so attachment=false can no longer coexist with inherited
image input (sonar-pro seed regenerated accordingly).
- Mapped entries copy the interleaved reasoning side-channel from the
aggregated llmgateway catalog when the deployment reasons (same wire
surface); glm-5.1 and kimi-k2.6 seeds now carry it.
- Toggle seeds carry the required leading wire-path comment.
- gpt-5.5 seeds author the 272k context pricing tier so resync
preserves it, matching the first-party and aggregated entries.
Claude-Session: https://claude.ai/code/session_017pReWhniXJcDL9aiQHqoFQ
* fix: never author zero limits, enforce vision on modalities
Round 4 of review feedback:
- A missing/zero context_length is no longer written as limit.context=0:
factored entries leave context unset and inherit the base, and
unfactored creates without a positive served context are skipped
(reported via sourceID) instead of publishing unusable limits. Applies
to both the aggregated and mapped builders.
- vision=false now forces non-image input modalities from the mapping
itself instead of trusting the model-level architecture, on both the
factored and unfactored create paths (and the existing-full fallback).
Claude-Session: https://claude.ai/code/session_017pReWhniXJcDL9aiQHqoFQ
* fix: scalable logo, require one mapping per entry
Review round 5: drop the fixed width/height from the new provider logo
(AGENTS.md blocker), and fail the mapped sync loudly when a kept model
does not carry exactly one providers[] mapping instead of letting the
builder silently fall back to noisy supported_parameters defaults.
Claude-Session: https://claude.ai/code/session_0131ZfUnTfJrCzygw3wE4bNF
* fix: inherit lab descriptions, author toggle headers
Review round 6: mapped factored resyncs no longer stamp a synthesized
describeModel blurb as a sticky description override (unset keeps
inheriting the lab text, matching merge-gateway/cortecs), and mapped
sync writes now author the required leading wire-path comment on files
that carry a toggle reasoning control via a new optional header on the
translateModel result (an existing on-disk header always wins).
Claude-Session: https://claude.ai/code/session_0131ZfUnTfJrCzygw3wE4bNF
* fix: keep mapping flags authoritative on resyncs
Review round 7: mapped existing-entry resyncs (factored and full) now
apply the deployment mapping's reasoning/vision/tools/structured-output
flags with the same authority as creates, so the written booleans and
the reasoning_options derived from them always move together and drift
self-heals hourly; prior curation only fills in where the mapping is
silent. Also documents in the together-ai/kimi-k2.6 seed header why
that pin is intentionally weaker than Together's first-party row (the
gateway serves it with tools/JSON off and a 32k output cap per its own
e2e'd catalog mapping).
Claude-Session: https://claude.ai/code/session_0131ZfUnTfJrCzygw3wE4bNF
* fix: realign vision modalities in both directions
Review round 8: mapped resyncs no longer keep a stale text-only
modalities override once the deployment's vision returns — a declared
vision=true clears the override on factored entries (base image/pdf
inputs inherit again) and recomputes from the served architecture on
full entries, mirroring how vision=false already strips them; only a
silent mapping leaves curated modalities untouched.
Claude-Session: https://claude.ai/code/session_0131ZfUnTfJrCzygw3wE4bNF
* fix: local perplexity resolution, no zero limits
Review round 9: drop the perplexity entry from the shared
CANONICAL_PROVIDER_PREFIXES (it would silently start factoring other
hosts' standalone perplexity files) — the llmgateway sync now resolves
lab IDs through resolveModelMetadataBaseModel, whose exact models/ path
match covers perplexity without touching other providers. Full-row
resyncs in both builders no longer fall back to the zero/absent
reported context: authored limits only ever carry known-positive
values, an authored 0 on disk counts as unusable, and a full row with
no usable context anywhere fails loudly (skipping would hand the file
to the delete-missing pass).
Claude-Session: https://claude.ai/code/session_0131ZfUnTfJrCzygw3wE4bNF
* fix: merge deployment efforts with curated controls
Review round 10: deployment reasoning_efforts now own only the
effort/toggle surface — curated non-effort controls such as
budget_tokens (the same host's $.reasoning.max_tokens path, mirroring
DigitalOcean's sync) survive from the existing file or the aggregated
sibling instead of being wiped on every resync. Mapped creates also
seed cost.tiers from the aggregated sibling's curated tiers, since the
gateway API exposes none and the bulk sync would otherwise author
tiered models at flat long-context rates; authored tiers still win on
resync.
Claude-Session: https://claude.ai/code/session_0131ZfUnTfJrCzygw3wE4bNF
* feat(sync): auto-resolve EmpirioLabs models from canonical metadata
The EmpirioLabs adapter only tried a few family prefixes, so models
with existing lab TOMLs were skipped. Resolve via family prefixes,
version-dot slugs, unique filenames, and dated/version suffixes.
Treat EmpirioLabs as a reviewed reasoning provider so hourly syncs
can auto-merge factored catalog updates.
* fix(sync): use mistralai prefix for EmpirioLabs Mistral ids
* test(sync): stop asserting qwen3-8-27b has no canonical
* Add Inceptron provider sync module
* Require review for Inceptron reasoning sync changes
Inceptron's models_dev reasoning metadata is provider-authored and is not independently constrained to reviewed lab or peer baselines. Keep it outside the reasoning auto-merge allowlist and assert that changes to its reasoning metadata require manual review.
* feat(models): add Meta Muse Glimmer 30B lab metadata
Add the lab model so OpenRouter, Vercel, Kilo, and other hosts can
base_model onto meta/muse-glimmer-30b instead of shipping standalone
copies.
* fix: map Muse Glimmer hosts onto the Meta lab model
Factor OpenRouter and Vercel onto base_model = meta/muse-glimmer-30b
and keep only host cost plus the documented low/medium/high/xhigh
reasoning_effort controls.
The Gateway catalog emits capabilities.reasoning = null on some routes
even when supports_reasoning is true. Treat null like a missing object
so sync does not crash while deriving reasoning_options.
The merge-gateway sync synthesized a bare reasoning toggle from
disable_supported and ignored reasoning.controls, so claude-opus-5 (newly
added, no curated reasoning_options) got a bare [[reasoning_options]] toggle
even though the route advertises a graded reasoning.effort control. The rest
of the Claude family carried toggle + effort because their options were
hand-authored; any future new model would regress the same way.
Map reasoning.controls into synthesized options: toggle when disable is
supported, plus effort when the route advertises effort and the API provides
effort_values. Author claude-opus-5's TOML to toggle + effort [low..max],
matching the family.
* fix(sync): harden DigitalOcean catalog translation
Stop incomplete DO catalog rows from corrupting curated model data:
- map mimo-* IDs to xiaomi base metadata
- only treat thinking=true as authoritative reasoning (not bare efforts)
- merge effort lists so incomplete remote values cannot drop none/xhigh
- normalize x-high → xhigh
- union modalities with authored data; skip text-only overrides on base models
- keep beta status for Public Preview names
* fix(sync): preserve DigitalOcean modality overrides
* fix(sync): prefer DigitalOcean catalog metadata
* fix(sync): fall back on empty reasoning efforts
* fix(sync): respect DigitalOcean modality removals
* fix(sync): map long-context pricing tiers from xAI and OpenRouter APIs
Both APIs already expose long-context rates, but sync preserved hand-authored
[[cost.tiers]] and never self-healed stale values (e.g. grok-4.5 cache_read).
- xAI: read *_long_context prices + long_context_threshold
- OpenRouter: map pricing.overrides → cost.tiers
* refactor(sync): simplify long-context tier mapping
Drop longContextPrice helper and conditional spreads; use || for xAI
zero-means-base and flatMap for OpenRouter overrides.
* fix(sync): treat omitted xAI long-context rates as unknown
0 means same-as-base; undefined means the field was omitted — only the
latter should keep hand-authored tiers instead of fabricating base prices.
* 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>
Models API has no fast-mode surface; keep authored experimental/provider.
Use factorBaseModel so attachment/reasoning/limit/modalities are not
rewritten when they already match models/ metadata.
OpenRouter preserves fast variant names when stripping -fast to resolve
canonical metadata. Venice resolves -fast IDs/names to base model
metadata without hardcoding each alias. Fix openrouter opus-5-fast TOML.
* chore(sync): update Vercel AI Gateway model catalog
* fix(vercel): factor opus-5-fast onto base opus and match fable reasoning_options
Strip -fast when resolving canonical base models so Claude Opus fast
variants inherit models/ metadata. Set vercel opus-5 reasoning_options to
match fable (toggle + effort low/medium/high/xhigh).
* fix(vercel): match anthropic opus-5 effort-only reasoning_options
---------
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Aiden Cline <aidenpcline@gmail.com>