Skip to content

feat(provider): add aimlapi.com model provider - #1

Open
Lookoff-AIMLAPI wants to merge 2 commits into
v2from
feat/aimlapi-provider
Open

feat(provider): add aimlapi.com model provider#1
Lookoff-AIMLAPI wants to merge 2 commits into
v2from
feat/aimlapi-provider

Conversation

@Lookoff-AIMLAPI

Copy link
Copy Markdown
Member

What this PR does / why we need it?

Adds aimlapi.com (AI/ML API) as a first-class model provider.

MaxKB can already reach AI/ML API today by pointing the built-in OpenAI provider at
https://api.aimlapi.com/v1, but the operator has to type every model name by hand and the
provider shows up as "OpenAI". This PR adds a dedicated provider directory following the
regolo shape (the last externally contributed OpenAI-compatible provider, 1Panel-dev#3041), covering
four model types: LLM, IMAGE (vision), EMBEDDING and TTI (image generation).

Summary of your change

apps/models_provider/impl/aimlapi_model_provider/ — credential + model class per type,
provider class, icon, plus the ModelProvideConstants registration, the
ui/.../provider-data.ts entry and the three .po files (zh_CN / zh_Hant / en_US).

Implementation details that are specific to this API, all of them found by probing it today:

  • Request params are built by omitting unset keys, never by sending null. Null tolerance
    differs per model on this API: google/gemini-2.5-flash accepts null everywhere, while
    openai/gpt-4o-mini and deepseek/deepseek-chat return 400 for temperature, top_p,
    seed and tools set to null. tools: null is the worst case — an agent loop that clears
    tools between turns succeeds on turn 1 and 400s on turn 2. filter_optional_params() in
    const.py drops None values on top of the base MaxKBBaseModel.filter_optional_params,
    and test_chat_model_omits_unset_params_instead_of_sending_null locks it in.
  • Attribution headers are scoped to the request origin. HTTP-Referer / X-Title identify
    MaxKB (the calling app); X-AIMLAPI-* are the channel headers. They are only attached when
    the configured API URL is api.aimlapi.com, so changing the URL to another vendor or to a
    proxy does not leak them, and caller-supplied headers win on a key clash. The shared constant
    is never mutated. Covered by three tests, including a regex assertion on the partner id —
    a malformed id is not rejected by the API, it just silently loses the attribution.
  • Embeddings use the openai SDK with plain strings (same shape as
    openai_model_provider/model/embedding.py) rather than LangChain's OpenAIEmbeddings.
    LangChain tokenises the input into token-id arrays first, and this API's /v1/embeddings
    only accepts strings — it answers 400 details[].path = "input" for the array form. This is
    the one thing a copy of the regolo embedding class would have got wrong.
  • TTI size/quality default to auto, which means "do not send the parameter". Image
    models disagree about the allowed values: openai/gpt-image-1 accepts only
    low|medium|high for quality and 400s on standard, which is what the other providers
    in this repo send by default.
  • TTI credential validation does not call GET /v1/models. That endpoint is public on this
    API and returns 200 for any key, including a bogus one, so it cannot validate a key. The
    check posts to /v1/images/generations with a placeholder model name instead: an invalid key
    returns 401 and a valid one returns 404, so the key is really validated and no image is
    generated (and nothing is billed). Verified both ways.
  • The "output max tokens" tooltip no longer promises a cost bound. On this API max_tokens
    does not reliably bound reasoning tokens — zhipu/glm-5.2 returned 849 tokens against a 120
    cap with finish_reason: "stop". The new string is translated in zh_CN and zh_Hant.

The last commit, chore(aimlapi): fork-only placement — do not send upstream, only moves the
provider to the front of the two hand-ordered lists. It is meant to be dropped before this is
offered upstream. MaxKB has no "recommended"/featured badge for providers, so none was invented.

Model list

Model names are hardcoded per provider in this repo, so every id shipped here was checked
against GET https://api.aimlapi.com/v1/models?include=all (id or alias — checking ids
alone is not enough) and, for the ids in the list below, called live. 30 distinct ids, 0
failures on the catalog check. Users can still type any other model name by hand.

Two notes for whoever refreshes this list later:

  • Prefer the dotted spelling: anthropic/claude-sonnet-4.5 is valid, -4-5 is not.
  • An id can be both a model and an alias of another model: a request for
    deepseek/deepseek-chat is answered by deepseek-v4-flash (the response model field says
    so). The list therefore ships deepseek/deepseek-v4-flash, whose echo matches what was asked
    for. x-ai/grok-4-6 echoes grok-4.6, same model, different spelling.

Verification

  • npm run build (type-check + vite build) in ui/: exit 0.
  • python apps/manage.py test <all 12 apps>:
    baseline on a pristine checkout 14 tests / 5 pass / 9 errors, after this change
    21 tests / 12 pass / 9 errors. The 9 errors are identical before and after: the test
    loader imports each app's models package as a test module and Django rejects it
    (doesn't declare an explicit app_label). They are unrelated to this change.
  • ruff check on every added file: clean.
  • msgfmt --check on the three .po files: clean; the new strings were verified to render
    under zh-hans, zh-hant and en.
  • One real inference call through the new provider code path (get_provider(...) .get_model(...), not a mock and not curl), plus tool calling, vision, embeddings and image
    generation:
provider name    : aimlapi.com
provider id      : model_aimlapi_provider
model types      : ['LLM', 'EMBEDDING', 'IMAGE', 'TTI']

[LLM] content    : MaxKB 是一个提供高效数据存储与管理的云服务平台。
[LLM] usage      : {'completion_tokens': 17, 'prompt_tokens': 18, 'total_tokens': 35, ...}
[LLM] headers    : {'HTTP-Referer': 'https://github.com/1Panel-dev/MaxKB', 'X-Title': 'MaxKB',
                    'X-AIMLAPI-Partner-ID': 'part_maxkb', 'X-AIMLAPI-Source': 'agent/maxkb'}
[TOOL] tool_calls: [{'name': 'get_weather', 'args': {'city': '北京'},
                     'id': 'call_6WEz7la7PsqOFjsAj5NAdaUB', 'type': 'tool_call'}]
[IMAGE] content  : Red.
[EMBEDDING] dim  : 1536 head: [-0.01503, -0.01166, -0.01041]
[TTI] params     : {'n': 1}
[TTI] check_auth : True
[TTI] urls       : ['https://s3.aimlapi.com/files/file-01a0652e-e79a-7175-8b46-ec...']
[CREDENTIAL] LLM : True

Credential validation was also exercised with a deliberately wrong key: LLM and TTI both
return False (401 from the API), so a bad key is not silently accepted.

Not verified

  • No STT / TTS / RERANKER / TTV / ITV models are registered. This API serves speech-to-text
    and video generation through asynchronous submit endpoints that do not match MaxKB's
    synchronous STT/TTS/TTV classes, so wiring them up needs its own change.
  • The provider icon had to be added with git add -f: .gitignore:189 ignores
    apps/models_provider/impl/*/icon/, yet every existing provider icon is tracked.
  • Only the model ids listed in the provider file were called live; the rest of the ~350 chat
    models on this API were verified against the catalog only.

Please indicate you've done the following:

  • Made sure tests are passing and test coverage is added if needed.
  • Made sure commit message follow the rule of Conventional Commits specification.
  • Considered the docs impact and opened a new docs issue or PR with docs changes if needed.

MaxKB 目前只能通过 OpenAI 供应商填写自定义 api_base 的方式访问
AI/ML API,模型需要手工输入,也没有独立的图标和渠道标识。
这里按照 regolo 的目录结构新增一个独立的供应商,支持 LLM、视觉、
向量和文生图四种模型类型。

几个和 AI/ML API 相关的实现细节:

- 请求参数按“未设置就不下发”的方式构造。AI/ML API 上不同模型对 null
  的容忍度不同:google/gemini-2.5-flash 可以接受,而 openai/gpt-4o-mini、
  deepseek/deepseek-chat 在 temperature、top_p、seed、tools 为 null 时
  直接返回 400。tools 尤其危险:Agent 第一轮成功、第二轮清空工具时失败。
  已补充回归测试。
- 归因请求头按请求地址生效,只有 API URL 指向 api.aimlapi.com 时才携带,
  用户改成其他服务商或中转代理时不会被带过去;合并而不是覆盖调用方的请求头。
- 向量模型直接用 openai SDK 下发字符串,而不是 langchain 的 OpenAIEmbeddings:
  后者会先把文本转成 token id 数组,AI/ML API 的 /v1/embeddings 只接受字符串。
- 文生图的 size/quality 默认值为 auto,即不下发该参数。不同图片模型接受的
  枚举值不同(openai/gpt-image-1 的 quality 只接受 low/medium/high,
  下发 standard 会 400)。
- 文生图的凭据校验没有使用 GET /v1/models:该接口是公开的,任意 Key 都
  返回 200,无法用来校验 Key。
- 输出最大 Token 数的提示文案补充说明:推理模型的思考过程可能超出该限制,
  它并不能限制单次请求的费用。
供应商列表(后端 ModelProvideConstants 枚举、前端 providerList)都是
手工排序的,这里把 aimlapi.com 放到第一位。MaxKB 的供应商没有
“推荐”标记之类的机制,因此没有新增任何标记,只调整了顺序。

这个提交只在我们自己的 fork 中保留,向上游提交时应当去掉。
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