Some checks are pending
E2E / Playwright e2e (push) Waiting to run
- Introduced new components for managing admin settings, including AdminListControls, AdminSettingsCard, and TechBrandSelectLabel. - Implemented dynamic loading for admin settings sections to optimize performance. - Enhanced the layout of various admin settings sections for better user experience. - Updated the AiAssistantSection to include LLM provider management and improved model selection. - Refactored authentication settings to streamline configuration and improve accessibility.
234 lines
8.2 KiB
TypeScript
234 lines
8.2 KiB
TypeScript
import type { ApiLLMProvider, ApiLLMProviderType } from "@/lib/contacts/discovery-types"
|
|
import { techBrandIcon } from "@/lib/admin-settings/tech-brand-icons"
|
|
|
|
export type { ApiLLMProviderType }
|
|
|
|
export type LLMProviderCatalogEntry = {
|
|
type: ApiLLMProviderType
|
|
label: string
|
|
description: string
|
|
icon?: string
|
|
docsUrl?: string
|
|
defaultBaseURL: string
|
|
defaultModel: string
|
|
baseURLPlaceholder?: string
|
|
}
|
|
|
|
export const LLM_PROVIDER_CATALOG: LLMProviderCatalogEntry[] = [
|
|
{
|
|
type: "openai",
|
|
label: "OpenAI",
|
|
description: "API officielle OpenAI (GPT, o-series).",
|
|
icon: techBrandIcon("openai"),
|
|
docsUrl: "https://platform.openai.com/docs/api-reference",
|
|
defaultBaseURL: "https://api.openai.com/v1",
|
|
defaultModel: "gpt-4o-mini",
|
|
},
|
|
{
|
|
type: "anthropic",
|
|
label: "Anthropic",
|
|
description: "Claude via la couche OpenAI-compatible d'Anthropic.",
|
|
icon: techBrandIcon("anthropic"),
|
|
docsUrl: "https://platform.claude.com/docs/en/api/openai-sdk",
|
|
defaultBaseURL: "https://api.anthropic.com/v1",
|
|
defaultModel: "claude-sonnet-4-6",
|
|
},
|
|
{
|
|
type: "mistral",
|
|
label: "Mistral AI",
|
|
description: "API Mistral (OpenAI-compatible).",
|
|
icon: techBrandIcon("mistral"),
|
|
docsUrl: "https://docs.mistral.ai/api/",
|
|
defaultBaseURL: "https://api.mistral.ai/v1",
|
|
defaultModel: "mistral-small-latest",
|
|
},
|
|
{
|
|
type: "azure_openai",
|
|
label: "Azure OpenAI",
|
|
description: "Modèles OpenAI déployés sur Azure AI Foundry.",
|
|
icon: techBrandIcon("azure_openai"),
|
|
docsUrl: "https://learn.microsoft.com/azure/ai-foundry/openai/reference",
|
|
defaultBaseURL: "https://VOTRE_RESSOURCE.openai.azure.com/openai/v1",
|
|
defaultModel: "gpt-4o-mini",
|
|
baseURLPlaceholder: "https://ma-ressource.openai.azure.com/openai/v1",
|
|
},
|
|
{
|
|
type: "azure_ai_anthropic",
|
|
label: "Anthropic via Azure",
|
|
description:
|
|
"Claude sur Microsoft Foundry. Remplacez VOTRE_RESSOURCE ; vérifiez la compatibilité OpenAI de votre déploiement.",
|
|
icon: techBrandIcon("azure_ai_anthropic"),
|
|
docsUrl:
|
|
"https://learn.microsoft.com/azure/ai-foundry/foundry-models/how-to/use-foundry-models-claude",
|
|
defaultBaseURL: "https://VOTRE_RESSOURCE.services.ai.azure.com/anthropic/v1",
|
|
defaultModel: "claude-sonnet-4-6",
|
|
baseURLPlaceholder:
|
|
"https://ma-ressource.services.ai.azure.com/anthropic/v1",
|
|
},
|
|
{
|
|
type: "aws_bedrock",
|
|
label: "Anthropic via AWS Bedrock",
|
|
description: "Claude et autres modèles via Bedrock (OpenAI-compatible).",
|
|
icon: techBrandIcon("aws_bedrock"),
|
|
docsUrl: "https://docs.aws.amazon.com/bedrock/latest/userguide/inference-chat-completions-mantle.html",
|
|
defaultBaseURL: "https://bedrock-runtime.us-east-1.amazonaws.com/openai/v1",
|
|
defaultModel: "us.anthropic.claude-sonnet-4-6",
|
|
baseURLPlaceholder: "https://bedrock-runtime.eu-west-1.amazonaws.com/openai/v1",
|
|
},
|
|
{
|
|
type: "google_gemini",
|
|
label: "Google Gemini",
|
|
description: "API Gemini en mode OpenAI-compatible.",
|
|
icon: techBrandIcon("google_gemini"),
|
|
docsUrl: "https://ai.google.dev/gemini-api/docs/openai",
|
|
defaultBaseURL: "https://generativelanguage.googleapis.com/v1beta/openai/",
|
|
defaultModel: "gemini-2.0-flash",
|
|
},
|
|
{
|
|
type: "groq",
|
|
label: "Groq",
|
|
description: "Inférence rapide (Llama, Mixtral, etc.).",
|
|
icon: techBrandIcon("groq"),
|
|
docsUrl: "https://console.groq.com/docs/openai",
|
|
defaultBaseURL: "https://api.groq.com/openai/v1",
|
|
defaultModel: "llama-3.3-70b-versatile",
|
|
},
|
|
{
|
|
type: "deepseek",
|
|
label: "DeepSeek",
|
|
description: "Modèles DeepSeek (OpenAI-compatible).",
|
|
icon: techBrandIcon("deepseek"),
|
|
docsUrl: "https://api-docs.deepseek.com/",
|
|
defaultBaseURL: "https://api.deepseek.com/v1",
|
|
defaultModel: "deepseek-chat",
|
|
},
|
|
{
|
|
type: "openrouter",
|
|
label: "OpenRouter",
|
|
description: "Passerelle multi-fournisseurs (Claude, GPT, Llama…).",
|
|
icon: techBrandIcon("openrouter"),
|
|
docsUrl: "https://openrouter.ai/docs",
|
|
defaultBaseURL: "https://openrouter.ai/api/v1",
|
|
defaultModel: "anthropic/claude-sonnet-4",
|
|
},
|
|
{
|
|
type: "together",
|
|
label: "Together AI",
|
|
description: "Modèles open-source hébergés.",
|
|
icon: techBrandIcon("together"),
|
|
docsUrl: "https://docs.together.ai/docs/openai-api",
|
|
defaultBaseURL: "https://api.together.xyz/v1",
|
|
defaultModel: "meta-llama/Llama-3.3-70B-Instruct-Turbo",
|
|
},
|
|
{
|
|
type: "fireworks",
|
|
label: "Fireworks AI",
|
|
description: "Inférence serverless pour modèles open-source.",
|
|
icon: techBrandIcon("fireworks"),
|
|
docsUrl: "https://docs.fireworks.ai/tools-sdks/openai-compatibility",
|
|
defaultBaseURL: "https://api.fireworks.ai/inference/v1",
|
|
defaultModel: "accounts/fireworks/models/llama-v3p3-70b-instruct",
|
|
},
|
|
{
|
|
type: "xai",
|
|
label: "xAI (Grok)",
|
|
description: "API Grok (OpenAI-compatible).",
|
|
icon: techBrandIcon("xai"),
|
|
docsUrl: "https://docs.x.ai/docs/guides/chat-completions",
|
|
defaultBaseURL: "https://api.x.ai/v1",
|
|
defaultModel: "grok-2-latest",
|
|
},
|
|
{
|
|
type: "ollama",
|
|
label: "Ollama (local)",
|
|
description: "Serveur Ollama local avec API OpenAI-compatible.",
|
|
icon: techBrandIcon("ollama"),
|
|
docsUrl: "https://github.com/ollama/ollama/blob/main/docs/openai.md",
|
|
defaultBaseURL: "http://localhost:11434/v1",
|
|
defaultModel: "llama3.2",
|
|
},
|
|
{
|
|
type: "ollama_cloud",
|
|
label: "Ollama Cloud",
|
|
description: "Modèles hébergés sur ollama.com (clé API requise).",
|
|
icon: techBrandIcon("ollama"),
|
|
docsUrl: "https://docs.ollama.com/cloud",
|
|
defaultBaseURL: "https://ollama.com/v1",
|
|
defaultModel: "gpt-oss:120b",
|
|
},
|
|
{
|
|
type: "custom",
|
|
label: "Endpoint personnalisé",
|
|
description: "Toute API compatible OpenAI (/v1/chat/completions).",
|
|
icon: techBrandIcon("custom"),
|
|
defaultBaseURL: "",
|
|
defaultModel: "",
|
|
baseURLPlaceholder: "https://api.example.com/v1",
|
|
},
|
|
]
|
|
|
|
export function llmCatalogEntry(type: ApiLLMProviderType): LLMProviderCatalogEntry {
|
|
return LLM_PROVIDER_CATALOG.find((entry) => entry.type === type) ?? LLM_PROVIDER_CATALOG.at(-1)!
|
|
}
|
|
|
|
export function emptyLlmProvider(type: ApiLLMProviderType = "openai"): ApiLLMProvider {
|
|
const entry = llmCatalogEntry(type)
|
|
return {
|
|
id: crypto.randomUUID(),
|
|
type,
|
|
name: entry.label,
|
|
base_url: entry.defaultBaseURL,
|
|
api_key: "",
|
|
default_model: entry.defaultModel,
|
|
}
|
|
}
|
|
|
|
export function inferLlmProviderType(provider: ApiLLMProvider): ApiLLMProviderType {
|
|
if (provider.type) return provider.type
|
|
const url = provider.base_url?.toLowerCase() ?? ""
|
|
if (url.includes("api.openai.com")) return "openai"
|
|
if (url.includes("api.anthropic.com")) return "anthropic"
|
|
if (url.includes("api.mistral.ai")) return "mistral"
|
|
if (url.includes("openai.azure.com")) return "azure_openai"
|
|
if (url.includes("services.ai.azure.com/anthropic")) return "azure_ai_anthropic"
|
|
if (url.includes("bedrock")) return "aws_bedrock"
|
|
if (url.includes("ollama.com")) return "ollama_cloud"
|
|
if (url.includes("localhost:11434") || url.includes("127.0.0.1:11434")) return "ollama"
|
|
if (url.includes("generativelanguage.googleapis.com")) return "google_gemini"
|
|
if (url.includes("api.groq.com")) return "groq"
|
|
if (url.includes("api.deepseek.com")) return "deepseek"
|
|
if (url.includes("openrouter.ai")) return "openrouter"
|
|
if (url.includes("api.together.xyz")) return "together"
|
|
if (url.includes("api.fireworks.ai")) return "fireworks"
|
|
if (url.includes("api.x.ai")) return "xai"
|
|
return "custom"
|
|
}
|
|
|
|
export function normalizeLlmProvider(provider: ApiLLMProvider): ApiLLMProvider {
|
|
const type = inferLlmProviderType(provider)
|
|
const entry = llmCatalogEntry(type)
|
|
return {
|
|
...provider,
|
|
type,
|
|
name: provider.name?.trim() || entry.label,
|
|
}
|
|
}
|
|
|
|
export function isLlmProviderConfigured(
|
|
provider: ApiLLMProvider,
|
|
options?: { apiKeyConfigured?: boolean },
|
|
): boolean {
|
|
const type = inferLlmProviderType(provider)
|
|
const entry = llmCatalogEntry(type)
|
|
if (!provider.base_url?.trim() && type !== "custom") {
|
|
return false
|
|
}
|
|
if (type === "ollama") {
|
|
return Boolean(provider.base_url?.trim())
|
|
}
|
|
if (type === "custom") {
|
|
return Boolean(provider.base_url?.trim())
|
|
}
|
|
return Boolean(provider.api_key?.trim() || options?.apiKeyConfigured)
|
|
}
|