ai-chat-ui/docs/CHANGELOG-2026-03-06.md

267 lines
7.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 开发日志 - 2026-03-06
## 修复:深度思考内容不显示
### 问题描述
启用深度思考后,后端日志显示已启用深度思考功能,但前端没有显示深度思考内容。
### 根本原因
前端 `src/services/api.ts` 中的 `streamChat` 方法只处理了普通 `content`,完全忽略了后端返回的 `reasoning_content`(深度思考内容)。
### 解决方案
#### 1. 前端 `src/services/api.ts`
添加 `StreamChunk` 接口,区分内容类型:
```typescript
// 流式响应块类型
export interface StreamChunk {
type: "content" | "reasoning";
text: string;
}
```
修改 `streamChat` 方法同时处理两种内容:
```typescript
const delta = data.choices?.[0]?.delta;
// 处理深度思考内容reasoning_content
const reasoningContent = delta?.reasoning_content;
if (reasoningContent) {
yield { type: "reasoning", text: reasoningContent };
}
// 处理普通内容
const content = delta?.content;
if (content) {
yield { type: "content", text: content };
}
```
#### 2. 前端 `src/components/chat/ChatMain.vue`
修改流式处理逻辑,将 `reasoning` 类型内容包装成 `<think>` 标签:
```typescript
let isInReasoning = false;
for await (const chunk of stream) {
if (chunk.type === "reasoning") {
if (!isInReasoning) {
isInReasoning = true;
fullText += "<think>\n";
}
fullText += chunk.text;
} else {
if (isInReasoning) {
isInReasoning = false;
fullText += "\n</think>\n";
}
fullText += chunk.text;
}
}
// 如果最后还在深度思考块中,关闭它
if (isInReasoning) {
fullText += "\n</think>";
}
```
`<think>` 标签会被 `markstream-vue` 库识别,并由 `ThinkingNode` 组件渲染。
---
## 功能:图片/文件附件自动切换模型
### 需求
当用户上传图片或文件PDF、DOCX等无论前端选择了什么模型后端都应强制使用 `glm-4.6v` 模型(支持多模态)。
### 解决方案
#### 后端 `server/adapters/glm_adapter.py`
**修改 `_build_messages` 方法**
返回值从 `(messages, has_vision)` 改为 `(messages, has_vision, has_files)`
```python
def _build_messages(
self, request: ChatCompletionRequest
) -> tuple[List[Dict], bool, bool]:
"""
构建 GLM 格式的消息
返回:(消息列表, 是否包含图片, 是否包含文件附件)
"""
messages = []
has_vision = False
has_files = bool(request.files) # 检查是否有文件附件
# ... 处理消息 ...
return messages, has_vision, has_files
```
**修改 `_resolve_model` 方法**
```python
def _resolve_model(self, model: str, has_vision: bool, has_files: bool = False) -> str:
"""解析实际使用的模型"""
model_lower = model.lower()
# 如果有图片或文件附件,强制使用 glm-4.6v(支持多模态)
if (has_vision or has_files) and model_lower not in VISION_MODELS:
logger.info(
f"[GLM] 检测到图片或文件附件,强制切换模型: {model} -> glm-4.6v"
)
return "glm-4.6v"
return model
```
**修改 `chat` 方法调用**
```python
glm_messages, has_vision, has_files = self._build_messages(request)
actual_model = self._resolve_model(request.model, has_vision, has_files)
```
---
## 功能DeepSeek 深度思考支持
### 需求
为 DeepSeek 的 `deepseek-reasoner` 模型添加深度思考支持,通过 `extra_body` 参数启用。
### 解决方案
#### 后端 `server/adapters/openai_adapter.py`
**添加支持深度思考的模型列表**
```python
# DeepSeek 支持深度思考的模型
DEEPSEEK_THINKING_MODELS = {"deepseek-reasoner"}
```
**修改 `chat` 方法**
```python
# DeepSeek 深度思考支持
extra_body = None
if self._provider_type == "deepseek" and request.deep_thinking:
if self._supports_thinking(request.model):
extra_body = {"thinking": {"type": "enabled"}}
kwargs["extra_body"] = extra_body
logger.info(f"[{provider_name}] 深度思考已启用: extra_body = {extra_body}")
```
**添加 `_supports_thinking` 方法**
```python
def _supports_thinking(self, model: str) -> bool:
"""检查模型是否支持深度思考"""
return model.lower() in DEEPSEEK_THINKING_MODELS
```
**修改 `_stream_chat` 和 `_sync_chat` 方法**
- 添加 `extra_body` 参数
- 增强 `reasoning_content` 日志输出
---
## 功能:模型列表从后端动态获取
### 需求
前端模型选择器中的模型列表改为从后端 API 获取,而非硬编码。
### 解决方案
#### 前端 `src/services/api.ts`
修改 `getModels` 方法,调用后端 API
```typescript
async getModels(): Promise<ModelInfo[]> {
try {
const response = await fetch(`${this.baseUrl}${API_ENDPOINTS.MODELS}`, {
method: "GET",
headers: {
"Content-Type": "application/json",
},
});
if (!response.ok) {
throw new Error(`获取模型列表失败: HTTP ${response.status}`);
}
const data = await response.json();
// 后端返回格式: { object: "list", data: [...] }
return data.data || [];
} catch (error) {
console.error("获取模型列表失败:", error);
// 返回默认模型列表作为降级
return [...];
}
}
```
#### 后端 `server/main.py`
已有 `/api/chat-ui/models` 端点:
```python
@app.get("/api/chat-ui/models")
async def get_models():
"""模型列表(聚合所有可用平台的模型)"""
from adapters import get_all_adapters
all_models = []
for provider, adapter in get_all_adapters().items():
if adapter.is_available():
models = adapter.list_models()
all_models.extend([m.to_dict() for m in models])
return {"object": "list", "data": all_models}
```
#### 特点
- **动态聚合**:自动聚合所有已配置 API Key 的平台模型
- **按需显示**:只有配置了 API Key 的平台才会显示模型
- **降级处理**API 获取失败时返回默认模型列表
---
## 涉及文件
| 文件 | 修改类型 |
|------|----------|
| `src/services/api.ts` | 新增 `StreamChunk` 接口,修改 `streamChat` 方法,修改 `getModels` 方法 |
| `src/components/chat/ChatMain.vue` | 修改流式处理逻辑,支持 `reasoning` 类型 |
| `server/adapters/glm_adapter.py` | 修改 `_build_messages``_resolve_model` 方法 |
| `server/adapters/openai_adapter.py` | 添加 DeepSeek 深度思考支持 |
---
## 测试建议
1. **GLM 深度思考测试**
- 选择支持深度思考的模型(如 glm-4.6v
- 开启深度思考开关
- 发送问题,确认前端显示深度思考内容块
2. **DeepSeek 深度思考测试**
- 选择 `deepseek-reasoner` 模型
- 开启深度思考开关
- 发送问题,确认后端日志显示 `extra_body = {'thinking': {'type': 'enabled'}}`
- 确认前端显示深度思考内容块
3. **模型自动切换测试**
- 选择非多模态模型(如 glm-4-flash
- 上传图片或 PDF 文件
- 确认后端日志显示模型切换为 glm-4.6v
- 确认多模态内容正确处理
4. **模型列表动态获取测试**
- 检查前端控制台,确认调用了 `/api/chat-ui/models`
- 确认模型选择器显示后端返回的模型列表
- 停止后端服务,确认降级模型列表正常显示