diff --git a/docs/latest/changelog/roadmap.md b/docs/latest/changelog/roadmap.md index f9f8d28b..35c65ab6 100644 --- a/docs/latest/changelog/roadmap.md +++ b/docs/latest/changelog/roadmap.md @@ -11,7 +11,6 @@ - 开发与生产环境隔离,构建生产镜像 - 集成 LangFuse (观望) 添加用户日志与用户反馈模块,可以在 AgentView 中查看信息 - 集成 neo4j mcp (或者自己构建工具) -- 工具组件重构以支持 todo,files 等渲染。 - 文档解析部分的 markdown 中的图片替换为内部可访问的链接 ### Bugs @@ -24,10 +23,11 @@ - 新增对于上传文件的智能体中间件 - 增强文件下载功能 - 新增多模态模型支持(当前仅支持图片,详见文档) -- 新建 DeepAgents 智能体(Demo) +- 新建 DeepAgents 智能体(Demo),支持 todo,files 等渲染。 - 新增基于知识库文件生成思维导图功能([#335](https://github.com/xerrors/Yuxi-Know/pull/335#issuecomment-3530976425)) - 新增基于知识库文件生成示例问题功能([#335](https://github.com/xerrors/Yuxi-Know/pull/335#issuecomment-3530976425)) - 新增知识库支持文件夹/压缩包上传的功能([#335](https://github.com/xerrors/Yuxi-Know/pull/335#issuecomment-3530976425)) +- 新增自定义模型支持 ### 修复 - 修复重排序模型实际未生效的问题 diff --git a/docs/latest/intro/model-config.md b/docs/latest/intro/model-config.md index 568adc0d..01d5b751 100644 --- a/docs/latest/intro/model-config.md +++ b/docs/latest/intro/model-config.md @@ -4,14 +4,14 @@ 系统支持多种大语言模型服务商,通过配置对应的 API 密钥即可使用: -| 服务商 | 环境变量 | 特点 | -|--------|----------|------| +| 服务商 | 环境变量 | 特点 | +| ------------------------------------------------ | ----------------------- | --------------------- | | [硅基流动](https://cloud.siliconflow.cn/i/Eo5yTHGJ) | `SILICONFLOW_API_KEY` | 🆓 免费额度,默认推荐 | -| OpenAI | `OPENAI_API_KEY` | GPT 系列模型 | -| DeepSeek | `DEEPSEEK_API_KEY` | 国产大模型 | -| OpenRouter | `OPENROUTER_API_KEY` | 多模型聚合平台 | -| 智谱清言 | `ZHIPUAI_API_KEY` | GLM 系列模型 | -| 阿里云百炼 | `DASHSCOPE_API_KEY` | 通义千问系列 | +| OpenAI | `OPENAI_API_KEY` | GPT 系列模型 | +| DeepSeek | `DEEPSEEK_API_KEY` | 国产大模型 | +| OpenRouter | `OPENROUTER_API_KEY` | 多模型聚合平台 | +| 智谱清言 | `ZHIPUAI_API_KEY` | GLM 系列模型 | +| 阿里云百炼 | `DASHSCOPE_API_KEY` | 通义千问系列 | 其余还支持火山、Together、vLLM、Ollama 等。 @@ -19,7 +19,7 @@ 在 `.env` 文件中添加对应的环境变量: -<<< @/../.env.template#model_provider{bash 2} +<<< @/../.env.template#model_provider{bash 5} ### 默认对话模型格式 @@ -29,8 +29,7 @@ default_model: siliconflow/deepseek-ai/DeepSeek-V3.2-Exp ``` -在 Web 界面中选择模型时也会自动按照这一格式保存,无需手动拆分提供商和模型名称。 - +在 Web 界面中选择模型时也会自动按照这一格式保存。 ::: tip 免费获取 API Key [硅基流动](https://cloud.siliconflow.cn/i/Eo5yTHGJ) 注册即送 14 元额度,支持多种开源模型。 @@ -38,93 +37,123 @@ default_model: siliconflow/deepseek-ai/DeepSeek-V3.2-Exp ## 自定义模型供应商 -::: warning -原本网页中的自定义模型已在 `0.3.x` 版本移除,请在 `src/config/static/models.py` 中按如下方式配置,并重启服务后选择并使用。此外,这里也推荐一下团队的另外一个小工具 [mvllm (Manage and Route vLLM Servers)](https://github.com/xerrors/mvllm)。 -::: - ::: tip 配置系统升级 (v0.3.x) 从 `v0.3.x` 版本开始,模型配置系统已升级为基于 Pydantic BaseModel 的类型安全配置,支持 TOML 格式的用户配置文件。 + - **默认配置**: `src/config/static/models.py` (Python 代码) - **用户配置**: `saves/config/base.toml` (TOML 格式,仅保存用户修改) -::: +- **自定义供应商**: `saves/config/custom_providers.toml` (独立配置文件) + ::: -系统理论上兼容任何 OpenAI 兼容的模型服务,包括: +系统提供了完整的自定义供应商管理功能,支持通过 Web 界面直接添加、编辑、测试和删除自定义模型供应商。 -- **vLLM**: 高性能推理服务 -- **Ollama**: 本地模型管理 -- **API 中转服务**: 各种代理和聚合服务 +### 使用方法 -如需添加新的模型供应商,请按以下步骤操作: +系统支持任何 OpenAI 兼容的云服务提供商 -### 1. 编辑模型配置文件 +#### 1. Web 界面操作(推荐) -**方式一:修改默认配置(推荐)** -编辑 `src/config/static/models.py` 文件中的 `DEFAULT_CHAT_MODEL_PROVIDERS` 字典 +访问 **系统设置 > 模型配置**,在"自定义供应商"部分点击 **添加自定义供应商**。这里的密钥可以直接填写也可以填写对应的环境变量名称。 -在 `src/config/static/models.py` 中添加新的模型供应商: +#### 2. 配置文件操作 -```python -DEFAULT_CHAT_MODEL_PROVIDERS: dict[str, ChatModelProvider] = { - # ... 现有配置 ... +如需通过配置文件管理,编辑 `saves/config/custom_providers.toml`: - "custom-provider": ChatModelProvider( - name="自定义提供商", - url="https://your-provider.com/docs", - base_url="https://api.your-provider.com/v1", - default="custom-model-name", - env="CUSTOM_API_KEY_ENV_NAME", - models=[ - "supported-model-name", - "another-model-name", - ], - ), +```toml +[model_names.local-vllm] +name = "本地 vLLM 服务" +url = "https://docs.vllm.ai" +base_url = "http://localhost:8000/v1" +default = "Qwen/Qwen2.5-7B-Instruct" +env = "LOCAL_VLLM_API_KEY" +models = [ + "Qwen/Qwen2.5-7B-Instruct", + "Qwen/Qwen2.5-14B-Instruct", +] +custom = true - # 本地 Ollama 服务 - "local-ollama": ChatModelProvider( - name="Local Ollama", - url="https://ollama.com", - base_url="http://localhost:11434/v1", - default="llama3.2", - env="NO_API_KEY", # 对于不需要API Key的服务,使用NO_API_KEY - models=["llama3.2", "qwen2.5"], - ), - - # 本地 vLLM 服务 - "local-vllm": ChatModelProvider( - name="Local vLLM", - url="https://docs.vllm.ai", - base_url="http://localhost:8000/v1", - default="Qwen/Qwen2.5-7B-Instruct", - env="NO_API_KEY", - models=[ - "Qwen/Qwen2.5-7B-Instruct", - "Qwen/Qwen2.5-14B-Instruct", - ], - ), -} +[model_names.local-ollama] +name = "本地 Ollama" +url = "https://ollama.com" +base_url = "http://localhost:11434/v1" +default = "llama3.2" +env = "NO_API_KEY" +models = ["llama3.2", "qwen2.5"] +custom = true ``` -### 2. 配置环境变量 +然后在 `.env` 文件中添加对应的环境变量: -在 `.env` 文件中添加对应的环境变量: ```env -CUSTOM_API_KEY_ENV_NAME=your_api_key_here +LOCAL_VLLM_API_KEY=your_api_key_here ``` -### 3. 重新部署 +### API 端点 -```bash -docker compose restart api-dev +系统提供以下 API 端点管理自定义供应商: + +- `GET /api/system/custom-providers` - 获取所有自定义供应商 +- `POST /api/system/custom-providers` - 添加自定义供应商 +- `PUT /api/system/custom-providers/{provider_id}` - 更新自定义供应商 +- `DELETE /api/system/custom-providers/{provider_id}` - 删除自定义供应商 +- `POST /api/system/custom-providers/{provider_id}/test` - 测试供应商连接 + +### 常见配置示例 + +#### vLLM 本地服务 + +```toml +[model_names.vllm-local] +name = "vLLM 本地服务" +base_url = "http://localhost:8000/v1" +default = "Qwen/Qwen2.5-7B-Instruct" +env = "NO_API_KEY" +models = [ + "Qwen/Qwen2.5-7B-Instruct", + "Qwen/Qwen2.5-14B-Instruct", + "meta-llama/Llama-3.1-8B-Instruct" +] ``` +#### Ollama 本地服务 + +```toml +[model_names.ollama-local] +name = "Ollama 本地服务" +base_url = "http://localhost:11434/v1" +default = "llama3.2" +env = "NO_API_KEY" +models = [ + "llama3.2:latest", + "qwen2.5:latest", + "codellama:latest" +] +``` + +#### 第三方 API 中转服务 + +```toml +[model_names.api-proxy] +name = "API 中转服务" +base_url = "https://api-proxy.example.com/v1" +default = "gpt-3.5-turbo" +env = "API_PROXY_KEY" +models = [ + "gpt-3.5-turbo", + "gpt-4", + "claude-3-sonnet" +] +``` + +### 故障排除 + +1. **测试连接失败**: 检查 API 地址格式和 API 密钥配置 +2. **模型不可用**: 确认模型名称拼写和服务端是否支持该模型 +3. **权限错误**: 确保用户具有管理员权限 +4. **配置未生效**: 检查环境变量配置和服务重启状态 + ## 嵌入模型和重排序模型 -::: warning 重要说明 -从 v0.2 版本开始,项目采用微服务架构,模型部署与项目本身完全解耦。如需使用本地模型,需要先通过 vLLM 或 Ollama 部署为 API 服务。 -::: - -### 本地模型部署 - #### 1. 配置模型信息 在 `src/config/static/models.py` 中的默认配置部分添加: @@ -195,4 +224,4 @@ vllm serve BAAI/bge-reranker-v2-m3 \ --task score \ --dtype fp16 \ --port 8000 -``` \ No newline at end of file +``` diff --git a/server/routers/system_router.py b/server/routers/system_router.py index 3b73bc43..92d3c146 100644 --- a/server/routers/system_router.py +++ b/server/routers/system_router.py @@ -210,3 +210,102 @@ async def get_all_chat_models_status(current_user: User = Depends(get_admin_user except Exception as e: logger.error(f"获取所有聊天模型状态失败: {e}") return {"message": f"获取所有聊天模型状态失败: {e}", "status": {"models": {}, "total": 0, "available": 0}} + + +# ============================================================================= +# === 自定义供应商管理分组 === +# ============================================================================= + + +@system.get("/custom-providers") +async def get_custom_providers(current_user: User = Depends(get_admin_user)): + """获取所有自定义供应商""" + try: + custom_providers = config.get_custom_providers() + return { + "providers": {provider: info.model_dump() for provider, info in custom_providers.items()}, + "message": "success" + } + except Exception as e: + logger.error(f"获取自定义供应商失败: {e}") + raise HTTPException(status_code=500, detail=f"获取自定义供应商失败: {str(e)}") + + +@system.post("/custom-providers") +async def add_custom_provider( + provider_id: str = Body(..., description="供应商ID"), + provider_data: dict = Body(..., description="供应商配置数据"), + current_user: User = Depends(get_admin_user) +): + """添加自定义供应商""" + try: + success = config.add_custom_provider(provider_id, provider_data) + if success: + return {"message": f"自定义供应商 {provider_id} 添加成功"} + else: + raise HTTPException(status_code=400, detail=f"供应商ID {provider_id} 已存在,请使用其他ID") + except Exception as e: + logger.error(f"添加自定义供应商失败 {provider_id}: {e}") + raise HTTPException(status_code=500, detail=f"添加自定义供应商失败: {str(e)}") + + +@system.put("/custom-providers/{provider_id}") +async def update_custom_provider( + provider_id: str, + provider_data: dict = Body(..., description="供应商配置数据"), + current_user: User = Depends(get_admin_user) +): + """更新自定义供应商""" + try: + success = config.update_custom_provider(provider_id, provider_data) + if success: + return {"message": f"自定义供应商 {provider_id} 更新成功"} + else: + raise HTTPException(status_code=404, detail=f"自定义供应商 {provider_id} 不存在或更新失败") + except Exception as e: + logger.error(f"更新自定义供应商失败 {provider_id}: {e}") + raise HTTPException(status_code=500, detail=f"更新自定义供应商失败: {str(e)}") + + +@system.delete("/custom-providers/{provider_id}") +async def delete_custom_provider(provider_id: str, current_user: User = Depends(get_admin_user)): + """删除自定义供应商""" + try: + success = config.delete_custom_provider(provider_id) + if success: + return {"message": f"自定义供应商 {provider_id} 删除成功"} + else: + raise HTTPException(status_code=404, detail=f"自定义供应商 {provider_id} 不存在或删除失败") + except Exception as e: + logger.error(f"删除自定义供应商失败 {provider_id}: {e}") + raise HTTPException(status_code=500, detail=f"删除自定义供应商失败: {str(e)}") + + +@system.post("/custom-providers/{provider_id}/test") +async def test_custom_provider( + provider_id: str, + request: dict = Body(..., description="测试请求"), + current_user: User = Depends(get_admin_user) +): + """测试自定义供应商连接""" + try: + # 从请求中获取model_name + model_name = request.get("model_name") + if not model_name: + raise HTTPException(status_code=400, detail="缺少model_name参数") + + # 检查供应商是否存在 + if provider_id not in config.model_names: + raise HTTPException(status_code=404, detail=f"供应商 {provider_id} 不存在") + + # 测试模型状态 + status = await test_chat_model_status(provider_id, model_name) + return {"status": status, "message": "测试完成"} + except HTTPException: + raise + except Exception as e: + logger.error(f"测试自定义供应商失败 {provider_id}/{model_name}: {e}") + return { + "message": f"测试自定义供应商失败: {e}", + "status": {"provider": provider_id, "model_name": model_name, "status": "error", "message": str(e)}, + } diff --git a/src/config/app.py b/src/config/app.py index 7031e7e5..f9b6ce13 100644 --- a/src/config/app.py +++ b/src/config/app.py @@ -26,6 +26,8 @@ from src.config.static.models import ( from src.utils.logging_config import logger + + class Config(BaseModel): """应用配置类""" @@ -116,6 +118,7 @@ class Config(BaseModel): super().__init__(**data) self._setup_paths() self._load_user_config() + self._load_custom_providers() self._handle_environment() def _setup_paths(self): @@ -166,6 +169,36 @@ class Config(BaseModel): except Exception as e: logger.error(f"Failed to load model names: {e}") + def _load_custom_providers(self): + """从独立的TOML文件加载自定义供应商配置""" + custom_config_file = self._config_file.parent / "custom_providers.toml" + + if not custom_config_file.exists(): + logger.info(f"Custom providers config file not found: {custom_config_file}") + return + + logger.info(f"Loading custom providers from {custom_config_file}") + try: + with open(custom_config_file, "rb") as f: + custom_config = tomli.load(f) + + # 加载自定义供应商 + if "model_names" in custom_config: + self._load_custom_model_providers(custom_config["model_names"]) + + except Exception as e: + logger.error(f"Failed to load custom providers from {custom_config_file}: {e}") + + def _load_custom_model_providers(self, providers_data): + """加载自定义模型供应商""" + try: + for provider, provider_data in providers_data.items(): + provider_data["custom"] = True + self.model_names[provider] = ChatModelProvider(**provider_data) + logger.info(f"Loaded {len(providers_data)} custom model providers") + except Exception as e: + logger.error(f"Failed to load custom model providers: {e}") + def _handle_environment(self): """处理环境变量和运行时状态""" # 处理模型目录 @@ -182,10 +215,13 @@ class Config(BaseModel): self.model_provider_status = {} for provider, info in self.model_names.items(): env_var = info.env + if env_var == "NO_API_KEY": self.model_provider_status[provider] = True else: - self.model_provider_status[provider] = bool(os.getenv(env_var)) + api_key = os.environ.get(env_var) + # 如果获取到的值与环境变量名不同,说明环境变量存在或配置了直接值 + self.model_provider_status[provider] = bool(api_key or info.custom) # 检查网络搜索 if os.getenv("TAVILY_API_KEY"): @@ -354,6 +390,170 @@ class Config(BaseModel): except Exception as e: logger.error(f"Failed to save models config to {self._config_file}: {e}") + # ============================================================ + # 自定义供应商管理方法 + # ============================================================ + + def add_custom_provider(self, provider_id: str, provider_data: dict) -> bool: + """添加自定义供应商 + + Args: + provider_id: 供应商唯一标识符 + provider_data: 供应商配置数据 + + Returns: + 是否添加成功 + """ + try: + # 处理环境变量,移除 ${} 包裹 + if "env" in provider_data and provider_data["env"]: + env_value = provider_data["env"] + if isinstance(env_value, str) and env_value.startswith("${") and env_value.endswith("}"): + provider_data["env"] = env_value[2:-1] + + # 确保标记为自定义供应商 + provider_data["custom"] = True + + # 检查供应商ID是否已存在(无论是内置还是自定义) + if provider_id in self.model_names: + logger.error(f"Provider ID already exists: {provider_id}") + return False + + # 添加到配置中 + self.model_names[provider_id] = ChatModelProvider(**provider_data) + + # 保存到自定义供应商配置文件 + self._save_custom_providers() + + # 重新处理环境变量 + self._handle_environment() + + logger.info(f"Added custom provider: {provider_id}") + return True + + except Exception as e: + logger.error(f"Failed to add custom provider {provider_id}: {e}") + return False + + def update_custom_provider(self, provider_id: str, provider_data: dict) -> bool: + """更新自定义供应商 + + Args: + provider_id: 供应商唯一标识符 + provider_data: 新的供应商配置数据 + + Returns: + 是否更新成功 + """ + try: + # 处理环境变量,移除 ${} 包裹 + if "env" in provider_data and provider_data["env"]: + env_value = provider_data["env"] + if isinstance(env_value, str) and env_value.startswith("${") and env_value.endswith("}"): + provider_data["env"] = env_value[2:-1] + + # 检查供应商是否存在且为自定义供应商 + if provider_id not in self.model_names: + logger.error(f"Provider not found: {provider_id}") + return False + + if not self.model_names[provider_id].custom: + logger.error(f"Cannot update non-custom provider: {provider_id}") + return False + + # 确保保持自定义供应商标记 + provider_data["custom"] = True + + # 更新供应商配置 + self.model_names[provider_id] = ChatModelProvider(**provider_data) + + # 保存到自定义供应商配置文件 + self._save_custom_providers() + + # 重新处理环境变量 + self._handle_environment() + + logger.info(f"Updated custom provider: {provider_id}") + return True + + except Exception as e: + logger.error(f"Failed to update custom provider {provider_id}: {e}") + return False + + def delete_custom_provider(self, provider_id: str) -> bool: + """删除自定义供应商 + + Args: + provider_id: 供应商唯一标识符 + + Returns: + 是否删除成功 + """ + try: + # 检查供应商是否存在且为自定义供应商 + if provider_id not in self.model_names: + logger.error(f"Provider not found: {provider_id}") + return False + + if not self.model_names[provider_id].custom: + logger.error(f"Cannot delete non-custom provider: {provider_id}") + return False + + # 从配置中删除 + del self.model_names[provider_id] + + # 保存到自定义供应商配置文件 + self._save_custom_providers() + + # 重新处理环境变量 + self._handle_environment() + + logger.info(f"Deleted custom provider: {provider_id}") + return True + + except Exception as e: + logger.error(f"Failed to delete custom provider {provider_id}: {e}") + return False + + def get_custom_providers(self) -> dict[str, ChatModelProvider]: + """获取所有自定义供应商 + + Returns: + 自定义供应商字典 + """ + return {k: v for k, v in self.model_names.items() if v.custom} + + def _save_custom_providers(self): + """保存自定义供应商到独立配置文件""" + if not self._config_file: + logger.warning("Config file path not set") + return + + custom_config_file = self._config_file.parent / "custom_providers.toml" + + try: + # 获取所有自定义供应商 + custom_providers = self.get_custom_providers() + + # 创建配置数据 + custom_config = {} + if custom_providers: + custom_config["model_names"] = { + provider: info.model_dump() for provider, info in custom_providers.items() + } + + # 确保目录存在 + custom_config_file.parent.mkdir(parents=True, exist_ok=True) + + # 写入配置文件 + with open(custom_config_file, "wb") as f: + tomli_w.dump(custom_config, f) + + logger.info(f"Custom providers saved to {custom_config_file}") + + except Exception as e: + logger.error(f"Failed to save custom providers to {custom_config_file}: {e}") + # 全局配置实例 config = Config() diff --git a/src/config/static/models.py b/src/config/static/models.py index 2cbc26ea..4874c25e 100644 --- a/src/config/static/models.py +++ b/src/config/static/models.py @@ -19,6 +19,7 @@ class ChatModelProvider(BaseModel): default: str = Field(..., description="默认模型名称") env: str = Field(..., description="API Key 环境变量名") models: list[str] = Field(default_factory=list, description="支持的模型列表") + custom: bool = Field(default=False, description="是否为自定义供应商") class EmbedModelInfo(BaseModel): diff --git a/src/models/chat.py b/src/models/chat.py index 42386eda..4fc66f2d 100644 --- a/src/models/chat.py +++ b/src/models/chat.py @@ -131,7 +131,7 @@ def select_model(model_provider=None, model_name=None, model_spec=None): # 其他模型,默认使用OpenAIBase try: model = OpenAIBase( - api_key=os.getenv(model_info.env), + api_key=os.environ.get(model_info.env, model_info.env), base_url=model_info.base_url, model_name=model_name, ) diff --git a/web/src/apis/base.js b/web/src/apis/base.js index 382023ae..dfd78f35 100644 --- a/web/src/apis/base.js +++ b/web/src/apis/base.js @@ -56,6 +56,17 @@ export async function apiRequest(url, options = {}, requiresAuth = true, respons errorData = await response.json() errorMessage = errorData.detail || errorData.message || errorMessage console.log('API错误详情:', errorData); + + // 如果是422错误,打印更详细的信息 + if (response.status === 422) { + console.error('422验证错误详情:', { + url, + requestMethod: requestOptions.method, + requestHeaders: requestOptions.headers, + requestBody: requestOptions.body, + responseData: errorData + }); + } } catch (e) { // 如果无法解析JSON,使用默认错误信息 console.log('无法解析错误响应JSON:', e); diff --git a/web/src/apis/system_api.js b/web/src/apis/system_api.js index 413950f3..1b4b0001 100644 --- a/web/src/apis/system_api.js +++ b/web/src/apis/system_api.js @@ -1,4 +1,4 @@ -import { apiGet, apiPost, apiAdminGet, apiAdminPost, apiSuperAdminPost } from './base' +import { apiGet, apiPost, apiAdminGet, apiAdminPost, apiAdminPut, apiAdminDelete } from './base' /** * 系统管理API模块 @@ -51,7 +51,7 @@ export const configApi = { */ updateConfigBatch: async (items) => apiAdminPost('/api/system/config/update', items), - + /** * 获取系统日志 * @returns {Promise} - 系统日志 @@ -119,5 +119,58 @@ export const chatModelApi = { } } +// ============================================================================= +// === 自定义供应商管理分组 === +// ============================================================================= + +export const customProviderApi = { + /** + * 获取所有自定义供应商 + * @returns {Promise} - 自定义供应商列表 + */ + getCustomProviders: async () => { + return apiAdminGet('/api/system/custom-providers') + }, + + /** + * 添加自定义供应商 + * @param {string} providerId - 供应商ID + * @param {Object} providerData - 供应商配置数据 + * @returns {Promise} - 添加结果 + */ + addCustomProvider: async (providerId, providerData) => { + return apiAdminPost('/api/system/custom-providers', { provider_id: providerId, provider_data: providerData }) + }, + + /** + * 更新自定义供应商 + * @param {string} providerId - 供应商ID + * @param {Object} providerData - 供应商配置数据 + * @returns {Promise} - 更新结果 + */ + updateCustomProvider: async (providerId, providerData) => { + return apiAdminPut(`/api/system/custom-providers/${encodeURIComponent(providerId)}`, providerData) + }, + + /** + * 删除自定义供应商 + * @param {string} providerId - 供应商ID + * @returns {Promise} - 删除结果 + */ + deleteCustomProvider: async (providerId) => { + return apiAdminDelete(`/api/system/custom-providers/${encodeURIComponent(providerId)}`) + }, + + /** + * 测试自定义供应商连接 + * @param {string} providerId - 供应商ID + * @param {string} modelName - 要测试的模型名称 + * @returns {Promise} - 测试结果 + */ + testCustomProvider: async (providerId, modelName) => { + return apiAdminPost(`/api/system/custom-providers/${encodeURIComponent(providerId)}/test`, { model_name: modelName }) + } +} + diff --git a/web/src/components/ModelProvidersComponent.vue b/web/src/components/ModelProvidersComponent.vue index 4d919055..1a3c1553 100644 --- a/web/src/components/ModelProvidersComponent.vue +++ b/web/src/components/ModelProvidersComponent.vue @@ -1,62 +1,156 @@