diff --git a/src-tauri/src/commands/ai/agentic/mod.rs b/src-tauri/src/commands/ai/agentic/mod.rs index 5b555a2..948699d 100644 --- a/src-tauri/src/commands/ai/agentic/mod.rs +++ b/src-tauri/src/commands/ai/agentic/mod.rs @@ -1829,25 +1829,24 @@ pub(crate) async fn run_agentic_loop( tracing::warn!( conv_id = %conv_id, max_iter = max_iterations, - "[ai] agentic 循环达最大轮次(max_iterations={})仍未收敛,转暂停态询问用户(F-260616-03)", - max_iterations, + "[ai] agentic 循环达最大轮次仍未收敛,自动完成(incomplete=true)", ); - // 轮 token 落库(保留末轮已生成内容,续跑/停止都据此累加) let usage = df_ai::provider::TokenUsage { prompt_tokens: tokens.prompt(), completion_tokens: tokens.completion(), total_tokens: tokens.total(), }; save_conversation(&session_arc, &db, &conv_id, Some(&usage), Some(&resolved_model), true).await; - // 暂停态保持 generating=true(防其他 send 抢占,仿审批),disarm guard 跳过 Drop 兜底复位 - guard.disarm(); - let ev = AiChatEvent::AiMaxRoundsReached { + guard.reset().await; + let _ = app_handle.emit("ai-chat-event", AiChatEvent::AiCompleted { + total_tokens: usage.total_tokens, + prompt_tokens: tokens.prompt(), + completion_tokens: tokens.completion(), + incomplete: Some(true), conversation_id: Some(conv_id.clone()), - }; - let _ = app_handle.emit("ai-chat-event", ev.clone()); - // L3 emit 双写:tunnel subscriber(阶段2)透传 miniapp - let _ = app_handle.state::().ai_event_bus.publish_event(ev); - return; // generating 保持 true,等 ai_continue_loop / ai_stop_loop + pinned_goals: pinned_goals_snapshot.clone(), + }); + return; } // 正常完成 diff --git a/src-tauri/src/commands/ai/stream_recv.rs b/src-tauri/src/commands/ai/stream_recv.rs index bb89804..d40d111 100644 --- a/src-tauri/src/commands/ai/stream_recv.rs +++ b/src-tauri/src/commands/ai/stream_recv.rs @@ -527,6 +527,7 @@ fn classify_status_or_class(status_or_class: &str) -> bool { // ============================================================ // 单测:诊断提取/格式化(纯函数,不发 HTTP、不依赖 app_handle) // ============================================================ + #[cfg(test)] mod tests { use super::*; diff --git a/src/components/ai/ChatInput.vue b/src/components/ai/ChatInput.vue index 4567d58..d1e14d7 100644 --- a/src/components/ai/ChatInput.vue +++ b/src/components/ai/ChatInput.vue @@ -244,8 +244,8 @@ const stopBtnState = computed<'idle' | 'stop' | 'stopping' | 'retry'>(() => { if (cs === 'stopping') return 'stopping' if (cs === 'error') return 'retry' if (cs === 'generating' || cs === 'compressed') return 'stop' - // conv_state 未收到(idle 或 null)→ 回退旧 streaming bool - return store.state.streaming ? 'stop' : 'idle' + // cs === null (conv_state 已 idle 收敛删项) 或 cs === 'idle' → 发送态 + return 'idle' }) // ── 技能 `/` 联想 ──