优化: 边界加固(AI loop竞态根治+数据/审批/反馈/并发/错误分类)
AI loop 竞态(P0):per-conv epoch/owner token + 存活心跳治 force_send 双loop + stop 3s兜底误判;旧loop stale 全跳过(guard/emit/save)
agentic 收尾(A2-B8):Fatal 退出落库user消息(镜像Exhausted)+ 入口早退补save + usage is_estimated 打标 + emit_ai_completed_once 单点收敛清审批残留
聊天清理(A2-B9):clearChat 先停loop→DB单事务→内存清(clear_conversation_atomic)+ 前端错误气泡
循环并发(A2-B11):三态 ProviderAcquire(NotConfigured/Acquired/Exhausted)+ 候选循环非阻塞+防抖3次饱和降级+单测
错误分类(A2-B12):stream error帧接入 classify_status_or_class + 关键词保守降级 + 7单测
数据(G1.2/G1.4):purge_with_descendants 级联补全(11表单事务+存在性守卫)+ move_task_queue 单事务收口(两调用方共用)
git只读(G3.1):run_git_status/diff/log success判定(exit_code差异语义,失败结构化{success:false,error})
安全(G5.2/G5.6):create_project 目录Err+name校验 + module.rs 路径遍历DRY(分段匹配修a..b.rs误伤)
幂等(V2/V32):裸ALTER全守卫化 + v1..v40全链重跑幂等测试(16过)
附:remote_bridge await 临时引用修(E0716)+ agentic emit 收敛 E0716 app_state 绑定修
This commit is contained in:
@@ -165,6 +165,11 @@ pub enum AiChatEvent {
|
||||
prompt_cache_miss_tokens: u32,
|
||||
/// 思考 token(deepseek-reasoner/o1 reasoning_tokens,隐藏输出)。0=非 reasoning 模型。
|
||||
reasoning_tokens: u32,
|
||||
/// G4.3(2026-08-05):本轮 token 用量是否估算值(agentic 运行时兜底
|
||||
/// round_usage.prompt_tokens==0 → estimated_prompt 处打标)。true=prompt 非真实
|
||||
/// (provider 未报,前端 MessageList 据此标「估算」),false=真实值。仅影响展示,
|
||||
/// 不参与 TokenAccumulator/save 累加路径。
|
||||
is_estimated: bool,
|
||||
/// 不完整标记(可选):Some(true)=网络中断保文,None 或 Some(false)=完整回复
|
||||
incomplete: Option<bool>,
|
||||
conversation_id: Option<String>,
|
||||
@@ -319,6 +324,23 @@ pub enum AiChatEvent {
|
||||
/// 本机技能列表(ai_list_skills 返回的进程内缓存,已按 skills>commands>plugins 优先级去重)
|
||||
skills: Vec<crate::commands::ai::skills::SkillInfo>,
|
||||
},
|
||||
/// 跨端模型列表同步(2026-08-05):device 响应 miniapp `list_models` 命令,
|
||||
/// 把活跃 provider 的 enabled 模型列表推回 miniapp 供模型选择器渲染。
|
||||
///
|
||||
/// 触发:remote_bridge `list_models` 路由 → `get_active_provider`(prompt.rs)读活跃
|
||||
/// provider → 过滤 `model_configs` 中 `enabled == true` → 映射 `ModelInfo { model_id, label }`
|
||||
/// → `publish_event`(跨端透传,经 EventBus→tunnel subscriber→relay→miniapp)。
|
||||
///
|
||||
/// 职责:miniapp 模型选择器(列出可选模型 + 当前默认),对齐桌面端模型选择体验;
|
||||
/// 选择结果经 `send_message` / `regenerate` 的 `model_override` 透传回 device(route 已透传)。
|
||||
AiModelList {
|
||||
/// 活跃 provider id(读 session.active_provider_id,兜底 provider.id)
|
||||
provider_id: String,
|
||||
/// 活跃 provider 默认模型(provider.default_model,选择器高亮/兜底)
|
||||
default_model: String,
|
||||
/// enabled 模型列表(已过滤 model_configs 中 enabled == true)
|
||||
models: Vec<ModelInfo>,
|
||||
},
|
||||
/// F-#95 跨端实体列表同步(Phase3,2026-06-23):device 响应 miniapp `list_entities` 命令,
|
||||
/// 把本地项目/任务/灵感全量列表(list_projects/list_tasks/list_ideas 全量等价路径)推回 miniapp。
|
||||
///
|
||||
@@ -371,6 +393,20 @@ pub enum AiChatEvent {
|
||||
},
|
||||
}
|
||||
|
||||
/// 模型信息(AiModelList 事件的载荷,对齐 apps/df-miniapp 模型选择器 ModelInfo 字段)。
|
||||
///
|
||||
/// 与 `df_ai_core::model::ModelConfig` 区别:仅含选择器渲染必需字段(model_id + 可选 label),
|
||||
/// 不携带 modalities/capabilities/权重等路由维度(省带宽,miniapp 仅需选模型)。label 为用户
|
||||
/// 自定义别名(可选),缺失时前端回退显示 model_id。
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct ModelInfo {
|
||||
/// 模型名(如 "glm-4-flash",对齐 ModelConfig.model_id)
|
||||
pub model_id: String,
|
||||
/// 用户自定义别名(可选;缺失时前端回退显示 model_id)
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub label: Option<String>,
|
||||
}
|
||||
|
||||
/// Plan 层信息(AiPlanCreated 事件的载荷)
|
||||
#[derive(Debug, Clone, serde::Serialize)]
|
||||
pub struct PlanLayerInfo {
|
||||
@@ -879,6 +915,22 @@ pub struct PerConvState {
|
||||
pub stop_flag: Arc<AtomicBool>,
|
||||
/// 即时停止唤醒(会话级):阻塞在 stream.next() 时 notify_one() 立即唤醒跳出 select!
|
||||
pub notify: Arc<tokio::sync::Notify>,
|
||||
/// 并发 loop epoch/owner token(F1 根治,2026-08-05):每 spawn 新 loop 前 `fetch_add(1)`。
|
||||
///
|
||||
/// loop 入口捕获自己的 epoch(`run_agentic_loop` 的 `loop_epoch` 形参),退出 / Guard 复位 /
|
||||
/// emit AiCompleted 时只认自己 epoch(owner)——`loop_epoch.load() == my_epoch` 才是 owner,
|
||||
/// 否则是旧 loop(已被 force_send/新 loop 接管),不 reset 新 loop 状态、不 emit 重复完成事件。
|
||||
///
|
||||
/// 治 F1:force_send 原先锁内 `stop_flag=true→false` 微秒窗口,旧 loop 阻塞在 stream_llm
|
||||
/// select!(不读 stop_flag)醒来 flag 已 false → 旧 loop 没停 → 同 conv 新旧两 loop 并行。
|
||||
/// epoch 让旧 loop 在新 loop 启动瞬间即失效(owner 判定失败),即便旧 loop 还在跑也不动新状态。
|
||||
pub loop_epoch: Arc<std::sync::atomic::AtomicU64>,
|
||||
/// 存活心跳(F2 根治,2026-08-05):loop 活跃期间由 `heartbeat_loop` 每 20s 更新(ms 时间戳)。
|
||||
///
|
||||
/// `ai_chat_stop` 3s 兜底据此判断 loop 是否真死:超阈值(60s)无心跳 = 真死才强制复位 +
|
||||
/// emit AiCompleted;loop 还在跑(长工具执行/流式/压缩,心跳持续)则豁免,不误迁 Idle、
|
||||
/// 不产生「假完成 + 旧 loop 仍活」的双完成竞态。
|
||||
pub last_heartbeat: Arc<std::sync::atomic::AtomicU64>,
|
||||
/// agentic loop 累计已跑 iteration 计数(语义同 AiSession.iteration_used)
|
||||
pub iteration_used: usize,
|
||||
/// 当前 agent 循环的语言设置(用于审批后恢复循环)
|
||||
@@ -963,6 +1015,8 @@ impl PerConvState {
|
||||
pinned_goals: Vec::new(),
|
||||
stop_flag: Arc::new(AtomicBool::new(false)),
|
||||
notify: Arc::new(tokio::sync::Notify::new()),
|
||||
loop_epoch: Arc::new(std::sync::atomic::AtomicU64::new(0)),
|
||||
last_heartbeat: Arc::new(std::sync::atomic::AtomicU64::new(0)),
|
||||
iteration_used: 0,
|
||||
agent_language: None,
|
||||
model_override: None,
|
||||
|
||||
Reference in New Issue
Block a user