package aiprovider import "strings" // Mode values stored on ai_providers.mode (UI / API). const ( ModeInternal = "internal" ModePopular = "popular" ModeCustom = "custom" ) // ModeInternalLabel is the analytics / job recording value when using platform OpenAI (admin settings or env fallback). const ModeInternalLabel = "internal" // ModeCustomLabel is the analytics value for custom OpenAI-compatible endpoints. const ModeCustomLabel = "custom" // PopularProvider is a curated OpenAI-compatible catalog entry. type PopularProvider struct { Name string `json:"name"` Label string `json:"label"` BaseURL string `json:"base_url"` DefaultModel string `json:"default_model"` Models []string `json:"models"` } // PopularCatalog lists OpenAI-compatible providers tenants can pick by API key only. var PopularCatalog = []PopularProvider{ { Name: "openai", Label: "OpenAI", BaseURL: "https://api.openai.com/v1", DefaultModel: "gpt-4o-mini", Models: []string{"gpt-4o-mini", "gpt-4o", "gpt-4.1-mini", "gpt-4.1"}, }, { Name: "google", Label: "Google (Gemini OpenAI compat)", BaseURL: "https://generativelanguage.googleapis.com/v1beta/openai", DefaultModel: "gemini-2.0-flash", Models: []string{"gemini-2.0-flash", "gemini-2.5-flash", "gemini-2.0-flash-lite"}, }, { Name: "groq", Label: "Groq", BaseURL: "https://api.groq.com/openai/v1", DefaultModel: "llama-3.3-70b-versatile", Models: []string{"llama-3.3-70b-versatile", "llama-3.1-8b-instant", "mixtral-8x7b-32768"}, }, { Name: "mistral", Label: "Mistral", BaseURL: "https://api.mistral.ai/v1", DefaultModel: "mistral-small-latest", Models: []string{"mistral-small-latest", "mistral-medium-latest", "mistral-large-latest"}, }, { Name: "deepseek", Label: "DeepSeek", BaseURL: "https://api.deepseek.com/v1", DefaultModel: "deepseek-chat", Models: []string{"deepseek-chat", "deepseek-reasoner"}, }, { Name: "openrouter", Label: "OpenRouter", BaseURL: "https://openrouter.ai/api/v1", DefaultModel: "openai/gpt-4o-mini", Models: []string{"openai/gpt-4o-mini", "anthropic/claude-sonnet-4", "google/gemini-2.0-flash-001"}, }, } func FindPopular(name string) (PopularProvider, bool) { name = strings.ToLower(strings.TrimSpace(name)) for _, p := range PopularCatalog { if p.Name == name { return p, true } } return PopularProvider{}, false } // AnalyticsMode returns the recorded provider mode for jobs/products. // Contract for analytics: "internal" | "popular:" | "custom" func AnalyticsMode(mode, popularName string) string { switch strings.ToLower(strings.TrimSpace(mode)) { case ModePopular: name := strings.ToLower(strings.TrimSpace(popularName)) if name == "" { name = "unknown" } return "popular:" + name case ModeCustom: return ModeCustomLabel default: return ModeInternalLabel } } // AnalyticsClass maps a stored ai_provider_mode value to a rollup class. // Returns: internal | popular | custom | unknown func AnalyticsClass(modeLabel string) string { m := strings.ToLower(strings.TrimSpace(modeLabel)) switch { case m == "" || m == "unknown": return "unknown" case m == ModeInternalLabel || m == ModeInternal: return ModeInternal case m == ModeCustomLabel || m == ModeCustom: return ModeCustom case strings.HasPrefix(m, "popular:"): return ModePopular default: return "unknown" } } // NormalizeAnalyticsMode coerces free-form labels into the analytics contract. func NormalizeAnalyticsMode(modeLabel string) string { m := strings.ToLower(strings.TrimSpace(modeLabel)) switch { case m == "" || m == "unknown": return "unknown" case m == ModeInternalLabel || m == ModeInternal: return ModeInternalLabel case m == ModeCustomLabel || m == ModeCustom: return ModeCustomLabel case strings.HasPrefix(m, "popular:"): name := strings.TrimSpace(strings.TrimPrefix(m, "popular:")) if name == "" { name = "unknown" } return "popular:" + name default: return "unknown" } } func normalizeMode(mode string) string { switch strings.ToLower(strings.TrimSpace(mode)) { case ModePopular: return ModePopular case ModeCustom: return ModeCustom default: return ModeInternal } }