重构: 后端编排拆解与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:
@@ -3,7 +3,7 @@
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::sync::Arc;
|
||||
|
||||
use tauri::{AppHandle, Emitter, Manager};
|
||||
use tauri::{AppHandle, Manager};
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
use df_ai::ai_tools::{AiToolRegistry, RiskLevel};
|
||||
@@ -11,6 +11,8 @@ use df_ai::provider::ChatMessage;
|
||||
use df_storage::crud::AiToolExecutionRepo;
|
||||
use df_storage::db::Database;
|
||||
|
||||
use super::event_bus::emit_ai_chat_event;
|
||||
use crate::state::{SettingsNorm, log_slow_lock};
|
||||
use super::{AiChatEvent, AiSession, ApprovalKind, PathAuthRequest, PendingApproval, ToolCallDraft};
|
||||
use super::tools::entity_resolve;
|
||||
|
||||
@@ -142,8 +144,7 @@ async fn execute_with_heartbeat(
|
||||
break;
|
||||
}
|
||||
let ev = AiChatEvent::AiHeartbeat { conversation_id: Some(conv.clone()) };
|
||||
let _ = app_h.emit("ai-chat-event", ev.clone());
|
||||
let _ = app_h.state::<crate::state::AppState>().ai_event_bus.publish_event(ev);
|
||||
emit_ai_chat_event(&app_h, ev);
|
||||
}
|
||||
});
|
||||
// RAII:execute 无论 Ok/Err/panic(unwind),_guard drop 自动 stop+abort,无心跳 task 泄漏。
|
||||
@@ -233,10 +234,7 @@ pub(crate) async fn process_tool_calls(
|
||||
let session = session_arc.lock().await;
|
||||
let __id = session.conv_read(conv_id)
|
||||
.and_then(|c| c.messages.last_assistant_message_id());
|
||||
let __hold = __lock_t.elapsed();
|
||||
if __hold > std::time::Duration::from_millis(30) {
|
||||
eprintln!("[LOCK-SLOW] process_tool_calls:192 持锁 {:?} (含 lock 等待)", __hold);
|
||||
}
|
||||
log_slow_lock(__lock_t, "process_tool_calls:192");
|
||||
__id
|
||||
},
|
||||
).await {
|
||||
@@ -260,8 +258,7 @@ pub(crate) async fn process_tool_calls(
|
||||
args: args.clone(),
|
||||
conversation_id: Some(conv_id.to_string()),
|
||||
};
|
||||
let _ = app_handle.emit("ai-chat-event", ev.clone());
|
||||
let _ = app_handle.state::<crate::state::AppState>().ai_event_bus.publish_event(ev);
|
||||
emit_ai_chat_event(&app_handle, ev);
|
||||
(idx, draft, args)
|
||||
})
|
||||
.collect();
|
||||
@@ -283,18 +280,14 @@ pub(crate) async fn process_tool_calls(
|
||||
let __lock_t = std::time::Instant::now();
|
||||
let mut session = session_arc.lock().await;
|
||||
session.conv(conv_id).messages.push(ChatMessage::tool_result(&draft.id, &err_payload));
|
||||
let __hold = __lock_t.elapsed();
|
||||
if __hold > std::time::Duration::from_millis(30) {
|
||||
eprintln!("[LOCK-SLOW] process_tool_calls:268 持锁 {:?} (含 lock 等待)", __hold);
|
||||
}
|
||||
log_slow_lock(__lock_t, "process_tool_calls:268");
|
||||
}
|
||||
let ev = AiChatEvent::AiToolCallCompleted {
|
||||
id: draft.id.clone(),
|
||||
result: serde_json::Value::String(err_payload.clone()),
|
||||
conversation_id: Some(conv_id.to_string()),
|
||||
};
|
||||
let _ = app_handle.emit("ai-chat-event", ev.clone());
|
||||
let _ = app_handle.state::<crate::state::AppState>().ai_event_bus.publish_event(ev);
|
||||
emit_ai_chat_event(&app_handle, ev);
|
||||
let risk = tools_arc.get(&draft.name).map(|t| t.risk_level).unwrap_or(RiskLevel::High);
|
||||
audit_tool_call(&audit_repo, conv_id, &draft.id, &draft.name, &draft.args,
|
||||
"failed", risk, Some(err_payload.clone()), Some("auto_resolve_fail"), current_message_id).await;
|
||||
@@ -306,16 +299,12 @@ pub(crate) async fn process_tool_calls(
|
||||
// 自动执行范围三档读取(前置到路径授权预校验之前:两处门控共用)。
|
||||
// KV df-ai-auto-execute-mode 取值 low/medium/all,默认 low;
|
||||
// low 仅 Low 自动(等价旧行为),medium Low+Medium,all 全自动无审批(完全 AI 接管)。
|
||||
// get_string_norm 归一去引号(前端 JSON.stringify 落库带引号),不归一则 medium/all 档静默失效全走审批。
|
||||
let auto_exec_mode = app_handle
|
||||
.state::<crate::state::AppState>()
|
||||
.settings
|
||||
.get("df-ai-auto-execute-mode")
|
||||
.get_string_norm("df-ai-auto-execute-mode")
|
||||
.await
|
||||
.ok()
|
||||
.flatten()
|
||||
// 前端 appSettings.set 以 JSON.stringify 落库(字符串值带引号,"all" != all),
|
||||
// 不归一则 medium/all 档静默失效全走审批(同 prompt.rs custom_prompt 的 trim 先例)。
|
||||
.map(|v| v.trim_matches('"').to_string())
|
||||
.unwrap_or_else(|| "low".to_string());
|
||||
|
||||
// ── 文件工具路径授权预校验 ──
|
||||
@@ -334,16 +323,12 @@ pub(crate) async fn process_tool_calls(
|
||||
// 「信任所有目录」总开关(KV df-ai-path-auth-bypass,默认关):开启后白名单外路径不再挂起
|
||||
// 授权弹窗(NeedsAuth 时直接放行),黑名单拦截也一并放行(产品拍板:体验优先,含敏感目录)。
|
||||
// 读取失败按关处理(fail-safe,默认行为不变);前端设置页改后下一轮 process_tool_calls 即生效。
|
||||
// get_bool_norm 归一比对(前端 JSON.stringify 落库带引号),默认 false 由调用方显式传参。
|
||||
let path_auth_bypass = app_handle
|
||||
.state::<crate::state::AppState>()
|
||||
.settings
|
||||
.get(PATH_AUTH_BYPASS_KEY)
|
||||
.await
|
||||
.ok()
|
||||
.flatten()
|
||||
// 前端 appSettings.set 以 JSON.stringify 落库(带引号),归一后比对(同 df-ai-trace-approve 先例)
|
||||
.map(|v| { let v = v.trim_matches('"'); v == "true" || v == "1" })
|
||||
.unwrap_or(false);
|
||||
.get_bool_norm(PATH_AUTH_BYPASS_KEY, false)
|
||||
.await;
|
||||
// 同步进程级黑名单放行标志:validate_path(tool_registry)无 bypass 参数,靠此标志放行黑名单;
|
||||
// 每轮工具执行前刷新,与下方 check_file_tool_auth 的入参 bypass 同源同值,两处拦截点语义一致。
|
||||
path_auth::set_blacklist_bypass(path_auth_bypass);
|
||||
@@ -385,10 +370,7 @@ pub(crate) async fn process_tool_calls(
|
||||
let __lock_t = std::time::Instant::now();
|
||||
let mut session = session_arc.lock().await;
|
||||
session.conv(conv_id).messages.push(ChatMessage::tool_result(&draft.id, &err_msg));
|
||||
let __hold = __lock_t.elapsed();
|
||||
if __hold > std::time::Duration::from_millis(30) {
|
||||
eprintln!("[LOCK-SLOW] process_tool_calls:262 持锁 {:?} (含 lock 等待)", __hold);
|
||||
}
|
||||
log_slow_lock(__lock_t, "process_tool_calls:262");
|
||||
}
|
||||
// L3 emit 双写:路径黑名单拒绝 emit Completed 双路发布。
|
||||
let ev = AiChatEvent::AiToolCallCompleted {
|
||||
@@ -396,8 +378,7 @@ pub(crate) async fn process_tool_calls(
|
||||
result: serde_json::Value::String(err_msg.clone()),
|
||||
conversation_id: Some(conv_id.to_string()),
|
||||
};
|
||||
let _ = app_handle.emit("ai-chat-event", ev.clone());
|
||||
let _ = app_handle.state::<crate::state::AppState>().ai_event_bus.publish_event(ev);
|
||||
emit_ai_chat_event(&app_handle, ev);
|
||||
// 审计:路径黑名单拒绝(decided_by=auto_blacklist),留痕可追溯
|
||||
let risk_level = tools_arc.get(&draft.name).map(|t| t.risk_level).unwrap_or(RiskLevel::High);
|
||||
audit_tool_call(
|
||||
@@ -433,18 +414,14 @@ pub(crate) async fn process_tool_calls(
|
||||
let __lock_t = std::time::Instant::now();
|
||||
let mut session = session_arc.lock().await;
|
||||
session.conv(conv_id).messages.push(ChatMessage::tool_result(&draft.id, &skip_msg));
|
||||
let __hold = __lock_t.elapsed();
|
||||
if __hold > std::time::Duration::from_millis(30) {
|
||||
eprintln!("[LOCK-SLOW] process_tool_calls:305 持锁 {:?} (含 lock 等待)", __hold);
|
||||
}
|
||||
log_slow_lock(__lock_t, "process_tool_calls:305");
|
||||
}
|
||||
let ev = AiChatEvent::AiToolCallCompleted {
|
||||
id: draft.id.clone(),
|
||||
result: serde_json::Value::String(skip_msg.clone()),
|
||||
conversation_id: Some(conv_id.to_string()),
|
||||
};
|
||||
let _ = app_handle.emit("ai-chat-event", ev.clone());
|
||||
let _ = app_handle.state::<crate::state::AppState>().ai_event_bus.publish_event(ev);
|
||||
emit_ai_chat_event(&app_handle, ev);
|
||||
let risk_level = tools_arc.get(&draft.name).map(|t| t.risk_level).unwrap_or(RiskLevel::High);
|
||||
audit_tool_call(
|
||||
&audit_repo, conv_id, &draft.id, &draft.name, &draft.args,
|
||||
@@ -473,10 +450,7 @@ pub(crate) async fn process_tool_calls(
|
||||
);
|
||||
// 占位 tool_result(与 RiskLevel 审批一致),ai_authorize_dir 批准后替换为真实结果。
|
||||
session.conv(conv_id).messages.push(ChatMessage::tool_result(&draft.id, &pending_placeholder_for(&draft.id)));
|
||||
let __hold = __lock_t.elapsed();
|
||||
if __hold > std::time::Duration::from_millis(30) {
|
||||
eprintln!("[LOCK-SLOW] process_tool_calls:325 持锁 {:?} (含 lock 等待)", __hold);
|
||||
}
|
||||
log_slow_lock(__lock_t, "process_tool_calls:325");
|
||||
}
|
||||
tracing::debug!(target: "ai_dirauth", conv = %conv_id, tool = %draft.name, tc_id = %draft.id, "emit AiDirAuthRequired(路径授权挂起,等 ai_authorize_dir 恢复)");
|
||||
let ev = AiChatEvent::AiDirAuthRequired {
|
||||
@@ -486,8 +460,7 @@ pub(crate) async fn process_tool_calls(
|
||||
dir: dir_str,
|
||||
conversation_id: Some(conv_id.to_string()),
|
||||
};
|
||||
let _ = app_handle.emit("ai-chat-event", ev.clone());
|
||||
let _ = app_handle.state::<crate::state::AppState>().ai_event_bus.publish_event(ev);
|
||||
emit_ai_chat_event(&app_handle, ev);
|
||||
let risk_level = tools_arc.get(&draft.name).map(|t| t.risk_level).unwrap_or(RiskLevel::High);
|
||||
audit_tool_call(
|
||||
&audit_repo, conv_id, &draft.id, &draft.name, &draft.args,
|
||||
@@ -507,16 +480,12 @@ pub(crate) async fn process_tool_calls(
|
||||
// 也进此向量,需透传真实等级,审计方可追溯「all 模式执行了多少高危命令」。
|
||||
let mut low_risk: Vec<(ToolCallDraft, serde_json::Value, RiskLevel)> = Vec::new();
|
||||
// F-#97 诊断:用户报「选 all 仍审批」时辅助定位 KV 是否真落库。
|
||||
// df-ai-trace-approve 开关控制(默认开)。常见误以为已生效但实际未点确认按钮致 KV 未落。
|
||||
// df-ai-trace-approve 开关控制(默认开,get_bool_norm 默认值调用方传参)。常见误以为已生效但实际未点确认按钮致 KV 未落。
|
||||
let trace_on = app_handle
|
||||
.state::<crate::state::AppState>()
|
||||
.settings
|
||||
.get("df-ai-trace-approve")
|
||||
.await
|
||||
.ok()
|
||||
.flatten()
|
||||
.map(|v| { let v = v.trim_matches('"'); v == "true" || v == "1" })
|
||||
.unwrap_or(true);
|
||||
.get_bool_norm("df-ai-trace-approve", true)
|
||||
.await;
|
||||
if trace_on {
|
||||
tracing::info!(
|
||||
conv_id = %conv_id,
|
||||
@@ -570,8 +539,7 @@ pub(crate) async fn process_tool_calls(
|
||||
result: serde_json::Value::String(content.clone()),
|
||||
conversation_id: Some(conv_clone),
|
||||
};
|
||||
let _ = app_clone.emit("ai-chat-event", ev.clone());
|
||||
let _ = app_clone.state::<crate::state::AppState>().ai_event_bus.publish_event(ev);
|
||||
emit_ai_chat_event(&app_clone, ev);
|
||||
// 数据变更类工具执行成功后 emit df-data-changed 联动刷新
|
||||
// (write_file/run_command 不在 data_change_for_tool 映射内,emit_data_changed 内部 None 即 noop)
|
||||
emit_data_changed(&app_clone, &draft.name);
|
||||
@@ -585,8 +553,7 @@ pub(crate) async fn process_tool_calls(
|
||||
result: serde_json::Value::String(content.clone()),
|
||||
conversation_id: Some(conv_clone),
|
||||
};
|
||||
let _ = app_clone.emit("ai-chat-event", ev.clone());
|
||||
let _ = app_clone.state::<crate::state::AppState>().ai_event_bus.publish_event(ev);
|
||||
emit_ai_chat_event(&app_clone, ev);
|
||||
(draft, risk_level, Err(content))
|
||||
}
|
||||
}
|
||||
@@ -668,8 +635,7 @@ pub(crate) async fn process_tool_calls(
|
||||
result: serde_json::Value::String(warned_content.clone()),
|
||||
conversation_id: Some(conv_id.to_string()),
|
||||
};
|
||||
let _ = app_handle.emit("ai-chat-event", ev.clone());
|
||||
let _ = app_handle.state::<crate::state::AppState>().ai_event_bus.publish_event(ev);
|
||||
emit_ai_chat_event(&app_handle, ev);
|
||||
// 短 lock 段:push tool_result(纯写,无 await)
|
||||
{
|
||||
let mut session = session_arc.lock().await;
|
||||
@@ -709,8 +675,7 @@ pub(crate) async fn process_tool_calls(
|
||||
result: val.clone(),
|
||||
conversation_id: Some(conv_clone),
|
||||
};
|
||||
let _ = app_clone.emit("ai-chat-event", ev.clone());
|
||||
let _ = app_clone.state::<crate::state::AppState>().ai_event_bus.publish_event(ev);
|
||||
emit_ai_chat_event(&app_clone, ev);
|
||||
// AR-11(方案A):数据变更工具执行成功后 emit df-data-changed,
|
||||
// 前端 store listen 刷新列表(仅命中映射的工具 emit,见 emit_data_changed)。
|
||||
emit_data_changed(&app_clone, &draft.name);
|
||||
@@ -729,8 +694,7 @@ pub(crate) async fn process_tool_calls(
|
||||
result: serde_json::Value::String(err_msg.clone()),
|
||||
conversation_id: Some(conv_clone),
|
||||
};
|
||||
let _ = app_clone.emit("ai-chat-event", ev.clone());
|
||||
let _ = app_clone.state::<crate::state::AppState>().ai_event_bus.publish_event(ev);
|
||||
emit_ai_chat_event(&app_clone, ev);
|
||||
(draft, serde_json::Value::Null, risk_level, Err(err_msg))
|
||||
}
|
||||
}
|
||||
@@ -831,8 +795,7 @@ pub(crate) async fn process_tool_calls(
|
||||
result: serde_json::Value::String(warned_content.clone()),
|
||||
conversation_id: Some(conv_id.to_string()),
|
||||
};
|
||||
let _ = app_handle.emit("ai-chat-event", ev.clone());
|
||||
let _ = app_handle.state::<crate::state::AppState>().ai_event_bus.publish_event(ev);
|
||||
emit_ai_chat_event(&app_handle, ev);
|
||||
// 短 lock 段:push tool_result(纯写,无 await)
|
||||
{
|
||||
let mut session = session_arc.lock().await;
|
||||
|
||||
Reference in New Issue
Block a user