feat: add Maxlayer provider - #5169
Open
kerjakuyco wants to merge 12 commits into
Open
Conversation
Maxlayer is an OpenRouter-fronted gateway with an OpenAI-compatible endpoint at https://inference.maxlayer.cloud/v1. It relays to OpenRouter and keeps OpenRouter's `publisher/model` IDs verbatim, so a Maxlayer ID resolves onto the same canonical metadata the OpenRouter sync uses and every model file stays override-only over `base_model`. The sync module is authoritative for price and nothing else. Maxlayer's `GET /v1/models` publishes one `category` string and a `capabilities` array that is empty on every synced row -- no `supported_parameters`, no modality list, no reasoning metadata -- so a model with no canonical match is skipped and reported rather than authored from guesses. That covers 163 of the 303 rate-carded text models; the remainder need a `models/<lab>/<model>.toml` upstream first. Two things the response cannot supply are read from the sibling OpenRouter provider file for the same ID: `reasoning_options`, which is required whenever the base declares `reasoning = true`, and a `limit.output` fallback for the 60-odd rows publishing a null `max_output_tokens`. Both are sound here because Maxlayer forwards the request body to OpenRouter unchanged, so the controls a caller sends are OpenRouter's, model for model. Prices are written from `pricing.input_per_million_tokens`, which is the sell price -- upstream's rate plus the credit fee and the platform markup, derived on every read. The sibling `base_*_per_million_tokens` fields carry upstream's own rate for display only and are deliberately not read; quoting those would undercharge every model on the list. `upstream_cost` models (image, video, multimodal embeddings) are out of scope: they bill what the request actually cost and publish no rate card to express. Plain embeddings are out of scope too, as they are for the OpenRouter provider. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A `toggle` reasoning option needs a leading comment naming the exact request field, since sync re-serializes these files and keeps only a leading block. 55 of the 163 files carry a toggle and none carried the header. The fields are OpenRouter's because that is the API a Maxlayer request reaches -- the body is forwarded unchanged, so `reasoning.enabled`, `reasoning.effort`, and `reasoning.max_tokens` are what a caller sends here too. The header says so rather than leaving a reader to wonder why a Maxlayer file cites OpenRouter's docs. Emitted from `translateModel` via the runner's `header` hook, so it applies only to files that actually have a toggle and a hand-written header on an existing file still wins. Change detection compares parsed model data and ignores comments, so the files were regenerated rather than updated in place; the diff is 330 added comment lines across 55 files and nothing else. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The five keys say it; the routes, the key prefix, and the missing SDK were notes for review rather than facts a catalog consumer reads. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The toggle header carried two lines of prose about where a Maxlayer request goes after it leaves, and a link to the upstream's docs. Neither is something a catalog consumer needs: what a caller sends is the wire path, and that is all the header has to name. Leaves the three field lines the toggle policy asks for, on the same 55 files. No model data changes. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`openRouterReasoningOptions` pushed the toggle from inside the branch that handles graded effort, so a model publishing `reasoning.mandatory = false` with no `supported_efforts` and no `supports_max_tokens` produced no options at all. The runner then backfilled `reasoning_options = []`, which does not mean "unknown" -- it asserts the caller has no control. These models do have one: reasoning is on by default and `reasoning.enabled` turns it off. Fixes 69 files. Each one is a model the upstream catalog reports as non-mandatory, and peers serving the same base model already publish a toggle -- `moonshotai/kimi-k2.6` had 41 of them. Confined to reasoning options on purpose. minimax-m2.5 also had upstream price drift; that is left for the daily sync rather than folded in here. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Reasoning options were read as `existing?.reasoning_options ?? sibling`, and `[]` is not nullish, so once the runner had backfilled an empty array the file could never learn anything better. The sibling files gained real toggles in the previous commit and all 13 affected models stayed empty until this changed. An empty array is the runner's placeholder for "no one supplied controls", never something an author chose, so it now loses to a sibling that has them. Every remaining empty is now provably right: 11 models the upstream catalog marks mandatory, where reasoning is always on and there is nothing to switch, and 2 that report no reasoning at all. Prices in the same 21 files moved because the live catalogue refreshed between syncs. All 163 match the published sell price exactly and the realised markup is uniform. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Contributor
Action items
|
This reverts commit 0c009b7. The bug is real, but it belongs in its own pull request rather than inside one that adds a provider. Rewriting 69 files in a provider this PR does not own buries the thing under review. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Leaves the model files as authored TOMLs, which is the shape a provider addition takes here, and confines the change to `providers/maxlayer/`. The module and its registration are worth having and are kept in this branch's history at 335ce01 if we want them in a follow-up. They are removed here because a sync module carries a reproducibility contract -- running it must reproduce the committed files -- and honouring that for 13 reasoning models needs a fix in a provider this PR should not touch. Authored files make the same 13 an ordinary claim about the host's API, reviewable on its own terms. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Contributor
Action items
|
`qwen/qwen-plus` and `minimax/minimax-m2-her` inherited `reasoning = true` from their lab entries and carried `reasoning_options = []`, so each resolved to a reasoner with no controls. Neither route reasons: the host catalogue publishes no reasoning parameters for them, and the OpenRouter, Kilo and nano-gpt entries for the same IDs all set `reasoning = false`. Now they do too, with no options. `openai/o1-pro` had a toggle. Its control is graded effort -- OpenAI first-party, poe, cloudflare-ai-gateway and edenai all author `["low", "medium", "high"]`, and no entry anywhere uses a toggle for it. Replaced, and the wire header went with it since there is no longer a toggle to document. Checked the rest of the class rather than only these: of 31 toggle-only files, 30 have `toggle` as a leading shape among peers serving the same base model, and o1-pro was the only outlier. Of the 13 files with empty options, 11 have `[]` as the dominant peer shape and are models the host catalogue marks reasoning-mandatory, where there is genuinely nothing to switch. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Contributor
Action items
|
The model files overrode cost, limits and reasoning and nothing else, so every capability the host narrows was silently inherited from the lab entry. A consumer reading the catalog would have been told o1-pro takes tools, gpt-4 takes attachments, and MiniMax-M2-Her does structured output -- none of which is true on this host. Copies the real deltas for 112 files: structured_output on 76, modalities on 38, and name, temperature, tool_call and attachment on a handful each. Each value was taken only where it genuinely differs from the lab entry, so the files stay override-only; nothing restates a value the base already carries. `name` is in that set because six IDs are variant routes -- the `-fast` and `-pro` lanes -- whose lab entry is the unsuffixed model. Descriptions were left alone: they are editorial rather than a statement about what this host can do. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Contributor
Action items
|
These twelve authored `reasoning_options = [{ type = "toggle" }]` while
the established entries for the same twelve IDs carry `[]`. The toggles
came from a sibling change that was later reverted out of this branch,
so they were left claiming a `reasoning.enabled` control that nothing in
the catalog documents. They now carry `[]`, matching the same-surface
peer, and the wire headers went with them since there is no longer a
toggle to name.
The upstream catalogue does report these as non-mandatory, which is what
prompted the toggles in the first place. That is a real finding but it is
a finding about the peer entry, not this one: the peer says `[]` for all
twelve, and a relay should not be the only place in the catalog claiming
a control. Correcting it belongs in the change that fixes the peer, where
both providers move together.
Reasoning options are now identical to the peer for 162 of 163 files. The
exception is `openai/o1-pro`, which review asked to carry graded effort
to match the lab entry and every other relay.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Contributor
Action items
|
`limit.output` came from the host's `max_output_tokens` wherever it was published, and fell back to the lab entry where it was null. For eleven routes the host publishes nothing, and the lab default was well short of what the route actually serves: muse-spark-1.1 was capped at 32_000 against a 1_048_576 context, grok-4.3 at 30_000 against 1_000_000. Those eleven now take the same-surface peer's value, which is the best evidence available when the host is silent. Seven changed. One moved down rather than up -- nemotron-3-ultra-550b-a55b serves 16_384, not the lab's 128_000 -- which is the same rule applied honestly rather than a search for larger numbers. Not guesswork for the rest: `limit.context` matches the host's published `context_window` on all 163 files, and `limit.output` matches the host's own `max_output_tokens` on the 152 where it publishes one. No file restates a limit the lab entry already carries, and none claims an output larger than its context. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Contributor
|
Checking whether any |
Author
0 redundant overrides across all 163 files. |
kerjakuyco
marked this pull request as draft
August 21, 2026 07:44
kerjakuyco
marked this pull request as ready for review
August 21, 2026 07:44
Contributor
|
No actionable findings. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds Maxlayer (https://maxlayer.cloud) — a multi-model inference gateway serving 399 models through an OpenAI-compatible endpoint at https://inference.maxlayer.cloud/v1. (Disclosure: I work on Maxlayer.)
What's included
provider.toml—@ai-sdk/openai-compatible,MAXLAYER_API_KEYlogo.svg(currentColor)base_modelreferences, across 20 publishers (OpenAI, Qwen, Google, Anthropic, Z.ai, DeepSeek, MiniMax, Moonshot, xAI, NVIDIA, Mistral, Cohere, Meta, ByteDance Seed, StepFun, Tencent, Xiaomi and others)Data sourcing
Pricing is mirrored from the public catalogue endpoint
GET /v1/models(no key required).pricing.input_per_million_tokensand its siblings are the rate a caller is billed, already in USD per million tokens, mapped toinput/output/cache_read/cache_write. The response also carriesbase_*_per_million_tokensfields — a display-only reference rate — which are deliberately not what this PR publishes; Maxlayer does mark up token rates, so costs sit above those reference figures rather than matching them. The card is flat: there is no band or threshold field anywhere in the payload, so no[[cost.tiers]]are authored even for IDs that are tiered on other hosts.Limits come from the same endpoint —
context_window→limit.contexton all 163 files,max_output_tokens→limit.outputon 152. Eleven routes publish no max output; those follow the same-surface peer entries in this repository rather than the lab default, which was short enough to misreport them badly (meta/muse-spark-1.1resolves to 1_048_576, not 32_000).Capabilities and
reasoning_optionsare not published by this endpoint — the model list carries nosupported_parameters, no modality list, and an emptycapabilitiesarray on every row. Rather than guess, both are set to match the same-surface peer entries in this repository for the same model ID:structured_outputon 76 files,modalitieson 38, andname/temperature/tool_call/attachmenton a handful each. 162 of 163 files match the peer's reasoning shape exactly; the exception isopenai/o1-pro, which carries gradedeffortto match the lab entry and every other host serving it. Where a file carries atoggleit also carries the leading wire-path comment naming the accepted request fields.Every file uses
base_modeland is override-only — no file restates a value the lab entry already carries, nestedlimitkeys included.The full live catalogue is 399 models. This PR covers the 163 rate-carded text models with a canonical lab entry; the remainder are 140 text models with no lab entry yet, 66 image and video models billed at actual request cost rather than from a rate card, and 30 embedding models.
bun validatepasses locally.