diff --git a/backend/app/api/v1/endpoints/config.py b/backend/app/api/v1/endpoints/config.py index 844bca1..bf1e8f5 100644 --- a/backend/app/api/v1/endpoints/config.py +++ b/backend/app/api/v1/endpoints/config.py @@ -239,7 +239,8 @@ async def test_llm_connection( current_user: User = Depends(deps.get_current_user), ) -> Any: """测试LLM连接是否正常""" - from app.services.llm.factory import LLMFactory + from app.services.llm.factory import LLMFactory, NATIVE_ONLY_PROVIDERS + from app.services.llm.adapters import LiteLLMAdapter, BaiduAdapter, MinimaxAdapter, DoubaoAdapter from app.services.llm.types import LLMConfig, LLMProvider, LLMRequest, LLMMessage, DEFAULT_MODELS try: @@ -278,8 +279,16 @@ async def test_llm_connection( max_tokens=50, # 测试使用较少的token ) - # 创建适配器并测试 - adapter = LLMFactory.create_adapter(config) + # 直接创建新的适配器实例(不使用缓存),确保使用最新的配置 + if provider in NATIVE_ONLY_PROVIDERS: + native_adapter_map = { + LLMProvider.BAIDU: BaiduAdapter, + LLMProvider.MINIMAX: MinimaxAdapter, + LLMProvider.DOUBAO: DoubaoAdapter, + } + adapter = native_adapter_map[provider](config) + else: + adapter = LiteLLMAdapter(config) test_request = LLMRequest( messages=[ @@ -290,6 +299,13 @@ async def test_llm_connection( response = await adapter.complete(test_request) + # 验证响应内容 + if not response or not response.content: + return LLMTestResponse( + success=False, + message="LLM 返回空响应,请检查 API Key 和配置" + ) + return LLMTestResponse( success=True, message="LLM连接测试成功", @@ -298,9 +314,32 @@ async def test_llm_connection( ) except Exception as e: + error_msg = str(e) + # 提供更友好的错误信息 + if "401" in error_msg or "invalid_api_key" in error_msg.lower() or "incorrect api key" in error_msg.lower(): + return LLMTestResponse( + success=False, + message="API Key 无效或已过期,请检查后重试" + ) + elif "authentication" in error_msg.lower(): + return LLMTestResponse( + success=False, + message="认证失败,请检查 API Key 是否正确" + ) + elif "timeout" in error_msg.lower(): + return LLMTestResponse( + success=False, + message="连接超时,请检查网络或 API 地址是否正确" + ) + elif "connection" in error_msg.lower(): + return LLMTestResponse( + success=False, + message="无法连接到 API 服务,请检查网络或 API 地址" + ) + return LLMTestResponse( success=False, - message=f"LLM连接测试失败: {str(e)}" + message=f"LLM连接测试失败: {error_msg}" ) diff --git a/backend/app/services/llm/adapters/litellm_adapter.py b/backend/app/services/llm/adapters/litellm_adapter.py index face748..880a476 100644 --- a/backend/app/services/llm/adapters/litellm_adapter.py +++ b/backend/app/services/llm/adapters/litellm_adapter.py @@ -102,6 +102,9 @@ class LiteLLMAdapter(BaseLLMAdapter): """发送请求到 LiteLLM""" import litellm + # 禁用 LiteLLM 的缓存,确保每次都实际调用 API + litellm.cache = None + # 构建消息 messages = [{"role": msg.role, "content": msg.content} for msg in request.messages] @@ -130,10 +133,30 @@ class LiteLLMAdapter(BaseLLMAdapter): kwargs["frequency_penalty"] = self.config.frequency_penalty kwargs["presence_penalty"] = self.config.presence_penalty - # 调用 LiteLLM - response = await litellm.acompletion(**kwargs) + try: + # 调用 LiteLLM + response = await litellm.acompletion(**kwargs) + except litellm.exceptions.AuthenticationError as e: + raise LLMError(f"API Key 无效或已过期: {str(e)}", self.config.provider, 401) + except litellm.exceptions.RateLimitError as e: + raise LLMError(f"API 调用频率超限: {str(e)}", self.config.provider, 429) + except litellm.exceptions.APIConnectionError as e: + raise LLMError(f"无法连接到 API 服务: {str(e)}", self.config.provider) + except litellm.exceptions.APIError as e: + raise LLMError(f"API 错误: {str(e)}", self.config.provider, getattr(e, 'status_code', None)) + except Exception as e: + # 捕获其他异常并重新抛出 + error_msg = str(e) + if "invalid_api_key" in error_msg.lower() or "incorrect api key" in error_msg.lower(): + raise LLMError(f"API Key 无效: {error_msg}", self.config.provider, 401) + elif "authentication" in error_msg.lower(): + raise LLMError(f"认证失败: {error_msg}", self.config.provider, 401) + raise # 解析响应 + if not response: + raise LLMError("API 返回空响应", self.config.provider) + choice = response.choices[0] if response.choices else None if not choice: raise LLMError("API响应格式异常: 缺少choices字段", self.config.provider) diff --git a/backend/app/services/llm/factory.py b/backend/app/services/llm/factory.py index fba107b..8790534 100644 --- a/backend/app/services/llm/factory.py +++ b/backend/app/services/llm/factory.py @@ -102,73 +102,117 @@ class LLMFactory: @classmethod def get_available_models(cls, provider: LLMProvider) -> List[str]: - """获取提供商的可用模型列表""" + """获取提供商的可用模型列表 (2025年最新)""" models = { LLMProvider.GEMINI: [ + "gemini-3-pro", + "gemini-3.0-deep-think", "gemini-2.5-flash", "gemini-2.5-pro", - "gemini-1.5-flash", - "gemini-1.5-pro", + "gemini-2.5-flash-lite", + "gemini-2.5-flash-live-api", + "veo-3.1", + "veo-3.1-fast", ], LLMProvider.OPENAI: [ + "gpt-5", + "gpt-5.1", + "gpt-5.1-instant", + "gpt-5.1-codex-max", "gpt-4o", "gpt-4o-mini", - "gpt-4-turbo", - "gpt-4", - "gpt-3.5-turbo", - "o1-preview", - "o1-mini", + "gpt-4.5", + "o4-mini", + "o3", + "o3-mini", + "gpt-oss-120b", + "gpt-oss-20b", ], LLMProvider.CLAUDE: [ - "claude-3-5-sonnet-20241022", - "claude-3-opus-20240229", - "claude-3-sonnet-20240229", - "claude-3-haiku-20240307", + "claude-opus-4.5", + "claude-sonnet-4.5", + "claude-haiku-4.5", + "claude-sonnet-4", + "claude-opus-4", + "claude-3.7-sonnet", + "claude-3.5-sonnet", + "claude-3.5-haiku", + "claude-3-opus", ], LLMProvider.QWEN: [ - "qwen-turbo", - "qwen-plus", - "qwen-max", - "qwen-max-longcontext", + "qwen3-max-instruct", + "qwen3-235b-a22b", + "qwen3-turbo", + "qwen3-32b", + "qwen3-4b", + "qwen3-embedding-8b", + "qwen-image", + "qwen-vl", + "qwen-audio", ], LLMProvider.DEEPSEEK: [ + "deepseek-v3.1-terminus", + "deepseek-r1-70b", + "deepseek-r1-zero", + "deepseek-v3.2-exp", "deepseek-chat", - "deepseek-coder", + "deepseek-reasoner", + "deepseek-ocr", ], LLMProvider.ZHIPU: [ + "glm-4.6", + "glm-4.6-reap-218b", + "glm-4.5", + "glm-4.5v", + "glm-4.5-air-106b", "glm-4-flash", - "glm-4", - "glm-4-plus", - "glm-4-long", + "glm-4v-flash", + "glm-4.1v-thinking", ], LLMProvider.MOONSHOT: [ - "moonshot-v1-8k", - "moonshot-v1-32k", - "moonshot-v1-128k", + "kimi-k2", + "kimi-k2-thinking", + "kimi-k2-instruct-0905", + "kimi-k1.5", + "kimi-vl", + "kimi-dev-72b", + "kimi-researcher", + "kimi-linear", ], LLMProvider.BAIDU: [ - "ERNIE-3.5-8K", - "ERNIE-4.0-8K", - "ERNIE-Speed-8K", + "ernie-4.5", + "ernie-4.5-21b-a3b-thinking", + "ernie-4.0-8k", + "ernie-3.5-8k", + "ernie-vl", ], LLMProvider.MINIMAX: [ - "abab6.5-chat", - "abab6.5s-chat", - "abab5.5-chat", + "minimax-m2", + "minimax-01-text", + "minimax-01-vl", + "minimax-m1", + "speech-2.6", + "hailuo-02", + "music-1.5", ], LLMProvider.DOUBAO: [ - "doubao-pro-32k", - "doubao-pro-128k", - "doubao-lite-32k", + "doubao-1.6-pro", + "doubao-1.5-pro", + "doubao-seed-code", + "doubao-seed-1.6", + "doubao-vision-language", ], LLMProvider.OLLAMA: [ - "llama3", - "llama3.1", - "llama3.2", - "codellama", - "mistral", - "deepseek-coder-v2", - "qwen2.5-coder", + "llama3.3-70b", + "qwen3-8b", + "gemma3-27b", + "dolphin-3.0-llama3.1-8b", + "cogito-v1", + "deepseek-r1", + "gpt-oss-120b", + "llama3.1-405b", + "mistral-nemo", + "phi-3", ], } return models.get(provider, []) diff --git a/backend/app/services/llm/types.py b/backend/app/services/llm/types.py index 50d438f..5db58e0 100644 --- a/backend/app/services/llm/types.py +++ b/backend/app/services/llm/types.py @@ -87,19 +87,19 @@ class LLMError(Exception): self.original_error = original_error -# 各平台默认模型 +# 各平台默认模型 (2025年最新推荐) DEFAULT_MODELS: Dict[LLMProvider, str] = { - LLMProvider.GEMINI: "gemini-2.5-flash", - LLMProvider.OPENAI: "gpt-4o-mini", - LLMProvider.CLAUDE: "claude-3-5-sonnet-20241022", - LLMProvider.QWEN: "qwen-turbo", - LLMProvider.DEEPSEEK: "deepseek-chat", - LLMProvider.ZHIPU: "glm-4-flash", - LLMProvider.MOONSHOT: "moonshot-v1-8k", - LLMProvider.BAIDU: "ERNIE-3.5-8K", - LLMProvider.MINIMAX: "abab6.5-chat", - LLMProvider.DOUBAO: "doubao-pro-32k", - LLMProvider.OLLAMA: "llama3", + LLMProvider.GEMINI: "gemini-3-pro", + LLMProvider.OPENAI: "gpt-5", + LLMProvider.CLAUDE: "claude-sonnet-4.5", + LLMProvider.QWEN: "qwen3-max-instruct", + LLMProvider.DEEPSEEK: "deepseek-v3.1-terminus", + LLMProvider.ZHIPU: "glm-4.6", + LLMProvider.MOONSHOT: "kimi-k2", + LLMProvider.BAIDU: "ernie-4.5", + LLMProvider.MINIMAX: "minimax-m2", + LLMProvider.DOUBAO: "doubao-1.6-pro", + LLMProvider.OLLAMA: "llama3.3-70b", } diff --git a/frontend/src/components/system/SystemConfig.tsx b/frontend/src/components/system/SystemConfig.tsx index 466327a..a8cbfe3 100644 --- a/frontend/src/components/system/SystemConfig.tsx +++ b/frontend/src/components/system/SystemConfig.tsx @@ -11,25 +11,25 @@ import { import { toast } from "sonner"; import { api } from "@/shared/api/database"; -// LLM 提供商配置 - 简化分类 +// LLM 提供商配置 - 2025年最新 const LLM_PROVIDERS = [ - { value: 'openai', label: 'OpenAI GPT', icon: '🟢', category: 'litellm', hint: 'gpt-4o, gpt-4o-mini 等' }, - { value: 'claude', label: 'Anthropic Claude', icon: '🟣', category: 'litellm', hint: 'claude-3.5-sonnet 等' }, - { value: 'gemini', label: 'Google Gemini', icon: '🔵', category: 'litellm', hint: 'gemini-1.5-flash 等' }, - { value: 'deepseek', label: 'DeepSeek', icon: '🔷', category: 'litellm', hint: 'deepseek-chat, deepseek-coder' }, - { value: 'qwen', label: '通义千问', icon: '🟠', category: 'litellm', hint: 'qwen-turbo, qwen-max 等' }, - { value: 'zhipu', label: '智谱AI (GLM)', icon: '🔴', category: 'litellm', hint: 'glm-4-flash, glm-4 等' }, - { value: 'moonshot', label: 'Moonshot (Kimi)', icon: '🌙', category: 'litellm', hint: 'moonshot-v1-8k 等' }, - { value: 'ollama', label: 'Ollama 本地', icon: '🖥️', category: 'litellm', hint: 'llama3, codellama 等' }, - { value: 'baidu', label: '百度文心', icon: '📘', category: 'native', hint: 'ERNIE-3.5-8K (需要 API_KEY:SECRET_KEY)' }, - { value: 'minimax', label: 'MiniMax', icon: '⚡', category: 'native', hint: 'abab6.5-chat 等' }, - { value: 'doubao', label: '字节豆包', icon: '🎯', category: 'native', hint: 'doubao-pro-32k 等' }, + { value: 'openai', label: 'OpenAI GPT', icon: '🟢', category: 'litellm', hint: 'gpt-5, gpt-5-mini, o3 等' }, + { value: 'claude', label: 'Anthropic Claude', icon: '🟣', category: 'litellm', hint: 'claude-sonnet-4.5, claude-opus-4 等' }, + { value: 'gemini', label: 'Google Gemini', icon: '🔵', category: 'litellm', hint: 'gemini-3-pro, gemini-3-flash 等' }, + { value: 'deepseek', label: 'DeepSeek', icon: '🔷', category: 'litellm', hint: 'deepseek-v3.1-terminus, deepseek-v3 等' }, + { value: 'qwen', label: '通义千问', icon: '🟠', category: 'litellm', hint: 'qwen3-max-instruct, qwen3-plus 等' }, + { value: 'zhipu', label: '智谱AI (GLM)', icon: '🔴', category: 'litellm', hint: 'glm-4.6, glm-4.5-flash 等' }, + { value: 'moonshot', label: 'Moonshot (Kimi)', icon: '🌙', category: 'litellm', hint: 'kimi-k2, kimi-k1.5 等' }, + { value: 'ollama', label: 'Ollama 本地', icon: '🖥️', category: 'litellm', hint: 'llama3.3-70b, qwen3-8b 等' }, + { value: 'baidu', label: '百度文心', icon: '📘', category: 'native', hint: 'ernie-4.5 (需要 API_KEY:SECRET_KEY)' }, + { value: 'minimax', label: 'MiniMax', icon: '⚡', category: 'native', hint: 'minimax-m2, minimax-m1 等' }, + { value: 'doubao', label: '字节豆包', icon: '🎯', category: 'native', hint: 'doubao-1.6-pro, doubao-1.5-pro 等' }, ]; const DEFAULT_MODELS: Record = { - openai: 'gpt-4o-mini', claude: 'claude-3-5-sonnet-20241022', gemini: 'gemini-2.5-flash', - deepseek: 'deepseek-chat', qwen: 'qwen-turbo', zhipu: 'glm-4-flash', moonshot: 'moonshot-v1-8k', - ollama: 'llama3', baidu: 'ERNIE-3.5-8K', minimax: 'abab6.5-chat', doubao: 'doubao-pro-32k', + openai: 'gpt-5', claude: 'claude-sonnet-4.5', gemini: 'gemini-3-pro', + deepseek: 'deepseek-v3.1-terminus', qwen: 'qwen3-max-instruct', zhipu: 'glm-4.6', moonshot: 'kimi-k2', + ollama: 'llama3.3-70b', baidu: 'ernie-4.5', minimax: 'minimax-m2', doubao: 'doubao-1.6-pro', }; interface SystemConfigData {