diff --git a/internal/app/app.go b/internal/app/app.go index 4c8ea3d..236c990 100644 --- a/internal/app/app.go +++ b/internal/app/app.go @@ -31,6 +31,7 @@ func New(cfg *config.Config) (*App, error) { if err := store.SetUIDKey(uidKey); err != nil { return nil, err } + cfg.AI = managerconfig.ResolveAIConfig(context.Background(), cfg.Manager, cfg.AI) st, err := store.Open(cfg.MySQL) if err != nil { return nil, err diff --git a/internal/managerconfig/client.go b/internal/managerconfig/client.go index 331e063..284914b 100644 --- a/internal/managerconfig/client.go +++ b/internal/managerconfig/client.go @@ -34,6 +34,36 @@ func ResolveUIDKey(ctx context.Context, cfg config.ManagerConfig, fallback strin return key } +func ResolveAIConfig(ctx context.Context, manager config.ManagerConfig, fallback config.AIConfig) config.AIConfig { + values, err := New(manager).ThirdPartyConfig(ctx, "im_ai", + "provider", + "deepseek_base_url", + "deepseek_api_key", + "deepseek_default_model", + "deepseek_complex_model", + ) + if err != nil || len(values) == 0 { + return fallback + } + resolved := fallback + if value := strings.TrimSpace(values["provider"]); value != "" { + resolved.Provider = value + } + if value := strings.TrimSpace(values["deepseek_base_url"]); value != "" { + resolved.DeepSeek.BaseURL = value + } + if value := strings.TrimSpace(values["deepseek_api_key"]); value != "" { + resolved.DeepSeek.APIKey = value + } + if value := strings.TrimSpace(values["deepseek_default_model"]); value != "" { + resolved.DefaultModel = value + } + if value := strings.TrimSpace(values["deepseek_complex_model"]); value != "" { + resolved.ComplexModel = value + } + return resolved +} + func New(cfg config.ManagerConfig) *Client { timeout := time.Duration(cfg.TimeoutMS) * time.Millisecond if timeout <= 0 { @@ -92,6 +122,41 @@ func (c *Client) UIDKey(ctx context.Context) (string, error) { return "", fmt.Errorf("uid key is empty") } +func (c *Client) ThirdPartyConfig(ctx context.Context, group string, keys ...string) (map[string]string, error) { + baseURL := strings.TrimRight(strings.TrimSpace(c.cfg.BaseURL), "/") + if baseURL == "" { + return nil, fmt.Errorf("manager base url is empty") + } + if strings.TrimSpace(c.cfg.Secret) == "" { + return nil, fmt.Errorf("manager secret is empty") + } + values := url.Values{} + if keys := cleanList(keys); len(keys) > 0 { + values.Set("keys", strings.Join(keys, ",")) + } + path := "/internal/im-config/third-party/" + url.PathEscape(strings.TrimSpace(group)) + if encoded := values.Encode(); encoded != "" { + path += "?" + encoded + } + req, err := c.newRequest(ctx, http.MethodGet, baseURL+path, nil) + if err != nil { + return nil, err + } + resp, err := c.http.Do(req) + if err != nil { + return nil, err + } + defer resp.Body.Close() + if resp.StatusCode < 200 || resp.StatusCode >= 300 { + return nil, fmt.Errorf("manager status %d", resp.StatusCode) + } + body, err := io.ReadAll(io.LimitReader(resp.Body, maxResponseBody)) + if err != nil { + return nil, err + } + return parseValues(body) +} + func (c *Client) newRequest(ctx context.Context, method string, rawURL string, body []byte) (*http.Request, error) { req, err := http.NewRequestWithContext(ctx, method, rawURL, bytes.NewReader(body)) if err != nil {