优化: df-ai/df-storage/df-nodes 底层加固: 上下文/模型/节点/仓储
df-ai: - context/mod.rs + manager_tests.rs: 上下文管理调整 + 测试同步(48 行) - openai_compat.rs: OpenAI 兼容层增强(+51) - presets/models.json: 模型预设 +18 df-storage: - crud/conversation_repo.rs: 会话仓储(loop_state 相关) - crud/task_repo.rs: 任务仓储 - crud/mod.rs: 导出调整 df-nodes: - task_advance_node.rs: 任务推进节点 验收: cargo check -p devflow EXIT 0。
This commit is contained in:
@@ -1094,4 +1094,43 @@ mod tests {
|
||||
.expect("snapshot");
|
||||
assert_eq!(snapshot, r#"[{"seq":0}]"#, "冲突时应保留首次快照");
|
||||
}
|
||||
|
||||
/// #904 契约:set_loop_state 写 "parked" 后 get_by_id 可读回(停靠出口写侧单点)。
|
||||
/// 锁死「停靠状态写入收敛进 emit_task_parked」依赖的 repo 能力——若 set_loop_state
|
||||
/// 或 from_row 的 loop_state 映射回归,停靠卡「继续」将再次撞「AI 未在暂停态」。
|
||||
#[tokio::test]
|
||||
async fn set_loop_state_parked_roundtrip() {
|
||||
let db = Database::open_in_memory().await.expect("open_in_memory");
|
||||
let repo = AiConversationRepo::new(&db);
|
||||
repo.insert(AiConversationRecord {
|
||||
id: "c1".into(),
|
||||
title: None,
|
||||
messages: "[]".into(),
|
||||
provider_id: None,
|
||||
model: None,
|
||||
models: None,
|
||||
archived: false,
|
||||
pinned: false,
|
||||
prompt_tokens: Some(0),
|
||||
completion_tokens: Some(0),
|
||||
pinned_goals: None,
|
||||
pending_approvals: None,
|
||||
created_at: "0".into(),
|
||||
updated_at: "0".into(),
|
||||
origin: None,
|
||||
boundary_json: None,
|
||||
loop_state: None,
|
||||
})
|
||||
.await
|
||||
.expect("insert");
|
||||
|
||||
assert!(repo.set_loop_state("c1", Some("parked")).await.expect("set"));
|
||||
let got = repo.get_by_id("c1").await.expect("get").expect("row");
|
||||
assert_eq!(got.loop_state.as_deref(), Some("parked"), "写 parked 应可读回");
|
||||
|
||||
// 清态(running 收敛出口)可读回 None
|
||||
assert!(repo.set_loop_state("c1", None).await.expect("clear"));
|
||||
let cleared = repo.get_by_id("c1").await.expect("get").expect("row");
|
||||
assert_eq!(cleared.loop_state, None, "清 loop_state 应可读回 None");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,6 +72,7 @@ macro_rules! impl_repo {
|
||||
update => |$u_conn:ident, $u_rec:ident| $update_body:expr
|
||||
) => {
|
||||
$(#[$meta])*
|
||||
#[derive(Clone)]
|
||||
pub struct $name {
|
||||
conn: Arc<Mutex<Connection>>,
|
||||
}
|
||||
|
||||
@@ -249,6 +249,34 @@ impl TaskRepo {
|
||||
.map_err(storage_err)?
|
||||
}
|
||||
|
||||
/// 判定任务是否在回收站(soft_delete 已设 deleted_at)。
|
||||
///
|
||||
/// 专供推进链 CAS 失败后的**原因区分**:`advance_status_atomic` 的 CAS SQL 带
|
||||
/// `deleted_at IS NULL` 条件,软删任务必然 affected==0 返回 None;调用方据此
|
||||
/// 回查本方法,把「已变更(并发推进或旁路修改)」这个误导性错误,精确改写为
|
||||
/// 「任务在回收站」,让 LLM/用户知道该先恢复而非重试。
|
||||
///
|
||||
/// 返回 true = 任务存在且 deleted_at 非 NULL(在回收站);false = 不存在或未删。
|
||||
/// 单列轻查询(SELECT 1),不拉全记录,失败回查开销可忽略。
|
||||
pub async fn is_deleted(&self, id: &str) -> Result<bool> {
|
||||
let conn = self.conn.clone();
|
||||
let id = id.to_owned();
|
||||
tokio::task::spawn_blocking(move || {
|
||||
let guard = conn.blocking_lock();
|
||||
let mut stmt = guard
|
||||
.prepare("SELECT 1 FROM tasks WHERE id = ?1 AND deleted_at IS NOT NULL")
|
||||
.map_err(storage_err)?;
|
||||
let found = stmt
|
||||
.query_row(params![id], |_| Ok(()))
|
||||
.optional()
|
||||
.map_err(storage_err)?
|
||||
.is_some();
|
||||
Ok(found)
|
||||
})
|
||||
.await
|
||||
.map_err(storage_err)?
|
||||
}
|
||||
|
||||
/// 按 id 查未删除任务(`WHERE id AND deleted_at IS NULL`)。
|
||||
///
|
||||
/// 专供 [`validate_parent_id`](super::task_validation) 的父任务存在性检查:
|
||||
|
||||
Reference in New Issue
Block a user