From 9e2aeffbe4cf11516e54676a2acdb82fae92df1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=BB=9D=E5=B0=98?= <237809796@qq.com> Date: Sun, 14 Jun 2026 16:07:21 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D:=20aichat=20AR-5/7/9?= =?UTF-8?q?=EF=BC=88stop=20=E5=85=9C=E5=BA=95=20+=20clean=20UI=20=E7=9C=9F?= =?UTF-8?q?=E5=88=A0=20+=20friendlyError=20i18n=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - AR-5 stopChat 本地先复位 streaming + clearStreamWatchdog(防审批态看门狗已 clear + AiCompleted 竞态丢失致 streaming 永真卡死) - AR-7 clean UI + 后端真删:crud.rs 加 AiConversationRepo::clear_messages(置空 messages JSON + 清零 token,保留对话壳)+ commands.rs ai_chat_clear 调之真删 DB + AiChat 垃桶按钮二次确认。**主代理补完 agent 半成品**:agent impl 声称改 commands.rs 实际 diff 零改动(self_boundary_check 造假),审查 semantic_check 正确抓到此 gap - AR-9 friendlyError 全走 i18n.global.t + zh/en 双语补 4 key(TS2589 用 as any 规避 vue-i18n 深度泛型) - cargo check / vue-tsc 0 error --- crates/df-storage/src/crud.rs | 22 ++++++++++++++++++++++ src-tauri/src/commands/ai/commands.rs | 11 +++++++++++ src/components/AiChat.vue | 11 +++++++++++ src/composables/ai/useAiEvents.ts | 13 +++++++++---- src/composables/ai/useAiSend.ts | 8 ++++++-- src/i18n/en/ai.ts | 4 ++++ src/i18n/zh-CN/ai.ts | 4 ++++ 7 files changed, 67 insertions(+), 6 deletions(-) diff --git a/crates/df-storage/src/crud.rs b/crates/df-storage/src/crud.rs index 80d3c28..fee1a64 100644 --- a/crates/df-storage/src/crud.rs +++ b/crates/df-storage/src/crud.rs @@ -1339,6 +1339,28 @@ impl KnowledgeEventsRepo { // AiConversationRepo 的整体更新已由 impl_repo! 宏统一生成的 update_full 提供。 impl AiConversationRepo { + /// 清空对话消息内容(保留 conversation 记录本身,只清 messages JSON + 清零 token 计数) + /// + /// "清空对话"语义:对话壳保留(侧栏仍可见,可继续在该对话内聊),仅清空历史消息。 + /// messages 是 ai_conversations 表内的 JSON 列而非独立行,故"删 messages"= 置空该列。 + pub async fn clear_messages(&self, id: &str) -> Result { + let conn = self.conn.clone(); + let id = id.to_owned(); + let now = now_millis_str(); + tokio::task::spawn_blocking(move || { + let guard = conn.blocking_lock(); + let affected = guard + .execute( + "UPDATE ai_conversations SET messages = '[]', prompt_tokens = 0, completion_tokens = 0, updated_at = ?1 WHERE id = ?2", + params![now, id], + ) + .map_err(|e| Error::Storage(e.to_string()))?; + Ok(affected > 0) + }) + .await + .map_err(|e| Error::Storage(e.to_string()))? + } + /// 设置归档标记(仅改 archived,不动 updated_at) /// /// 区别于 update_field(后者强制 SET updated_at=now,会把归档/取消归档误判为内容更新, diff --git a/src-tauri/src/commands/ai/commands.rs b/src-tauri/src/commands/ai/commands.rs index d6baec9..8bcf19a 100644 --- a/src-tauri/src/commands/ai/commands.rs +++ b/src-tauri/src/commands/ai/commands.rs @@ -214,8 +214,19 @@ pub async fn ai_pending_tool_calls( #[tauri::command] pub async fn ai_chat_clear(state: State<'_, AppState>) -> Result<(), String> { let mut session = state.ai_session.lock().await; + // 取活跃对话 id 后释放锁(避免持 session 锁调 DB) + let active_id = session.active_conversation_id.clone(); session.messages.clear(); session.pending_approvals.clear(); + drop(session); + // 真删 DB:清空该对话 messages JSON + 清零 token(保留对话壳),刷新不再恢复(AR-7) + if let Some(id) = active_id { + state + .ai_conversations + .clear_messages(&id) + .await + .map_err(|e| e.to_string())?; + } Ok(()) } diff --git a/src/components/AiChat.vue b/src/components/AiChat.vue index 92ff771..f6b8fe8 100644 --- a/src/components/AiChat.vue +++ b/src/components/AiChat.vue @@ -103,6 +103,10 @@ + +