Drop one-shot tmp/axe scripts and agent i18n scratch so the Gitea tree is deployable.
149 lines
4.3 KiB
Go
149 lines
4.3 KiB
Go
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:<name>" | "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
|
|
}
|
|
}
|