修复: DeepSeek 400 全量扫描 + 队列 per-conv 隔离
- openai_compat: 扫描所有 assistant 消息剥离 orphan tool_calls(原仅查末条) - queue 加 conversationId 字段,按会话精准 drain - regenerate/editMessage 只清本会话排队消息 - newConversation 保留旧会话排队消息 - AiError 只清出错会话的队列项
This commit is contained in:
@@ -45,7 +45,7 @@ pub mod stream_recv;
|
||||
pub mod title;
|
||||
pub mod tool_registry;
|
||||
|
||||
use agentic::conv_state::ConvState;
|
||||
use agentic::conv_state::{ConvState, ConvStateStore};
|
||||
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::sync::Arc;
|
||||
@@ -558,18 +558,13 @@ impl AiSession {
|
||||
/// `ai_chat_stop`(commands/chat.rs) 改调本方法替代各自手写的 has_pending,
|
||||
/// 收敛两处状态机判定到单一入口。
|
||||
///
|
||||
/// 兜底/回退:若本方法判定异常需快速回退手写 has_pending,调用方注释已保留原
|
||||
/// 三路组合(generating + path_auth + risk)字面量,改一行即可切回。
|
||||
pub fn session_state(&self, conv_id: &str) -> SessionState {
|
||||
// 阶段3a 单真相源合并后:两类挂起(path_auth + risk)合一进 pending_approvals,
|
||||
// kind 字段区分语义但状态机只看「有无挂起」,单表 any 一次即可(消除原双表 OR 合并判断)。
|
||||
/// B-Phase3:conv_state 读侧切 ConvStateStore(单源收敛)。
|
||||
pub fn session_state(&self, conv_id: &str, conv_states: &ConvStateStore) -> 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.conv_read(conv_id).map(|c| c.conv_state.is_active()).unwrap_or(false) {
|
||||
// 读侧(双轨收口批2):改读 conv_state.is_active()(Generating+Compressed),判断是否在
|
||||
// 活跃生成。is_active() 与原 generating 语义一致(压缩期间 loop 仍活跃,应判为 Streaming)。
|
||||
} else if conv_states.is_active(conv_id) {
|
||||
SessionState::Streaming
|
||||
} else {
|
||||
SessionState::Idle
|
||||
@@ -613,7 +608,8 @@ mod tests_f09_per_conv {
|
||||
fn test_per_conv_state_new() {
|
||||
let s = PerConvState::new();
|
||||
// 批3 收口:generating bool 已退役,生成态初值校验改读 conv_state(应为 Idle)。
|
||||
assert_eq!(s.conv_state, ConvState::Idle, "conv_state 初值应为 Idle");
|
||||
// B-Phase3:conv_state 已迁 ConvStateStore,PerConvState 不再持有。
|
||||
// conv_state 由 guard.new(Idle→Generating)经 conv_states.transition 迁移。
|
||||
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");
|
||||
@@ -644,7 +640,8 @@ mod tests_f09_per_conv {
|
||||
let ptr_first = {
|
||||
let s = session.conv("conv-a");
|
||||
// 批3 收口:generating bool 已退役,首建会话生成态应为 Idle。
|
||||
assert_eq!(s.conv_state, ConvState::Idle);
|
||||
// B-Phase3:conv_state 已迁 ConvStateStore。
|
||||
// conv_state 初值由 guard/入口保证,PerConvState 不再持有。
|
||||
assert_eq!(s.iteration_used, 0);
|
||||
s as *const PerConvState
|
||||
};
|
||||
@@ -816,10 +813,7 @@ mod tests_f09_per_conv {
|
||||
pub struct PerConvState {
|
||||
/// 对话历史(ContextManager:会话级消息真相源,裁剪仅影响发送视图)
|
||||
pub messages: ContextManager,
|
||||
/// L2 统一状态机:对话生命周期状态(单一真相源,批2 持久化供读侧/前端消费)。
|
||||
/// 写收敛:经 GeneratingGuard/入口 transition_to 守卫迁移,非直接赋值。
|
||||
/// 批3 双轨收口:generating bool 已退役,此 enum 成为生成态唯一真相源。
|
||||
pub conv_state: ConvState,
|
||||
// B-Phase3:conv_state 字段已迁到 ConvStateStore(无锁真相源),此字段已删。
|
||||
/// G1 目标钉扎真相源:从 LLM 工具调用推理的目标列表(内容态字段,与生命周期态正交)。
|
||||
///
|
||||
/// 治 R1(目标消息被压缩/compressed 物理出局,sanitize step0 过滤 is_active 致目标丢失):
|
||||
@@ -907,7 +901,7 @@ impl PerConvState {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
messages: ContextManager::new(ContextConfig::default()),
|
||||
conv_state: ConvState::Idle,
|
||||
|
||||
pinned_goals: Vec::new(),
|
||||
stop_flag: Arc::new(AtomicBool::new(false)),
|
||||
notify: Arc::new(tokio::sync::Notify::new()),
|
||||
|
||||
Reference in New Issue
Block a user