优化: token消息级持久化 + 技能注入修复 + TopBar减法/UI调
token持久化(方案A,治压缩/切会话后历史token不显):ChatMessage/AiMessageRecord 加 prompt_tokens/completion_tokens(serde + DB V38 迁移 + message_repo 映射);agentic push_assistant_message 设本轮 token + provider/title 构造默认 None;前端 AiMessage 加字段 + switchConversation reload 映射 tokenUsage(双轨:消息级新+会话级旧累计保留) 技能注入修复:read_skill_content_stripped 改 skills_cached 扫盘(防御 SKILLS None 致不注入)+ 细化诊断(缓存/path/fs 各步) TopBar减法/UI:删铅笔新建(与侧栏+重复)/删垃圾桶clear-chat(危险,clear-context归档替代)/删系统就绪装饰占位;更多菜单popout CSS补全(修样式错乱);provider绿点有信息化(绿/红/灰基于AI请求成败)+垂直居中;goals面板补top:100%(修位置飘)+dot/check/remove CSS+goals/history item统一+history index边距;4面板互斥(点一个收其他) MessageList token v-if 去 !streaming(修发新消息历史token消失)
This commit is contained in:
@@ -381,20 +381,33 @@ pub(crate) fn invalidate_skills() {
|
||||
/// 注:guard(std::sync::RwLockReadGuard 非 Send)必须在 spawn_blocking 的 .await 前 drop,
|
||||
/// 否则非 Send 跨 await 点致 future 不 Send(MentionResolver 要求 Send)。guard 用 { } 限作用域。
|
||||
pub(crate) async fn read_skill_content_stripped(name: String) -> Option<String> {
|
||||
// 在作用域内取 path 后立即 drop guard,避免非 Send guard 跨 spawn_blocking await 点
|
||||
// 锁中毒 → skills_lock_async 返 None → ? 早返 None(对齐 P1-260617-3 中毒降级返空 desc)。
|
||||
let path: String = {
|
||||
let g = skills_lock_async().await?;
|
||||
let skills = g.as_ref()?;
|
||||
let info = skills.iter().find(|s| s.name == name)?;
|
||||
info.path.clone()
|
||||
// 用 skills_cached(扫盘填充 + clone)替代 skills_lock_async(只读):防御 SKILLS 为 None
|
||||
// (首次/invalidate 后)时 ? 早返 None 致技能不注入。skills_cached 触发 get_or_init 扫盘确保填充。
|
||||
let path: Option<String> = {
|
||||
let skills = skills_cached().await;
|
||||
skills.iter().find(|s| s.name == name).map(|s| s.path.clone())
|
||||
};
|
||||
let path = match path {
|
||||
Some(p) => p,
|
||||
None => {
|
||||
tracing::warn!(skill = %name, "[read_skill_content_stripped] 缓存中未找到该技能(name 不匹配/未扫到/user_invocable:false)");
|
||||
return None;
|
||||
}
|
||||
};
|
||||
// 单文件读(spawn_blocking 防 Windows fs 慢),细化诊断各失败步便于定位真断点。
|
||||
// path move 进 spawn_blocking,先 clone 留诊断用(tracing 借用 move 后值会 E0382)。
|
||||
let path_for_log = path.clone();
|
||||
let md = match tokio::task::spawn_blocking(move || fs::read_to_string(&path)).await {
|
||||
Ok(Ok(content)) => content,
|
||||
Ok(Err(e)) => {
|
||||
tracing::warn!(skill = %name, path = %path_for_log, error = %e, "[read_skill_content_stripped] 读 SKILL.md 失败(路径/权限/编码)");
|
||||
return None;
|
||||
}
|
||||
Err(e) => {
|
||||
tracing::warn!(skill = %name, error = %e, "[read_skill_content_stripped] spawn_blocking join 失败");
|
||||
return None;
|
||||
}
|
||||
};
|
||||
// 单文件读取仍是同步 fs,但只一次小读;持续阻塞风险远低于扫盘递归。
|
||||
// 仍包 spawn_blocking 与慢路径一致(对齐 detect_stack:防 Windows fs 调度慢)。
|
||||
let md = tokio::task::spawn_blocking(move || fs::read_to_string(&path))
|
||||
.await
|
||||
.ok()?
|
||||
.ok()?;
|
||||
Some(strip_frontmatter(&md))
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user