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:
1 parent
7881db81ec
commit
da5bdef4cb
13 files changed
+614
-154
No files matched your search
@@ -21,7 +21,8 @@ export async function PUT(request, { params }) {
|
||||
return NextResponse.json({ error: "Prefix is required" }, { status: 400 });
|
||||
}
|
||||
|
||||
if (!apiType || !["chat", "responses"].includes(apiType)) {
|
||||
// Only validate apiType for OpenAI Compatible nodes
|
||||
if (node.type === "openai-compatible" && (!apiType || !["chat", "responses"].includes(apiType))) {
|
||||
return NextResponse.json({ error: "Invalid OpenAI compatible API type" }, { status: 400 });
|
||||
}
|
||||
|
||||
@@ -29,12 +30,27 @@ export async function PUT(request, { params }) {
|
||||
return NextResponse.json({ error: "Base URL is required" }, { status: 400 });
|
||||
}
|
||||
|
||||
const updated = await updateProviderNode(id, {
|
||||
let sanitizedBaseUrl = baseUrl.trim();
|
||||
|
||||
// Sanitize Base URL for Anthropic Compatible
|
||||
if (node.type === "anthropic-compatible") {
|
||||
sanitizedBaseUrl = sanitizedBaseUrl.replace(/\/$/, "");
|
||||
if (sanitizedBaseUrl.endsWith("/messages")) {
|
||||
sanitizedBaseUrl = sanitizedBaseUrl.slice(0, -9); // remove /messages
|
||||
}
|
||||
}
|
||||
|
||||
const updates = {
|
||||
name: name.trim(),
|
||||
prefix: prefix.trim(),
|
||||
apiType,
|
||||
baseUrl: baseUrl.trim(),
|
||||
});
|
||||
baseUrl: sanitizedBaseUrl,
|
||||
};
|
||||
|
||||
if (node.type === "openai-compatible") {
|
||||
updates.apiType = apiType;
|
||||
}
|
||||
|
||||
const updated = await updateProviderNode(id, updates);
|
||||
|
||||
const connections = await getProviderConnections({ provider: id });
|
||||
await Promise.all(connections.map((connection) => (
|
||||
@@ -42,8 +58,8 @@ export async function PUT(request, { params }) {
|
||||
providerSpecificData: {
|
||||
...(connection.providerSpecificData || {}),
|
||||
prefix: prefix.trim(),
|
||||
apiType,
|
||||
baseUrl: baseUrl.trim(),
|
||||
apiType: node.type === "openai-compatible" ? apiType : undefined,
|
||||
baseUrl: sanitizedBaseUrl,
|
||||
nodeName: updated.name,
|
||||
}
|
||||
})
|
||||
|
||||
Reference in new issue
Block a user