Skip to content

fix(engine): map anthropic-compatible custom providers to anthropic-messages api - #1702

Merged
gsxdsm merged 2 commits into
Runfusion:mainfrom
vamsi-ship-it:fix/anthropic-compatible-custom-provider-api-key
Jun 21, 2026
Merged

fix(engine): map anthropic-compatible custom providers to anthropic-messages api#1702
gsxdsm merged 2 commits into
Runfusion:mainfrom
vamsi-ship-it:fix/anthropic-compatible-custom-provider-api-key

Conversation

@vamsi-ship-it

@vamsi-ship-it vamsi-ship-it commented Jun 21, 2026

Copy link
Copy Markdown
Contributor

Problem

Adding a custom provider with apiType: "anthropic-compatible" (e.g. a self-hosted Claude proxy or an enterprise AI gateway) registers fine, but any task that tries to run a model from it fails at stream time with:

Error: No API provider registered for api: anthropic

The provider is visible in the model picker and [pi] Registered custom provider "<name>" is logged, so it looks configured — the failure only surfaces when an agent actually streams.

Root cause

resolveCustomProviderApiType in packages/engine/src/pi.ts maps anthropic-compatible to the api key "anthropic":

if (apiType === "anthropic-compatible") {
  return "anthropic";
}

But pi-ai (@earendil-works/pi-ai) registers the Anthropic Messages API under "anthropic-messages" (see registerBuiltInApiProviders() in dist/providers/register-builtins.js — the registered keys are anthropic-messages, openai-completions, openai-responses, azure-openai-responses, openai-codex-responses, google-generative-ai, google-vertex, mistral-conversations, bedrock-converse-stream).

At stream time, pi-ai's resolveApiProvider(model.api) calls getApiProvider("anthropic"), gets undefined, and throws. The other two arms of resolveCustomProviderApiType (openai-responses and the default openai-completions) map to real registry keys and work — only the anthropic arm pointed at a key that is never registered.

Fix

Map anthropic-compatible"anthropic-messages" (one line + clarifying comment).

Tests

The existing registration test (registers custom providers from global settings) covered openai-compatible and openai-responses but not anthropic-compatible — which is how this shipped. Extended it with an anthropic-compatible provider asserting api: "anthropic-messages" is what gets registered, as a regression guard.

  • pnpm --filter @fusion/engine test:core → 649 passed
  • pi-create-fn-agent.test.ts → 76 passed
  • pnpm --filter @fusion/engine typecheck → clean
  • eslint → clean

Verification

Reproduced end-to-end against a real anthropic-compatible gateway: before the fix, planning failed with No API provider registered for api: anthropic; after the fix (running from source via pnpm dev dashboard), a task planned successfully using <provider>/claude-opus-4-8 with the model streaming normally and no errors.

🤖 Generated with Claude Code


Open in Stage

Summary by CodeRabbit

Release Notes

  • Bug Fixes

    • Resolved a runtime issue where anthropic-compatible custom providers failed to register due to an incorrect API mapping.
    • Custom provider registration now uses the correct Anthropic Messages API configuration.
  • Tests

    • Added regression coverage to verify anthropic-compatible providers register with the expected API key and base configuration.
    • Strengthened assertions to prevent accidental use of unsupported API identifiers during provider registration.

…essages api

resolveCustomProviderApiType mapped the `anthropic-compatible` provider type
to the api key "anthropic", but pi-ai (@earendil-works/pi-ai) registers the
Anthropic Messages API under "anthropic-messages". Any custom provider
configured as anthropic-compatible selected a model whose `api` did not match
a registered provider, throwing "No API provider registered for api: anthropic"
at stream time (the model registered fine, but failed when a task tried to
stream).

The openai-responses and default (openai-completions) arms already map to real
registry keys and work; only the anthropic arm pointed at an unregistered key.

Extend the existing custom-provider registration test (which covered
openai-compatible and openai-responses but not anthropic-compatible) with a
regression assertion that anthropic-compatible maps to "anthropic-messages".

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@ghost

ghost commented Jun 21, 2026

Copy link
Copy Markdown

Ready to review this PR? Stage has broken it down into 3 individual chapters for you:

Title
1 Map anthropic-compatible providers to anthropic-messages API
2 Add regression tests for custom provider registration
3 Add changeset for anthropic-compatible fix
Open in Stage

Chapters generated by Stage for commit efd3743 on Jun 21, 2026 9:00am UTC.

@coderabbitai

coderabbitai Bot commented Jun 21, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: c9c94f45-5549-43f4-864b-8678b39955cb

📥 Commits

Reviewing files that changed from the base of the PR and between 5a422b0 and efd3743.

📒 Files selected for processing (2)
  • packages/engine/src/__tests__/pi-create-fn-agent.test.ts
  • packages/engine/src/pi.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/engine/src/pi.ts

📝 Walkthrough

Walkthrough

resolveCustomProviderApiType in pi.ts is corrected to return "anthropic-messages" instead of "anthropic" for the anthropic-compatible provider type, fixing a runtime error. Regression test coverage is added with a Custom Anthropic provider fixture and validation assertions, plus a patch changeset entry.

Changes

Anthropic-compatible provider mapping fix

Layer / File(s) Summary
Correct anthropic-compatible API key in resolveCustomProviderApiType
packages/engine/src/pi.ts
Return type union replaces "anthropic" with "anthropic-messages", and the anthropic-compatible branch now returns "anthropic-messages" so custom providers register under the correct pi-api key.
Regression test and changeset
packages/engine/src/__tests__/pi-create-fn-agent.test.ts, .changeset/fix-anthropic-compatible-custom-provider.md
Adds a Custom Anthropic provider mock to the global settings test fixture and extends assertions to verify registerProviderMock is called with "anthropic-messages" (not bare "anthropic"), validate all API identifiers match an allowlist, and document the fix.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~8 minutes

Suggested reviewers

  • gsxdsm

Poem

🐇 Hop hop, a key was wrong, you see,
"anthropic" bare caused much misery!
Now "anthropic-messages" leads the way,
Custom providers register without delay.
The tests assert what once went astray — 🌸

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately and specifically describes the main change: mapping anthropic-compatible custom providers to the anthropic-messages API key, which directly addresses the core bug fix in the PR.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@greptile-apps

greptile-apps Bot commented Jun 21, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes a runtime failure where custom providers configured as anthropic-compatible would register successfully but throw "No API provider registered for api: anthropic" the moment a task tried to stream — because resolveCustomProviderApiType was returning the bare "anthropic" key, which pi-ai never registers.

  • packages/engine/src/pi.ts: resolveCustomProviderApiType now returns "anthropic-messages" for anthropic-compatible inputs, matching the key pi-ai actually registers; the return type union and JSDoc are updated accordingly.
  • packages/engine/src/__tests__/pi-create-fn-agent.test.ts: Adds an anthropic-compatible fixture, a positive assertion for api: "anthropic-messages", a negative guard against the broken bare "anthropic" key, and a set-membership invariant that will catch any future arm returning an unregistered key.

Confidence Score: 5/5

Safe to merge — a single-line targeted correction with regression test coverage and no side effects on other provider paths.

The change touches one return value in one small function. The other two arms (openai-responses, openai-completions) are untouched, the fix is directly confirmed by the new test assertions, and the existing 649-test suite passed. No config, schema, or interface changes are involved.

No files require special attention.

Important Files Changed

Filename Overview
packages/engine/src/pi.ts One-line fix: anthropic-compatible now resolves to "anthropic-messages" instead of the never-registered bare "anthropic" key; return type signature updated accordingly with a thorough JSDoc.
packages/engine/src/tests/pi-create-fn-agent.test.ts Adds an anthropic-compatible provider fixture, a positive assertion for api: "anthropic-messages", a negative guard against the broken api: "anthropic", and a set-membership invariant covering all registered API keys.
.changeset/fix-anthropic-compatible-custom-provider.md Patch-level changeset entry with a clear description of the root cause and fix.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A["Custom provider config\napiType: 'anthropic-compatible'"] --> B["resolveCustomProviderApiType(apiType)"]
    B --> C{"apiType?"}
    C -- "anthropic-compatible" --> D["return 'anthropic-messages' ✅\n(was 'anthropic' ❌)"]
    C -- "openai-responses" --> E["return 'openai-responses'"]
    C -- "default" --> F["return 'openai-completions'"]
    D --> G["registerProvider(name, { api: 'anthropic-messages', ... })"]
    E --> H["registerProvider(name, { api: 'openai-responses', ... })"]
    F --> I["registerProvider(name, { api: 'openai-completions', ... })"]
    G --> J["pi-ai getApiProvider('anthropic-messages') → found ✅"]
    style D fill:#d4edda,stroke:#28a745
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
    A["Custom provider config\napiType: 'anthropic-compatible'"] --> B["resolveCustomProviderApiType(apiType)"]
    B --> C{"apiType?"}
    C -- "anthropic-compatible" --> D["return 'anthropic-messages' ✅\n(was 'anthropic' ❌)"]
    C -- "openai-responses" --> E["return 'openai-responses'"]
    C -- "default" --> F["return 'openai-completions'"]
    D --> G["registerProvider(name, { api: 'anthropic-messages', ... })"]
    E --> H["registerProvider(name, { api: 'openai-responses', ... })"]
    F --> I["registerProvider(name, { api: 'openai-completions', ... })"]
    G --> J["pi-ai getApiProvider('anthropic-messages') → found ✅"]
    style D fill:#d4edda,stroke:#28a745
Loading

Reviews (2): Last reviewed commit: "fix(review): address CodeRabbit feedback..." | Re-trigger Greptile

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/engine/src/__tests__/pi-create-fn-agent.test.ts`:
- Around line 1398-1406: The current regression test for the
anthropic-compatible provider in the test file only asserts one specific path.
You need to expand this regression test to cover all documented surfaces in
docs/testing.md to ensure comprehensive coverage of the bug guard. Review the
testing documentation to identify the full surface checklist, then add
additional test cases or assertions within the test function to validate each
documented surface. Additionally, convert the inline comment explaining the
regression requirement (the comment block starting with "anthropic-compatible
must map...") from plain TS comment format to FNXC-formatted requirement
metadata as per the coding guidelines for test files.
- Around line 1401-1406: The test assertion for registerProviderMock only
validates that the correct "custom-anthropic" provider is registered but does
not explicitly prevent an incorrect bare "anthropic" registration from
occurring. Add a negative assertion using not.toHaveBeenCalledWith to verify
that registerProviderMock was never called with just "anthropic" as the provider
identifier, which will strengthen the regression test by explicitly guarding
against the unwanted provider registration.

In `@packages/engine/src/pi.ts`:
- Around line 1011-1014: The comment block describing the Anthropic Messages API
registration requirement is not in FNXC format. Convert this comment block to
use the FNXC-formatted metadata pattern following the structure
FNXC:<Area-of-product> yyyy-MM-dd-hh:mm: to maintain requirement traceability
consistency across the repository. Replace the existing multi-line comment with
the standardized FNXC format while preserving the core decision information
about mapping "anthropic" to "anthropic-messages" for the API provider registry.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: dd7ae5d0-af3c-41fa-8cd3-91ab75c6811a

📥 Commits

Reviewing files that changed from the base of the PR and between 0422b43 and 5a422b0.

📒 Files selected for processing (3)
  • .changeset/fix-anthropic-compatible-custom-provider.md
  • packages/engine/src/__tests__/pi-create-fn-agent.test.ts
  • packages/engine/src/pi.ts

Comment thread packages/engine/src/__tests__/pi-create-fn-agent.test.ts Outdated
Comment thread packages/engine/src/__tests__/pi-create-fn-agent.test.ts
Comment thread packages/engine/src/pi.ts Outdated
- Add JSDoc to resolveCustomProviderApiType (docstring coverage + AGENTS.md
  jsdoc convention) and convert the inline rationale to FNXC format.
- FNXC-format the test rationale comment.
- Strengthen the regression test per FN-5893 (fix the invariant, not the repro):
  - negative assertion that no provider is ever registered with the bare
    "anthropic" api key;
  - assert every api key passed to registerProvider is one pi-ai's registry
    actually registers, so a typo in any arm (not just anthropic) fails here.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@vamsi-ship-it

Copy link
Copy Markdown
Contributor Author

Addressed the CodeRabbit feedback in efd3743:

  1. FNXC format (pi.ts + test comment) — done. The pi.ts rationale is now a JSDoc on resolveCustomProviderApiType (also resolves the docstring-coverage warning and matches the AGENTS.md jsdoc convention) with an FNXC:CustomProviders tag; the test comment is FNXC-formatted too.

  2. Negative assertion — added expect(registerProviderMock).not.toHaveBeenCalledWith(expect.anything(), expect.objectContaining({ api: "anthropic" })) so a regression back to the bare "anthropic" key fails explicitly.

  3. Cover the invariant, not just one repro (FN-5893) — rather than only asserting the anthropic arm, the test now asserts the general invariant on the touched surface (providers/execution paths): every api key handed to registerProvider must be one pi-ai's registry actually registers. This catches a typo in any arm, not just anthropic.

Verification: pnpm --filter @fusion/engine test:core (649 passed), pi-create-fn-agent.test.ts (76 passed), engine typecheck clean, eslint clean.

@gsxdsm

gsxdsm commented Jun 21, 2026

Copy link
Copy Markdown
Collaborator

Great fix thank you

@gsxdsm
gsxdsm merged commit 222e781 into Runfusion:main Jun 21, 2026
6 checks passed
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