Skip to content

Integration request from AIML API - #7461

Open
hugoaimlapi wants to merge 15 commits into
nexu-io:mainfrom
aimlapi:main
Open

Integration request from AIML API#7461
hugoaimlapi wants to merge 15 commits into
nexu-io:mainfrom
aimlapi:main

Conversation

@hugoaimlapi

Copy link
Copy Markdown

HUMAN:

Hi! I'm Hugo from aimlapi.com — an AI aggregator that gives access to 1000+ models in one API, trusted by 400k+ users.

We'd love to be available as a verified provider option inside OpenDesign — so we went ahead and did all the technical work on our side.

To build our partnership, we offer a 50/50 revenue share on all traffic from this integration. (P.S.: tracking starts as soon as this release goes live, so no earnings will be lost during setup)

My contacts: hugo@aimlapi.com (email / Slack), Telegram: @hug0the


AGENT:

Fixes #7459

Why

aimlapi.com is OpenAI-wire-compatible, so it already works through the generic "OpenAI" tab with a hand-typed base URL — but it isn't a provider: no tab, no key-console link, no curated model list, and users have to guess which of ~900 catalog ids to type. And unlike AIHubMix (APP-Code), requests to aimlapi.com carry no attribution, so aimlapi.com can't tell OpenDesign traffic from anyone else's.

This PR adds aimlapi.com as a native BYOK provider, following the AIHubMix pattern from #3583: one integration module (apps/daemon/src/integrations/aimlapi.ts) owns the identity + outbound headers, and every daemon call to aimlapi.com — BYOK chat through the OpenCode runtime, the /api/proxy/aimlapi/stream route, connection test and model discovery — funnels through it.

What users will see

  • Settings → BYOK (and the onboarding provider picker) has a new aimlapi.com tab. Pick it, paste an aimlapi.com key; the base URL defaults to https://api.aimlapi.com/v1. The key field links to aimlapi.com/app/keys.
  • The Model dropdown opens on a curated list of flagship catalog ids (openai/gpt-5.6-terra, anthropic/claude-sonnet-5, anthropic/claude-opus-5, google/gemini-3.6-flash, deepseek/deepseek-v4-pro, x-ai/grok-4-5, moonshot/kimi-k3); the live catalog loads from GET /v1/models once a key is entered.
  • Test connection works for the new tab.
  • Nothing changes for existing users: the default protocol stays Anthropic, saved configs load unchanged; aimlapi.com is opt-in.

Surface area

  • UI — new aimlapi.com tab in Settings → BYOK and in the onboarding provider picker; new preset in KNOWN_PROVIDERS / BYOK_PROVIDER_PRESET_SPECS
  • Keyboard shortcut
  • CLI / env var — the daemon reads an optional AIMLAPI_PARTNER_ID to override the compiled-in partner id (staging/test only; nothing required, no OD_* var)
  • API / contract — new POST /api/proxy/aimlapi/stream; aimlapi added to ByokChatProtocol, ConnectionTestProtocol, MemoryExtractionProvider in packages/contracts
  • Extension point
  • i18n keys — none added (labels are the brand string aimlapi.com)
  • New top-level dependency — root package.json / pnpm-lock.yaml unchanged
  • Default behavior change — opt-in; default protocol unchanged

Screenshots

To follow — Settings → BYOK with aimlapi.com selected, and a successful Test connection.

Bug fix verification

Not a bug fix — feature PR.

Validation

  • Unit tests added in apps/daemon/tests/runtimes/byok-opencode.test.ts: the OpenCode provider config for aimlapi uses @ai-sdk/openai-compatible with the aimlapi.com base URL, and carries the X-AIMLAPI-Source / X-AIMLAPI-Partner-ID pair on real chat traffic.
  • pnpm guard / pnpm typecheck / package tests: relying on this PR's CI run; will fix anything it flags.
  • How to check manually: Settings → BYOK → aimlapi.com → paste a key → Test connection, then send a chat message. Every request to api.aimlapi.com (smoke test, /v1/models, chat completions) should carry X-AIMLAPI-Source: agent/open-design and X-AIMLAPI-Partner-ID; on the aimlapi.com side the traffic shows up under the OpenDesign partner id.

Notes

  • Follows the existing AIHubMix attribution pattern (aihubmixHeaders() / APP-Code). Nothing in the agent loop or routing changes; previously saved settings load unchanged.
  • Attribution is injected daemon-side only (never in the browser bundle), so a user can't strip or forge it from the web app.

aimlapihello and others added 12 commits August 26, 2026 15:45
aimlapi.com is an OpenAI-wire-compatible aggregator: one key fronts ~900
models. Modelled on the AIHubMix integration, which is the closest
existing case here — same wire shape, same "aggregator with a fixed
attribution header" problem.

Attribution follows AIHubMix's own reasoning verbatim: every outbound call
point funnels through `aimlapiHeaders()` instead of hand-building
`Authorization` inline, so "every aimlapi.com request carries our
attribution" stays enforceable in one place rather than re-derived per
call site. aimlapi.com wants X-AIMLAPI-Source + X-AIMLAPI-Partner-ID on
everything it serves, and a missing header is silent — the request
succeeds, it just serves untagged.

The daemon gets a dedicated /api/proxy/aimlapi/stream rather than a
hostname branch inside the generic OpenAI route. Two reasons: the pair
then rides unconditionally instead of depending on a hostname match, and
it keeps the picker tab -> daemon log line -> upstream call chain
readable, which is the same argument the AIHubMix client documents. It
also keeps the pair out of the browser bundle, where a user could strip
or forge it.

Chat only. Media generation (the image/video/speech tool loop AIHubMix
carries) is deliberately out of scope for this change.

Typecheck pending: the workspace install had not finished when this was
committed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Completes the previous commit. `ApiProtocol` is exhaustively mapped in
several `Record<ApiProtocol, …>` tables, so adding the union member is
only half the change — the compiler names every table that still has to
answer for it, and each one is a real user-visible surface:

- suggested models: the flagships aimlapi.com marks hottest, so the BYOK
  dropdown opens on something useful rather than empty
- fast model: gemini-3.6-flash, for the memory extractor's auto pill
- protocol tab, label, key placeholder, and the console link that sends
  users to https://aimlapi.com/app/keys
- `ConnectionTestProtocol` and `MemoryExtractionProvider` in the contracts
  package, so the key smoke test and the memory extractor accept it

Verified: typecheck clean in all three packages — daemon, web, contracts.

Note for anyone repeating this: the daemon resolves
`@open-design/contracts` through `dist`, so a contracts source edit does
nothing until that package is rebuilt. The daemon's postinstall runs its
own tsc, which is where the two contract mismatches surfaced.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Both ids resolve, but openai/gpt-5.6-terra is the intended alias for the
suggested-model default; -pro is a separate, heavier SKU.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Add aimlapi.com as a BYOK chat provider
The protocol tab was appended, putting it last in the picker while every
other table in the file already lists it first alphabetically. Moved to
the front so the ordering is consistent with the labels, placeholders and
console-link maps.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
List aimlapi.com first among providers
The 'aimlapi' protocol is already fully wired natively (state/apiProtocols.ts:
its own tab, suggested models, default model, labels, and a dedicated
providers/aimlapi-compatible.ts handler) — but KNOWN_PROVIDERS had no entry
for it, so defaultApiProtocolConfig()/switchApiProtocolConfig() couldn't
resolve a base URL when the aimlapi.com tab was selected in Settings: the
Base URL field fell back to empty/placeholder instead of prefilling
https://api.aimlapi.com/v1.

Adds that entry with protocol: 'aimlapi' (matching the existing tab id) and
a preferredModels list mirrored from SUGGESTED_MODELS_BY_PROTOCOL.aimlapi so
the two stay in sync.
fix: register aimlapi.com in KNOWN_PROVIDERS with the correct protocol
Registering aimlapi.com in Settings (KNOWN_PROVIDERS/BYOK_PROVIDER_PRESET_SPECS)
and daemon model listing wasn't enough to actually start a chat: the run
preflight in ProjectView.tsx and the daemon's OpenCode provider mapper both
enumerate supported BYOK protocols explicitly, and 'aimlapi' was missing from
both lists plus the shared ByokChatProtocol contract type. That made every
run treated as an unconfigured provider and bounced back to Settings even
with a valid, saved, tested API key.

- packages/contracts: add 'aimlapi' to ByokChatProtocol
- apps/daemon/runtimes/byok-opencode: map aimlapi to @ai-sdk/openai-compatible
- apps/web/ProjectView: recognize aimlapi in the BYOK run-preflight gate
- apps/daemon/provider-models: aimlapi model listing (Load models) support
- apps/web/state/config: surface aimlapi.com first in the BYOK provider grid
fix(aimlapi): recognize aimlapi.com as a supported BYOK chat protocol
Real BYOK chat runs through the OpenCode runtime, not the /api/proxy/*
routes: `streamMessage` — the web-provider dispatcher those routes exist
for — is called nowhere outside `providers/`, and ProjectView dispatches
`streamViaDaemon({ agentId: 'byok-opencode' })`. So the attribution pair
added with the provider only ever covered Test connection and model
discovery, while every actual inference request went out untagged.

That is the failure mode worth naming: nothing breaks. The chat works,
the connection test is green, and the traffic simply does not count.

`buildProviderEntry` hands `options` to the provider factory, which
forwards `headers` upstream, so the pair goes there. aimlapi.com now has
its own case rather than sharing the senseaudio/aihubmix one, since it is
the only one of the three that carries headers.

Also adds aimlapi to `usesAnthropicProxy`'s allow-list. Without it the
protocol falls through to the trailing baseUrl check and is treated as an
Anthropic proxy, which sends image attachments down the Anthropic path
for aimlapi.com users.

Test asserts the pair on the generated runtime config; confirmed it fails
when the headers are removed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Attribute the runtime that actually serves chat
@hugoaimlapi hugoaimlapi changed the title 50/50 RevShare Integration: OpenDesign & AIML API Integration request from AIML API Aug 26, 2026
@lefarcen

Copy link
Copy Markdown
Contributor

Thanks @hugoaimlapi — the native provider path and daemon-side attribution direction are clear, and the AIHubMix prior-art makes the scope easy to follow. I’m adding the initial labels and reviewer routing now so the right folks can pick this up.

💡 To drive this PR to merge hands-free, paste this to your AI coding agent (Claude Code / Codex / opencode / Cursor …):
Take over nexu-io/open-design#7461 until it merges — read https://raw.githubusercontent.com/nexu-io/looper/main/skills/pr-takeover/SKILL.md and follow it.

@lefarcen

Copy link
Copy Markdown
Contributor

🧪 This PR has changes that need a manual QA pass before merge — please hold off self-merging for now; we’ll loop QA in once it’s merge-ready (and design/product have signed off, where applicable).

@lefarcen
lefarcen requested a review from mrcfps August 26, 2026 15:03
@lefarcen lefarcen added size/L PR changes 300-700 lines risk/high High risk: apps/desktop, daemon, auth, migration, workflows, package deps type/feature New feature needs-product-review Feature PR awaiting product sign-off before merge (see roadmap) needs-design-review Awaiting design review (external UI change); cleared by the Odcrew App applying design-approved needs-validation Runtime change detected; needs human or /explore agent validation. labels Aug 26, 2026
@hugoaimlapi

Copy link
Copy Markdown
Author

Okay, thank you for the reply
Will be very glad to connect with your team to answer all questions and if you need support from our dev team - also feel free to seek help

My Slack: hugo@aimlapi.com (and Telegram if it's more comfortable for you: @hug0the)

@mrcfps mrcfps left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @hugoaimlapi — the native BYOK path, the shared aimlapiHeaders() helper, and the later OpenCode attribution fix are a thoughtful, well-documented contribution.

Primary chat looks correctly wired: Settings → Test connection → byok-opencode with @ai-sdk/openai-compatible should work, and existing installs stay on Anthropic. A few follow-ups below on memory extraction, the unused proxy copy, and picker order — none of these should block the main chat path.

🔁 Powered by Looper · runner=reviewer · agent=omp · An autonomous AI dev team for your GitHub repos.

Comment thread packages/contracts/src/api/memory.ts Outdated
Comment thread apps/daemon/src/routes/chat.ts Outdated
Comment thread apps/web/src/state/config.ts Outdated
…oute

Addresses the two review findings on nexu-io#7461.

Memory extraction never learned the protocol. ProjectView forwards
byokChatProvider.provider = 'aimlapi' on every BYOK turn, but
memory-llm.ts PROVIDER_DEFAULTS had no aimlapi entry, and pickProvider()
only enters the "same as chat" branch when PROVIDER_DEFAULTS[provider]
is truthy. An aimlapi.com chat therefore fell through to ANTHROPIC/
OPENAI credentials or skipped extraction outright — the "I'm chatting
with X but memory used Y" surprise that snapshot path exists to prevent.
In practice it skipped: the new spec, run against the old code, records
zero outbound calls. Mirrors the AIHubMix wiring: a PROVIDER_DEFAULTS
entry, an env-key lookup, and the attribution pair spread from
callOpenAI so the extractor's own request is not silently untagged.

isExtractionProvider() also rejected a saved { provider: 'aimlapi' }
override as invalid. It had drifted from MemoryExtractionProvider, which
already admits senseaudio and aihubmix as well; all three reach the
extractor through the OpenAI-compatible path, so the guard is now
exhaustive over the type.

/api/proxy/aimlapi/stream was a 159-line near-copy of the OpenAI stream
route, including an Azure max_completion_tokens retry that is dead on
api.aimlapi.com. BYOK chat goes through byok-opencode, not /api/proxy/*,
and its only caller — apps/web/src/providers/aimlapi-compatible.ts — was
never imported. Both are removed rather than left to drift from the
handler they were copied from; aimlapiHeaders() stays on the connection
test, model listing and byok-opencode, which are the live call sites.

@mrcfps mrcfps left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @hugoaimlapi — the follow-up commit is a solid cleanup. Memory extraction now follows the aimlapi.com chat snapshot (PROVIDER_DEFAULTS + attribution + tests), and the unused /api/proxy/aimlapi/stream copy is gone.

One new blocker on this head: the new aimlapi-api transcript id is not in the BYOK OpenCode family allowlist, so multi-turn chat will drop prior context. Details are inline.

The earlier picker-order thread is still open on BYOK_PROVIDER_PRESET_SPECS / KNOWN_PROVIDERS / API_PROTOCOL_TABS; I left that on the existing thread rather than re-filing it.

🔁 Powered by Looper · runner=reviewer · agent=omp · An autonomous AI dev team for your GitHub repos.

Comment thread apps/web/src/utils/apiProtocol.ts Outdated
Addresses the picker-order review finding on nexu-io#7461.

The entries were prepended, which put a third-party aggregator ahead of
Anthropic and OpenAI in Settings -> BYOK, the onboarding picker and the
protocol tabs. Record insertion order is not a display contract, but
API_PROTOCOL_TABS, KNOWN_PROVIDERS and BYOK_PROVIDER_PRESET_SPECS are —
they are a product default for every new BYOK setup, not an alphabetical
coincidence. All three now append the entry last, where AIHubMix already
sits among the native tabs.

The Record<ApiProtocol, ...> maps listed aimlapi first only because it
was prepended to the ApiProtocol union; the union member and the map
keys move with it so the whole set reads as appended rather than mixed.
ConnectionTestProtocol and MemoryExtractionProvider get the same
treatment (ByokChatProtocol was already appended).

Pure reordering: no key, value or behaviour changes, and DEFAULT_CONFIG
.apiProtocol stays 'anthropic', so no install changes vendor.
Addresses the transcript-family review finding on
nexu-io#7461.

ProjectView stamps each API-mode assistant turn with
apiProtocolAgentId(config.apiProtocol) — 'aimlapi-api' for this tab —
and then sends the run through streamViaDaemon({ agentId:
'byok-opencode' }). scopeHistoryToAgent walks back from the latest user
turn and cuts the history at the first assistant turn whose agentId is
not in the target's family; isSameTranscriptAgentFamily resolves that
through the closed API_MODE_AGENT_IDS set, which 'aimlapi-api' had never
joined. AIHubMix hit the same seam and is already a member.

The effect is primary-path breakage for the feature this PR adds: from
turn 2 on, an aimlapi.com BYOK chat ships only the latest user message
and the model loses the conversation it just had. The new spec pins it —
against the unfixed code the transcript collapses to exactly
'## user\nmake the second step clearer'.

Also labels 'aimlapi-api' in AGENT_LABELS so the chat header shows the
provider instead of falling back to the raw agent id.
@lefarcen

Copy link
Copy Markdown
Contributor

Thanks for the quick follow-up, @hugoaimlapi. The latest updates appear to address the Looper threads that were still open on the previous head, so at this point the main thing left is a refreshed reviewer pass on the current commit.

@mrcfps mrcfps left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@hugoaimlapi thank you for the careful follow-up commits — this is a thoughtful, well-scoped BYOK integration.

I re-reviewed head a90feec against the earlier threads. The previous issues are addressed on this head: memory extraction now follows the aimlapi.com chat snapshot (PROVIDER_DEFAULTS, attribution headers, and memory-aimlapi-byok.test.ts), the unused /api/proxy/aimlapi/stream copy is gone, the picker lists aimlapi.com after the first-party providers, and aimlapi-api is in the BYOK OpenCode transcript family with a regression test.

On the current diff, the main path looks correctly wired: Settings → aimlapi.com tab, Test connection and /v1/models go through aimlapiHeaders(), real chat goes through byok-opencode with @ai-sdk/openai-compatible plus the attribution pair, and existing installs stay on Anthropic. Nice work getting the AIHubMix-shaped seams in place without changing default behavior.

🔁 Powered by Looper · runner=reviewer · agent=omp · An autonomous AI dev team for your GitHub repos.

@github-actions

Copy link
Copy Markdown
Contributor

Visual regression review

Head: a90feec · Base: 9881cff

3 changed · 50 unchanged · 0 new without baseline · 0 failed

Changed cases

Case Main PR Diff
visual-settings-byok
1,177 px (0.09%)
main pr diff
visual-settings-byok-model-dropdown
2,660 px (0.21%)
main pr diff
visual-settings-byok-openai
1,177 px (0.09%)
main pr diff
Unchanged cases
Case Main PR Diff
visual-avatar-local-agent-list
0 px (0.00%)
main pr diff
visual-avatar-local-agent-list-panel
0 px (0.00%)
main pr diff
visual-avatar-menu
0 px (0.00%)
main pr diff
visual-avatar-menu-panel
0 px (0.00%)
main pr diff
visual-avatar-open-design-model-picker
0 px (0.00%)
main pr diff
visual-critical-settings
0 px (0.00%)
main pr diff
visual-critical-workspace
0 px (0.00%)
main pr diff
visual-critical-workspace-preview
0 px (0.00%)
main pr diff
visual-deepseek-unpaid-campaign-600
0 px (0.00%)
main pr diff
visual-deepseek-unpaid-campaign-short-height
0 px (0.00%)
main pr diff
visual-design-system-detail
1 px (0.00%)
main pr diff
visual-design-systems
1 px (0.00%)
main pr diff
visual-home
0 px (0.00%)
main pr diff
visual-home-catalog
0 px (0.00%)
main pr diff
visual-home-context-picker
0 px (0.00%)
main pr diff
visual-home-context-picker-popover
0 px (0.00%)
main pr diff
visual-home-plugin-filter
0 px (0.00%)
main pr diff
visual-home-plugin-use-staged
0 px (0.00%)
main pr diff
visual-home-plugin-use-with-query
0 px (0.00%)
main pr diff
visual-home-staged-attachment
0 px (0.00%)
main pr diff

Visual diff is advisory only and does not block merging.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs-design-review Awaiting design review (external UI change); cleared by the Odcrew App applying design-approved needs-product-review Feature PR awaiting product sign-off before merge (see roadmap) needs-validation Runtime change detected; needs human or /explore agent validation. risk/high High risk: apps/desktop, daemon, auth, migration, workflows, package deps size/L PR changes 300-700 lines type/feature New feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

50/50 RevShare Integration: OpenDesign & AIML API

5 participants