diff --git a/internal/service/ai.go b/internal/service/ai.go index db3b0d7..3ca88d2 100644 --- a/internal/service/ai.go +++ b/internal/service/ai.go @@ -4,10 +4,10 @@ // (OpenAI, DeepSeek, Qwen, Ollama, …). Today we expose two operations: // // - SmartSearch: interpret a free-form Chinese / English query and -// return a normalised JSON intent the React UI can -// translate into filter params. +// return a normalised JSON intent the React UI can +// translate into filter params. // - Recommend: given a list of recently-watched titles, generate -// a short list of "you might like…" recommendations. +// a short list of "you might like…" recommendations. // // The service is disabled (every method returns nil) when ai.enabled is // false or ai.api_key is empty. @@ -74,58 +74,6 @@ func (a *AIService) Status(ctx context.Context) AIStatus { return AIStatus{Enabled: cfg.Enabled, Provider: cfg.Provider, Model: cfg.Model} } -type aiRuntimeConfig struct { - Enabled bool - Provider string - APIBase string - APIKey string - Model string -} - -func (a *AIService) resolveRuntimeConfig(ctx context.Context) aiRuntimeConfig { - out := aiRuntimeConfig{ - Enabled: a.cfg.AI.Enabled && strings.TrimSpace(a.cfg.AI.APIKey) != "", - Provider: strings.TrimSpace(a.cfg.AI.Provider), - APIBase: strings.TrimSpace(a.cfg.AI.APIBase), - APIKey: strings.TrimSpace(a.cfg.AI.APIKey), - Model: strings.TrimSpace(a.cfg.AI.Model), - } - if out.Provider == "" { - out.Provider = "openai" - } - if out.APIBase == "" { - out.APIBase = "https://api.openai.com/v1" - } - if out.Model == "" { - out.Model = "gpt-4o-mini" - } - - if a.apiConfig != nil { - resolved, err := a.apiConfig.Resolve(ctx, "openai") - if err != nil { - if a.log != nil { - a.log.Warn("ai: failed to resolve openai api config", zap.Error(err)) - } - return out - } - if resolved.BaseURL != "" { - out.APIBase = strings.TrimSpace(resolved.BaseURL) - } - if resolved.APIKey != "" { - out.APIKey = strings.TrimSpace(resolved.APIKey) - } - if resolved.Enabled && out.APIKey != "" { - out.Enabled = true - out.Provider = "openai" - return out - } - if !resolved.Enabled && (resolved.APIKey != "" || resolved.BaseURL != "" || resolved.Extra != "") { - out.Enabled = false - } - } - return out -} - // SearchIntent is the structured output the smart search endpoint returns. type SearchIntent struct { Query string `json:"query"` diff --git a/internal/service/ai_runtime_config.go b/internal/service/ai_runtime_config.go new file mode 100644 index 0000000..6fb5418 --- /dev/null +++ b/internal/service/ai_runtime_config.go @@ -0,0 +1,60 @@ +package service + +import ( + "context" + "strings" + + "go.uber.org/zap" +) + +type aiRuntimeConfig struct { + Enabled bool + Provider string + APIBase string + APIKey string + Model string +} + +func (a *AIService) resolveRuntimeConfig(ctx context.Context) aiRuntimeConfig { + out := aiRuntimeConfig{ + Enabled: a.cfg.AI.Enabled && strings.TrimSpace(a.cfg.AI.APIKey) != "", + Provider: strings.TrimSpace(a.cfg.AI.Provider), + APIBase: strings.TrimSpace(a.cfg.AI.APIBase), + APIKey: strings.TrimSpace(a.cfg.AI.APIKey), + Model: strings.TrimSpace(a.cfg.AI.Model), + } + if out.Provider == "" { + out.Provider = "openai" + } + if out.APIBase == "" { + out.APIBase = "https://api.openai.com/v1" + } + if out.Model == "" { + out.Model = "gpt-4o-mini" + } + + if a.apiConfig != nil { + resolved, err := a.apiConfig.Resolve(ctx, "openai") + if err != nil { + if a.log != nil { + a.log.Warn("ai: failed to resolve openai api config", zap.Error(err)) + } + return out + } + if resolved.BaseURL != "" { + out.APIBase = strings.TrimSpace(resolved.BaseURL) + } + if resolved.APIKey != "" { + out.APIKey = strings.TrimSpace(resolved.APIKey) + } + if resolved.Enabled && out.APIKey != "" { + out.Enabled = true + out.Provider = "openai" + return out + } + if !resolved.Enabled && (resolved.APIKey != "" || resolved.BaseURL != "" || resolved.Extra != "") { + out.Enabled = false + } + } + return out +}