优化: L2统一状态机完整(ConvState持久化+事件暴露+前端status联合)

1a PerConvState.conv_state持久化(入口/reset/drop写收敛,CONV_STATE_ENABLED门控)
1b AiChatEvent加AiConvStateChanged变体,guard持app_handle在迁移点emit
1c 前端convStates真相源+停止按钮三态(status union)+MaxRoundsCard读conv_state
双轨过渡:enum视图与generating bool并行,off降级可回退
This commit is contained in:
2026-06-22 00:34:43 +08:00
parent d2cada97cd
commit cdb227f999
9 changed files with 264 additions and 13 deletions

View File

@@ -43,6 +43,8 @@ pub mod stream_recv;
pub mod title;
pub mod tool_registry;
use agentic::conv_state::ConvState;
use std::collections::{HashMap, HashSet};
use std::sync::Arc;
use std::sync::atomic::AtomicBool;
@@ -197,6 +199,20 @@ pub enum AiChatEvent {
options: Vec<String>,
conversation_id: Option<String>,
},
/// L2 统一状态机(批2 1b,2026-06-22):对话生命周期状态变更通知。
///
/// 后端 `ConvState`(Idle/Generating/Stopping/Error/Compressed)经写收敛
/// (入口/guard reset/drop 迁移)后 emit 此事件,前端 status union 据此更新展示态
/// (替代散布的 generating bool 派生判断,收敛状态机读侧到事件流)。
///
/// 渐进/兜底:`CONV_STATE_ENABLED` 开关门控 —— off 时不 emit(前端按旧 bool 行为派生,
/// 向后兼容);on 时此事件与既有 generating 行为双轨,前端可按需切换消费源。
/// `conv_state` 序列化为 snake_case(idle/generating/stopping/error/compressed),
/// 供前端 union discriminator 直接使用。
AiConvStateChanged {
conv_state: ConvState,
conversation_id: Option<String>,
},
}
// ============================================================
@@ -626,6 +642,10 @@ pub struct PerConvState {
pub messages: ContextManager,
/// 是否正在生成
pub generating: bool,
/// L2 统一状态机:对话生命周期状态(单一真相源,批2 持久化供读侧/前端消费)。
/// 写收敛:经 GeneratingGuard/入口 transition_to 守卫迁移,非直接赋值。
/// 与 generating bool 双轨过渡(bool 核心复位,enum 视图),批2+ 读侧迁移后 enum 主。
pub conv_state: ConvState,
/// 停止信号(会话级):ai_chat_stop 置位,agentic loop / stream_llm 检测后尽快退出
pub stop_flag: Arc<AtomicBool>,
/// 即时停止唤醒(会话级):阻塞在 stream.next() 时 notify_one() 立即唤醒跳出 select!
@@ -676,6 +696,7 @@ impl PerConvState {
Self {
messages: ContextManager::new(ContextConfig::default()),
generating: false,
conv_state: ConvState::Idle,
stop_flag: Arc::new(AtomicBool::new(false)),
notify: Arc::new(tokio::sync::Notify::new()),
iteration_used: 0,