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 @@