重构: F-09 batch4 启用多会话上线(决策e真并发·F-09完整)
- IPC签名加conv_id: ai_is_generating/ai_chat_send/ai_chat_force_send/ai_chat_stop + 内部移除active一致性校验(后台conv可操作) - 删switch readonly分支 + 删ai_conversation_create强制结束旧loop(决策e,切走旧loop不退出) - 删双写桥接+顶层字段: messages/generating/stop_flag/notify/iteration_used/agent_language/model_override/session_trust全删,per_conv唯一真相源 + 删双写代码(全模块)+ 删fallback读 + pending_approvals retain目标conv - 前端: api/ai.ts+useAiSend/useAiWindow传conv_id 保留: session_state(SW预留标allow)/readonly(前端不读);零调用方预留按用户指导保留 主代兜底: cargo check --workspace 0 + vue-tsc 0 + test 98 + grep顶层删除/per_conv唯一印证 F-09完整(batch1 PerConvState+batch2迁移+batch5限流调整+batch8恢复+batch4上线);双会话人工验收留用户
This commit is contained in:
@@ -279,12 +279,15 @@ fn workspace_root_str() -> String {
|
||||
use std::path::PathBuf;
|
||||
|
||||
/// AI 会话内状态(Mutex 保护)
|
||||
///
|
||||
/// F-260616-09 B 批4(决策 e 真并发上线):会话级状态全部迁入 [`per_conv`](Self::per_conv)
|
||||
/// HashMap(按 conversation_id 切分),顶层单例字段(messages/generating/stop_flag/notify/
|
||||
/// iteration_used/agent_language/model_override/session_trust)已退役删除。批2 双写桥接代码
|
||||
/// (IPC 写路径的顶层双写行 + 各文件 fallback 读)在批4 全部清理完毕,per_conv 是唯一真相源。
|
||||
pub struct AiSession {
|
||||
/// 对话历史(ContextManager:唯一消息真相源,裁剪仅影响发送视图,不影响持久化)
|
||||
pub messages: ContextManager,
|
||||
/// 当前提供商 ID
|
||||
pub active_provider_id: Option<String>,
|
||||
/// 当前活跃对话 ID
|
||||
/// 当前活跃对话 ID(应用级路由键:当前展示的 conv;切走即变,不影响后台 loop)
|
||||
pub active_conversation_id: Option<String>,
|
||||
/// 活跃对话创建时间(懒创建:首条消息落库前仅存内存,upsert 时用作 created_at)
|
||||
pub active_conv_created_at: Option<String>,
|
||||
@@ -300,111 +303,51 @@ pub struct AiSession {
|
||||
/// **未来**:若多会话并发、审批量显著增大,可考虑加二级索引
|
||||
/// (`conversation_id → Vec<tool_call_id>`)把按会话过滤降到 O(1)。
|
||||
pub pending_approvals: HashMap<String, PendingApproval>,
|
||||
/// 是否正在生成
|
||||
pub generating: bool,
|
||||
/// 当前 agent 循环的语言设置(用于审批后恢复循环)
|
||||
pub agent_language: Option<String>,
|
||||
/// 停止信号:ai_chat_stop 置位,agentic loop / stream_llm 检测后尽快退出
|
||||
pub stop_flag: Arc<AtomicBool>,
|
||||
/// 即时停止唤醒(B-260615-14):stream_llm 阻塞在 `stream.next()` 等 chunk 时,
|
||||
/// `notify_one()` 立即唤醒跳出 select!,不再等 30s 心跳 tick 轮询。
|
||||
/// F-260616-09 B 阶段 多会话并发架构 — 会话级状态按 conversation_id 切分。
|
||||
///
|
||||
/// 语义边界:Notify 仅承载「即时唤醒」这一职责——停止的真值仍由 `stop_flag`
|
||||
/// (AtomicBool)决定。`ai_chat_stop` 置 `stop_flag=true` 后调 `notify_one()`;
|
||||
/// 被唤醒的 select! 分支再判 `stop_flag` 决定退出或继续,避免 Notify 的通知
|
||||
/// 与「真的该停」耦合(防误唤醒继续跑)。
|
||||
///
|
||||
/// `Arc<Notify>` 可 Clone(Notify 内部用 Atomic 计数,Clone 等价于 Arc 引用 +1),
|
||||
/// 不影响 AiSession 其他字段语义。
|
||||
pub notify: Arc<tokio::sync::Notify>,
|
||||
/// agentic loop 累计已跑 iteration 计数(F-260616-11 决策 a)。
|
||||
///
|
||||
/// **语义**:「下一轮起算值」。run_agentic_loop 每轮写 `iteration + 1`,审批等待/
|
||||
/// 达 max 暂停退出时即「当前轮+1」。续跑时由调用方决定如何接:
|
||||
/// - **审批续跑**(ai_approve):传本字段作 start_iteration(**累计**,防多次审批反复
|
||||
/// 跑满 max_iterations 致 token 失控);
|
||||
/// - **达 max 续跑**(ai_continue_loop):重置本字段=0 + 传 0(**重计**,F-260616-03
|
||||
/// 决策 a,用户点继续=授权重来);
|
||||
/// - **新消息首次发送**(ai_chat_send):重置本字段=0(新对话生命周期从头计数)。
|
||||
///
|
||||
/// 单例一份:当前 AiSession 是全局单例(F-09 B 多会话架构落地时改 per-conv)。
|
||||
pub iteration_used: usize,
|
||||
/// F-01 阶段6: 用户指定模型 override(主对话专用)。
|
||||
///
|
||||
/// **语义**:前端顶部下拉「指定」模式选的 model_id;None/空=自动模式(路由器选)。
|
||||
/// 仅主对话(agentic)生效——标题/扫描/灵感等内部调用仍走路由,不读此字段。
|
||||
///
|
||||
/// **兜底原则**(agentic.rs run_agentic_loop 内强制):override 非空且在该 provider
|
||||
/// model_configs 池中 → 用 override;否则用 resolved_model(路由结果)。绝不让
|
||||
/// override 导致无模型。
|
||||
///
|
||||
/// **生命周期**:ai_chat_send/regenerate/edit 写入(随发消息带);新对话/会话切换时清空
|
||||
/// (防上一对话 override 残留污染新对话)。try_continue_agent_loop 读此字段透传给续跑
|
||||
/// loop(审批续跑/达 max 续跑保持 override 一致,语义=同一主对话)。
|
||||
pub model_override: Option<String>,
|
||||
/// AE-2025-04 会话级信任(Session Trust):内存态,随会话销毁,不落库。
|
||||
/// 同会话已批准过同类操作(同工具+同目录 TrustKey 命中)后自动放行,跳过 pending 审批 +
|
||||
/// 二次确认(AE-05)。换会话(new/switch)必须清空重审(见 ai_conversation_create /
|
||||
/// ai_conversation_switch 清字段)。
|
||||
pub session_trust: HashSet<TrustKey>,
|
||||
/// F-260616-09 B 阶段批1 多会话并发架构:会话级状态按 conversation_id 切分。
|
||||
///
|
||||
/// **批2 状态(per-conv 是新真相源)**:批2 已迁移所有调用方(agentic.rs loop +
|
||||
/// **批4 状态(per_conv 唯一真相源)**:批2-批4 已迁移所有调用方(agentic.rs loop +
|
||||
/// GeneratingGuard + try_continue + commands.rs IPC 写路径 + audit.rs process_tool_calls +
|
||||
/// conversation.rs save + title.rs + knowledge_inject.rs + lib.rs L0)读写 `session.conv(conv_id).*`
|
||||
/// / `session.conv_read(conv_id)`。顶层单例字段(messages/generating/stop_flag 等)批2 期间
|
||||
/// 作双写桥接保留(IPC 仍双写顶层保 ai_is_generating 等读路径兼容,批4 IPC 迁移后顶层移除)。
|
||||
/// conversation.rs save + title.rs + knowledge_inject.rs + lib.rs L0)读写
|
||||
/// `session.conv(conv_id).*` / `session.conv_read(conv_id)`。顶层单例会话级字段已全部删除。
|
||||
pub per_conv: HashMap<String, PerConvState>,
|
||||
}
|
||||
|
||||
impl AiSession {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
messages: ContextManager::new(ContextConfig::default()),
|
||||
active_provider_id: None,
|
||||
active_conversation_id: None,
|
||||
active_conv_created_at: None,
|
||||
pending_approvals: HashMap::new(),
|
||||
generating: false,
|
||||
agent_language: None,
|
||||
stop_flag: Arc::new(AtomicBool::new(false)),
|
||||
notify: Arc::new(tokio::sync::Notify::new()),
|
||||
iteration_used: 0,
|
||||
model_override: None,
|
||||
session_trust: HashSet::new(),
|
||||
per_conv: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// 取即时停止唤醒器引用(B-260615-14)。
|
||||
/// 推导指定会话读侧状态视图(只读,不写任何字段)。
|
||||
///
|
||||
/// 供 `stream_llm`(接 `&Notify` 进 select! 监听 `notified()`)与 `ai_chat_stop`
|
||||
/// (置 `stop_flag` 后调 `notify_one()`)等外部持有方获取同一 Notify 实例。
|
||||
/// 返回 `&Notify` 而非 clone,避免不必要的 Arc 引用计数调整。
|
||||
pub fn stop_notify(&self) -> &tokio::sync::Notify {
|
||||
&self.notify
|
||||
}
|
||||
|
||||
/// 推导会话读侧状态视图(只读,不写任何字段)。
|
||||
/// 优先级:该 conv 有 pending 审批 → [`SessionState::AwaitingApproval`];
|
||||
/// 否则 per_conv.generating → [`SessionState::Streaming`];否则 → [`SessionState::Idle`]。
|
||||
///
|
||||
/// 优先级:`pending_approvals` 非空 → [`SessionState::AwaitingApproval`];
|
||||
/// 否则 `generating` → [`SessionState::Streaming`];否则 → [`SessionState::Idle`]。
|
||||
/// 收敛点:调用方读「某会话处于什么阶段」时统一走本方法,替代散布的
|
||||
/// `if pending / if generating` 三路组合。
|
||||
///
|
||||
/// 收敛点:调用方读「会话处于什么阶段」时统一走本方法,替代散布的
|
||||
/// `if !pending_approvals.is_empty() / if generating` 三路组合。
|
||||
/// F-260616-09 B 批4:per_conv 化后接 conv_id 参数(顶层 generating 已删除);
|
||||
/// pending 判断改为按 conv_id 过滤(决策 e 真并发下各 conv 独立)。
|
||||
///
|
||||
/// # 待替换调用点(本次仅新增视图,不替换调用点)
|
||||
///
|
||||
/// - `agentic.rs:283` — agent loop 入口/恢复处对 generating + 审批的组合判断
|
||||
/// - `commands.rs:250` — `ai_pending_tool_calls` 等读取前的状态门控
|
||||
/// - `commands.rs:451` — 审批提交/会话复位路径的状态判断
|
||||
/// - `agentic.rs` — agent loop 入口/恢复处对 generating + 审批的组合判断
|
||||
/// - `commands.rs` — `ai_pending_tool_calls` 等读取前的状态门控
|
||||
/// - `commands.rs` — 审批提交/会话复位路径的状态判断
|
||||
///
|
||||
/// 上述三处替换由 P0 任务承接,本方法先就位供其切换。
|
||||
#[allow(dead_code)] // SW-260618-21 b:预留(P0 终态化任务承接·当前 0 调用·保留扩展点)
|
||||
pub fn session_state(&self) -> SessionState {
|
||||
if !self.pending_approvals.is_empty() {
|
||||
pub fn session_state(&self, conv_id: &str) -> SessionState {
|
||||
let has_pending = self.pending_approvals.values()
|
||||
.any(|a| a.conversation_id.as_deref() == Some(conv_id));
|
||||
if has_pending {
|
||||
SessionState::AwaitingApproval
|
||||
} else if self.generating {
|
||||
} else if self.conv_read(conv_id).map(|c| c.generating).unwrap_or(false) {
|
||||
SessionState::Streaming
|
||||
} else {
|
||||
SessionState::Idle
|
||||
|
||||
Reference in New Issue
Block a user