Skip to content

feat(providers): add AtmoRouter - #11453

Open
bayuh85 wants to merge 3 commits into
anomalyco:devfrom
bayuh85:feat/atmorouter-provider
Open

bayuh85 wants to merge 3 commits into
anomalyco:devfrom
bayuh85:feat/atmorouter-provider

Conversation

@bayuh85

@bayuh85 bayuh85 commented Oct 25, 2026 •

Copy link
Copy Markdown

Summary

This PR adds AtmoRouter (https://atmorouter.dev) as provider atmorouter. AtmoRouter is a multi-model relay. This entry models only its OpenAI-compatible Chat Completions API; no [api.messages] tables are added.

  • Endpoint: https://atmorouter.dev/v1. Authentication: ATMOROUTER_API_KEY. npm: @ai-sdk/openai-compatible.
  • The catalog covers 27 coding/agent chat models across the Claude, GPT, Gemini, GLM, Kimi, Qwen, DeepSeek, MiniMax and Grok families.
    • Each model references existing lab metadata through base_model (schema 2).
    • Each model file contains only real overrides plus the relay's reasoning surface.
  • logo.svg is AtmoRouter's bolt mark: currentColor, square 0 0 32 32 viewBox, no fixed dimensions.

OpenCode consumes this registry, so no OpenCode source change is needed.

Sources and scope

Model IDs, context/output limits and input kinds come from AtmoRouter's public catalog at GET https://atmorouter.dev/v1/models, fetched on 2026-10-25. The authenticated list had 167 entries. Lab facts and release dates are inherited through base_model, not guessed.

  • Pricing is omitted on purpose. This follows the precedent of relays whose prices are not fixed per model.
    • AtmoRouter bills each request at the rate of whichever offer tier serves it, and tier prices change continuously.
    • By default, a request can route to a tier priced above the published "From" rate unless the account sets a budget.
    • Putting the "From" price in cost would understate what users pay. The reason is recorded in provider.toml and in each model's header comment.
  • Limits. A limit is overridden only where AtmoRouter publishes a lower value than the lab. When the lab's limit.input would then exceed the new context, base_model_omit = ["limit.input"] drops it.
    • GPT-5.6 Sol/Luna/Terra and GPT-6 Sol: context 272k.
    • GPT-6.1 Sol: context 500k.
    • Gemini 3.1 Pro: context 1M.
    • GLM-5.3 and GLM-5.3 Flash: output 48k.
    • Kimi K3 and K3.1 Pro: context 1M, output 32k.
    • MiniMax M3: context 1M, output 48k.
    • Where AtmoRouter lists a higher value than the lab, the lab value is kept, because it was not verified:
      • GPT-5.3 Codex: 1M listed vs the lab's 400k.
      • Sonnet 4.6: 128k output listed vs 64k.
      • DeepSeek V4.1 Flash and V4 Pro: 393,216 output listed vs 384,000.
  • Modalities are the lab's input MIME types filtered to the kinds AtmoRouter lists for each model. AtmoRouter lists no PDF/file kind, so PDF is dropped where the lab has it. Output is text.
  • Reasoning. AtmoRouter is a same-surface relay that takes top-level reasoning_effort. Each model copies the reasoning controls of its lab entry (effort levels, toggle, budget). A toggle or budget is kept only when a live call showed AtmoRouter forwards that field (see "Reasoning controls" below); no universal ladder is invented.
    • kimi-k2.7-code is supported = true with no effort list, like its lab entry.
    • minimax-m3 copies the lab's toggle. Through AtmoRouter, thinking is on when thinking is omitted and off with thinking.type = disabled, so the default is recorded as on.
    • The lowest and highest effort of every list was sent live and accepted (see below).
    • claude-sonnet-5 leaves out the lab's toggle because thinking.type = disabled still returned reasoning through AtmoRouter.
    • system_updates, effort_updates and tools.updates are left unset, because AtmoRouter does not document them.
  • Live testing. Every model was live-tested before inclusion. moon/kimi-k3 returned HTTP 502 in testing and was left out in favour of cb/kimi-k3, which passed.

Docs: AtmoRouter, docs, models.

Validation

On Linux with Bun 1.4.2, against dev at 7cd0423:

  • bun install --frozen-lockfile: passed.
  • bun validate: passed.
  • cd packages/sdk && bun run test: passed, 25 tests, 0 failures.
  • git diff --cached --check: passed.
  • bun run v1:check: exits 1, and every reported change is an addition.
    • It reports the 27 new atmorouter/* models as "Added to v1" and the new provider's fields (id, env, npm, api, name, doc) as added. It reports "v1 output changed in 3 of 22 views".
    • No existing provider or model changes.
    • Prime Inference (feat: add Prime Inference provider #9497) was merged the same way, with its models in v1. If you would rather mark these v2_only, I'm happy to change that.

Live test

Run on 25 Oct 2026, about 16:50–17:10 WIB (UTC+7), against https://atmorouter.dev/v1 with a real AtmoRouter key. The key is not in this PR or its logs.

  • Model list. Authenticated GET /v1/models returned HTTP 200 in 1.14s with 167 models. All 27 IDs below are listed.
  • Chat. Each model got Reply with exactly: pong at temperature 0.2 and max_tokens 256, once non-streaming and once streaming.
    • Every call returned HTTP 200 with pong, and every stream ended with [DONE].
    • The cb/gemini-3.1-pro non-streaming call was slow in this single sample; its stream finished in about 5s.
  • Effort probe. The same prompt was sent with reasoning_effort set to the lowest and the highest value in each model's effort list. Every call returned HTTP 200 (the lists were revised after review; the changed boundaries were re-sent at about 17:25 WIB).
    • This shows the relay accepts every listed boundary value.
    • It does not show that each level changes the upstream behaviour.
Model base_model Non-stream Stream total / first token Effort probe (lowest / highest)
cc/claude-sonnet-4-6 anthropic/claude-sonnet-4-6 2.15s 2.39s / 2.38s low 200, max 200
cc/claude-opus-4-7 anthropic/claude-opus-4-7 1.64s 1.71s / 1.7s low 200, max 200
cc/claude-opus-4-6 anthropic/claude-opus-4-6 2.76s 2.91s / 2.89s low 200, max 200
cc/claude-opus-4-8 anthropic/claude-opus-4-8 1.83s 1.62s / 1.55s low 200, max 200
cc/claude-haiku-4-5 anthropic/claude-haiku-4-5 2.46s 1.85s / 1.83s n/a (budget only, like the lab)
cc/claude-sonnet-5 anthropic/claude-sonnet-5 1.54s 1.69s / 1.69s low 200, max 200
cc/claude-opus-5 anthropic/claude-opus-5 2.10s 2.09s / 1.67s low 200, max 200
cc/claude-sonnet-5-5 anthropic/claude-sonnet-5-5 2.21s 2.98s / 2.97s low 200, max 200
cc/claude-opus-5-5 anthropic/claude-opus-5-5 1.77s 2.17s / 2.16s low 200, max 200
cx/gpt-5.6-sol openai/gpt-5.6-sol 4.98s 4.01s / 3.9s none 200, max 200
cx/gpt-5.6-luna openai/gpt-5.6-luna 7.03s 6.68s / 6.56s none 200, max 200
cx/gpt-5.6-terra openai/gpt-5.6-terra 7.78s 6.98s / 6.89s none 200, max 200
cx/gpt-6-sol openai/gpt-6-sol 13.44s 7.66s / 7.56s none 200, max 200
cx/gpt-6.1-sol openai/gpt-6.1-sol 7.18s 6.98s / 6.88s low 200, max 200
cb/gpt-5.3-codex openai/gpt-5.3-codex 3.16s 4.65s / 4.49s none 200, xhigh 200
cb/gemini-3.1-pro google/gemini-3.1-pro-preview 24.85s 5.11s / 4.78s low 200, high 200
cbcn/glm-5.3 zhipuai/glm-5.3 3.72s 2.77s / 2.65s low 200, max 200
cbcn/glm-5.3-flash zhipuai/glm-5.3-flash 2.51s 2.45s / 2.39s low 200, max 200
ali/kimi-k2.7-code moonshotai/kimi-k2.7-code 2.07s 1.62s / 1.59s n/a (no effort list)
cb/kimi-k3 moonshotai/kimi-k3 4.09s 4.13s / 4.12s low 200, max 200
cb/kimi-k3.1-pro moonshotai/kimi-k3.1-pro 5.04s 6.78s / 6.78s low 200, max 200
ali/qwen3.8-max alibaba/qwen3.8-max 2.20s 1.90s / 1.87s low 200, xhigh 200
ali/qwen3.8-flash alibaba/qwen3.8-flash 1.34s 2.31s / 2.27s low 200, xhigh 200
ali/deepseek-v4.1-flash deepseek/deepseek-v4.1-flash 1.92s 3.85s / 3.85s low 200, max 200
cbcn/deepseek-v4-pro deepseek/deepseek-v4-pro 2.77s 2.09s / 2.07s low 200, max 200
cb/minimax-m3 minimax/MiniMax-M3 1.88s 2.48s / 2.46s n/a (toggle, no effort list)
grok/grok-4.7 xai/grok-4.7 2.01s 2.27s / 2.23s low 200, xhigh 200

Reasoning controls (live, about 17:25–17:30 WIB)

A multi-step arithmetic prompt was sent to check which lab controls AtmoRouter actually forwards. reasoning_tokens comes from usage.completion_tokens_details.

Model Control sent Result
cbcn/deepseek-v4-pro thinking.type = disabled 0 reasoning tokens (default: 1087)
ali/deepseek-v4.1-flash thinking.type = disabled no reasoning (default: 3266)
ali/qwen3.8-max enable_thinking = false / thinking_budget = 200 no reasoning / exactly 200 reasoning tokens (default: 1728)
ali/qwen3.8-flash enable_thinking = false / thinking_budget = 200 no reasoning / exactly 200 reasoning tokens (default: 4625)
cb/gpt-5.3-codex reasoning_effort = none 200, 129 reasoning tokens (low: 238, xhigh: 413)
cb/kimi-k3, cb/kimi-k3.1-pro reasoning_effort = none still reasoned (316 / 211 tokens), so none was dropped
cb/minimax-m3 thinking.type = disabled / adaptive / enabled 0 reasoning tokens for all three (omitted: ~860, twice), so toggle with default on
cc/claude-sonnet-5 thinking.type = disabled still reasoned (112 tokens), so no toggle
cc/claude-haiku-4-5, cc/claude-sonnet-4-6 thinking.budget_tokens = 1024 / 12000 200 both; Anthropic treats the budget as a soft cap, so this shows only that the field is accepted

@github-actions

Copy link
Copy Markdown
Contributor

Action items

  • [high] [violation] providers/atmorouter/models/cc/claude-haiku-4-5.toml:13 - Check: Baseline effort = lab + same-surface peer set, not fixed low/medium/high. Why: Native providers/anthropic/models/claude-haiku-4-5.toml is budget-only with no effort, relay peers are toggle+budget (CrossModel/Merge), budget (Opencode) or reasoning=false (Requesty); the only effort peer (LLMGateway) is 5-level low/medium/high/xhigh/max. No baseline has 3-level low/medium/high. Action: Replace with the host-verified surface (budget/toggle if Messages is forwarded, or peer-supported effort with docs/live proof each level has distinct effect) and cite it.
  • [high] [violation] providers/atmorouter/models/cb/gpt-5.3-codex.toml:14 - Check: Relay must copy underlying controls from lab + same-surface peers. Why: Both providers/openai/models/gpt-5.3-codex.toml and providers/openrouter/models/openai/gpt-5.3-codex.toml are none/low/medium/high/xhigh; PR drops none to 4 levels with only “lowest/highest accepted” and no proof the host rejects none. Action: Restore none or provide host docs/live proof none is rejected/ignored on this host.
  • [high] [violation] providers/atmorouter/models/cbcn/deepseek-v4-pro.toml:9 - Check: DeepSeek V4 baseline is toggle + low/high/max per AGENTS.md. Why: First-party providers/deepseek/models/deepseek-v4-pro.toml is toggle=true, effort=[low,high,max] with xhigh→high mapping; PR uses effort=[none,high,xhigh] with no toggle — invents distinct xhigh, drops low/max, collapses off to none without proof none disables thinking. Same pattern in ali/deepseek-v4.1-flash.toml. Action: Change to toggle=true + effort=[low,high,max] with leading Toggle/Effort wire comments, or provide host docs/live proof each of none/high/xhigh has distinct meaningful effect and no toggle exists.
  • [medium] [possible mistake] providers/atmorouter/models/cb/kimi-k3.toml:9 - Check: Do not invent off where lab says always-on. Why: providers/moonshotai/models/kimi-k3.toml and kimi-k3.1-pro.toml are effort=[low,high,max] with “no off toggle”; PR adds none (none/low/high/max, same in cb/kimi-k3.1-pro.toml) with only “accepted live” — acceptance does not prove none disables reasoning. Action: Remove none or prove live that none returns no reasoning content vs ignored.
  • [medium] [possible mistake] providers/atmorouter/models/ali/qwen3.8-flash.toml:14 - Check: Copy toggle/budget when lab/peers expose them unless host lacks the field. Why: providers/alibaba/models/qwen3.8-flash.toml is toggle+effort=[low,medium,xhigh]+budget and CrossModel peer is toggle+effort=[low,medium,xhigh]; PR drops to effort-only with no proof AtmoRouter lacks enable_thinking/thinking_budget (same in ali/qwen3.8-max.toml). Action: Add toggle (+budget if forwarded) with wire comments, or cite host docs/live showing those fields absent/rejected.
  • [medium] [possible mistake] providers/atmorouter/models/cc/claude-opus-4-7.toml:14 - Check: Extra levels beyond lab/peers need host docs or live meaningful effect. Why: Native is low/medium/high/xhigh/max (no none); Requesty peer is none/low/medium/high/max (no xhigh); PR unions to 6-level none/low/medium/high/xhigh/max with only “lowest/highest accepted” — no proof none disables or xhigh differs from high. Same union in cc/claude-opus-4-8.toml, cc/claude-opus-5.toml, cc/claude-sonnet-5.toml. Action: Align to one baseline or provide per-level docs/live proof of distinct effect.
  • [low] [possible mistake] providers/atmorouter/provider.toml:1 - Check: Model [api.*] tables are the complete API list and replace the provider default. Why: Header/body advertise OpenAI Chat Completions plus Anthropic Messages-compatible API, but all 27 models list no [api.*] and therefore expose only the default https://atmorouter.dev/v1 chat-completions route. Action: Add [api.messages] tables where supported (with base_url/sdk) or correct the comment/body to chat-completions only.

Copy each model's lab effort levels, add the DeepSeek and Qwen toggles and the Qwen/Claude budgets that AtmoRouter forwards (verified live), and scope provider.toml to Chat Completions only.
@github-actions

Copy link
Copy Markdown
Contributor

Action items

  • [high] [violation] providers/atmorouter/models/cb/minimax-m3.toml:9 - Check: Relay reasoning controls must copy the lab + same-surface peers; reasoning = { supported = true } alone means affirmatively always-on, not uncertainty. Why: First-party providers/minimax/models/MiniMax-M3.toml exposes toggle = true (thinking.type = adaptive|disabled) and peers expose controls (providers/opencode/models/minimax-m3.toml has toggle, providers/requesty/models/minimax-m3.toml has effort), but this file claims always-on with only "documents no reasoning control" and no live disabled-returns-no-reasoning test like the DeepSeek/Qwen/Claude files. Action: Live-test thinking.type = disabled|adaptive on AtmoRouter Chat Completions; if it disables thinking add toggle = true with a leading # Toggle: wire comment, otherwise replace the docs-only note with the dated live result.

@github-actions

Copy link
Copy Markdown
Contributor

No actionable findings.

@github-actions github-actions Bot added the reviewer: ready Automated review found no actionable items label Oct 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

reviewer: ready Automated review found no actionable items

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant