feat: Add Anthropic Compatible provider support

- Added support for 'anthropic-compatible' provider nodes in backend.
- Implemented isAnthropicCompatible logic in open-sse for /messages URL construction and headers.
- Added UI for creating and managing Anthropic Compatible providers in the dashboard.
- Updated validation logic for Anthropic-compatible endpoints.
- Sanitize base URL input (strip trailing /messages) to prevent 404s and improve UX.
- Improve validation: use GET /models (2xx success), and support x-api-key / Authorization Bearer hybrid proxies.
- Enable model import via /models for Anthropic Compatible providers.
- Ensure Authorization is omitted when x-api-key is present to avoid strict proxy conflicts.
- Resolve Anthropic-compatible credentials by prefix during model resolution (e.g., acx/model).
- Update default executor to match provider header/url behavior for Anthropic-compatible providers.
This commit is contained in:
ramhaidar
2026-02-03 15:11:41 +07:00
parent 7881db81ec
commit da5bdef4cb
13 changed files with 614 additions and 154 deletions
+22 -2
View File
@@ -1,7 +1,7 @@
import { NextResponse } from "next/server";
import { getProviderConnections, createProviderConnection, getProviderNodeById, isCloudEnabled } from "@/models";
import { APIKEY_PROVIDERS } from "@/shared/constants/config";
import { isOpenAICompatibleProvider } from "@/shared/constants/providers";
import { isOpenAICompatibleProvider, isAnthropicCompatibleProvider } from "@/shared/constants/providers";
import { getConsistentMachineId } from "@/shared/utils/machineId";
import { syncToCloud } from "@/app/api/sync/cloud/route";
@@ -33,7 +33,11 @@ export async function POST(request) {
const { provider, apiKey, name, priority, globalPriority, defaultModel, testStatus } = body;
// Validation
if (!provider || (!APIKEY_PROVIDERS[provider] && !isOpenAICompatibleProvider(provider))) {
const isValidProvider = APIKEY_PROVIDERS[provider] ||
isOpenAICompatibleProvider(provider) ||
isAnthropicCompatibleProvider(provider);
if (!provider || !isValidProvider) {
return NextResponse.json({ error: "Invalid provider" }, { status: 400 });
}
if (!apiKey) {
@@ -62,6 +66,22 @@ export async function POST(request) {
baseUrl: node.baseUrl,
nodeName: node.name,
};
} else if (isAnthropicCompatibleProvider(provider)) {
const node = await getProviderNodeById(provider);
if (!node) {
return NextResponse.json({ error: "Anthropic Compatible node not found" }, { status: 404 });
}
const existingConnections = await getProviderConnections({ provider });
if (existingConnections.length > 0) {
return NextResponse.json({ error: "Only one connection is allowed for this Anthropic Compatible node" }, { status: 400 });
}
providerSpecificData = {
prefix: node.prefix,
baseUrl: node.baseUrl,
nodeName: node.name,
};
}
const newConnection = await createProviderConnection({