feat(providers): add aimlapi.com as a model provider - #1
Open
Lookoff-AIMLAPI wants to merge 3 commits into
Open
Conversation
The reseller slot in registry.py exists for exactly this shape — many labs' models behind one key, in the reseller's own id namespace — and aimlapi.com fills it with a plain Chat Completions endpoint, so the descriptor is the existing _compat() four-liner rather than new machinery. Two things it does NOT inherit from the neighbouring rows: Model ids. OpenRouter's slugs do not transfer: `z-ai/glm-5.2`, `moonshotai/kimi-k2.6` and `meta-llama/llama-4-maverick` are neither ids nor aliases there, and llama-4 has no entry at all. Every curated row was taken from their live catalog and then round-tripped with a real completion, because an id there can also be an alias of a *different* model — the catalog check alone can pass while a different model serves the request. Key verification. Their `/models` is public: it answers 200 to a bogus key, an empty key, and no Authorization header, so the generic list-models probe behind the Test button would accept a typo and leave the user to discover it at the first real turn. Their Test sends a one-token chat completion instead, which is the cheapest call that actually exercises the credential. The gateway also reads app-attribution headers, which needed a `default_headers` kwarg on OpenAIProvider — the same knob CodexProvider already uses. It is opt-in per descriptor and scoped to aimlapi.com's own ORIGIN rather than to the provider name, so a key repointed at a proxy or another vendor carries nothing extra.
Partnership placement, kept as its own commit so it can be dropped whole
before this goes anywhere else. Nothing here is a behaviour change.
- DESCRIPTORS is hand-ordered, so aimlapi.com moves to the head of it.
- PROVIDER_ORDER likewise. Note this list is now only the tiebreaker for
identical display titles: the gallery itself sorts alphabetically by title
("a curated order reads as vendor bias", owner ruling 2026-08-21, quoted at
ProviderSetup.tsx). That sort is deliberately left alone.
- README's reseller clause names us first.
No "Recommended"/featured badge: the provider gallery has no such concept and
inventing one for a single vendor would be a bigger change than the provider.
The placeholder part_openworker was a readable stand-in chosen before the partner was registered. Registration mints the id server-side, so the real value is part_nLTCEqZPnFgQDrMIuu7M5CoQ. A wrong or unknown partner id is accepted with a 200 and silently not attributed, so this would not have surfaced at runtime.
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.
What this adds
aimlapi.comas a model provider, in the reseller slotregistry.pyalready has for"many labs' models behind one key, using THEIR model namespaces". It speaks plain
OpenAI Chat Completions, so the descriptor is the existing
_compat()helper and thefootprint follows the BytePlus/Volcengine Ark PR (andrewyng#504) that added the last two providers:
descriptor, curated matrix rows, a vendored brand mark, gallery + key-console wiring,
backend tests, a GUI unit test and a Playwright e2e.
aimlapiaimlapi.comhttps://api.aimlapi.com/v1(prefilled, editable)AIMLAPI_API_KEYzhipu/glm-5.2— same model the three neighbouring resellers recommendhttps://aimlapi.com/app/keysFour curated matrix rows, matching the budget the other resellers get:
aimlapi:openai/gpt-5.6-solaimlapi:anthropic/claude-sonnet-4.6aimlapi:zhipu/glm-5.2aimlapi:deepseek/deepseek-v4-proTwo things that are not copy-paste from the neighbouring rows
Model ids do not transfer from OpenRouter. Three of the four ids in the OpenRouter
matrix rows are neither an id nor an alias on this vendor:
z-ai/glm-5.2(theirs iszhipu/glm-5.2),moonshotai/kimi-k2.6, andmeta-llama/llama-4-maverick— there is nollama-4entry in their catalog at all. Every id shipped here was taken fromGET /v1/models?include=all, filtered totype == "openai/chat-completions", and thenround-tripped with a real completion, because on that API an id can be both a model and
an alias of a different model (asking for
deepseek/deepseek-chatis served bydeepseek-v4-flash) — so the catalog check can pass while another model answers. Theechoed
modelwas checked for each. Also note their Anthropic ids are duplicated in adashed spelling that publishes no capabilities at all; the dotted
claude-sonnet-4.6isthe real entry, and that is the one used here.
Context windows come from
info.contextLength; there is no top-levelcontext_lengthfield on that API.
Key verification cannot use
/models. Their/modelsis public: it returns 200 for abogus key, an empty key, and with no
Authorizationheader at all. The genericlist-models probe behind the Test button would therefore green-light a typo and leave the
user to find out at their first real turn.
verify_provider_keysends a one-token chatcompletion for this provider instead, which is the cheapest call that actually exercises
the credential.
Attribution headers
The gateway reads
HTTP-Referer/X-Title(the OpenRouter convention) plus two headersof its own. Supporting them needed a
default_headerskwarg onOpenAIProvider— thesame knob
CodexProvideralready uses on its own client. It is:there is a lockdown test for that;
user-editable, so a key repointed at a corporate proxy or at another vendor carries
nothing extra. Covered by a test that includes a lookalike host;
HTTP-Referer/X-Titlename OpenWorker, the calling app, notthe gateway.
A test asserts the partner id matches
^part_[A-Za-z0-9]{1,64}$and the source matches the<channel>/<client>shape, because the gateway silently drops a malformed value rather thanerroring — nothing at runtime would catch a typo.
Regression test for a validator quirk worth knowing about
That API 400s on an explicit
nullfortemperature,top_p,seed,tools,tool_choice,response_format,stream,stream_options,parallel_tool_calls,max_tokensandmax_completion_tokens— which is exactly what an SDK serialises when aprovider forwards its unset optionals.
OpenAIProvideralready builds its request fromonly what the caller passed and gates
toolson truthiness, so this repo is not affected;tests now pin that on both the
completeandstreampaths so it stays that way. Thetoolsone is the nasty variant: a host that clears tools between turns by nulling thefield succeeds on turn 1 and fails on turn 2 of every agent loop, which is why the live
check below runs a full two-turn loop.
Verification
Baseline recorded on a pristine
maincheckout first.main)pytest testsnpm run build(tsc + vite)npm test(vitest)playwright test e2e/settings.spec.tsThe 5 vitest failures are identical on both trees — all in
Sidebar.tsx, untouched here.One real inference call through the provider path (not curl, not a mock): built via
build_provider_client("aimlapi", {}, None)with onlyAIMLAPI_API_KEYin theenvironment, exactly as
ProviderRouterbuilds it.Two-turn tool loop and streaming, same client:
Each of the four shipped model ids was also round-tripped individually; the echoed
modelmatched the model asked for in every case.
What I could not verify
pdf— images are verified liveon the two vision rows, PDFs keep the
pdf_support.pyfallback like every other reseller.visionandfile_inputformoonshotai/kimi-k2-thinking, but a live image request returns404 "No endpoints found that support image input". That model is not in the shippedrows; noting it because it means the catalog's vision flag is not by itself trustworthy.
max_tokenscap on reasoning models.zhipu/glm-5.2withmax_tokens: 120returned
completion_tokens: 5002(reasoning_tokens: 4971) andfinish_reason: "stop"— the cap did not bound the reasoning phase, and nothing in the response says so. Not
something this PR can fix, but it means the matrix's cost assumptions for that row are
optimistic.
Commits
feat(providers): add aimlapi.com as an OpenAI-compatible reseller— the provider.chore(aimlapi): fork-only placement — do not send upstream— placement only, keptseparate so it can be dropped whole. It moves the descriptor to the head of the
hand-ordered
DESCRIPTORSandPROVIDER_ORDER, and fronts the README's resellerclause. It does not touch the gallery's own sort, which is alphabetical by display
title by an explicit owner ruling ("a curated order reads as vendor bias", 2026-08-21,
ProviderSetup.tsx), and it adds no "Recommended" badge because the gallery has no suchconcept to reuse.
Note on the repo's PR policy
README.mdasks for screenshots on every PR: "For any PR, please attach screenshots ofwhat was broken and how it is fixed now." Nothing was broken here — this is an addition —
and the GUI surface it touches is covered by the new Playwright e2e assertion instead
(gallery card, brand mark, prefilled endpoint, curated model preview). Screenshots should
be attached before this is offered anywhere beyond this fork.