修复: 模型停用无效根因 + 上下文归位/压缩守卫

- 模型停用无效根因: get_active_provider 两条路径均不校验 enabled(显式活跃直返/默认只挑 is_default) → 收敛为启用集合解析,无启用 provider 明确报错
- 压缩/标题兜底模型不校验 enabled → 新增 resolve_fallback_model 纯函数(title 复用同一实现,单一源无双轨)
- 上下文多夹层归位修复: gather_tool_responses 聚拢该头全部响应(此前第二个夹层之后的响应仍被判未闭合剥除)
- 压缩后台任务加内容版本哨兵(取首条消息 id,否掉 len:压缩期间 loop 正常 append 会致守卫永不生效) + panic 兜底复位标志
- 902 超时类失败与其它 Fatal 区分停靠文案(点继续可续跑)
- 子代理事件按 conv 改挂(route_scope) + 运行期事件推父会话视图
- 飞书 13a 真根因: run_stream_refresher 死代码(提前 drop)+静默钟被心跳重置 → 帧通道+仅内容事件重置
- 项目详情文件查看: F22 目录不存在误显非 Git 仓库 / F24 模块为仓库子目录时 git 徽标全失效(scope_git_path 按 show-prefix 剥前缀)
- 更新徽章通道错配(Tauri 事件 vs window CustomEvent 同名不同通道,徽章此前永不触发)
- ProjectCard 卡片根 keydown 顶替内嵌按钮(补 e.target 守卫)
- i18n 坏键 ai.aiTool.rejectedHint → aiTool.rejectedHint(双缺会渲染裸 key)
- miniapp 三处: 会话列表刷新漏网/看门狗节流致新一轮无兜底/newConversation 漏清看门狗
This commit is contained in:
lxy
2026-10-10 08:08:48 +08:00
parent 2a3a6cd25b
commit 9c68f98d89
24 changed files with 1023 additions and 3644 deletions
+17 -2
View File
@@ -40,6 +40,7 @@ pub(crate) fn build_audit_record(
result: Option<String>,
decided_by: Option<&str>,
message_id: Option<&str>,
duration_ms: Option<i64>,
) -> AiToolExecutionRecord {
let executed_at = if decided_by.is_some() { Some(now_millis()) } else { None };
// D2a 结果外置:超 8KB 的审计 result 落盘留句柄,治 DB 膨胀(devflow.db 193.7MB 实证)。
@@ -66,6 +67,10 @@ pub(crate) fn build_audit_record(
requested_at: now_millis(),
executed_at,
decided_by: decided_by.map(|s| s.to_string()),
// V50 纯执行耗时:由调用方在真执行路径测量后传入(执行前→执行后墙钟);
// 未真执行(deny/pending/缓存命中)传 None。与 requested_at→executed_at 解耦,
// 后者在审批路径混入等待人工批准时长。
duration_ms,
}
}
@@ -88,10 +93,11 @@ pub(crate) async fn audit_tool_call(
result: Option<String>,
decided_by: Option<&str>,
message_id: Option<&str>,
duration_ms: Option<i64>,
) {
let record = build_audit_record(
conv_id, tool_call_id, tool_name, arguments,
status, risk_level, result, decided_by, message_id,
status, risk_level, result, decided_by, message_id, duration_ms,
);
if let Err(e) = repo.insert(record).await {
tracing::error!(
@@ -119,7 +125,8 @@ pub(crate) async fn record_audit(
decided_by: Option<&str>,
message_id: Option<&str>,
) {
audit_tool_call(repo, conv_id, tool_call_id, tool_name, arguments, status, risk_level, result, decided_by, message_id).await;
// 事件总线旁路(无真执行计时),duration_ms 传 None。
audit_tool_call(repo, conv_id, tool_call_id, tool_name, arguments, status, risk_level, result, decided_by, message_id, None).await;
}
/// 查询审计历史记录(分页,按 requested_at 倒序)。
@@ -159,6 +166,10 @@ pub struct ToolExecutionDto {
pub executed_at: Option<String>,
/// human/auto,None 表示尚未决策
pub decided_by: Option<String>,
/// 纯执行耗时(毫秒,V50):真执行墙钟,与审批等待解耦;None=未执行/老数据。
/// 前端审计面板可用此替代 executed_at-requested_at(后者含审批等待)。
#[serde(skip_serializing_if = "Option::is_none")]
pub duration_ms: Option<i64>,
}
/// 审批历史查询入参(前端透传,空值=不过滤)。
@@ -247,6 +258,7 @@ pub async fn list_tool_executions(
requested_at: r.requested_at,
executed_at: r.executed_at,
decided_by: r.decided_by,
duration_ms: r.duration_ms,
})
.collect();
let has_more = (offset as i64 + items.len() as i64) < total;
@@ -522,6 +534,7 @@ mod tests {
requested_at: t.to_string(),
executed_at: None,
decided_by: None,
duration_ms: None,
})
.await
.expect("测试数据插入应成功");
@@ -591,6 +604,7 @@ mod tests {
requested_at: t.to_string(),
executed_at: Some(t.to_string()),
decided_by: Some("auto".to_string()),
duration_ms: None,
})
.await
.expect("测试数据插入应成功");
@@ -618,6 +632,7 @@ mod tests {
origin: origin.map(|s| s.to_string()),
boundary_json: None,
loop_state: None,
status: "active".to_string(),
})
.await
.expect("测试会话插入应成功");