重构: 后端编排拆解与DRY债收敛及命名纠偏
阶段1拆解: stream 系外移 stream_relay.rs,mod.rs 3048→2607,loop_body 502→83 技能收尾: regenerate/edit 回溯补技能,新增 read_skill 工具,项目级技能扫描 DRY: emit 双写 46 处收敛 event_bus 正典,KV 引号归一收口,LOCK-SLOW 33 处单行化 警示骨架 4→1 与压缩兜底 2→1,噪音目录档位收敛,truncate 同名陷阱消除 工具元数据落 ToolMeta 八维,7 张 match 表改查 registry,parity 测试锁等价 命名: is_noise_entry/teardown_round/probe_missed_terminal_event 等 6 项纠偏
This commit is contained in:
@@ -32,17 +32,18 @@
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use tauri::{AppHandle, Emitter, Manager};
|
||||
use tauri::AppHandle;
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
use df_ai::context_helpers::{extract_keyword_summary, CompressedSummary};
|
||||
use df_ai::provider::ChatMessage;
|
||||
use df_storage::models::AiProviderRecord;
|
||||
|
||||
use crate::state::{AppState, LlmConcurrency};
|
||||
use crate::state::LlmConcurrency;
|
||||
|
||||
// compress_via_llm 位于 commands::ai::compress(mod.rs 的 super::compress)。
|
||||
use super::super::compress::compress_via_llm;
|
||||
use super::super::event_bus::emit_ai_chat_event;
|
||||
use super::{AiChatEvent, AiSession};
|
||||
|
||||
/// 压缩保护区条数(对齐 clear/compress IPC 的 PROTECT_COUNT=6)。
|
||||
@@ -109,13 +110,10 @@ pub(super) async fn maybe_auto_compress(
|
||||
};
|
||||
|
||||
if should_compress {
|
||||
// emit 压缩开始 + 置位防重入 + 读 active 克隆(不改 status)。
|
||||
let ev = AiChatEvent::AiCompressing {
|
||||
// emit 压缩开始(emit+publish 双写经 emit_ai_chat_event)+ 置位防重入 + 读 active 克隆(不改 status)。
|
||||
emit_ai_chat_event(app_handle, AiChatEvent::AiCompressing {
|
||||
conversation_id: Some(conv_id.to_string()),
|
||||
};
|
||||
let _ = app_handle.emit("ai-chat-event", ev.clone());
|
||||
// L3 emit 双写:tunnel subscriber透传 miniapp
|
||||
let _ = app_handle.state::<AppState>().ai_event_bus.publish_event(ev);
|
||||
});
|
||||
let (mut active_msgs, lang) = {
|
||||
let mut session = session_arc.lock().await;
|
||||
let conv = session.conv(&conv_id);
|
||||
@@ -220,30 +218,15 @@ pub(super) async fn maybe_auto_compress(
|
||||
session.conv(&conv_id).messages.set_compressing(false);
|
||||
}
|
||||
}
|
||||
if KEYWORD_FALLBACK_ENABLED {
|
||||
// 同 LLM 失败兜底:标 compressed 释放 token + 关键词摘要塞首位(若非空)。
|
||||
let inserted = {
|
||||
let mut session = session_arc.lock().await;
|
||||
if !session.per_conv.contains_key(&conv_id) {
|
||||
return;
|
||||
}
|
||||
let conv = session.conv(&conv_id);
|
||||
let _compressed = conv.messages.compress_old_messages(protect_start);
|
||||
if !keyword_fallback.is_empty() {
|
||||
conv.messages.insert_at(0, ChatMessage::system(&keyword_fallback));
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
};
|
||||
if inserted {
|
||||
tracing::info!(
|
||||
conv_id = %conv_id,
|
||||
pre_tokens = pre_compress_tokens,
|
||||
"[ai] 压缩 provider 重建失败兜底(后台):关键词摘要已插首位(compressed 标记已扣 token)"
|
||||
);
|
||||
}
|
||||
}
|
||||
keyword_fallback_commit(
|
||||
&session_arc,
|
||||
&conv_id,
|
||||
protect_start,
|
||||
&keyword_fallback,
|
||||
pre_compress_tokens,
|
||||
"压缩 provider 重建失败",
|
||||
)
|
||||
.await;
|
||||
return;
|
||||
}
|
||||
};
|
||||
@@ -286,13 +269,10 @@ pub(super) async fn maybe_auto_compress(
|
||||
// 治 Task#1:loop 自动压缩用 AiAutoCompressed(非手动变体),
|
||||
// 桌面端静默(仅复位 isCompressing,不弹 toast 不 switchConversation)。
|
||||
// miniapp 仍插摘要气泡(对端发生压缩告知用户)。
|
||||
let ev = AiChatEvent::AiAutoCompressed {
|
||||
emit_ai_chat_event(&app_handle, AiChatEvent::AiAutoCompressed {
|
||||
conversation_id: Some(conv_id.clone()),
|
||||
summary: summary.nl_summary,
|
||||
};
|
||||
let _ = app_handle.emit("ai-chat-event", ev.clone());
|
||||
// L3 emit 双写:tunnel subscriber透传 miniapp
|
||||
let _ = app_handle.state::<AppState>().ai_event_bus.publish_event(ev);
|
||||
});
|
||||
}
|
||||
Ok(None) => {
|
||||
// 保护区外无 active 可压缩(已全 compressed/archived)→ noop,仅复位 is_compressing。
|
||||
@@ -329,30 +309,15 @@ pub(super) async fn maybe_auto_compress(
|
||||
"[ai] 自动压缩失败(后台),降级走关键词摘要兜底(KEYWORD_FALLBACK_ENABLED={})",
|
||||
KEYWORD_FALLBACK_ENABLED,
|
||||
);
|
||||
if KEYWORD_FALLBACK_ENABLED {
|
||||
// 标 compressed 释放 token + 关键词摘要塞首位(若非空)。
|
||||
let inserted = {
|
||||
let mut session = session_arc.lock().await;
|
||||
if !session.per_conv.contains_key(&conv_id) {
|
||||
return;
|
||||
}
|
||||
let conv = session.conv(&conv_id);
|
||||
let _compressed = conv.messages.compress_old_messages(protect_start);
|
||||
if !keyword_fallback.is_empty() {
|
||||
conv.messages.insert_at(0, ChatMessage::system(&keyword_fallback));
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
};
|
||||
if inserted {
|
||||
tracing::info!(
|
||||
conv_id = %conv_id,
|
||||
pre_tokens = pre_compress_tokens,
|
||||
"[ai] 压缩失败兜底(后台):关键词摘要已插首位(compressed 标记已扣 token)"
|
||||
);
|
||||
}
|
||||
}
|
||||
keyword_fallback_commit(
|
||||
&session_arc,
|
||||
&conv_id,
|
||||
protect_start,
|
||||
&keyword_fallback,
|
||||
pre_compress_tokens,
|
||||
"压缩失败",
|
||||
)
|
||||
.await;
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -360,3 +325,47 @@ pub(super) async fn maybe_auto_compress(
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
/// 关键词兜底提交单点(DRY #8 收敛,2026-10-01)。
|
||||
///
|
||||
/// 抽自 [`maybe_auto_compress`] 后台 task 内的逐字双胞胎块:压缩 provider 重建失败路径
|
||||
/// 与压缩 LLM 失败路径共用「KEYWORD_FALLBACK_ENABLED 门控 → 锁内 check+mutate
|
||||
/// (标 compressed 释放 token + 关键词摘要插首位,非空才插)→ 成功插入打 info」。
|
||||
/// 两条失败路径只差日志正文,经 `log_head` 参数化(拼为
|
||||
/// "[ai] {log_head}兜底(后台):关键词摘要已插首位(compressed 标记已扣 token)")。
|
||||
///
|
||||
/// conv 已删(per_conv 无条目)→ 直接返回不兜底(对齐原 return 语义:防惰性重建污染新 conv)。
|
||||
async fn keyword_fallback_commit(
|
||||
session_arc: &Arc<Mutex<AiSession>>,
|
||||
conv_id: &str,
|
||||
protect_start: usize,
|
||||
keyword_fallback: &str,
|
||||
pre_tokens: u32,
|
||||
log_head: &str,
|
||||
) {
|
||||
if !KEYWORD_FALLBACK_ENABLED {
|
||||
return;
|
||||
}
|
||||
let inserted = {
|
||||
let mut session = session_arc.lock().await;
|
||||
if !session.per_conv.contains_key(conv_id) {
|
||||
return;
|
||||
}
|
||||
let conv = session.conv(conv_id);
|
||||
let _compressed = conv.messages.compress_old_messages(protect_start);
|
||||
if keyword_fallback.is_empty() {
|
||||
false
|
||||
} else {
|
||||
conv.messages.insert_at(0, ChatMessage::system(keyword_fallback));
|
||||
true
|
||||
}
|
||||
};
|
||||
if inserted {
|
||||
tracing::info!(
|
||||
conv_id = %conv_id,
|
||||
pre_tokens,
|
||||
"[ai] {}兜底(后台):关键词摘要已插首位(compressed 标记已扣 token)",
|
||||
log_head
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user