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) }