修复: aichat 切换/新建对话缺陷(队列串会话/回切丢消息/审批误终态化/删除复活/切换链路)

This commit is contained in:
lxy
2026-08-08 14:20:10 +08:00
parent 795e05f42d
commit 9f75db5b15
8 changed files with 511 additions and 325 deletions
+25 -4
View File
@@ -223,10 +223,8 @@ async fn save_conversation_inner(
// 累积致 token 暴增。仅影响持久化视图,不污染内存真相源(ContextManager)——build_for_request
// 仍读全量 messages。
//
// F-260616-09 B 批4:per_conv.messages 唯一真相源(conv_id 来源:本函数入参,与 save 落库的 conv 一致)。
// loop 内 save 由 run_agentic_loop 入参 conv_id 透传;IPC 路径(commands.rs)save 也传 conv_id。
// conv() 惰性建:save 路径 conv 必然已建(send/regenerate/edit/switch 均先 conv());若极端
// 未建(如启动恢复无 live conv),conv() 建空 PerConvState,save 空 messages(幂等不污染)。
// per_conv.messages 是唯一真相源(conv_id 来源:本函数入参,与 save 落库的 conv 一致)。
// conv() 惰性建:save 路径 conv 必然已建;极端未建(如启动恢复无 live conv)建空态,save 空消息(幂等)。
// BUG-2026-07-19: lock 段只 clone 必要读(messages + 元数据),truncate 移出 lock。
// 原 clone+truncate 全在 lock 内,对话历史长(多轮+大工具结果)时 clone+truncate 持锁可达秒级,
// 致 guard.reset/process_tool_calls 等 session lock 竞争超时(aichat 卡死连环:工具卡片不呈现+
@@ -238,6 +236,16 @@ async fn save_conversation_inner(
// 避免同时持有 session 的 mut 和 immut 借用(E0502)。
let (mut msgs, provider_id, created_at, pinned_goals, persisted_count, needs_full_rewrite) = {
let mut session = session_arc.lock().await;
// 已删除对话直接跳过写入(loop 在「push 后、save 前」被删除的幽灵复活根治):
// 此处不惰性重建 per_conv、不触发 INSERT 空壳 + insert_batch 复活。
// 删除语义:删了就删了,后续 save 不应复活。return 前 guard 自动 drop(MutexGuard),无持锁泄漏。
if session.deleted_convs.contains(conv_id) {
tracing::debug!(
"save_conversation 跳过已删除对话(loop 在途 save 不复活): conv_id={}",
conv_id
);
return;
}
let __wait = __lock_start.elapsed();
let provider_id = session.active_provider_id.clone();
let created_at = session.active_conv_created_at.clone();
@@ -386,6 +394,19 @@ async fn save_conversation_inner(
}
}
Ok(None) => {
// 兜底二次校验(封在途 save 走到 Ok(None) 的复活窗口):save 起始时可能尚未
// 记录删除标记,但 get_by_id 恰在 DB 删除之后返回 Ok(None)——INSERT 前再确认一次。
let deleted_now = {
let session = session_arc.lock().await;
session.deleted_convs.contains(conv_id)
};
if deleted_now {
tracing::debug!(
"save_conversation 落库前检测到对话已删除,跳过 INSERT(不复活): conv_id={}",
conv_id
);
return;
}
// 懒创建首次落库(此为空对话不落库的落库点:走到这里 messages 必非空)
// messages JSON 列首次落库也写(兼容未跑迁移的老库 fallback 读路径),
// 同时写 ai_messages(新读路径真相源)。