Skip to content

docs(built-in-agent): document aimlapi.com as an OpenAI-compatible provider - #1

Open
Lookoff-AIMLAPI wants to merge 5 commits into
mainfrom
docs/aimlapi-openai-compatible
Open

docs(built-in-agent): document aimlapi.com as an OpenAI-compatible provider#1
Lookoff-AIMLAPI wants to merge 5 commits into
mainfrom
docs/aimlapi-openai-compatible

Conversation

@Lookoff-AIMLAPI

Copy link
Copy Markdown
Member

What does this PR do?

Adds aimlapi.com to the OpenAI-compatible providers list on the Built-in Agent
Model Selection
page, plus a <Callout> describing the one call-form caveat that applies to it.

This is a documentation change on purpose — a resolveModel provider case is not on the table.
Upstream PR CopilotKit#6584 ("add OrcaRouter as a built-in model provider") was closed unmerged on 2026-08-31
with the reason stated plainly:

Adding a built-in case buys the four namespaced ids and an env var, and costs us a hardcoded base
URL plus a model list to keep current as your catalog changes. It also sets a precedent we'd have to
apply evenhandedly to every gateway that asks, which isn't a list we want inside resolveModel.

If you'd like OrcaRouter to be discoverable to CopilotKit users, a line in the OpenAI-compatible
providers list on that Model Selection page is something I'd review — that's how Novita was handled
recently.

aimlapi.com is the same shape of thing, so this follows the route the maintainer named, and copies
the shape of the two Novita commits (55be3506a, 9907d2d72) exactly: one line in the prose list,
one warn Callout.

The caveat, and why it is not a copy of Novita's

Novita's caveat is uniform — it has no Responses endpoint at all. aimlapi.com's is
per model, which is a nastier failure for a reader:

call form openai/gpt-4o-mini openai/gpt-4.1 google/gemini-2.5-flash deepseek/deepseek-v4-flash
provider.chat(model) (Chat Completions) 200 200 200 200
provider(model) (Responses, as in the OpenRouter example above it) 200 200 404 Model not found 404 Model not found

The gateway's catalog has 353 models on openai/chat-completions and 60 on
openai/responses/submit, and the Responses 60 are the openai/ family plus one x-ai id. So a reader
who copies the adjacent OpenRouter example verbatim and swaps in a non-OpenAI model gets
404 Model not found, which reads like a bad model id rather than a wrong call form. Hence the
Callout.

Commits

Commit Contents Upstream-safe?
docs(built-in-agent): mention aimlapi.com in OpenAI-compatible providers list the list line + the Callout; aimlapi.com appended at the end of the list, exactly where Novita was appended yes
chore(aimlapi): fork-only partner-id placeholder — do not send upstream an empty AIMLAPI_PARTNER_ID constant + a test asserting empty-or-^part_[A-Za-z0-9]{1,64}$ no — drop it
chore(aimlapi): fork-only placement — do not send upstream moves aimlapi.com to the front of the hand-ordered list no — drop it

The last two commits are separated so they can be dropped before any upstream PR.

On attribution headers

CopilotKit does call api.aimlapi.com directly — the AI SDK OpenAI provider inside BuiltInAgent
opens the connection itself; only the base URL and key come from user configuration. So unlike a
catalog or a purely docs-level integration, a request path that could carry the four attribution
headers exists here.

The headers are not shipped, for two reasons. No partner id has been registered for CopilotKit,
and an invented one is worse than none — the gateway accepts a request carrying a malformed id and
drops the attribution silently, so a wrong value never fails visibly and the traffic simply earns
nothing. And scoping headers to our origin (the rule that stops them riding a request to another
provider) requires knowing the aimlapi base URL inside resolveModel, which is exactly the change
upstream declined. The placeholder commit therefore leaves the constant empty with a test that pins
the id shape, so a bogus value cannot be pasted in unnoticed later.

Verification

Baseline was recorded on a pristine checkout of main before any change.

Check Baseline After
nx run @copilotkit/runtime:test 152 files / 2256 passed, 0 failed 153 files / 2259 passed, 0 failed (+3 from the new placeholder test)
showcase/shell-docs npm run test 77 files, 536 passed / 6 failed 77 files, 536 passed / 6 failed — identical set
tsx scripts/validate-doc-model-names.ts pass pass
showcase/shell-docs npm run typecheck pass
showcase/shell-docs npm run build Next.js production build succeeded, 226/226 static pages
nx run @copilotkit/runtime:check-types pass
oxlint / oxfmt --check on the new files 0 warnings, 0 errors / correctly formatted

The 6 pre-existing shell-docs failures are unrelated to this change and identical before and after
(public-assets.test.ts ×3 — PNG fixtures; angular-docs-content.test.ts ×2; llm-text.test.ts ×1).

Live inference — through the documented configuration path, not curl

Every claim in the Callout was produced by driving CopilotKit's own BuiltInAgent against
https://api.aimlapi.com/v1 with a real key, using only documented configuration. No mocks.

createOpenAI({ baseURL }).chat("openai/gpt-4o-mini")BuiltInAgent:

types:  RUN_STARTED, TEXT_MESSAGE_CHUNK, RUN_FINISHED
text:   "aimlapi-copilotkit-ok"
finish: stop
usage:  provider "openai.chat", model "openai/gpt-4o-mini",
        inputTokens 175, outputTokens 9, totalTokens 184

The bare form (createOpenAI({ baseURL })("openai/gpt-4o-mini"), i.e. Responses) returned the same
text with provider "openai.responses", 169/10/179 tokens. The string model form with
OPENAI_BASE_URL=https://api.aimlapi.com/v1 and model: "openai/openai/gpt-4o-mini" — which goes
through the existing openai case in resolveModel unmodified — returned the same again. So all
three configuration paths this repo already offers reach the gateway with no code change.

Tool calling was exercised once over the same path (maxSteps: 3, one defineTool):

types:     RUN_STARTED, TOOL_CALL_START, TOOL_CALL_ARGS, TOOL_CALL_END, RUN_FINISHED
toolCall:  get_weather
args:      {"city":"Lisbon"}
finish:    tool-calls
usage:     inputTokens 186, outputTokens 15, totalTokens 201

Each of the four model ids named in the Callout was called live through BuiltInAgent on both call
forms; the table above is that result, not a catalog reading.

What was not verified

  • Only the four model ids named in the docs were probed. The Responses/Chat split is stated as the
    gateway's catalog reports it (60 vs 353) plus those four live checks — no exhaustive sweep.
  • The rendered page was verified only through the production build (226/226 pages) and the test
    suite, not visually in a browser.

Related PRs and Issues

  • Upstream CopilotKit/CopilotKit#6584 (closed unmerged) — the policy this PR is shaped around.
  • Upstream CopilotKit/CopilotKit#6502 — how Novita was added; this change copies its shape.

Checklist

  • I have read the Contribution Guide
  • If the PR changes or adds functionality, I have updated the relevant documentation
  • "Allow edits by maintainers" is checked

…ers list

aimlapi.com exposes an OpenAI-compatible route at https://api.aimlapi.com/v1,
so it already works through the createOpenAI({ baseURL }) pattern this page
documents for OpenRouter, Ollama, Together, Groq and Novita. Nothing in the
runtime has to change for it; readers just have no way to discover that today.

The callout exists because the failure it prevents is silent-looking and
model-dependent, which is worse than Novita's uniform one: the gateway does
serve Responses, but only for the openai/ ids it routes. The bare
provider("model") call form in the adjacent OpenRouter example therefore works
for openai/gpt-4o-mini and openai/gpt-4.1 and returns 404 Model not found for
google/, deepseek/ and the Anthropic ids — so a reader who copies the example
and swaps in a non-OpenAI model gets an error that looks like a bad model id
rather than a wrong call form.

Verified by driving BuiltInAgent against the endpoint on both call forms:
chat form succeeded on openai/gpt-4o-mini, openai/gpt-4.1,
google/gemini-2.5-flash and deepseek/deepseek-v4-flash; the bare form
succeeded on the two openai/ ids and returned 404 on the other two.
CopilotKit does open the connection to api.aimlapi.com itself, so unlike a
catalog or a pure docs entry there is a real request path that could carry
partner attribution. No partner id has been registered for CopilotKit yet, so
the constant is empty on purpose rather than filled with a plausible-looking
value: the gateway accepts a request carrying a malformed id and drops the
attribution silently, so a bad value would never fail visibly at runtime and
the traffic would simply earn nothing. The test is what makes that failure
mode visible, so it asserts empty-or-well-formed instead of non-empty and
pins the shapes a hand-written id gets wrong.

Nothing imports the constant and nothing should until an id exists. Actually
sending the headers means scoping them to our origin, which in this codebase
means a hardcoded aimlapi base URL inside resolveModel — the change upstream
declined in CopilotKit#6584. Drop this commit before any upstream
PR; it is scaffolding for a colleague, not a contribution.
The OpenAI-compatible provider list on this page is hand-ordered prose, not a
generated or alphabetical list, so position is a choice someone makes rather
than something the tooling decides. This moves aimlapi.com from the end of
that list, where the preceding commit appended it the way Novita was added, to
the front.

That is a partnership-placement decision, not a documentation improvement, and
the page reads exactly as well either way — so it is isolated here for a
colleague to drop before any upstream PR. The docs page has no
"Recommended"/featured badge mechanism, so none was invented; ordering is the
only lever the page offers.
The placeholder part_copilotkit was a readable stand-in chosen before the
partner was registered. Registration mints the id server-side, so the
real value is part_B5Xmawp87YODJfuBUtiCbR2m. A wrong or unknown partner id is accepted with a
200 and silently not attributed, so this would not have surfaced at runtime.
The placeholder export const AIMLAPI_PARTNER_ID = ""; was a readable stand-in chosen before the
partner was registered. Registration mints the id server-side, so the
real value is export const AIMLAPI_PARTNER_ID = "part_B5Xmawp87YODJfuBUtiCbR2m";. A wrong or unknown partner id is accepted with a
200 and silently not attributed, so this would not have surfaced at runtime.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant