Skip to content

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

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

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

Conversation

@Lookoff-AIMLAPI

Copy link
Copy Markdown
Member

What

Adds aimlapi.com as a provider to the catwalk database, so Crush users on
AI/ML API get a maintained model list instead of hand-writing a custom
openai-compat block.

Two commits, deliberately separable:

Commit Contents Upstream-safe?
feat(aimlapi): add aimlapi.com provider the provider itself yes
chore(aimlapi): fork-only placement — do not send upstream moves us to the head of providerRegistry no — drop this commit before offering anything upstream

The provider commit

Shape follows the last three provider additions. No cmd/aimlapi/main.go
auto-updater: GET /v1/models returns 353 chat entries, which is not a useful
model picker, and regenerating it would churn the config on every catalog
change. The curated route is the coralbricks (charmbracelet#502) shape.

Why a hand-curated list of nine

353 chat models are reachable. The nine here are the coding-relevant flagships
plus a cheap small model, each one checked live.

Attribution headers

default_headers is the seam the repo already uses for exactly this —
aihubmix.json ships APP-Code: IUFF7106, and openrouter.json,
vercel.json and huggingface.json all ship HTTP-Referer/X-Title.

"default_headers": {
  "HTTP-Referer": "https://charm.land",
  "X-Title": "Crush",
  "X-AIMLAPI-Partner-ID": "part_crush",
  "X-AIMLAPI-Source": "agent/crush"
}

HTTP-Referer and X-Title name Crush, not us — they identify the calling
application, matching the values openrouter.json and vercel.json already
carry. The two X-AIMLAPI-* headers are ours and are scoped to this provider
entry only; nothing else in the database carries them, and
TestAIMLAPIDefaultHeaders asserts that no other provider ever does, so
attribution cannot ride along to somebody else's API or to a proxy fronting
ours.

The partner id is regex-tested against ^part_[A-Za-z0-9]{1,64}$. This matters
because the gateway ignores a malformed value rather than rejecting the
request — a typo would be invisible at runtime and simply earn nothing.

Verification

Build: go build ./... → exit 0.

Lint: gofumpt -l . → clean. golangci-lint run ./... (v2.13.1, the
version pinned in mise.toml) → 0 issues.

Tests (go test ./... -v, subtest counts):

pass fail
baseline, pristine checkout of this fork's main 40 1
after this branch 41 (+ TestAIMLAPIDefaultHeaders) 1

The single failure is identical before and after and is not ours:

--- FAIL: TestValidDefaultModels/GitHub_Copilot
    Default large model "claude-sonnet-4.6" not found in provider "GitHub Copilot"

It is already fixed upstream in 155f0c2 fix(copilot): fix default model, three
commits ahead of where this fork's main currently sits.

Model ids: all 9 checked against the live catalog as id or alias, and
filtered to type == "openai/chat-completions" — 9/9 present as canonical ids
on the chat surface, 0 stale. Each was additionally called live: all 9 returned
HTTP 200 for a plain completion, accepted reasoning_effort, and produced a
real tool_calls response.

One real inference call, through the added code path — not curl, not a
mock. A small program imports charm.land/catwalk/pkg/embedded, looks up
InferenceProviderAIMLAPI, and takes the endpoint, the model ids, the API-key
env var name and the headers entirely from the entry this PR adds:

provider name        : aimlapi.com
provider id          : aimlapi
api_endpoint         : https://api.aimlapi.com/v1
api_key env          : AIMLAPI_API_KEY
default large model  : openai/gpt-5.6-terra
default small model  : openai/gpt-5.6-luna
default header       : HTTP-Referer: https://charm.land
default header       : X-Title: Crush
default header       : X-AIMLAPI-Partner-ID: part_crush
default header       : X-AIMLAPI-Source: agent/crush

chat  : HTTP 200  model=gpt-5.6-terra  usage=17/10/27
chat  : content="catwalk aimlapi provider works" finish_reason=stop
tools : HTTP 200  model=gpt-5.6-terra  usage=137/17/154
tools : tool_call=get_weather args={"city":"Paris"} finish_reason=tool_calls
small : HTTP 200  model=gpt-5.6-luna  usage=13/6/19
small : content="small model ok" finish_reason=stop

Server: go run . then GET /v2/providers → 42 providers, aimlapi.com
present with 9 models and all four headers intact through JSON round-trip.

What could not be verified

  • reasoning_levels is ["low","medium","high"] for every model. Those
    three were each confirmed to return HTTP 200. xhigh, max, none and
    minimal appear on other providers in this repo but were not tested here, so
    they are not claimed.
  • Cache pricing. cost_per_1m_out_cached is the cache read price and
    cost_per_1m_in_cached the cache write price, per CRUSH.md. Write prices
    are published for the Anthropic and GPT-5.6 entries only; the rest are left at
    0 rather than guessed.
  • supports_attachments is set from live probes, not from the published
    catalog. For anthropic/claude-opus-5 and anthropic/claude-sonnet-5 the
    catalog's modalities.input says ["text"], but both models in fact accept
    and correctly describe an inline image; deepseek/deepseek-v4-pro correctly
    rejects one with HTTP 400. This is an upstream metadata bug on the aimlapi.com
    side and has been reported there.
  • Prices come from GET /v1/models?include=pricing and are a point-in-time
    snapshot. Without a cmd/aimlapi updater they will drift; refreshing them is
    a manual edit.

Crush users who buy their inference through aimlapi.com currently have to
hand-write a custom provider block; catwalk is where that belongs so the
model list, prices and context windows stay maintained in one place.

The endpoint is OpenAI-compatible, so `openai-compat` covers it. Nine chat
models are curated by hand rather than generated: the catalog carries 353
chat entries, which is not a useful model picker, and importing it wholesale
would churn on every upstream catalog change.

Attribution rides in `default_headers`, the seam the repo already uses for
this (aihubmix's APP-Code, openrouter's HTTP-Referer/X-Title). HTTP-Referer
and X-Title name Crush, matching openrouter.json and vercel.json, because
they identify the calling application, not the gateway.

The partner id is covered by a regex test: the gateway ignores a malformed
value instead of rejecting the request, so a typo would never show up at
runtime. The same test asserts no other provider carries an X-AIMLAPI-*
header, so attribution cannot ride along to somebody else's API.
Moves aimlapi.com to the head of providerRegistry so it lands first in
/v2/providers and therefore first in Crush's provider picker.

This is a partnership placement decision, not a correctness change, and it
contradicts the comment the registry already carries ("Let's keep the main
providers at the top", then alphabetical). It is kept as its own commit so
it can be dropped before anything is offered upstream, where the entry
belongs in its alphabetical slot after aihubmix.

catwalk has no "recommended" or "featured" flag on a provider, so there is
nothing to badge; ordering is the only prominence mechanism that exists.
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.

2 participants