mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-04-25 00:36:31 +02:00
feat: add Chinese LLM providers support with auto-fill API Base URL
- Add support for DeepSeek, Qwen (Alibaba), Kimi (Moonshot), and GLM (Zhipu) - Implement auto-fill API Base URL when selecting Chinese LLM providers - Add smart validation and warnings for missing API endpoints - Fix session state management in task logging service - Add comprehensive Chinese setup documentation - Add database migration for new LLM provider enums Closes #383
This commit is contained in:
parent
402039f02f
commit
917cf4f398
9 changed files with 565 additions and 5 deletions
323
docs/chinese-llm-setup.md
Normal file
323
docs/chinese-llm-setup.md
Normal file
|
|
@ -0,0 +1,323 @@
|
|||
# 国产 LLM 配置指南 | Chinese LLM Setup Guide
|
||||
|
||||
本指南将帮助你在 SurfSense 中配置和使用国产大语言模型。
|
||||
|
||||
This guide helps you configure and use Chinese LLM providers in SurfSense.
|
||||
|
||||
---
|
||||
|
||||
## 📋 支持的提供商 | Supported Providers
|
||||
|
||||
SurfSense 现已支持以下国产 LLM:
|
||||
|
||||
- ✅ **DeepSeek** - 国产高性能 AI 模型
|
||||
- ✅ **阿里通义千问 (Alibaba Qwen)** - 阿里云通义千问大模型
|
||||
- ✅ **月之暗面 Kimi (Moonshot)** - 月之暗面 Kimi 大模型
|
||||
- ✅ **智谱 AI GLM (Zhipu)** - 智谱 AI GLM 系列模型
|
||||
|
||||
---
|
||||
|
||||
## 🚀 快速开始 | Quick Start
|
||||
|
||||
### 通用配置步骤 | General Configuration Steps
|
||||
|
||||
1. 登录 SurfSense Dashboard
|
||||
2. 进入 **Settings** → **API Keys** (或 **LLM Configurations**)
|
||||
3. 点击 **Add New Configuration**
|
||||
4. 从 **Provider** 下拉菜单中选择你的国产 LLM 提供商
|
||||
5. 填写必填字段(见下方各提供商详细配置)
|
||||
6. 点击 **Save**
|
||||
|
||||
---
|
||||
|
||||
## 1️⃣ DeepSeek 配置 | DeepSeek Configuration
|
||||
|
||||
### 获取 API Key
|
||||
|
||||
1. 访问 [DeepSeek 开放平台](https://platform.deepseek.com/)
|
||||
2. 注册并登录账号
|
||||
3. 进入 **API Keys** 页面
|
||||
4. 点击 **Create New API Key**
|
||||
5. 复制生成的 API Key (格式: `sk-xxx`)
|
||||
|
||||
### 在 SurfSense 中配置
|
||||
|
||||
| 字段 | 值 | 说明 |
|
||||
|------|-----|------|
|
||||
| **Configuration Name** | `DeepSeek Chat` | 配置名称(自定义) |
|
||||
| **Provider** | `DEEPSEEK` | 选择 DeepSeek |
|
||||
| **Model Name** | `deepseek-chat` | 推荐模型<br>其他选项: `deepseek-coder` |
|
||||
| **API Key** | `sk-xxx...` | 你的 DeepSeek API Key |
|
||||
| **API Base URL** | `https://api.deepseek.com` | DeepSeek API 地址 |
|
||||
| **Parameters** | _(留空)_ | 使用默认参数 |
|
||||
|
||||
### 示例配置
|
||||
|
||||
```
|
||||
Configuration Name: DeepSeek Chat
|
||||
Provider: DEEPSEEK
|
||||
Model Name: deepseek-chat
|
||||
API Key: sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
API Base URL: https://api.deepseek.com
|
||||
```
|
||||
|
||||
### 可用模型
|
||||
|
||||
- **deepseek-chat**: 通用对话模型(推荐)
|
||||
- **deepseek-coder**: 代码专用模型
|
||||
|
||||
### 定价
|
||||
- 请访问 [DeepSeek 定价页面](https://platform.deepseek.com/pricing) 查看最新价格
|
||||
|
||||
---
|
||||
|
||||
## 2️⃣ 阿里通义千问 (Alibaba Qwen) 配置
|
||||
|
||||
### 获取 API Key
|
||||
|
||||
1. 访问 [阿里云百炼平台](https://dashscope.aliyun.com/)
|
||||
2. 登录阿里云账号
|
||||
3. 开通 DashScope 服务
|
||||
4. 进入 **API-KEY 管理**
|
||||
5. 创建并复制 API Key
|
||||
|
||||
### 在 SurfSense 中配置
|
||||
|
||||
| 字段 | 值 | 说明 |
|
||||
|------|-----|------|
|
||||
| **Configuration Name** | `通义千问 Max` | 配置名称(自定义) |
|
||||
| **Provider** | `ALIBABA_QWEN` | 选择阿里通义千问 |
|
||||
| **Model Name** | `qwen-max` | 推荐模型<br>其他选项: `qwen-plus`, `qwen-turbo` |
|
||||
| **API Key** | `sk-xxx...` | 你的 DashScope API Key |
|
||||
| **API Base URL** | `https://dashscope.aliyuncs.com/compatible-mode/v1` | 阿里云 API 地址 |
|
||||
| **Parameters** | _(留空)_ | 使用默认参数 |
|
||||
|
||||
### 示例配置
|
||||
|
||||
```
|
||||
Configuration Name: 通义千问 Max
|
||||
Provider: ALIBABA_QWEN
|
||||
Model Name: qwen-max
|
||||
API Key: sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
API Base URL: https://dashscope.aliyuncs.com/compatible-mode/v1
|
||||
```
|
||||
|
||||
### 可用模型
|
||||
|
||||
- **qwen-max**: 最强性能,适合复杂任务
|
||||
- **qwen-plus**: 性价比高,适合日常使用(推荐)
|
||||
- **qwen-turbo**: 速度快,适合简单任务
|
||||
|
||||
### 定价
|
||||
- 请访问 [阿里云百炼定价](https://help.aliyun.com/zh/model-studio/getting-started/billing) 查看最新价格
|
||||
|
||||
---
|
||||
|
||||
## 3️⃣ 月之暗面 Kimi (Moonshot) 配置
|
||||
|
||||
### 获取 API Key
|
||||
|
||||
1. 访问 [Moonshot AI 开放平台](https://platform.moonshot.cn/)
|
||||
2. 注册并登录账号
|
||||
3. 进入 **API Key 管理**
|
||||
4. 创建新的 API Key
|
||||
5. 复制 API Key
|
||||
|
||||
### 在 SurfSense 中配置
|
||||
|
||||
| 字段 | 值 | 说明 |
|
||||
|------|-----|------|
|
||||
| **Configuration Name** | `Kimi` | 配置名称(自定义) |
|
||||
| **Provider** | `MOONSHOT` | 选择月之暗面 Kimi |
|
||||
| **Model Name** | `moonshot-v1-32k` | 推荐模型<br>其他选项: `moonshot-v1-8k`, `moonshot-v1-128k` |
|
||||
| **API Key** | `sk-xxx...` | 你的 Moonshot API Key |
|
||||
| **API Base URL** | `https://api.moonshot.cn/v1` | Moonshot API 地址 |
|
||||
| **Parameters** | _(留空)_ | 使用默认参数 |
|
||||
|
||||
### 示例配置
|
||||
|
||||
```
|
||||
Configuration Name: Kimi 32K
|
||||
Provider: MOONSHOT
|
||||
Model Name: moonshot-v1-32k
|
||||
API Key: sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
API Base URL: https://api.moonshot.cn/v1
|
||||
```
|
||||
|
||||
### 可用模型
|
||||
|
||||
- **moonshot-v1-8k**: 8K 上下文(基础版)
|
||||
- **moonshot-v1-32k**: 32K 上下文(推荐)
|
||||
- **moonshot-v1-128k**: 128K 上下文(长文本专用)
|
||||
|
||||
### 定价
|
||||
- 请访问 [Moonshot AI 定价](https://platform.moonshot.cn/pricing) 查看最新价格
|
||||
|
||||
---
|
||||
|
||||
## 4️⃣ 智谱 AI GLM (Zhipu) 配置
|
||||
|
||||
### 获取 API Key
|
||||
|
||||
1. 访问 [智谱 AI 开放平台](https://open.bigmodel.cn/)
|
||||
2. 注册并登录账号
|
||||
3. 进入 **API 管理**
|
||||
4. 创建新的 API Key
|
||||
5. 复制 API Key
|
||||
|
||||
### 在 SurfSense 中配置
|
||||
|
||||
| 字段 | 值 | 说明 |
|
||||
|------|-----|------|
|
||||
| **Configuration Name** | `GLM-4` | 配置名称(自定义) |
|
||||
| **Provider** | `ZHIPU` | 选择智谱 AI |
|
||||
| **Model Name** | `glm-4` | 推荐模型<br>其他选项: `glm-4-flash`, `glm-3-turbo` |
|
||||
| **API Key** | `xxx.yyy...` | 你的智谱 API Key |
|
||||
| **API Base URL** | `https://open.bigmodel.cn/api/paas/v4` | 智谱 API 地址 |
|
||||
| **Parameters** | _(留空)_ | 使用默认参数 |
|
||||
|
||||
### 示例配置
|
||||
|
||||
```
|
||||
Configuration Name: GLM-4
|
||||
Provider: ZHIPU
|
||||
Model Name: glm-4
|
||||
API Key: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.xxxxxxxxxxxxxxxx
|
||||
API Base URL: https://open.bigmodel.cn/api/paas/v4
|
||||
```
|
||||
|
||||
### 可用模型
|
||||
|
||||
- **glm-4**: GLM-4 旗舰模型(推荐)
|
||||
- **glm-4-flash**: 快速推理版本
|
||||
- **glm-3-turbo**: 高性价比版本
|
||||
|
||||
### 定价
|
||||
- 请访问 [智谱 AI 定价](https://open.bigmodel.cn/pricing) 查看最新价格
|
||||
|
||||
---
|
||||
|
||||
## ⚙️ 高级配置 | Advanced Configuration
|
||||
|
||||
### 自定义参数 | Custom Parameters
|
||||
|
||||
你可以在 **Parameters** 字段中添加自定义参数(JSON 格式):
|
||||
|
||||
```json
|
||||
{
|
||||
"temperature": 0.7,
|
||||
"max_tokens": 2000,
|
||||
"top_p": 0.9
|
||||
}
|
||||
```
|
||||
|
||||
### 常用参数说明
|
||||
|
||||
| 参数 | 说明 | 默认值 | 范围 |
|
||||
|------|------|--------|------|
|
||||
| `temperature` | 控制输出随机性,越高越随机 | 0.7 | 0.0 - 1.0 |
|
||||
| `max_tokens` | 最大输出 Token 数 | 模型默认 | 1 - 模型上限 |
|
||||
| `top_p` | 核采样参数 | 1.0 | 0.0 - 1.0 |
|
||||
|
||||
---
|
||||
|
||||
## 🔧 故障排除 | Troubleshooting
|
||||
|
||||
### 常见问题
|
||||
|
||||
#### 1. **错误: "Invalid API Key"**
|
||||
- ✅ 检查 API Key 是否正确复制(无多余空格)
|
||||
- ✅ 确认 API Key 是否已激活
|
||||
- ✅ 检查账户余额是否充足
|
||||
|
||||
#### 2. **错误: "Connection timeout"**
|
||||
- ✅ 确认 API Base URL 是否正确
|
||||
- ✅ 检查网络连接
|
||||
- ✅ 确认防火墙是否允许访问
|
||||
|
||||
#### 3. **错误: "Model not found"**
|
||||
- ✅ 确认模型名称是否拼写正确
|
||||
- ✅ 检查该模型是否已开通
|
||||
- ✅ 参照上方文档确认可用模型名称
|
||||
|
||||
#### 4. **文档处理卡住 (IN_PROGRESS)**
|
||||
- ✅ 检查模型名称中是否有多余空格
|
||||
- ✅ 确认 API Key 有效且有额度
|
||||
- ✅ 查看后端日志: `docker compose logs backend`
|
||||
|
||||
### 查看日志
|
||||
|
||||
```bash
|
||||
# 查看后端日志
|
||||
docker compose logs backend --tail 100
|
||||
|
||||
# 实时查看日志
|
||||
docker compose logs -f backend
|
||||
|
||||
# 搜索错误
|
||||
docker compose logs backend | grep -i "error"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 💡 最佳实践 | Best Practices
|
||||
|
||||
### 1. 模型选择建议
|
||||
|
||||
| 任务类型 | 推荐模型 | 说明 |
|
||||
|---------|---------|------|
|
||||
| **文档摘要** | Qwen-Plus, GLM-4 | 平衡性能和成本 |
|
||||
| **代码分析** | DeepSeek-Coder | 代码专用 |
|
||||
| **长文本处理** | Kimi 128K | 超长上下文 |
|
||||
| **快速响应** | Qwen-Turbo, GLM-4-Flash | 速度优先 |
|
||||
|
||||
### 2. 成本优化
|
||||
|
||||
- 🎯 **Long Context LLM**: 使用 Qwen-Plus 或 GLM-4(处理文档摘要)
|
||||
- ⚡ **Fast LLM**: 使用 Qwen-Turbo 或 GLM-4-Flash(快速对话)
|
||||
- 🧠 **Strategic LLM**: 使用 Qwen-Max 或 DeepSeek-Chat(复杂推理)
|
||||
|
||||
### 3. API Key 安全
|
||||
|
||||
- ❌ 不要在公开代码中硬编码 API Key
|
||||
- ✅ 定期轮换 API Key
|
||||
- ✅ 为不同用途创建不同的 Key
|
||||
- ✅ 设置合理的额度限制
|
||||
|
||||
---
|
||||
|
||||
## 📚 相关资源 | Resources
|
||||
|
||||
### 官方文档
|
||||
|
||||
- [DeepSeek 文档](https://platform.deepseek.com/docs)
|
||||
- [阿里云百炼文档](https://help.aliyun.com/zh/model-studio/)
|
||||
- [Moonshot AI 文档](https://platform.moonshot.cn/docs)
|
||||
- [智谱 AI 文档](https://open.bigmodel.cn/dev/api)
|
||||
|
||||
### SurfSense 文档
|
||||
|
||||
- [安装指南](../README.md)
|
||||
- [贡献指南](../CONTRIBUTING.md)
|
||||
- [部署指南](../DEPLOYMENT_GUIDE.md)
|
||||
|
||||
---
|
||||
|
||||
## 🆘 需要帮助? | Need Help?
|
||||
|
||||
如果遇到问题,可以通过以下方式获取帮助:
|
||||
|
||||
- 💬 [GitHub Issues](https://github.com/MODSetter/SurfSense/issues)
|
||||
- 💬 [Discord Community](https://discord.gg/ejRNvftDp9)
|
||||
- 📧 Email: [项目维护者邮箱]
|
||||
|
||||
---
|
||||
|
||||
## 🔄 更新日志 | Changelog
|
||||
|
||||
- **2025-01-12**: 初始版本,添加 DeepSeek、Qwen、Kimi、GLM 支持
|
||||
|
||||
---
|
||||
|
||||
**祝你使用愉快!Happy coding with Chinese LLMs! 🚀**
|
||||
|
||||
|
|
@ -20,6 +20,12 @@ from app.db import Base # Assuming your Base is defined in app.db
|
|||
# access to the values within the .ini file in use.
|
||||
config = context.config
|
||||
|
||||
# Override SQLAlchemy URL from environment variables when available
|
||||
# 如果环境变量提供了数据库连接字符串,则优先使用该配置
|
||||
database_url = os.getenv("DATABASE_URL")
|
||||
if database_url:
|
||||
config.set_main_option("sqlalchemy.url", database_url)
|
||||
|
||||
# Interpret the config file for Python logging.
|
||||
# This line sets up loggers basically.
|
||||
if config.config_file_name is not None:
|
||||
|
|
|
|||
|
|
@ -0,0 +1,118 @@
|
|||
"""Add Chinese LLM providers to LiteLLMProvider enum
|
||||
添加国产 LLM 提供商到 LiteLLMProvider 枚举
|
||||
|
||||
Revision ID: 26
|
||||
Revises: 25
|
||||
"""
|
||||
|
||||
from collections.abc import Sequence
|
||||
|
||||
from alembic import op
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = "26"
|
||||
down_revision: str | None = "25"
|
||||
branch_labels: str | Sequence[str] | None = None
|
||||
depends_on: str | Sequence[str] | None = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
"""
|
||||
Add Chinese LLM providers to LiteLLMProvider enum.
|
||||
添加国产 LLM 提供商到 LiteLLMProvider 枚举。
|
||||
|
||||
Adds support for:
|
||||
- DEEPSEEK: DeepSeek AI models
|
||||
- ALIBABA_QWEN: Alibaba Qwen (通义千问) models
|
||||
- MOONSHOT: Moonshot AI (月之暗面 Kimi) models
|
||||
- ZHIPU: Zhipu AI (智谱 GLM) models
|
||||
"""
|
||||
|
||||
# Add DEEPSEEK to the enum if it doesn't already exist
|
||||
# 如果不存在则添加 DEEPSEEK 到枚举
|
||||
op.execute(
|
||||
"""
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM pg_enum
|
||||
WHERE enumtypid = 'litellmprovider'::regtype
|
||||
AND enumlabel = 'DEEPSEEK'
|
||||
) THEN
|
||||
ALTER TYPE litellmprovider ADD VALUE 'DEEPSEEK';
|
||||
END IF;
|
||||
END$$;
|
||||
"""
|
||||
)
|
||||
|
||||
# Add ALIBABA_QWEN to the enum if it doesn't already exist
|
||||
# 如果不存在则添加 ALIBABA_QWEN 到枚举
|
||||
op.execute(
|
||||
"""
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM pg_enum
|
||||
WHERE enumtypid = 'litellmprovider'::regtype
|
||||
AND enumlabel = 'ALIBABA_QWEN'
|
||||
) THEN
|
||||
ALTER TYPE litellmprovider ADD VALUE 'ALIBABA_QWEN';
|
||||
END IF;
|
||||
END$$;
|
||||
"""
|
||||
)
|
||||
|
||||
# Add MOONSHOT to the enum if it doesn't already exist
|
||||
# 如果不存在则添加 MOONSHOT 到枚举
|
||||
op.execute(
|
||||
"""
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM pg_enum
|
||||
WHERE enumtypid = 'litellmprovider'::regtype
|
||||
AND enumlabel = 'MOONSHOT'
|
||||
) THEN
|
||||
ALTER TYPE litellmprovider ADD VALUE 'MOONSHOT';
|
||||
END IF;
|
||||
END$$;
|
||||
"""
|
||||
)
|
||||
|
||||
# Add ZHIPU to the enum if it doesn't already exist
|
||||
# 如果不存在则添加 ZHIPU 到枚举
|
||||
op.execute(
|
||||
"""
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM pg_enum
|
||||
WHERE enumtypid = 'litellmprovider'::regtype
|
||||
AND enumlabel = 'ZHIPU'
|
||||
) THEN
|
||||
ALTER TYPE litellmprovider ADD VALUE 'ZHIPU';
|
||||
END IF;
|
||||
END$$;
|
||||
"""
|
||||
)
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
"""
|
||||
Remove Chinese LLM providers from LiteLLMProvider enum.
|
||||
从 LiteLLMProvider 枚举中移除国产 LLM 提供商。
|
||||
|
||||
Note: PostgreSQL doesn't support removing enum values directly.
|
||||
This would require recreating the enum type and updating all dependent objects.
|
||||
For safety, this downgrade is a no-op.
|
||||
|
||||
注意:PostgreSQL 不支持直接删除枚举值。
|
||||
这需要重建枚举类型并更新所有依赖对象。
|
||||
为了安全起见,此降级操作为空操作。
|
||||
"""
|
||||
# PostgreSQL doesn't support removing enum values directly
|
||||
# This would require a complex migration recreating the enum
|
||||
# PostgreSQL 不支持直接删除枚举值
|
||||
# 这需要复杂的迁移来重建枚举
|
||||
pass
|
||||
|
||||
|
|
@ -78,6 +78,10 @@ class ChatType(str, Enum):
|
|||
|
||||
|
||||
class LiteLLMProvider(str, Enum):
|
||||
"""
|
||||
Enum for LLM providers supported by LiteLLM.
|
||||
LiteLLM 支持的 LLM 提供商枚举。
|
||||
"""
|
||||
OPENAI = "OPENAI"
|
||||
ANTHROPIC = "ANTHROPIC"
|
||||
GROQ = "GROQ"
|
||||
|
|
@ -101,6 +105,11 @@ class LiteLLMProvider(str, Enum):
|
|||
ALEPH_ALPHA = "ALEPH_ALPHA"
|
||||
PETALS = "PETALS"
|
||||
COMETAPI = "COMETAPI"
|
||||
# Chinese LLM Providers (OpenAI-compatible) / 国产 LLM 提供商(OpenAI 兼容)
|
||||
DEEPSEEK = "DEEPSEEK" # DeepSeek
|
||||
ALIBABA_QWEN = "ALIBABA_QWEN" # 阿里通义千问
|
||||
MOONSHOT = "MOONSHOT" # 月之暗面 (Kimi)
|
||||
ZHIPU = "ZHIPU" # 智谱 AI (GLM)
|
||||
CUSTOM = "CUSTOM"
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1070,6 +1070,7 @@ async def process_file_in_background(
|
|||
},
|
||||
)
|
||||
except Exception as e:
|
||||
await session.rollback()
|
||||
await task_logger.log_task_failure(
|
||||
log_entry,
|
||||
f"Failed to process file: {filename}",
|
||||
|
|
|
|||
|
|
@ -83,11 +83,11 @@ async def get_user_llm_instance(
|
|||
)
|
||||
return None
|
||||
|
||||
# Build the model string for litellm
|
||||
# Build the model string for litellm / 构建 LiteLLM 的模型字符串
|
||||
if llm_config.custom_provider:
|
||||
model_string = f"{llm_config.custom_provider}/{llm_config.model_name}"
|
||||
else:
|
||||
# Map provider enum to litellm format
|
||||
# Map provider enum to litellm format / 将提供商枚举映射为 LiteLLM 格式
|
||||
provider_map = {
|
||||
"OPENAI": "openai",
|
||||
"ANTHROPIC": "anthropic",
|
||||
|
|
@ -99,6 +99,11 @@ async def get_user_llm_instance(
|
|||
"AZURE_OPENAI": "azure",
|
||||
"OPENROUTER": "openrouter",
|
||||
"COMETAPI": "cometapi",
|
||||
# Chinese LLM providers (OpenAI-compatible) / 国产 LLM(OpenAI 兼容)
|
||||
"DEEPSEEK": "openai", # DeepSeek uses OpenAI-compatible API
|
||||
"ALIBABA_QWEN": "openai", # Qwen uses OpenAI-compatible API
|
||||
"MOONSHOT": "openai", # Moonshot (Kimi) uses OpenAI-compatible API
|
||||
"ZHIPU": "openai", # Zhipu (GLM) uses OpenAI-compatible API
|
||||
# Add more mappings as needed
|
||||
}
|
||||
provider_prefix = provider_map.get(
|
||||
|
|
|
|||
|
|
@ -73,6 +73,16 @@ class TaskLoggingService:
|
|||
Returns:
|
||||
Log: The updated log entry
|
||||
"""
|
||||
# Ensure session is in a valid state / 确保 session 处于有效状态
|
||||
if not self.session.is_active:
|
||||
await self.session.rollback()
|
||||
|
||||
# Refresh log_entry to avoid expired state / 刷新 log_entry 避免过期状态
|
||||
try:
|
||||
await self.session.refresh(log_entry)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Update the existing log entry
|
||||
log_entry.status = LogStatus.SUCCESS
|
||||
log_entry.message = message
|
||||
|
|
@ -114,6 +124,17 @@ class TaskLoggingService:
|
|||
Returns:
|
||||
Log: The updated log entry
|
||||
"""
|
||||
# Ensure session is in a valid state / 确保 session 处于有效状态
|
||||
if not self.session.is_active:
|
||||
await self.session.rollback()
|
||||
|
||||
# Refresh log_entry to avoid expired state / 刷新 log_entry 避免过期状态
|
||||
try:
|
||||
await self.session.refresh(log_entry)
|
||||
except Exception:
|
||||
# If refresh fails, the object might be detached / 如果刷新失败,对象可能已分离
|
||||
pass
|
||||
|
||||
# Update the existing log entry
|
||||
log_entry.status = LogStatus.FAILED
|
||||
log_entry.level = LogLevel.ERROR
|
||||
|
|
@ -161,6 +182,16 @@ class TaskLoggingService:
|
|||
Returns:
|
||||
Log: The updated log entry
|
||||
"""
|
||||
# Ensure session is in a valid state / 确保 session 处于有效状态
|
||||
if not self.session.is_active:
|
||||
await self.session.rollback()
|
||||
|
||||
# Refresh log_entry to avoid expired state / 刷新 log_entry 避免过期状态
|
||||
try:
|
||||
await self.session.refresh(log_entry)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
log_entry.message = progress_message
|
||||
|
||||
if progress_metadata:
|
||||
|
|
|
|||
|
|
@ -90,6 +90,17 @@ export function ModelConfigManager({ searchSpaceId }: ModelConfigManagerProps) {
|
|||
setFormData((prev) => ({ ...prev, [field]: value }));
|
||||
};
|
||||
|
||||
// Handle provider change with auto-fill API Base URL / 处理 Provider 变更并自动填充 API Base URL
|
||||
const handleProviderChange = (providerValue: string) => {
|
||||
const provider = LLM_PROVIDERS.find((p) => p.value === providerValue);
|
||||
setFormData((prev) => ({
|
||||
...prev,
|
||||
provider: providerValue,
|
||||
// Auto-fill API Base URL if provider has a default / 如果提供商有默认值则自动填充
|
||||
api_base: provider?.apiBase || prev.api_base,
|
||||
}));
|
||||
};
|
||||
|
||||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
if (!formData.name || !formData.provider || !formData.model_name || !formData.api_key) {
|
||||
|
|
@ -468,7 +479,7 @@ export function ModelConfigManager({ searchSpaceId }: ModelConfigManagerProps) {
|
|||
<Label htmlFor="provider">Provider *</Label>
|
||||
<Select
|
||||
value={formData.provider}
|
||||
onValueChange={(value) => handleInputChange("provider", value)}
|
||||
onValueChange={handleProviderChange}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="Select a provider">
|
||||
|
|
@ -537,13 +548,39 @@ export function ModelConfigManager({ searchSpaceId }: ModelConfigManagerProps) {
|
|||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="api_base">API Base URL (Optional)</Label>
|
||||
<Label htmlFor="api_base">
|
||||
API Base URL
|
||||
{selectedProvider?.apiBase && (
|
||||
<span className="text-xs font-normal text-muted-foreground ml-2">
|
||||
(Auto-filled for {selectedProvider.label})
|
||||
</span>
|
||||
)}
|
||||
</Label>
|
||||
<Input
|
||||
id="api_base"
|
||||
placeholder="e.g., https://api.openai.com/v1"
|
||||
placeholder={selectedProvider?.apiBase || "e.g., https://api.openai.com/v1"}
|
||||
value={formData.api_base}
|
||||
onChange={(e) => handleInputChange("api_base", e.target.value)}
|
||||
/>
|
||||
{selectedProvider?.apiBase && formData.api_base === selectedProvider.apiBase && (
|
||||
<p className="text-xs text-green-600 flex items-center gap-1">
|
||||
<CheckCircle className="h-3 w-3" />
|
||||
Using recommended API endpoint for {selectedProvider.label}
|
||||
</p>
|
||||
)}
|
||||
{selectedProvider?.apiBase && !formData.api_base && (
|
||||
<p className="text-xs text-amber-600 flex items-center gap-1">
|
||||
<AlertCircle className="h-3 w-3" />
|
||||
⚠️ API Base URL is required for {selectedProvider.label}. Click to auto-fill:
|
||||
<button
|
||||
type="button"
|
||||
className="underline font-medium"
|
||||
onClick={() => handleInputChange("api_base", selectedProvider.apiBase || "")}
|
||||
>
|
||||
{selectedProvider.apiBase}
|
||||
</button>
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Optional Inference Parameters */}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ export interface LLMProvider {
|
|||
label: string;
|
||||
example: string;
|
||||
description: string;
|
||||
apiBase?: string; // Default API Base URL for the provider / 提供商的默认 API Base URL
|
||||
}
|
||||
|
||||
export const LLM_PROVIDERS: LLMProvider[] = [
|
||||
|
|
@ -90,6 +91,35 @@ export const LLM_PROVIDERS: LLMProvider[] = [
|
|||
example: "gpt-5-mini, claude-sonnet-4-5",
|
||||
description: "Access 500+ AI models through one unified API",
|
||||
},
|
||||
// Chinese LLM Providers / 国产 LLM 提供商
|
||||
{
|
||||
value: "DEEPSEEK",
|
||||
label: "DeepSeek",
|
||||
example: "deepseek-chat, deepseek-coder",
|
||||
description: "Chinese high-performance AI models",
|
||||
apiBase: "https://api.deepseek.com",
|
||||
},
|
||||
{
|
||||
value: "ALIBABA_QWEN",
|
||||
label: "Qwen",
|
||||
example: "qwen-max, qwen-plus, qwen-turbo",
|
||||
description: "Alibaba Cloud Qwen LLM",
|
||||
apiBase: "https://dashscope.aliyuncs.com/compatible-mode/v1",
|
||||
},
|
||||
{
|
||||
value: "MOONSHOT",
|
||||
label: "Kimi",
|
||||
example: "moonshot-v1-8k, moonshot-v1-32k, moonshot-v1-128k",
|
||||
description: "Moonshot AI Kimi models",
|
||||
apiBase: "https://api.moonshot.cn/v1",
|
||||
},
|
||||
{
|
||||
value: "ZHIPU",
|
||||
label: "GLM",
|
||||
example: "glm-4, glm-4-flash, glm-3-turbo",
|
||||
description: "Zhipu AI GLM series models",
|
||||
apiBase: "https://open.bigmodel.cn/api/paas/v4",
|
||||
},
|
||||
{
|
||||
value: "CUSTOM",
|
||||
label: "Custom Provider",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue