fix(engine): map anthropic-compatible custom providers to anthropic-messages 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>
This commit is contained in:
7
.changeset/fix-anthropic-compatible-custom-provider.md
Normal file
7
.changeset/fix-anthropic-compatible-custom-provider.md
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
---
|
||||||
|
"@runfusion/fusion": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Fix anthropic-compatible custom providers failing with "No API provider registered for api: anthropic".
|
||||||
|
|
||||||
|
`resolveCustomProviderApiType` mapped the `anthropic-compatible` provider type to the api key `"anthropic"`, but pi-ai registers the Anthropic Messages API under `"anthropic-messages"`. Any custom provider configured as `anthropic-compatible` (self-hosted Claude proxy, gateway, etc.) therefore selected a model whose `api` did not match a registered provider and threw at stream time. Mapped it to `"anthropic-messages"` and added a regression assertion alongside the existing openai-compatible / openai-responses coverage.
|
||||||
@@ -1363,6 +1363,14 @@ describe("createFnAgent", () => {
|
|||||||
apiKey: "RESPONSES_API_KEY",
|
apiKey: "RESPONSES_API_KEY",
|
||||||
models: [{ id: "responses-model", name: "Responses Model" }],
|
models: [{ id: "responses-model", name: "Responses Model" }],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: "770e8400-e29b-41d4-a716-446655440002",
|
||||||
|
name: "Custom Anthropic",
|
||||||
|
apiType: "anthropic-compatible",
|
||||||
|
baseUrl: "https://anthropic.example",
|
||||||
|
apiKey: "ANTHROPIC_API_KEY",
|
||||||
|
models: [{ id: "anthropic-model", name: "Anthropic Model" }],
|
||||||
|
},
|
||||||
] as any);
|
] as any);
|
||||||
|
|
||||||
const { createFnAgent } = await import("../pi.js");
|
const { createFnAgent } = await import("../pi.js");
|
||||||
@@ -1387,6 +1395,15 @@ describe("createFnAgent", () => {
|
|||||||
apiKey: "RESPONSES_API_KEY",
|
apiKey: "RESPONSES_API_KEY",
|
||||||
models: [expect.objectContaining({ id: "responses-model", name: "Responses Model" })],
|
models: [expect.objectContaining({ id: "responses-model", name: "Responses Model" })],
|
||||||
}));
|
}));
|
||||||
|
// anthropic-compatible must map to pi-ai's registered "anthropic-messages"
|
||||||
|
// api key, NOT bare "anthropic" (which throws "No API provider registered
|
||||||
|
// for api: anthropic" at stream time). Regression guard for that bug.
|
||||||
|
expect(registerProviderMock).toHaveBeenCalledWith("custom-anthropic", expect.objectContaining({
|
||||||
|
baseUrl: "https://anthropic.example",
|
||||||
|
api: "anthropic-messages",
|
||||||
|
apiKey: "ANTHROPIC_API_KEY",
|
||||||
|
models: [expect.objectContaining({ id: "anthropic-model", name: "Anthropic Model" })],
|
||||||
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
it("avoids lock-based SettingsManager.create when loading extension providers", async () => {
|
it("avoids lock-based SettingsManager.create when loading extension providers", async () => {
|
||||||
|
|||||||
@@ -1006,9 +1006,13 @@ export interface AgentOptions {
|
|||||||
permanentAgentGating?: PermanentAgentGatingContext;
|
permanentAgentGating?: PermanentAgentGatingContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
function resolveCustomProviderApiType(apiType: string): "anthropic" | "openai-responses" | "openai-completions" {
|
function resolveCustomProviderApiType(apiType: string): "anthropic-messages" | "openai-responses" | "openai-completions" {
|
||||||
if (apiType === "anthropic-compatible") {
|
if (apiType === "anthropic-compatible") {
|
||||||
return "anthropic";
|
// pi-ai registers the Anthropic Messages API under the key
|
||||||
|
// "anthropic-messages" (see @earendil-works/pi-ai register-builtins).
|
||||||
|
// Returning bare "anthropic" throws "No API provider registered for
|
||||||
|
// api: anthropic" at stream time, so map to the real registry key.
|
||||||
|
return "anthropic-messages";
|
||||||
}
|
}
|
||||||
if (apiType === "openai-responses") {
|
if (apiType === "openai-responses") {
|
||||||
return "openai-responses";
|
return "openai-responses";
|
||||||
|
|||||||
Reference in New Issue
Block a user