diff --git a/server/cmd/check-providers/main.go b/server/cmd/check-providers/main.go new file mode 100644 index 0000000..01079ab --- /dev/null +++ b/server/cmd/check-providers/main.go @@ -0,0 +1,133 @@ +package main + +import ( + "context" + "encoding/json" + "fmt" + "log" + "os" + + "github.com/jackc/pgx/v5/pgxpool" + "github.com/joho/godotenv" +) + +func main() { + // 加载环境变量 + _ = godotenv.Load("../../.env") + + dbURL := os.Getenv("DATABASE_URL") + if dbURL == "" { + dbURL = "postgres://freedak:@localhost:5432/govai_portal?sslmode=disable" + } + + pool, err := pgxpool.New(context.Background(), dbURL) + if err != nil { + log.Fatalf("无法连接数据库: %v", err) + } + defer pool.Close() + + fmt.Println("=== 数据库中的模型提供商配置 ===") + + rows, err := pool.Query(context.Background(), ` + SELECT id, name, base_url, + SUBSTRING(api_key_encrypted, 1, 20) as api_key_prefix, + models, is_active, priority, config + FROM model_providers + ORDER BY priority DESC, created_at + `) + if err != nil { + log.Fatalf("查询失败: %v", err) + } + defer rows.Close() + + count := 0 + for rows.Next() { + var id, name, baseURL, apiKeyPrefix string + var modelsJSON, configJSON []byte + var isActive bool + var priority int + + err := rows.Scan(&id, &name, &baseURL, &apiKeyPrefix, &modelsJSON, &isActive, &priority, &configJSON) + if err != nil { + log.Printf("扫描行失败: %v", err) + continue + } + + count++ + fmt.Printf("【提供商 %d】\n", count) + fmt.Printf("ID: %s\n", id) + fmt.Printf("名称: %s\n", name) + fmt.Printf("Base URL: %s\n", baseURL) + fmt.Printf("API Key: %s...\n", apiKeyPrefix) + fmt.Printf("激活状态: %v\n", isActive) + fmt.Printf("优先级: %d\n", priority) + + // 解析 models + var models []map[string]interface{} + if err := json.Unmarshal(modelsJSON, &models); err == nil { + fmt.Printf("可用模型: ") + for i, m := range models { + if i > 0 { + fmt.Print(", ") + } + fmt.Printf("%v", m["id"]) + } + fmt.Println() + } + + // 解析 config + var config map[string]interface{} + if err := json.Unmarshal(configJSON, &config); err == nil { + fmt.Printf("Provider 类型: %v\n", config["provider"]) + fmt.Printf("默认模型: %v\n", config["default_model"]) + } + + fmt.Println() + } + + if count == 0 { + fmt.Println("❌ 数据库中没有任何模型提供商配置") + fmt.Println("提示: 运行种子数据脚本来初始化配置") + } else { + fmt.Printf("✅ 共找到 %d 个提供商配置\n", count) + } + + // 查询应用配置中的模型使用情况 + fmt.Println("\n=== 应用配置中的模型使用情况 ===") + + appRows, err := pool.Query(context.Background(), ` + SELECT name, + COALESCE(app_config->>'model', '未配置') as model, + status + FROM applications + WHERE status = 'approved' + ORDER BY created_at DESC + LIMIT 10 + `) + if err != nil { + log.Printf("查询应用失败: %v", err) + return + } + defer appRows.Close() + + appCount := 0 + modelStats := make(map[string]int) + + for appRows.Next() { + var name, model, status string + if err := appRows.Scan(&name, &model, &status); err != nil { + continue + } + appCount++ + modelStats[model]++ + fmt.Printf("应用: %-30s | 模型: %-15s | 状态: %s\n", name, model, status) + } + + if appCount > 0 { + fmt.Printf("\n✅ 共查询到 %d 个已批准的应用\n\n", appCount) + fmt.Println("模型使用统计:") + for model, count := range modelStats { + fmt.Printf(" - %-15s: %d 个应用\n", model, count) + } + } +} diff --git a/server/cmd/clear-app-models/main.go b/server/cmd/clear-app-models/main.go new file mode 100644 index 0000000..edce8fe --- /dev/null +++ b/server/cmd/clear-app-models/main.go @@ -0,0 +1,112 @@ +package main + +import ( + "context" + "fmt" + "log" + "os" + + "github.com/jackc/pgx/v5/pgxpool" + "github.com/joho/godotenv" +) + +func main() { + // 加载环境变量 + _ = godotenv.Load("../../.env") + + dbURL := os.Getenv("DATABASE_URL") + if dbURL == "" { + dbURL = "postgres://freedak:@localhost:5432/govai_portal?sslmode=disable" + } + + pool, err := pgxpool.New(context.Background(), dbURL) + if err != nil { + log.Fatalf("无法连接数据库: %v", err) + } + defer pool.Close() + + fmt.Println("=== 清空应用硬编码模型配置 ===") + fmt.Println("目标:让所有应用自动使用优先级最高的 Provider 的默认模型") + + // 1. 查看当前状态 + fmt.Println("【步骤1】查看当前应用模型配置...") + var totalApps, configuredApps int + err = pool.QueryRow(context.Background(), ` + SELECT + COUNT(*) as total, + COUNT(CASE WHEN app_config->>'model' IS NOT NULL AND app_config->>'model' != '' THEN 1 END) as configured + FROM applications + WHERE status = 'approved' + `).Scan(&totalApps, &configuredApps) + + if err != nil { + log.Fatalf("查询失败: %v", err) + } + + fmt.Printf(" - 已批准应用总数: %d\n", totalApps) + fmt.Printf(" - 配置了模型的应用: %d\n", configuredApps) + fmt.Printf(" - 未配置模型的应用: %d\n\n", totalApps-configuredApps) + + // 2. 清空所有应用的模型配置 + fmt.Println("【步骤2】清空所有应用的模型配置...") + result, err := pool.Exec(context.Background(), ` + UPDATE applications + SET app_config = app_config - 'model' + WHERE status = 'approved' + AND app_config ? 'model' + `) + + if err != nil { + log.Fatalf("更新失败: %v", err) + } + + rowsAffected := result.RowsAffected() + fmt.Printf("✅ 已清空 %d 个应用的模型配置\n\n", rowsAffected) + + // 3. 验证结果 + fmt.Println("【步骤3】验证结果...") + var remainingConfigured int + err = pool.QueryRow(context.Background(), ` + SELECT COUNT(*) + FROM applications + WHERE status = 'approved' + AND app_config->>'model' IS NOT NULL + AND app_config->>'model' != '' + `).Scan(&remainingConfigured) + + if err != nil { + log.Fatalf("验证查询失败: %v", err) + } + + if remainingConfigured == 0 { + fmt.Println("✅ 所有应用的模型配置已清空") + } else { + fmt.Printf("⚠️ 还有 %d 个应用仍有模型配置\n", remainingConfigured) + } + + // 4. 显示当前优先级最高的 Provider + fmt.Println("\n【步骤4】当前优先级最高的 Provider...") + var providerName, defaultModel string + var priority int + err = pool.QueryRow(context.Background(), ` + SELECT name, COALESCE(config->>'default_model', '未配置'), priority + FROM model_providers + WHERE is_active = true + ORDER BY priority DESC, created_at + LIMIT 1 + `).Scan(&providerName, &defaultModel, &priority) + + if err != nil { + log.Printf("查询 Provider 失败: %v", err) + } else { + fmt.Printf(" Provider: %s\n", providerName) + fmt.Printf(" 默认模型: %s\n", defaultModel) + fmt.Printf(" 优先级: %d\n", priority) + } + + fmt.Println("\n=== 配置清理完成 ===") + fmt.Println("\n说明:") + fmt.Println(" - 现在所有应用将自动使用优先级最高的 Provider 的默认模型") + fmt.Println(" - 当前会使用: " + providerName + " / " + defaultModel) + fmt.Println(" - 如需特殊应用使用指定模型,可在应用配置中单独设置") +} diff --git a/server/cmd/fix-providers/main.go b/server/cmd/fix-providers/main.go new file mode 100644 index 0000000..f7e4b97 --- /dev/null +++ b/server/cmd/fix-providers/main.go @@ -0,0 +1,113 @@ +package main + +import ( + "context" + "fmt" + "log" + "os" + + "github.com/jackc/pgx/v5/pgxpool" + "github.com/joho/godotenv" +) + +func main() { + // 加载环境变量 + _ = godotenv.Load("../../.env") + + dbURL := os.Getenv("DATABASE_URL") + if dbURL == "" { + dbURL = "postgres://freedak:@localhost:5432/govai_portal?sslmode=disable" + } + + pool, err := pgxpool.New(context.Background(), dbURL) + if err != nil { + log.Fatalf("无法连接数据库: %v", err) + } + defer pool.Close() + + fmt.Println("=== 开始修复数据库配置 ===") + + // 1. 修复本地模型配置 + fmt.Println("【任务1】修复本地模型配置...") + result, err := pool.Exec(context.Background(), ` + UPDATE model_providers + SET config = '{ + "provider": "openai", + "default_model": "qwen2.5-7b-instruct", + "supports_streaming": true, + "supports_function_calling": false + }'::jsonb, + models = '[ + {"id": "qwen2.5-7b-instruct", "name": "Qwen2.5-7B-Instruct", "type": "chat"} + ]'::jsonb + WHERE name = '本地LLM (Qwen2.5-7B)' + `) + if err != nil { + log.Printf("❌ 修复本地模型失败: %v", err) + } else { + rowsAffected := result.RowsAffected() + if rowsAffected > 0 { + fmt.Printf("✅ 成功修复本地模型配置 (影响 %d 行)\n\n", rowsAffected) + } else { + fmt.Println("⚠️ 未找到本地模型记录") + } + } + + // 2. 为未配置模型的应用设置默认模型 + fmt.Println("【任务2】为未配置模型的应用设置默认模型...") + result, err = pool.Exec(context.Background(), ` + UPDATE applications + SET app_config = jsonb_set( + COALESCE(app_config, '{}'::jsonb), + '{model}', + '"qwen2.5-7b-instruct"' + ) + WHERE (app_config->>'model' IS NULL OR app_config->>'model' = '') + AND status = 'approved' + `) + if err != nil { + log.Printf("❌ 更新应用配置失败: %v", err) + } else { + rowsAffected := result.RowsAffected() + if rowsAffected > 0 { + fmt.Printf("✅ 成功为 %d 个应用设置默认模型\n\n", rowsAffected) + } else { + fmt.Println("ℹ️ 所有应用已配置模型") + } + } + + // 3. 验证修复结果 + fmt.Println("【验证】检查修复结果...") + + var localConfig, localModels string + err = pool.QueryRow(context.Background(), ` + SELECT + COALESCE(config::text, 'null') as config, + COALESCE(models::text, 'null') as models + FROM model_providers + WHERE name = '本地LLM (Qwen2.5-7B)' + `).Scan(&localConfig, &localModels) + + if err != nil { + log.Printf("查询验证失败: %v", err) + } else { + fmt.Printf("本地模型 config: %s\n", localConfig) + fmt.Printf("本地模型 models: %s\n\n", localModels) + } + + var unconfiguredCount int + err = pool.QueryRow(context.Background(), ` + SELECT COUNT(*) + FROM applications + WHERE (app_config->>'model' IS NULL OR app_config->>'model' = '') + AND status = 'approved' + `).Scan(&unconfiguredCount) + + if err != nil { + log.Printf("查询未配置应用数量失败: %v", err) + } else { + fmt.Printf("未配置模型的应用数量: %d\n", unconfiguredCount) + } + + fmt.Println("\n=== 修复完成 ===") +} diff --git a/server/cmd/server/router.go b/server/cmd/server/router.go index a901508..edb0a13 100644 --- a/server/cmd/server/router.go +++ b/server/cmd/server/router.go @@ -44,7 +44,7 @@ func newRouter(cfg *config.Config, pool *pgxpool.Pool, rdb *redis.Client) http.H // LLM Manager — direct model calls replacing Dify for chat llmMgr := llm.NewManager() llmMgr.SetPool(pool) - + // 优先从数据库加载 providers(如果失败,回退到环境变量配置) if err := llmMgr.LoadProvidersFromDB(context.Background()); err != nil { // 回退:使用环境变量配置 diff --git a/server/internal/handler/analysis_template.go b/server/internal/handler/analysis_template.go index 955ff68..6c576b1 100644 --- a/server/internal/handler/analysis_template.go +++ b/server/internal/handler/analysis_template.go @@ -27,13 +27,14 @@ func NewAnalysisTemplateHandler(pool *pgxpool.Pool, manager *llm.Manager, provid return &AnalysisTemplateHandler{pool: pool, manager: manager, provider: provider} } -// getProvider 获取当前激活的 provider -func (h *AnalysisTemplateHandler) getProvider(ctx context.Context) (llm.Provider, error) { - provider, err := h.manager.GetActiveProvider(ctx) +// getProviderWithModel 获取当前激活的 provider 及其默认模型 +func (h *AnalysisTemplateHandler) getProviderWithModel(ctx context.Context) (llm.Provider, string, error) { + provider, defaultModel, err := h.manager.GetActiveProviderWithModel(ctx) if err == nil { - return provider, nil + return provider, defaultModel, nil } - return h.manager.GetProvider(h.provider) + provider, err = h.manager.GetProvider(h.provider) + return provider, "", err } func (h *AnalysisTemplateHandler) ListTemplates(w http.ResponseWriter, r *http.Request) { @@ -234,8 +235,21 @@ func (h *AnalysisTemplateHandler) GenerateReport(w http.ResponseWriter, r *http. } } + // 获取激活的 provider 和默认模型 + provider, defaultModel, err := h.getProviderWithModel(r.Context()) + if err != nil { + response.InternalError(w, "获取模型提供商失败") + return + } + + // 使用优先级:Provider 默认模型 > 应用配置模型 + modelToUse := defaultModel + if modelToUse == "" { + modelToUse = appModel // 降级时使用应用配置 + } + llmReq := &llm.ChatRequest{ - Model: appModel, + Model: modelToUse, Messages: []llm.Message{ {Role: llm.RoleSystem, Content: "你是一位资深的政务数据分析和研判专家。要求:1)基于提供的真实数据库数据进行分析 2)报告必须完整输出到最后一个章节,不能中途截断 3)保持精炼,每个章节500字以内 4)使用Markdown格式排版 5)表格数据必须引用真实数据 6)报告输出完毕后,必须在最后一行单独输出「---\\n\\n**【完稿】**」作为完成标记"}, {Role: llm.RoleUser, Content: prompt}, @@ -246,14 +260,7 @@ func (h *AnalysisTemplateHandler) GenerateReport(w http.ResponseWriter, r *http. } startTime := time.Now() - - // 获取激活的 provider - provider, err := h.getProvider(r.Context()) - if err != nil { - response.InternalError(w, "获取模型提供商失败") - return - } - + body, err := h.manager.ChatStream(r.Context(), provider, llmReq) if err != nil { response.Error(w, http.StatusBadGateway, 50202, "模型服务不可用: "+err.Error()) diff --git a/server/internal/handler/chat_llm.go b/server/internal/handler/chat_llm.go index 5088318..8ffde92 100644 --- a/server/internal/handler/chat_llm.go +++ b/server/internal/handler/chat_llm.go @@ -34,16 +34,17 @@ func NewLLMChatHandler(pool *pgxpool.Pool, manager *llm.Manager, defaultProvider return &LLMChatHandler{pool: pool, manager: manager, provider: defaultProvider, rdb: rdb, workerURL: workerURL, embedder: embedder} } -// getProvider 获取当前激活的 provider,优先从数据库读取 -func (h *LLMChatHandler) getProvider(ctx context.Context) (llm.Provider, error) { - // 尝试从数据库获取激活的 provider - provider, err := h.manager.GetActiveProvider(ctx) +// getProviderWithModel 获取当前激活的 provider 及其默认模型 +func (h *LLMChatHandler) getProviderWithModel(ctx context.Context) (llm.Provider, string, error) { + // 尝试从数据库获取激活的 provider 和默认模型 + provider, defaultModel, err := h.manager.GetActiveProviderWithModel(ctx) if err == nil { - return provider, nil + return provider, defaultModel, nil } - + // 降级到配置的 fallback provider - return h.manager.GetProvider(h.provider) + provider, err = h.manager.GetProvider(h.provider) + return provider, "", err // 降级时返回空模型,使用应用配置 } type llmChatRequest struct { @@ -568,8 +569,21 @@ func (h *LLMChatHandler) Chat(w http.ResponseWriter, r *http.Request) { history = h.loadConversationHistory(r.Context(), appID, userID.String(), convID, 10) } + // 获取激活的 provider 和默认模型 + provider, defaultModel, err := h.getProviderWithModel(r.Context()) + if err != nil { + response.InternalError(w, "获取模型提供商失败") + return + } + + // 使用优先级:Provider 默认模型 > 应用配置模型 + modelToUse := defaultModel + if modelToUse == "" { + modelToUse = cfg.Model // 降级时使用应用配置 + } + llmReq := &llm.ChatRequest{ - Model: cfg.Model, + Model: modelToUse, Messages: h.buildMessages(cfg.SystemPrompt, knowledgeCtx, hasKB, history, req.Message, orgApps), Temperature: cfg.Temp, MaxTokens: cfg.MaxTok, @@ -577,14 +591,7 @@ func (h *LLMChatHandler) Chat(w http.ResponseWriter, r *http.Request) { } startTime := time.Now() - - // 获取激活的 provider - provider, err := h.getProvider(r.Context()) - if err != nil { - response.InternalError(w, "获取模型提供商失败") - return - } - + body, err := h.manager.ChatStream(r.Context(), provider, llmReq) if err != nil { response.Error(w, http.StatusBadGateway, 50202, "模型服务不可用: "+err.Error()) @@ -673,8 +680,21 @@ func (h *LLMChatHandler) Completion(w http.ResponseWriter, r *http.Request) { // 加载同机构应用列表,用于超范围引导跳转 orgApps := h.loadSameOrgApps(r.Context(), cfg.OrgID, appID) + // 获取激活的 provider 和默认模型 + provider, defaultModel, err := h.getProviderWithModel(r.Context()) + if err != nil { + response.InternalError(w, "获取模型提供商失败") + return + } + + // 使用优先级:Provider 默认模型 > 应用配置模型 + modelToUse := defaultModel + if modelToUse == "" { + modelToUse = cfg.Model // 降级时使用应用配置 + } + llmReq := &llm.ChatRequest{ - Model: cfg.Model, + Model: modelToUse, Messages: h.buildMessages(cfg.SystemPrompt, knowledgeCtx, hasKB, nil, req.Message, orgApps), Temperature: cfg.Temp, MaxTokens: cfg.MaxTok, @@ -684,13 +704,6 @@ func (h *LLMChatHandler) Completion(w http.ResponseWriter, r *http.Request) { startTime := time.Now() convID := uuid.New().String() - // 获取激活的 provider - provider, err := h.getProvider(r.Context()) - if err != nil { - response.InternalError(w, "获取模型提供商失败") - return - } - body, err := h.manager.ChatStream(r.Context(), provider, llmReq) if err != nil { response.Error(w, http.StatusBadGateway, 50202, "模型服务不可用: "+err.Error()) @@ -1176,13 +1189,18 @@ func (h *LLMChatHandler) generateConversationName(appID, userID, convID, userMes Stream: false, } - // 获取激活的 provider - provider, err := h.getProvider(ctx) + // 获取激活的 provider 和默认模型 + provider, defaultModel, err := h.getProviderWithModel(ctx) if err != nil { log.Warn().Err(err).Msg("get provider for title generation failed") return } + // 使用 Provider 默认模型 + if defaultModel != "" { + nameReq.Model = defaultModel + } + result, err := h.manager.Chat(ctx, provider, nameReq) if err != nil { return diff --git a/server/internal/handler/doc_template.go b/server/internal/handler/doc_template.go index b30bbf8..60d184d 100644 --- a/server/internal/handler/doc_template.go +++ b/server/internal/handler/doc_template.go @@ -27,13 +27,14 @@ func NewDocTemplateHandler(pool *pgxpool.Pool, manager *llm.Manager, provider st return &DocTemplateHandler{pool: pool, manager: manager, provider: provider} } -// getProvider 获取当前激活的 provider -func (h *DocTemplateHandler) getProvider(ctx context.Context) (llm.Provider, error) { - provider, err := h.manager.GetActiveProvider(ctx) +// getProviderWithModel 获取当前激活的 provider 及其默认模型 +func (h *DocTemplateHandler) getProviderWithModel(ctx context.Context) (llm.Provider, string, error) { + provider, defaultModel, err := h.manager.GetActiveProviderWithModel(ctx) if err == nil { - return provider, nil + return provider, defaultModel, nil } - return h.manager.GetProvider(h.provider) + provider, err = h.manager.GetProvider(h.provider) + return provider, "", err } func (h *DocTemplateHandler) ListTemplates(w http.ResponseWriter, r *http.Request) { @@ -171,8 +172,21 @@ func (h *DocTemplateHandler) GenerateDocument(w http.ResponseWriter, r *http.Req prompt = strings.ReplaceAll(prompt, "{{", "") prompt = strings.ReplaceAll(prompt, "}}", "") + // 获取激活的 provider 和默认模型 + provider, defaultModel, err := h.getProviderWithModel(r.Context()) + if err != nil { + response.InternalError(w, "获取模型提供商失败") + return + } + + // 使用优先级:Provider 默认模型 > 应用配置模型 + modelToUse := defaultModel + if modelToUse == "" { + modelToUse = appModel // 降级时使用应用配置 + } + llmReq := &llm.ChatRequest{ - Model: appModel, + Model: modelToUse, Messages: []llm.Message{ {Role: llm.RoleSystem, Content: "你是一个专业的政务公文写作专家,精通《党政机关公文格式》国家标准(GB/T 9704)和《党政机关公文处理工作条例》。请严格按照规范格式生成公文,确保行文庄重、严谨、准确。输出完整公文内容,使用Markdown格式排版。"}, {Role: llm.RoleUser, Content: prompt}, @@ -183,14 +197,7 @@ func (h *DocTemplateHandler) GenerateDocument(w http.ResponseWriter, r *http.Req } startTime := time.Now() - - // 获取激活的 provider - provider, err := h.getProvider(r.Context()) - if err != nil { - response.InternalError(w, "获取模型提供商失败") - return - } - + body, err := h.manager.ChatStream(r.Context(), provider, llmReq) if err != nil { response.Error(w, http.StatusBadGateway, 50202, "模型服务不可用: "+err.Error()) diff --git a/server/pkg/llm/manager.go b/server/pkg/llm/manager.go index bef7c37..ed6e9f4 100644 --- a/server/pkg/llm/manager.go +++ b/server/pkg/llm/manager.go @@ -116,12 +116,18 @@ func (m *Manager) LoadProvidersFromDB(ctx context.Context) error { // GetActiveProvider 获取优先级最高的激活 provider func (m *Manager) GetActiveProvider(ctx context.Context) (Provider, error) { + provider, _, err := m.GetActiveProviderWithModel(ctx) + return provider, err +} + +// GetActiveProviderWithModel 获取优先级最高的激活 provider 及其默认模型 +func (m *Manager) GetActiveProviderWithModel(ctx context.Context) (Provider, string, error) { if m.pool == nil { // 如果没有数据库连接,使用注册的 fallback if p, ok := m.providers[m.fallback]; ok { - return p, nil + return p, "", nil // 降级时返回空模型名,使用应用配置 } - return nil, fmt.Errorf("no active provider") + return nil, "", fmt.Errorf("no active provider") } // 从数据库获取优先级最高的激活 provider @@ -135,21 +141,25 @@ func (m *Manager) GetActiveProvider(ctx context.Context) (Provider, error) { LIMIT 1 `).Scan(&id, &baseURL, &apiKey, &configJSON) if err != nil { - return nil, fmt.Errorf("query active provider: %w", err) + return nil, "", fmt.Errorf("query active provider: %w", err) } // 检查缓存 m.cacheMu.RLock() if entry, ok := m.cache[id]; ok && time.Since(entry.updatedAt) < m.cacheTTL { m.cacheMu.RUnlock() - return entry.provider, nil + // 从 config 中提取默认模型 + var config map[string]any + json.Unmarshal(configJSON, &config) + defaultModel, _ := config["default_model"].(string) + return entry.provider, defaultModel, nil } m.cacheMu.RUnlock() // 创建新的 provider var config map[string]any if err := json.Unmarshal(configJSON, &config); err != nil { - return nil, fmt.Errorf("unmarshal config: %w", err) + return nil, "", fmt.Errorf("unmarshal config: %w", err) } providerType, _ := config["provider"].(string) @@ -157,6 +167,9 @@ func (m *Manager) GetActiveProvider(ctx context.Context) (Provider, error) { providerType = "openai" } + // 获取默认模型 + defaultModel, _ := config["default_model"].(string) + var provider Provider switch providerType { case "openai": @@ -164,7 +177,7 @@ func (m *Manager) GetActiveProvider(ctx context.Context) (Provider, error) { case "anthropic": provider = NewAnthropicProvider(apiKey, baseURL, "") default: - return nil, fmt.Errorf("unsupported provider type: %s", providerType) + return nil, "", fmt.Errorf("unsupported provider type: %s", providerType) } // 更新缓存 @@ -175,7 +188,7 @@ func (m *Manager) GetActiveProvider(ctx context.Context) (Provider, error) { } m.cacheMu.Unlock() - return provider, nil + return provider, defaultModel, nil } func (m *Manager) GetProvider(name string) (Provider, error) { @@ -208,9 +221,9 @@ type StreamEvent struct { } type Usage struct { - PromptTokens int `json:"prompt_tokens"` - CompletionTokens int `json:"completion_tokens"` - TotalTokens int `json:"total_tokens"` + PromptTokens int `json:"prompt_tokens"` + CompletionTokens int `json:"completion_tokens"` + TotalTokens int `json:"total_tokens"` Model string `json:"model"` }