- 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.2 KiB
5.2 KiB
Bug 修复 v3 - 刷新后重复加载问题
修复的问题
刷新后加载两遍内容
原因:
loadInitialData()会调用fetchAndDisplayHistory()加载历史记录- 1 秒后
restoreTaskState()启动轮询 loadRunningTask()设置lastEventIndex = 0,从头开始获取所有事件- 导致已经在历史中的事件被重复处理
解决方案:
1. 等待历史加载完成
在 restoreTaskState() 中检查历史是否已加载:
async restoreTaskState() {
// 检查历史是否已加载
const hasMessages = Array.isArray(this.messages) && this.messages.length > 0;
if (!hasMessages) {
// 等待历史加载完成后再恢复
setTimeout(() => {
this.restoreTaskState();
}, 500);
return;
}
// 历史已加载,启动轮询
taskStore.startPolling(...);
}
2. 计算正确的事件偏移量
在 loadRunningTask() 中获取任务详情,计算已处理的事件数量:
async loadRunningTask(conversationId) {
// 查找运行中的任务
const runningTask = result.data.find(...);
if (runningTask) {
// 获取任务详情,计算已处理的事件数量
const detailResponse = await fetch(`/api/tasks/${runningTask.task_id}`);
const detailResult = await detailResponse.json();
// 设置为当前事件数量,只获取新事件
this.lastEventIndex = detailResult.data.next_offset || detailResult.data.events.length;
debugLog('[Task] 设置起始偏移量:', this.lastEventIndex);
}
}
工作流程
页面刷新恢复流程(修复后)
-
页面加载 (
mounted())- 调用
loadInitialData() - 加载历史记录(
fetchAndDisplayHistory()) - 1 秒后调用
restoreTaskState()
- 调用
-
任务恢复 (
restoreTaskState())- 检查是否已在流式输出中 → 跳过
- 查找运行中的任务
- 检查历史是否已加载 → 如果未加载,等待 500ms 后重试
- 历史已加载 → 继续
-
加载任务详情 (
loadRunningTask())- 查找运行中的任务
- 获取任务详情
- 计算已处理的事件数量
- 设置
lastEventIndex为当前事件数量
-
启动轮询 (
startPolling())- 从
lastEventIndex开始轮询 - 只获取新事件
- 每 150ms 轮询一次
- 从
-
处理新事件
- 只处理新产生的事件
- 追加到已有的历史记录后面
- 不重复显示已有内容
关键改进
1. 等待历史加载
- 在
restoreTaskState()中检查messages是否已加载 - 如果未加载,延迟 500ms 后重试
- 确保历史加载完成后再启动轮询
2. 正确的事件偏移量
- 在
loadRunningTask()中获取任务详情 - 计算已处理的事件数量(
next_offset或events.length) - 设置
lastEventIndex为当前事件数量 - 轮询只获取新事件(
from=lastEventIndex)
3. 避免重复处理
- 历史记录由
fetchAndDisplayHistory()加载 - 轮询只处理新事件
- 不会重复显示已有内容
修改的文件
1. static/src/app/methods/taskPolling.ts
等待历史加载:
async restoreTaskState() {
// 检查历史是否已加载
const hasMessages = Array.isArray(this.messages) && this.messages.length > 0;
if (!hasMessages) {
debugLog('[TaskPolling] 历史未加载,等待历史加载完成');
setTimeout(() => {
this.restoreTaskState();
}, 500);
return;
}
debugLog('[TaskPolling] 历史已加载,启动轮询');
// ... 启动轮询
}
2. static/src/stores/task.ts
计算事件偏移量:
async loadRunningTask(conversationId) {
const runningTask = result.data.find(...);
if (runningTask) {
// 获取任务详情
const detailResponse = await fetch(`/api/tasks/${runningTask.task_id}`);
const detailResult = await detailResponse.json();
// 设置为当前事件数量
this.lastEventIndex = detailResult.data.next_offset || detailResult.data.events.length;
debugLog('[Task] 设置起始偏移量:', this.lastEventIndex);
}
}
测试场景
1. 正常发送消息
- 发送消息
- 观察输出是否正常
- 观察是否只显示一次
2. 刷新页面(任务进行中)
- 任务执行中刷新页面
- 观察历史记录是否正确显示
- 观察是否只显示一次
- 观察新内容是否正常追加
- 观察工具块是否保留
3. 刷新页面(任务完成后)
- 任务完成后刷新页面
- 观察历史记录是否完整
- 观察是否没有重复内容
4. 多次刷新
- 任务执行中多次刷新
- 观察每次刷新是否正常
- 观察是否有累积的重复内容
构建
cd static
npm run build
构建成功!
static/dist/assets/main.js(690.04 kB)static/dist/assets/task.js(4.01 kB)
最终效果
- ✅ 刷新后不重复显示内容
- ✅ 历史记录正确加载
- ✅ 新事件正常追加
- ✅ 工具块正确显示
- ✅ 流畅的输出效果(150ms 轮询)
现在可以测试了!刷新页面应该只显示一次内容,新内容会正常追加。