Skip to content

feat(providers): add aimlapi.com as an OpenAI-compatible gateway provider - #1

Open
Lookoff-AIMLAPI wants to merge 3 commits into
mainfrom
feat/aimlapi-provider
Open

feat(providers): add aimlapi.com as an OpenAI-compatible gateway provider#1
Lookoff-AIMLAPI wants to merge 3 commits into
mainfrom
feat/aimlapi-provider

Conversation

@Lookoff-AIMLAPI

Copy link
Copy Markdown
Member

What changed

Adds aimlapi.com as a built-in OpenAI-compatible gateway provider, following the
two-step recipe in nanobot/providers/registry.py's own module docstring and the
shape of the merged Eden AI provider (upstream HKUDS#4861).

  • Registers providers.aimlapi with AIMLAPI_API_KEY and the default base URL
    https://api.aimlapi.com/v1.
  • Preserves model IDs in their documented vendor/model form
    (strip_model_prefix=False); the full ID is sent upstream.
  • Sends reasoning_effort as the normal top-level Chat Completions parameter,
    not OpenRouter's nested {"reasoning": {"effort": ...}} shape.
  • Exposes the provider through the WebUI provider settings and model catalog flow
    (brand logo, icon, and deferred model search — the catalog is ~900 entries, so
    it belongs in DEFERRED_MODEL_LIST_PROVIDERS alongside OpenRouter and AiHubMix).
  • Documents a copyable setup in docs/providers.md and docs/configuration.md.

The display label is aimlapi.com; the machine identifier is aimlapi.

Attribution headers

ProviderSpec.default_extra_headers already exists and is already used
(kimi_coding ships a User-Agent through it), and
openai_compat_provider.py already sends HTTP-Referer / X-OpenRouter-Title
identifying nanobot to OpenRouter. This reuses that machinery rather than adding
any:

HTTP-Referer:          https://github.com/HKUDS/nanobot
X-Title:               nanobot
X-AIMLAPI-Source:      agent/hkuds-nanobot
X-AIMLAPI-Partner-ID:  part_hkudsnanobot

HTTP-Referer and X-Title name nanobot, not the gateway — they identify the
calling application, which is the OpenRouter convention the repo already follows.
The headers hang off this provider's spec, so they cannot ride a request to any
other provider; factory._provider_extra_headers builds a fresh dict per provider
and lets a user's providers.aimlapi.extraHeaders win on a key clash, so the
registry constant is never mutated and user configuration is never dropped. A
malformed partner ID is dropped silently by the gateway rather than rejected, so
its shape is asserted in a test.

Commits

Two commits, deliberately separable:

  1. feat(providers): add aimlapi.com as an OpenAI-compatible gateway provider
    the provider itself.
  2. chore(aimlapi): fork-only placement — do not send upstream — moves the entry
    to the head of the gateway block in the registry and to the head of the gateway
    rows in both docs lists. This is placement, not function, and
    docs/providers.md says in its own words that "The docs show concrete provider
    names so the JSON is copyable, not because nanobot ranks providers." Drop this
    commit before offering the change upstream. The repository has no
    "recommended"/"featured" provider badge and none was invented.

Verification

Python suite, same machine, same virtualenv, -p no:randomly:

passed failed skipped
baseline (main, d81aa5a) 6409 1 19
this branch 6417 1 19

The one failure is identical on both:
tests/cli/test_tui_launcher.py::test_launcher_keeps_the_tui_alive_while_an_existing_gateway_recovers
TypeError: 'FakeProcess' object does not support the context manager protocol,
a local Python 3.13 subprocess.run incompatibility unrelated to this change.

  • ruff check nanobot/ — clean.
  • uv run --no-sync basedpyright (after install_channel_dependencies --all-channels)
    — 0 errors, 0 warnings, 0 notes.
  • webui: bun run test — 73 files / 1125 tests pass, identical to baseline;
    bun run build succeeds; uv build --wheel succeeds.
    (The WebUI suite needs NODE_OPTIONS=--localstorage-file=... on Node 26 to run
    at all — without it all 73 files fail in src/tests/setup.ts on both main and
    this branch.)

Live inference

One real call through the added code path — load_provider_snapshot() over the
exact config.json printed in docs/providers.md, no mocks:

provider : aimlapi | model: openai/gpt-5 | base: https://api.aimlapi.com/v1
apiKey resolved from ${AIMLAPI_API_KEY}: True
wire headers: {"HTTP-Referer": "https://github.com/HKUDS/nanobot",
               "X-AIMLAPI-Partner-ID": "part_hkudsnanobot",
               "X-AIMLAPI-Source": "agent/hkuds-nanobot",
               "X-Title": "nanobot"}

[chat] content: 'nanobot aimlapi ok'
[chat] usage  : 16 in / 63 out
[tools] tool_calls: [('get_weather', {'city': 'Paris'})]

Tool calling round-trips correctly. The WebUI catalog path
(provider_models_payload) was also run live against the real endpoint:
status available, catalog_kind catalog, 785 distinct model IDs.

Model IDs

Every ID that appears in the docs was checked against the live catalog at
https://api.aimlapi.com/v1/models, against both id and aliases, and
restricted to type == "openai/chat-completions":
openai/gpt-5, anthropic/claude-sonnet-4-6, google/gemini-3.1-pro-preview
— all present and routable.

Not verified

  • CI itself has not run on this branch; the numbers above are from a local
    reproduction of the documented CI commands.
  • Image generation and transcription surfaces were not wired up or tested; this
    change covers chat completions only, as the Eden AI provider does.

…ider

Users who hold an aimlapi.com key currently have to fall back to a named
custom provider: they must know the base URL, and they lose preset
detection, the `nanobot status` label and the Settings model catalog. A
registry entry buys all of that for the same twelve lines the other
gateways cost.

Model IDs are sent unchanged so the catalog's `vendor/model` form keeps
working, and reasoning is the plain top-level `reasoning_effort` rather
than OpenRouter's nested request shape, because the endpoint is
OpenAI-compatible.

The default headers identify nanobot to the gateway the same way the
existing OpenRouter attribution block does. They live on this provider's
spec, so they cannot ride a request to any other provider, and the
factory builds a fresh dict per provider with user-supplied
`extraHeaders` taking precedence. A malformed partner id is dropped
silently upstream rather than rejected, so its shape is asserted in a
test instead of being discovered in production.
Moves the aimlapi.com entry to the head of the gateway block in the
registry and to the head of the gateway rows in both docs lists. The
registry tuple's order is match priority, so this also makes aimlapi.com
win gateway fallback ahead of the other gateways.

This is placement, not function: nothing here is needed for the provider
to work, and docs/providers.md states that the docs do not rank
providers. It is kept as its own commit so it can be dropped before the
change is offered upstream. The repository has no "recommended" or
"featured" provider badge, and none was invented.
The placeholder part_hkudsnanobot was a readable stand-in chosen before the
partner was registered. Registration mints the id server-side, so the
real value is part_TcTxHfamJ2kkNiFsYzEVELTy. 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