- Remove direct task.cancel() calls, use stop flag instead - Monitor stop flag every 100ms during tool execution - Cancel tool task immediately when stop flag is detected - Return "命令执行被用户取消" as tool result with role=tool - Save cancellation result to conversation history - Prevent abrupt task termination, allow graceful shutdown Changes: - server/socket_handlers.py: Comment out pending_task.cancel() - server/tasks.py: Comment out entry['task'].cancel() - server/chat_flow_tool_loop.py: Add stop flag monitoring loop Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
5.3 KiB
5.3 KiB
Bug 修复 v2 - 工具块和轮询优化
修复的问题
1. 工具块无法显示
原因: 工具事件处理逻辑不完整,缺少关键的工具创建和更新逻辑
解决方案:
- 完整实现
handleToolPreparing()- 创建工具准备块 - 完整实现
handleToolStart()- 工具开始执行 - 完整实现
handleToolUpdateAction()- 工具状态更新和完成 - 添加
update_action事件处理(工具完成的关键事件) - 在 WebSocket 中跳过
update_action事件
2. 轮询频率太慢(1秒)
原因: 1秒的轮询间隔无法提供流式输出的体验
解决方案:
- 将轮询间隔从 1000ms 改为 150ms
- 接近流式输出的效果(每秒约 6-7 次更新)
- 在
stores/task.ts中修改startPolling()方法
3. 刷新后加载两遍内容
原因:
restoreTaskState()没有检查是否已在流式输出中handleTaskComplete()会重新加载历史记录
解决方案:
- 在
restoreTaskState()中添加状态检查,避免重复恢复 - 检查最后一条消息是否是 assistant 消息
- 在
handleTaskComplete()中移除fetchAndDisplayHistory(),只更新统计
修改的文件
1. static/src/app/methods/taskPolling.ts
工具处理逻辑:
handleToolPreparing(data) {
// 创建工具准备块
const action = {
id: data.id,
type: 'tool',
tool: {
status: 'preparing',
name: data.name,
// ... 完整的工具属性
}
};
msg.actions.push(action);
this.preparingTools.set(data.id, action);
}
handleToolStart(data) {
// 从 preparing 转为 running
let action = this.preparingTools.get(data.preparing_id);
action.tool.status = 'running';
action.tool.arguments = data.arguments;
// ... 更新工具状态
}
handleToolUpdateAction(data) {
// 更新工具状态(包括完成)
let targetAction = this.toolFindAction(data.id, ...);
targetAction.tool.status = data.status;
targetAction.tool.result = data.result;
// ... 更新工具结果
}
恢复逻辑优化:
async restoreTaskState() {
// 检查是否已在流式输出中
if (this.streamingMessage || this.taskInProgress) {
return;
}
// 检查是否已有 assistant 消息
const lastMessage = this.messages[this.messages.length - 1];
const hasAssistantMessage = lastMessage && lastMessage.role === 'assistant';
// 只标记状态,不清空消息
this.streamingMessage = true;
this.taskInProgress = true;
}
任务完成优化:
handleTaskComplete(data) {
// 只更新统计,不重新加载历史
this.fetchConversationTokenStatistics();
this.updateCurrentContextTokens();
// 移除了 fetchAndDisplayHistory()
}
2. static/src/stores/task.ts
轮询频率优化:
startPolling(eventHandler) {
// 150ms 间隔,接近流式输出效果
this.pollingInterval = window.setInterval(() => {
this.pollTaskEvents(handler);
}, 150);
}
3. static/src/composables/useLegacySocket.ts
跳过 update_action 事件:
ctx.socket.on('update_action', (data) => {
if (ctx.usePollingMode) {
return; // 跳过 WebSocket 事件
}
// ... 原有逻辑
});
事件处理流程
工具执行流程
tool_preparing→ 创建工具准备块tool_start→ 工具开始执行,更新状态为 runningupdate_action→ 工具完成,更新状态为 completed,显示结果
轮询流程
- 每 150ms 轮询一次
- 获取新事件(通过
from参数) - 处理事件,更新界面
- 任务完成后停止轮询
刷新恢复流程
- 页面加载完成
- 延迟 1 秒后调用
restoreTaskState() - 检查是否已在流式输出中(避免重复)
- 检查是否已有 assistant 消息(历史已加载)
- 只标记状态,启动轮询
- 轮询只处理新事件,不重建历史
性能优化
- 轮询频率: 150ms(每秒约 6-7 次)
- 事件增量获取: 通过
from参数只获取新事件 - 避免重复加载: 检查状态和消息,避免重复恢复
- 避免重复显示: 任务完成后不重新加载历史
测试建议
-
工具块显示
- 发送需要调用工具的消息
- 观察工具块是否正常显示
- 观察工具状态变化(preparing → running → completed)
- 观察工具结果是否正确显示
-
流式输出效果
- 发送消息,观察输出是否流畅
- 观察思考块、文本块的更新频率
- 对比之前 1 秒轮询的卡顿感
-
刷新恢复
- 任务执行中刷新页面
- 观察是否只显示一遍内容
- 观察新内容是否正常追加
- 观察工具块是否保留
-
任务完成
- 等待任务完成
- 观察是否有重复内容
- 观察统计是否正确更新
构建
cd static
npm run build
构建成功!
static/dist/assets/main.js(689.89 kB)static/dist/assets/task.js(3.68 kB)
已知改进
- ✅ 工具块正常显示
- ✅ 轮询频率提升到 150ms(接近流式效果)
- ✅ 刷新后不重复显示内容
- ✅ 任务完成后不重复加载历史
- ✅ 状态检查避免重复恢复
现在可以启动服务器测试了!