重构: crud.rs按表拆分(SMELL-P1-9) + F-09批1 PerConvState数据结构
- SMELL-P1-9: crud.rs 2212行→crud/6文件(mod/settings/project_repo/task_repo/conversation_repo/idea_repo) re-export pub use *_repo::* 零调用方改动,宏 pub(crate) use + 子模块 use super::impl_repo 基线测试锁12表白名单+13Repo构造 - F-09 批1: PerConvState struct(9字段对齐AiSession::new)+AiSession.per_conv HashMap+conv()/conv_read()访问器+3单测 纯新增无行为变化,b-1主代自主裁决采纳,批2迁移承接 主代统一兜底: cargo check --workspace 0 + df-storage 35+11 + devflow 96 passed
This commit is contained in:
@@ -347,6 +347,13 @@ pub struct AiSession {
|
||||
/// 二次确认(AE-05)。换会话(new/switch)必须清空重审(见 ai_conversation_create /
|
||||
/// ai_conversation_switch 清字段)。
|
||||
pub session_trust: HashSet<TrustKey>,
|
||||
/// F-260616-09 B 阶段批1 多会话并发架构:会话级状态按 conversation_id 切分。
|
||||
///
|
||||
/// **批1 状态(纯新增无行为变化)**:此 HashMap 初始空,批1 不迁移任何调用方 ——
|
||||
/// 现有所有路径继续读写顶层单例字段(messages/generating/stop_flag 等),完全无感。
|
||||
/// 批2 承接迁移:调用方改走 `session.conv(conv_id).*`,迁移完成后顶层单例字段废弃删除。
|
||||
#[allow(dead_code)] // F-09 B 批1 共存期:批2 迁移承接(0 写入方)
|
||||
pub per_conv: HashMap<String, PerConvState>,
|
||||
}
|
||||
|
||||
impl AiSession {
|
||||
@@ -364,6 +371,7 @@ impl AiSession {
|
||||
iteration_used: 0,
|
||||
model_override: None,
|
||||
session_trust: HashSet::new(),
|
||||
per_conv: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -401,6 +409,194 @@ impl AiSession {
|
||||
SessionState::Idle
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// F-260616-09 B 阶段批1 PerConvState 访问器(设计 §2.1.2)
|
||||
//
|
||||
// 批1 仅定义访问器,**不迁移调用方**(批2 承接)。当前 0 调用方,行为完全不变。
|
||||
// 批2 迁移:所有 `session.messages` / `session.generating` 等读写改走
|
||||
// `session.conv(conv_id).*`(写)/ `session.conv_read(conv_id)`(读)。
|
||||
// ============================================================
|
||||
|
||||
/// 取或惰性创建指定会话的可变状态(设计 §2.1.2)。
|
||||
///
|
||||
/// 不存在则 `entry().or_insert_with(PerConvState::new)` 创建一份默认状态,
|
||||
/// 初值对齐 [`AiSession::new`] / [`PerConvState::new`]。
|
||||
/// 已存在则返回同一实例引用(同 conv_id 复用)。
|
||||
///
|
||||
/// **批1 状态**:0 调用方(批2 迁移承接)。行为不变。
|
||||
#[allow(dead_code)] // F-09 B 批1 共存期:批2 迁移承接(0 调用方)
|
||||
pub fn conv(&mut self, conv_id: &str) -> &mut PerConvState {
|
||||
self.per_conv
|
||||
.entry(conv_id.to_string())
|
||||
.or_insert_with(PerConvState::new)
|
||||
}
|
||||
|
||||
/// 只读取指定会话状态(不创建)。
|
||||
///
|
||||
/// 未创建的 conv_id 返 `None`,已创建返 `Some(&PerConvState)`。用于读取路径(如
|
||||
/// 列举/查询)避免误触发惰性创建。
|
||||
///
|
||||
/// **批1 状态**:0 调用方(批2 迁移承接)。行为不变。
|
||||
#[allow(dead_code)] // F-09 B 批1 共存期:批2 迁移承接(0 调用方)
|
||||
pub fn conv_read(&self, conv_id: &str) -> Option<&PerConvState> {
|
||||
self.per_conv.get(conv_id)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests_f09_per_conv {
|
||||
use super::*;
|
||||
|
||||
/// 验证 PerConvState::new() 各字段初值正确(对齐 AiSession::new 基线)。
|
||||
#[test]
|
||||
fn test_per_conv_state_new() {
|
||||
let s = PerConvState::new();
|
||||
assert!(!s.generating, "generating 初值应为 false");
|
||||
assert_eq!(s.iteration_used, 0, "iteration_used 初值应为 0");
|
||||
assert!(s.agent_language.is_none(), "agent_language 初值应为 None");
|
||||
assert!(s.model_override.is_none(), "model_override 初值应为 None");
|
||||
assert!(s.session_trust.is_empty(), "session_trust 初值应为空 HashSet");
|
||||
assert!(s.created_at.is_none(), "created_at 初值应为 None");
|
||||
// stop_flag 初值 false(与 AiSession::new 对齐)
|
||||
assert!(
|
||||
!s.stop_flag.load(std::sync::atomic::Ordering::SeqCst),
|
||||
"stop_flag 初值应为 false"
|
||||
);
|
||||
// notify 是 Arc<Notify>,无可见状态,仅验证不 panic(构造即视为对齐)
|
||||
// messages 是 ContextManager,内部状态;仅验证能取到引用(初值对齐由 ContextManager::new 保证)
|
||||
let _messages_ref: &ContextManager = &s.messages;
|
||||
}
|
||||
|
||||
/// 验证 conv() 首次创建 + 二次复用同一实例。
|
||||
#[test]
|
||||
fn test_conv_lazy_create() {
|
||||
let mut session = AiSession::new();
|
||||
assert!(session.per_conv.is_empty(), "新建 session 的 per_conv 应为空");
|
||||
|
||||
// 首次 conv() 创建
|
||||
let ptr_first = {
|
||||
let s = session.conv("conv-a");
|
||||
assert!(!s.generating);
|
||||
assert_eq!(s.iteration_used, 0);
|
||||
s as *const PerConvState
|
||||
};
|
||||
assert_eq!(session.per_conv.len(), 1, "首次 conv() 后 per_conv 应有 1 项");
|
||||
|
||||
// 二次 conv(同 conv_id) 返回同一实例(指针相等)
|
||||
let ptr_second = session.conv("conv-a") as *const PerConvState;
|
||||
assert_eq!(
|
||||
ptr_first, ptr_second,
|
||||
"同 conv_id 二次 conv() 应返回同一实例"
|
||||
);
|
||||
assert_eq!(session.per_conv.len(), 1, "二次 conv(同 id) 不应新增项");
|
||||
|
||||
// 不同 conv_id 创建独立实例
|
||||
let _s_b = session.conv("conv-b");
|
||||
assert_eq!(session.per_conv.len(), 2, "不同 conv_id 应新建独立实例");
|
||||
}
|
||||
|
||||
/// 验证 conv_read() 未创建的 conv_id 返 None(只读,不惰性创建)。
|
||||
#[test]
|
||||
fn test_conv_read_none() {
|
||||
let session = AiSession::new();
|
||||
// 未创建的 conv_id
|
||||
assert!(
|
||||
session.conv_read("never-created").is_none(),
|
||||
"未创建的 conv_id conv_read() 应返 None"
|
||||
);
|
||||
// conv_read 不触发创建(per_conv 仍空)
|
||||
assert!(
|
||||
session.per_conv.is_empty(),
|
||||
"conv_read 不应触发惰性创建"
|
||||
);
|
||||
|
||||
// 已创建的 conv_id 返 Some(只读访问)
|
||||
let mut session = session;
|
||||
let _ = session.conv("exists");
|
||||
assert!(
|
||||
session.conv_read("exists").is_some(),
|
||||
"已创建的 conv_id conv_read() 应返 Some"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// F-260616-09 B 阶段 多会话并发架构 — PerConvState(批1 纯新增)
|
||||
//
|
||||
// 设计文档: docs/02-架构设计/F-09-多会话并发架构设计-2026-06-19.md §2.1.2
|
||||
//
|
||||
// 批1 范围(纯新增无行为变化):
|
||||
// - 仅新增 PerConvState struct + new() + AiSession.per_conv 字段 + conv()/conv_read() 访问器
|
||||
// - **不迁移**现有单例字段(messages/generating/stop_flag/notify/iteration_used/
|
||||
// agent_language/model_override/session_trust 保留原样),不迁移现有调用方(批2 承接)
|
||||
// - per_conv HashMap 初始空,旧调用路径继续读写 AiSession 顶层单例字段,完全无感
|
||||
//
|
||||
// 共存期:批1 后顶层单例字段(唯一真相源)与 per_conv(全空,无写入)并存,行为不变。
|
||||
// 批2 迁移:把所有调用方从 session.messages / session.generating 等改为 session.conv(conv_id).*,
|
||||
// 迁移完成后顶层单例字段废弃删除(批3)。
|
||||
// ============================================================
|
||||
|
||||
/// 单会话级状态(F-260616-09 B 阶段批1 纯新增,设计 §2.1.2)。
|
||||
///
|
||||
/// 持有当前会话(按 conversation_id 切分)私有的可变状态。批1 仅定义 + 惰性创建访问器,
|
||||
/// **不迁移** AiSession 顶层单例字段(共存期顶层字段仍是唯一真相源)。
|
||||
///
|
||||
/// 字段初值逐字对齐 [`AiSession::new`](#method.new),保证批2 迁移调用方时行为不变。
|
||||
#[allow(dead_code)] // F-09 B 批1 共存期:0 调用方(批2 迁移承接),字段待迁移后消费
|
||||
pub struct PerConvState {
|
||||
/// 对话历史(ContextManager:会话级消息真相源,裁剪仅影响发送视图)
|
||||
pub messages: ContextManager,
|
||||
/// 是否正在生成
|
||||
pub generating: bool,
|
||||
/// 停止信号(会话级):ai_chat_stop 置位,agentic loop / stream_llm 检测后尽快退出
|
||||
pub stop_flag: Arc<AtomicBool>,
|
||||
/// 即时停止唤醒(会话级):阻塞在 stream.next() 时 notify_one() 立即唤醒跳出 select!
|
||||
pub notify: Arc<tokio::sync::Notify>,
|
||||
/// agentic loop 累计已跑 iteration 计数(语义同 AiSession.iteration_used)
|
||||
pub iteration_used: usize,
|
||||
/// 当前 agent 循环的语言设置(用于审批后恢复循环)
|
||||
pub agent_language: Option<String>,
|
||||
/// 用户指定模型 override(主对话专用,语义同 AiSession.model_override)
|
||||
pub model_override: Option<String>,
|
||||
/// 会话级信任(Session Trust):随会话销毁,不落库
|
||||
pub session_trust: HashSet<TrustKey>,
|
||||
/// 会话创建时间(懒创建:首条消息落库前仅存内存,upsert 时用作 created_at)
|
||||
pub created_at: Option<String>,
|
||||
}
|
||||
|
||||
impl PerConvState {
|
||||
/// 新建默认会话级状态。
|
||||
///
|
||||
/// 各字段初值**逐字对齐 [`AiSession::new`]** —— 批2 迁移调用方时行为不变。
|
||||
/// - messages: ContextManager::new(ContextConfig::default())
|
||||
/// - generating: false
|
||||
/// - stop_flag: Arc::new(AtomicBool::new(false))
|
||||
/// - notify: Arc::new(tokio::sync::Notify::new())
|
||||
/// - iteration_used: 0
|
||||
/// - agent_language: None
|
||||
/// - model_override: None
|
||||
/// - session_trust: HashSet::new()
|
||||
/// - created_at: None(批1 新增字段,AiSession 现有 active_conv_created_at 同语义)
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
messages: ContextManager::new(ContextConfig::default()),
|
||||
generating: false,
|
||||
stop_flag: Arc::new(AtomicBool::new(false)),
|
||||
notify: Arc::new(tokio::sync::Notify::new()),
|
||||
iteration_used: 0,
|
||||
agent_language: None,
|
||||
model_override: None,
|
||||
session_trust: HashSet::new(),
|
||||
created_at: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for PerConvState {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
/// 待审批的工具调用
|
||||
|
||||
Reference in New Issue
Block a user