Skip to content

Commit fcea5cc

Browse files
committed
style(rust): 统一 ai_refine_task 拆分文件换行与行数口径
1 parent ee02012 commit fcea5cc

3 files changed

Lines changed: 394 additions & 393 deletions

File tree

Lines changed: 115 additions & 114 deletions
Original file line numberDiff line numberDiff line change
@@ -1,116 +1,117 @@
1-
//! AI 精修任务终态骨架(拆分自 ai_refine_task.rs;AGENTS.md §3 单文件 ≤300 行)。
2-
//!
3-
//! @ai-context: 会话级/笔记级精修任务共用的收尾骨架:catch_unwind 把 panic 归一为
4-
//! Failed(spawn_blocking 的 JoinHandle 未被 await——否则任务状态永久
5-
//! 停 Pending、前端永久"排队中");成功路径:轨迹落库 → 结果写注册表 →
6-
//! set_task → 终态帧 → 审计 → finish_ai_task + trim_ai_tasks。
7-
//! @ai-context: 锁序红线:`st.ai_tasks` 的写入用**显式块**(块尾释放守卫),set_task
8-
//! 在其后**重新加同一把 std::Mutex**——两者合并 = 自死锁(std Mutex 不可
9-
//! 重入),任务永久 Running。该序列不可分割。
10-
//! @ai-context: 审计写入 push_refine_audit 的守卫活到 `if let` 块尾(窗口 = push_audit
11-
//! 调用)——不得把 now/format! 挪进或挪出该块。
1+
//! AI 精修任务终态骨架(拆分自 ai_refine_task.rs;AGENTS.md §3 单文件 ≤300 行)。
2+
//!
3+
//! @ai-context: 会话级/笔记级精修任务共用的收尾骨架:catch_unwind 把 panic 归一为
4+
//! Failed(spawn_blocking 的 JoinHandle 未被 await——否则任务状态永久
5+
//! 停 Pending、前端永久"排队中");成功路径:轨迹落库 → 结果写注册表 →
6+
//! set_task → 终态帧 → 审计 → finish_ai_task + trim_ai_tasks。
7+
//! @ai-context: 锁序红线:`st.ai_tasks` 的写入用**显式块**(块尾释放守卫),set_task
8+
//! 在其后**重新加同一把 std::Mutex**——两者合并 = 自死锁(std Mutex 不可
9+
//! 重入),任务永久 Running。该序列不可分割。
10+
//! @ai-context: 审计写入 push_refine_audit 的守卫活到 `if let` 块尾(窗口 = push_audit
11+
//! 调用)——不得把 now/format! 挪进或挪出该块。
1212
13-
use crate::ai_chat::{AiTurn, trajectory_to_json};
14-
use crate::ai_task::{AiTaskFailure, AiTaskState};
15-
use crate::commands::AppState;
16-
use crate::commands_ai_refine::{set_task, AiRefineResult};
17-
18-
use super::RefineStreamFrame;use super::stream::emit_refine_stream;
13+
use crate::ai_chat::{AiTurn, trajectory_to_json};
14+
use crate::ai_task::{AiTaskFailure, AiTaskState};
15+
use crate::commands::AppState;
16+
use crate::commands_ai_refine::{set_task, AiRefineResult};
1917

20-
/// 精修任务审计记录(F1:REQ-140 轨迹可见化——summary 不含原文,隐私红线)。
21-
/// v0.17.0:summary_ctx 泛化("session=1"/"note=3"——会话级/笔记级共用)。
22-
fn push_refine_audit(st: &AppState, summary_ctx: &str, result: &str, model: Option<&str>) {
23-
let now = crate::db_sessions_rows::unix_seconds();
24-
if let Ok(mut g) = st.ai_guardrails.lock() {
25-
g.push_audit(crate::ai_guardrails::AiAuditEntry {
26-
at_unix: now,
27-
upload_summary: format!("refine {} model={}", summary_ctx, model.unwrap_or("?")),
28-
result: result.to_string(),
29-
});
30-
}
31-
}
32-
33-
/// 任务收尾公共骨架(会话级/笔记级共用——v0.17.0 REQ-246 提取)。
34-
///
35-
/// @ai-context: 彻底检测加固(2026-08-21):spawn_blocking 的 JoinHandle 未被
36-
/// await——闭包内 panic 会被 tokio 吞掉,任务状态永久停在
37-
/// Pending(前端永久显示"任务排队中",无失败可重试)。
38-
/// catch_unwind 把 panic 归一为 Failed 状态,状态流转永不失联。
39-
/// @ai-context: F2-B4:单片失败重试后仍失败 → 保留已成功片(partial_failed
40-
/// 语义:failed_slices > 0,前端显示"部分成功 x/y 片")。
41-
pub(crate) fn run_refine_task_skeleton(
42-
st: AppState,
43-
task_id: u64,
44-
target_summary: String,
45-
work: impl FnOnce() -> Result<(AiRefineResult, Vec<AiTurn>), AiTaskFailure>,
46-
) {
47-
let started = std::time::Instant::now();
48-
let outcome = std::panic::catch_unwind(std::panic::AssertUnwindSafe(work)).unwrap_or_else(|_| {
49-
Err(AiTaskFailure::Other(
50-
"精修任务内部错误(panic)——请重试;若复现请反馈".to_string(),
51-
))
52-
});
53-
let elapsed_ms = started.elapsed().as_millis() as i64;
54-
match outcome {
55-
Ok((result, turns)) => {
56-
eprintln!(
57-
"[refine-task] task={} succeeded slices={} failed={} diff={}",
58-
task_id,
59-
result.slices,
60-
result.failed_slices,
61-
result.diff.len()
62-
);
63-
// v0.16.0(REQ-230):轨迹落库(提示词/回答全文——任务对话视图数据源)
64-
if let Some(json) = trajectory_to_json(&turns) {
65-
if let Err(e) = st.db.update_ai_task_trajectory(task_id, &json) {
66-
eprintln!("[refine-task] task={} 轨迹落库失败(不阻断): {}", task_id, e);
67-
}
68-
}
69-
{
70-
let mut tasks = st.ai_tasks.lock().unwrap_or_else(|e| e.into_inner());
71-
if let Some(entry) = tasks.get_mut(&task_id) {
72-
entry.result = serde_json::to_value(&result).ok();
73-
}
74-
}
75-
set_task(&st, task_id, AiTaskState::Succeeded);
76-
// REQ-247:终态帧(前端消息定格为最终摘要+双入口)
77-
emit_refine_stream(&st, task_id, RefineStreamFrame::Done {
78-
slices: result.slices,
79-
failed_slices: result.failed_slices,
80-
});
81-
// F1 修复(2026-08-21):精修调用上审计——REQ-140 轨迹可见化
82-
push_refine_audit(&st, &target_summary, "ok", Some(&result.model));
83-
// F2 任务中心:终态落库(写库失败不阻断——H2 设计)+ 保留策略
84-
// 裁剪(审查修复:trim 原只在启动时跑,运行期终态任务会累积——
85-
// 每次终态后清理超限旧终态,防表膨胀)
86-
let result_json = serde_json::to_string(&result).ok();
87-
let _ = st.db.finish_ai_task(
88-
task_id,
89-
"succeeded",
90-
result_json.as_deref(),
91-
None,
92-
elapsed_ms,
93-
);
94-
let _ = st.db.trim_ai_tasks();
95-
}
96-
Err(reason) => {
97-
// 打印具体 message——区分"未配置密钥"vs"密钥无效(401/403)"(真机排查)
98-
eprintln!(
99-
"[refine-task] task={} failed kind={} msg={}",
100-
task_id,
101-
reason.kind(),
102-
reason.message()
103-
);
104-
set_task(&st, task_id, AiTaskState::Failed { reason: reason.clone() });
105-
push_refine_audit(&st, &target_summary, "error", None);
106-
let _ = st.db.finish_ai_task(
107-
task_id,
108-
"failed",
109-
None,
110-
Some(&format!("{}: {}", reason.kind(), reason.message())),
111-
elapsed_ms,
112-
);
113-
let _ = st.db.trim_ai_tasks();
114-
}
115-
}
116-
}
18+
use super::RefineStreamFrame;
19+
use super::stream::emit_refine_stream;
20+
21+
/// 精修任务审计记录(F1:REQ-140 轨迹可见化——summary 不含原文,隐私红线)。
22+
/// v0.17.0:summary_ctx 泛化("session=1"/"note=3"——会话级/笔记级共用)。
23+
fn push_refine_audit(st: &AppState, summary_ctx: &str, result: &str, model: Option<&str>) {
24+
let now = crate::db_sessions_rows::unix_seconds();
25+
if let Ok(mut g) = st.ai_guardrails.lock() {
26+
g.push_audit(crate::ai_guardrails::AiAuditEntry {
27+
at_unix: now,
28+
upload_summary: format!("refine {} model={}", summary_ctx, model.unwrap_or("?")),
29+
result: result.to_string(),
30+
});
31+
}
32+
}
33+
34+
/// 任务收尾公共骨架(会话级/笔记级共用——v0.17.0 REQ-246 提取)。
35+
///
36+
/// @ai-context: 彻底检测加固(2026-08-21):spawn_blocking 的 JoinHandle 未被
37+
/// await——闭包内 panic 会被 tokio 吞掉,任务状态永久停在
38+
/// Pending(前端永久显示"任务排队中",无失败可重试)。
39+
/// catch_unwind 把 panic 归一为 Failed 状态,状态流转永不失联。
40+
/// @ai-context: F2-B4:单片失败重试后仍失败 → 保留已成功片(partial_failed
41+
/// 语义:failed_slices > 0,前端显示"部分成功 x/y 片")。
42+
pub(crate) fn run_refine_task_skeleton(
43+
st: AppState,
44+
task_id: u64,
45+
target_summary: String,
46+
work: impl FnOnce() -> Result<(AiRefineResult, Vec<AiTurn>), AiTaskFailure>,
47+
) {
48+
let started = std::time::Instant::now();
49+
let outcome = std::panic::catch_unwind(std::panic::AssertUnwindSafe(work)).unwrap_or_else(|_| {
50+
Err(AiTaskFailure::Other(
51+
"精修任务内部错误(panic)——请重试;若复现请反馈".to_string(),
52+
))
53+
});
54+
let elapsed_ms = started.elapsed().as_millis() as i64;
55+
match outcome {
56+
Ok((result, turns)) => {
57+
eprintln!(
58+
"[refine-task] task={} succeeded slices={} failed={} diff={}",
59+
task_id,
60+
result.slices,
61+
result.failed_slices,
62+
result.diff.len()
63+
);
64+
// v0.16.0(REQ-230):轨迹落库(提示词/回答全文——任务对话视图数据源)
65+
if let Some(json) = trajectory_to_json(&turns) {
66+
if let Err(e) = st.db.update_ai_task_trajectory(task_id, &json) {
67+
eprintln!("[refine-task] task={} 轨迹落库失败(不阻断): {}", task_id, e);
68+
}
69+
}
70+
{
71+
let mut tasks = st.ai_tasks.lock().unwrap_or_else(|e| e.into_inner());
72+
if let Some(entry) = tasks.get_mut(&task_id) {
73+
entry.result = serde_json::to_value(&result).ok();
74+
}
75+
}
76+
set_task(&st, task_id, AiTaskState::Succeeded);
77+
// REQ-247:终态帧(前端消息定格为最终摘要+双入口)
78+
emit_refine_stream(&st, task_id, RefineStreamFrame::Done {
79+
slices: result.slices,
80+
failed_slices: result.failed_slices,
81+
});
82+
// F1 修复(2026-08-21):精修调用上审计——REQ-140 轨迹可见化
83+
push_refine_audit(&st, &target_summary, "ok", Some(&result.model));
84+
// F2 任务中心:终态落库(写库失败不阻断——H2 设计)+ 保留策略
85+
// 裁剪(审查修复:trim 原只在启动时跑,运行期终态任务会累积——
86+
// 每次终态后清理超限旧终态,防表膨胀)
87+
let result_json = serde_json::to_string(&result).ok();
88+
let _ = st.db.finish_ai_task(
89+
task_id,
90+
"succeeded",
91+
result_json.as_deref(),
92+
None,
93+
elapsed_ms,
94+
);
95+
let _ = st.db.trim_ai_tasks();
96+
}
97+
Err(reason) => {
98+
// 打印具体 message——区分"未配置密钥"vs"密钥无效(401/403)"(真机排查)
99+
eprintln!(
100+
"[refine-task] task={} failed kind={} msg={}",
101+
task_id,
102+
reason.kind(),
103+
reason.message()
104+
);
105+
set_task(&st, task_id, AiTaskState::Failed { reason: reason.clone() });
106+
push_refine_audit(&st, &target_summary, "error", None);
107+
let _ = st.db.finish_ai_task(
108+
task_id,
109+
"failed",
110+
None,
111+
Some(&format!("{}: {}", reason.kind(), reason.message())),
112+
elapsed_ms,
113+
);
114+
let _ = st.db.trim_ai_tasks();
115+
}
116+
}
117+
}

0 commit comments

Comments
 (0)