mirror of
https://github.com/andwati/dokploy.git
synced 2026-10-21 04:24:30 +00:00
The Ollama detection matched any URL containing "ollama", which hides the API Key field for Ollama Cloud (ollama.com) and drops the key from the createOllama() client, so cloud requests go out unauthenticated. Narrow the rule to localhost-only Ollama and forward the API key as a Bearer header when provided. Co-authored-by: Claude Opus 4.7 (1M context) <[email protected]>
162 lines
4.1 KiB
TypeScript
162 lines
4.1 KiB
TypeScript
import { createAnthropic } from "@ai-sdk/anthropic";
|
|
import { createAzure } from "@ai-sdk/azure";
|
|
import { createCohere } from "@ai-sdk/cohere";
|
|
import { createDeepInfra } from "@ai-sdk/deepinfra";
|
|
import { createMistral } from "@ai-sdk/mistral";
|
|
import { createOpenAI } from "@ai-sdk/openai";
|
|
import { createOpenAICompatible } from "@ai-sdk/openai-compatible";
|
|
import { createOllama } from "ai-sdk-ollama";
|
|
|
|
export function getProviderName(apiUrl: string) {
|
|
if (apiUrl.includes("api.openai.com")) return "openai";
|
|
if (apiUrl.includes("azure.com")) return "azure";
|
|
if (apiUrl.includes("api.anthropic.com")) return "anthropic";
|
|
if (apiUrl.includes("api.cohere.ai")) return "cohere";
|
|
if (apiUrl.includes("api.perplexity.ai")) return "perplexity";
|
|
if (apiUrl.includes("api.mistral.ai")) return "mistral";
|
|
if (apiUrl.includes(":11434") || apiUrl.includes("ollama")) return "ollama";
|
|
if (apiUrl.includes("api.deepinfra.com")) return "deepinfra";
|
|
if (apiUrl.includes("generativelanguage.googleapis.com")) return "gemini";
|
|
if (apiUrl.includes("openrouter.ai")) return "openrouter";
|
|
if (apiUrl.includes("api.z.ai")) return "zai";
|
|
if (apiUrl.includes("api.minimax.io")) return "minimax";
|
|
return "custom";
|
|
}
|
|
|
|
export function selectAIProvider(config: { apiUrl: string; apiKey: string }) {
|
|
const providerName = getProviderName(config.apiUrl);
|
|
|
|
switch (providerName) {
|
|
case "openai":
|
|
return createOpenAI({
|
|
apiKey: config.apiKey,
|
|
baseURL: config.apiUrl,
|
|
});
|
|
case "azure":
|
|
// Azure OpenAI-compatible endpoints already include /v1 in the path.
|
|
// Using createAzure with such URLs causes a doubled /v1//v1/ suffix.
|
|
if (config.apiUrl.includes("/v1")) {
|
|
return createOpenAICompatible({
|
|
name: "azure",
|
|
baseURL: config.apiUrl,
|
|
headers: {
|
|
"api-key": config.apiKey,
|
|
Authorization: `Bearer ${config.apiKey}`,
|
|
},
|
|
});
|
|
}
|
|
return createAzure({
|
|
apiKey: config.apiKey,
|
|
baseURL: config.apiUrl,
|
|
});
|
|
case "anthropic":
|
|
return createAnthropic({
|
|
apiKey: config.apiKey,
|
|
baseURL: config.apiUrl,
|
|
});
|
|
case "cohere":
|
|
return createCohere({
|
|
baseURL: config.apiUrl,
|
|
apiKey: config.apiKey,
|
|
});
|
|
case "perplexity":
|
|
return createOpenAICompatible({
|
|
name: "perplexity",
|
|
baseURL: config.apiUrl,
|
|
headers: {
|
|
Authorization: `Bearer ${config.apiKey}`,
|
|
},
|
|
});
|
|
case "mistral":
|
|
return createMistral({
|
|
baseURL: config.apiUrl,
|
|
apiKey: config.apiKey,
|
|
});
|
|
case "ollama":
|
|
return createOllama({
|
|
baseURL: config.apiUrl,
|
|
headers: config.apiKey
|
|
? { Authorization: `Bearer ${config.apiKey}` }
|
|
: undefined,
|
|
});
|
|
case "deepinfra":
|
|
return createDeepInfra({
|
|
baseURL: config.apiUrl,
|
|
apiKey: config.apiKey,
|
|
});
|
|
case "gemini":
|
|
return createOpenAICompatible({
|
|
name: "gemini",
|
|
baseURL: config.apiUrl,
|
|
headers: {
|
|
Authorization: `Bearer ${config.apiKey}`,
|
|
},
|
|
});
|
|
case "openrouter":
|
|
return createOpenAICompatible({
|
|
name: "openrouter",
|
|
baseURL: config.apiUrl,
|
|
headers: {
|
|
Authorization: `Bearer ${config.apiKey}`,
|
|
},
|
|
});
|
|
case "zai":
|
|
return createOpenAICompatible({
|
|
name: "zai",
|
|
baseURL: config.apiUrl,
|
|
headers: {
|
|
Authorization: `Bearer ${config.apiKey}`,
|
|
},
|
|
});
|
|
case "minimax":
|
|
return createOpenAICompatible({
|
|
name: "minimax",
|
|
baseURL: config.apiUrl,
|
|
headers: {
|
|
Authorization: `Bearer ${config.apiKey}`,
|
|
},
|
|
});
|
|
case "custom":
|
|
return createOpenAICompatible({
|
|
name: "custom",
|
|
baseURL: config.apiUrl,
|
|
headers: {
|
|
Authorization: `Bearer ${config.apiKey}`,
|
|
},
|
|
});
|
|
default:
|
|
throw new Error(`Unsupported AI provider: ${providerName}`);
|
|
}
|
|
}
|
|
|
|
export const getProviderHeaders = (
|
|
apiUrl: string,
|
|
apiKey: string,
|
|
): Record<string, string> => {
|
|
// Anthropic
|
|
if (apiUrl.includes("anthropic")) {
|
|
return {
|
|
"x-api-key": apiKey,
|
|
"anthropic-version": "2023-06-01",
|
|
};
|
|
}
|
|
|
|
// Mistral
|
|
if (apiUrl.includes("mistral")) {
|
|
return {
|
|
Authorization: `Bearer ${apiKey}`,
|
|
};
|
|
}
|
|
|
|
// Default (OpenAI style)
|
|
return {
|
|
Authorization: `Bearer ${apiKey}`,
|
|
};
|
|
};
|
|
export interface Model {
|
|
id: string;
|
|
object: string;
|
|
created: number;
|
|
owned_by: string;
|
|
}
|