diff --git a/backend/app/api/model_configs.py b/backend/app/api/model_configs.py index f3df533..c4a7712 100644 --- a/backend/app/api/model_configs.py +++ b/backend/app/api/model_configs.py @@ -29,10 +29,28 @@ router = APIRouter( ) +# 与前端 ModelConfigs.vue 的提供商列表保持一致。 +# anthropic/google/cohere 为原生 API;其余均走 OpenAI 兼容端点。 +VALID_PROVIDERS = [ + 'openai', 'deepseek', 'anthropic', 'local', + 'qwen', 'zhipu', 'baidu', 'moonshot', 'bytedance', 'minimax', + 'siliconflow', 'xunfei', 'hunyuan', 'yi', 'baichuan', 'openrouter', + 'cohere', 'xai', 'together', 'fireworks', 'perplexity', 'deepinfra', + 'groq', 'google', 'mistral', +] + +# 可用 OpenAI 兼容协议直连测试的 provider(anthropic/google/cohere 协议不同,暂除外) +OPENAI_COMPATIBLE_PROVIDERS = { + 'local', 'qwen', 'zhipu', 'baidu', 'moonshot', 'bytedance', 'minimax', + 'siliconflow', 'xunfei', 'hunyuan', 'yi', 'baichuan', 'openrouter', + 'xai', 'together', 'fireworks', 'perplexity', 'deepinfra', 'groq', 'mistral', +} + + class ModelConfigCreate(BaseModel): """模型配置创建模型""" name: str - provider: str # openai/deepseek/anthropic/local + provider: str # 见 VALID_PROVIDERS model_name: str api_key: str base_url: Optional[str] = None @@ -103,8 +121,7 @@ async def create_model_config( 注意:API密钥会加密存储 """ # 验证提供商 - valid_providers = ['openai', 'deepseek', 'anthropic', 'local'] - if config_data.provider not in valid_providers: + if config_data.provider not in VALID_PROVIDERS: raise ValidationError(f"不支持的提供商: {config_data.provider}") # 检查名称是否重复 @@ -189,8 +206,7 @@ async def update_model_config( config.name = config_data.name if config_data.provider is not None: - valid_providers = ['openai', 'deepseek', 'anthropic', 'local'] - if config_data.provider not in valid_providers: + if config_data.provider not in VALID_PROVIDERS: raise ValidationError(f"不支持的提供商: {config_data.provider}") config.provider = config_data.provider @@ -277,6 +293,21 @@ async def test_model_config( api_key=decrypted_api_key, base_url=config.base_url ) + elif config.provider in OPENAI_COMPATIBLE_PROVIDERS: + # OpenAI 兼容协议通用测试(moonshot/qwen/zhipu/siliconflow 等) + if not config.base_url: + return { + "status": "error", + "message": f"提供商 {config.provider} 需要填写 API 地址(base_url)" + } + from openai import AsyncOpenAI + client = AsyncOpenAI(api_key=decrypted_api_key, base_url=config.base_url) + await client.chat.completions.create( + model=config.model_name, + messages=[{"role": "user", "content": "test"}], + max_tokens=5, + timeout=30, + ) else: return { "status": "warning", diff --git a/frontend/src/views/ModelConfigs.vue b/frontend/src/views/ModelConfigs.vue index 115ad15..350e06e 100644 --- a/frontend/src/views/ModelConfigs.vue +++ b/frontend/src/views/ModelConfigs.vue @@ -376,6 +376,10 @@ function createModelGroups(): ProviderGroup[] { { provider: 'moonshot', label: '月之暗面 Kimi', defaultBaseUrl: 'https://api.moonshot.cn/v1', models: [ + { name: 'kimi-k2-0905-preview', desc: 'K2 增强版', price: '¥4/16', checked: false }, + { name: 'kimi-k2-0711-preview', desc: 'K2 旗舰', price: '¥4/16', checked: false }, + { name: 'kimi-k2-turbo-preview', desc: 'K2 高速版', price: '¥8/32', checked: false }, + { name: 'kimi-latest', desc: '自动指向最新', price: '¥4/16', checked: false }, { name: 'moonshot-v1-8k', desc: '标准长度', price: '¥0.15/0.15', checked: false }, { name: 'moonshot-v1-32k', desc: '中长文本', price: '¥0.3/0.3', checked: false }, { name: 'moonshot-v1-128k', desc: '超长文本', price: '¥0.6/0.6', checked: false },