Skip to content

feat(plugin-ai): add aimlapi.com as an LLM provider - #1

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

feat(plugin-ai): add aimlapi.com as an LLM provider#1
Lookoff-AIMLAPI wants to merge 3 commits into
mainfrom
feat/aimlapi-provider

Conversation

@Lookoff-AIMLAPI

Copy link
Copy Markdown
Member

This is a ...

  • New feature

Motivation

Admins can already reach AI/ML API today through openai-completions with a custom baseURL, so this is not new
capability — it buys discoverability, a correct model list and attribution. Configured that way the service shows up
in the provider list as "OpenAI (completions)", the model picker is empty unless every id is typed by hand, and
nothing tells AI/ML API which application the traffic came from.

This adds aimlapi.com as a first-class LLM provider in plugin-ai, following the shape of the two most recent
provider additions in this repo (shengsuanyun, orcarouter).

Description

Serversrc/server/llm-providers/aimlapi.ts

  • AimlapiProvider extends LLMProvider, LangChain ChatOpenAI over the OpenAI-compatible Chat Completions API.
  • Base URL https://api.aimlapi.com/v1; user-overridable per service like every other provider.
  • listModels() fetches GET {baseURL}/models with the configured key and narrows the catalog to entries the Chat
    Completions client can actually drive (type === "openai/chat-completions"). The catalog mixes image, video,
    speech and embedding models into the same list, so an unfiltered list would offer models that cannot be chatted
    with. Entries with no type are kept, so a user pointing baseURL at another OpenAI-compatible endpoint still
    gets a list.
  • No model ids are hardcoded. The list is fetched, so it cannot go stale.
  • Attribution headers, following orcarouter.ts which already injects HTTP-Referer / X-Title from service
    options:
    • HTTP-Referer: https://github.com/nocobase/nocobase and X-Title: NocoBase — these identify NocoBase as the
      calling application, not AI/ML API, and both stay overridable per service through the settings form.
    • X-AIMLAPI-Partner-ID / X-AIMLAPI-Source — identify the integration itself.
    • All four are scoped to the api.aimlapi.com origin. baseURL is user-configurable, so a service could point
      at another vendor or at a proxy that merely fronts the same API; neither should receive them.
    • The header map is a frozen module constant and every request gets a copy, so per-service overrides can never
      mutate it.

Client (v2) — the provider settings form (API key + optional referer/title, mirroring OrcaRouter's), the provider
options registration, and a localized description. openAICompletionFields is reused for model options rather than
duplicating an identical field list.

i18n — one new string, added to both en-US and zh-CN.

Risks are contained: nothing existing changes behaviour. The one shared file with more than an added line is
src/client-v2/llm-providers/index.ts, where the provider is appended to the two builtin registries.

Fork-only commit

The second commit, chore(aimlapi): fork-only placement — do not send upstream, is partnership placement rather than
a functional change and is deliberately isolated so it can be dropped before this is offered upstream:

  • puts aimlapi first in getProviderSortIndex's hand-written sort order;
  • seeds recommendedModels.aimlapi, which is empty for every provider today, so the entry carries the existing
    "Recommended" tag.

Only the first commit is intended for upstream.

Verification

Test baseline recorded on a pristine checkout first, then re-run with the change:

Suite Baseline (clean tree) With this branch
yarn test packages/plugins/@nocobase/plugin-ai --run 6 files failed to collect / 29 passed (35) — 171 tests passed 6 files failed to collect / 30 passed (36) — 182 tests passed
yarn test:client packages/plugins/@nocobase/plugin-ai/src/client-v2 --run 54 files / 286 tests passed 54 files / 286 tests passed

The 6 collection failures are identical before and after and unrelated to this change: jwa
buffer-equal-constant-time throws TypeError: Cannot read properties of undefined (reading 'prototype') on Node
26 (SlowBuffer removal). Affected suites: ai-files-access, ai-workflow-tasks, checkpoint,
knowledge-base-retrieval-strategy-migration, mcp-client-events, workflow-files.

The 11 new tests cover the base URL, the display name, the header set, the origin scoping, the per-service override
path, and the catalog filter. One of them asserts the partner id matches ^part_[A-Za-z0-9]{1,64}$ — a malformed id
is dropped silently by the gateway, so a typo would otherwise be invisible at runtime.

yarn build — full monorepo build: exit 0. (yarn build @nocobase/plugin-ai alone fails on a fresh checkout because the core packages have no
built declarations yet; the full build produces packages/plugins/@nocobase/plugin-ai/dist/server/llm-providers/aimlapi.js
and aimlapi.d.ts.)

yarn eslint on every touched file: clean, exit 0.

One real inference call through the provider, not a mock and not a raw curl — a temporary vitest file that
constructs AimlapiProvider and calls it, with a real key passed via env (the file is not part of this diff):

LIVE listModels code: undefined errMsg: undefined count: 353
LIVE first 5: [{"id":"openai/gpt-3.5-turbo"},{"id":"openai/gpt-3.5-turbo-0125"},{"id":"openai/gpt-3.5-turbo-1106"},{"id":"openai/gpt-4"},{"id":"openai/gpt-4.1-mini"}]
LIVE headers sent: {"HTTP-Referer":"https://github.com/nocobase/nocobase","X-Title":"NocoBase","X-AIMLAPI-Partner-ID":"part_nocobase","X-AIMLAPI-Source":"agent/nocobase"}
LIVE chat content: "NOCOBASE_AIMLAPI_OK"
LIVE usage: {"promptTokens":19,"completionTokens":8,"totalTokens":27}
LIVE tool_calls: [{"name":"get_weather","args":{"city":"Lisbon"},"type":"tool_call","id":"call_..."}]
 Test Files  1 passed (1)
      Tests  3 passed (3)

So: catalog listing (353 chat models out of 936 catalog entries), a chat completion, and a tool call all go through
the provider added here, with the four headers on the wire.

The four model ids in the fork-only recommendedModels seed were each checked against the live catalog by id and
alias
and are all openai/chat-completions entries: openai/gpt-4o, anthropic/claude-sonnet-4.5,
google/gemini-2.5-pro, deepseek/deepseek-chat. Note anthropic/claude-sonnet-4-5 (hyphen) is neither an id nor
an alias; the dotted form is the real one.

Not verified

  • No end-to-end run in a browser: the provider was exercised through its server class, not through the LLM Services
    settings page. The client changes are covered only by the existing unit tests.
  • Streaming (LLMProvider.stream) was not exercised live, only the non-streaming invoke path.
  • No migration is needed (no schema change), so none was written.

Changelog

Language Changelog
🇺🇸 English feat(plugin-ai): add aimlapi.com as an LLM provider
🇨🇳 Chinese feat(plugin-ai): 新增 aimlapi.com 大模型服务商

Docs

Language Link
🇺🇸 English n/a — no doc site page exists for individual LLM providers
🇨🇳 Chinese n/a

Checklists

  • All changes have been self-tested and work as expected
  • Test cases are updated/provided
  • Doc is not needed
  • Documentation was not changed
  • Component demo not needed
  • Changelog is provided

Admins can already reach AI/ML API through `openai-completions` with a custom
base URL, but that route leaves the service unnamed in the provider list, offers
no model catalog, and sends no attribution. A first-class entry gives the
provider its own name and description, loads the account's Chat Completions
catalog, and lets AI/ML API attribute the traffic to NocoBase.

The provider follows the shengsuanyun shape (LangChain `ChatOpenAI` over the
OpenAI-compatible Chat Completions API, plus a `listModels` override) and the
orcarouter shape for attribution headers. `HTTP-Referer` and `X-Title` identify
NocoBase as the calling application and stay overridable per service; the
`X-AIMLAPI-*` pair identifies the integration. All four are scoped to the AI/ML
API origin, because `baseURL` is user-configurable and attribution must not ride
a request to another vendor or to a proxy that merely fronts the same API.

No model ids are hardcoded: the catalog is fetched at configuration time and
narrowed to entries the Chat Completions client can actually drive, so the list
cannot rot.
Puts aimlapi.com first in the provider selector's hand-written sort order and
seeds the existing per-provider recommended-model list, which is empty for every
other provider today, so the AI/ML API entry carries the "Recommended" tag.

This is partnership placement, not a functional change, and it is deliberately
isolated in one commit so it can be dropped before the provider itself is
offered upstream. The four model ids were checked against the live catalog
(ids and aliases) and are all `openai/chat-completions` entries.
The placeholder part_nocobase was a readable stand-in chosen before the
partner was registered. Registration mints the id server-side, so the
real value is part_vIQFIgEDs9Yk0yizVcgoM5Sp. 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