重构: AI流式断线保文StreamResult三分支+重试对齐决策a1,推进链落df-nodes
This commit is contained in:
@@ -101,9 +101,11 @@ pub async fn ai_regenerate(
|
||||
let llm_concurrency = state.llm_concurrency.clone();
|
||||
// F-260616-01: loop 入口 load 快照,当前 loop 锁定边界(热改下次发消息生效)
|
||||
let max_iterations = state.agent_max_iterations.load(Ordering::SeqCst);
|
||||
// F-260616-07: 流式失败重试次数快照
|
||||
let max_retries = state.agent_max_retries.load(Ordering::SeqCst);
|
||||
|
||||
tauri::async_runtime::spawn(async move {
|
||||
run_agentic_loop(session_arc, tools_arc, db, app_handle, provider_config, system_prompt, conv_id, knowledge_config, llm_concurrency, max_iterations).await;
|
||||
run_agentic_loop(session_arc, tools_arc, db, app_handle, provider_config, system_prompt, conv_id, knowledge_config, llm_concurrency, max_iterations, max_retries).await;
|
||||
});
|
||||
|
||||
Ok("ok".to_string())
|
||||
@@ -195,9 +197,11 @@ pub async fn ai_chat_send(
|
||||
let llm_concurrency = state.llm_concurrency.clone();
|
||||
// F-260616-01: loop 入口 load 快照,当前 loop 锁定边界(热改下次发消息生效)
|
||||
let max_iterations = state.agent_max_iterations.load(Ordering::SeqCst);
|
||||
// F-260616-07: 流式失败重试次数快照
|
||||
let max_retries = state.agent_max_retries.load(Ordering::SeqCst);
|
||||
|
||||
tauri::async_runtime::spawn(async move {
|
||||
run_agentic_loop(session_arc, tools_arc, db, app_handle, provider_config, system_prompt, conv_id, knowledge_config, llm_concurrency, max_iterations).await;
|
||||
run_agentic_loop(session_arc, tools_arc, db, app_handle, provider_config, system_prompt, conv_id, knowledge_config, llm_concurrency, max_iterations, max_retries).await;
|
||||
});
|
||||
|
||||
Ok("ok".to_string())
|
||||
@@ -213,10 +217,20 @@ pub async fn ai_approve(
|
||||
) -> Result<String, String> {
|
||||
let mut session = state.ai_session.lock().await;
|
||||
|
||||
let approval = session
|
||||
.pending_approvals
|
||||
.remove(&tool_call_id)
|
||||
.ok_or_else(|| format!("未找到挂起的审批: {}", tool_call_id))?;
|
||||
let approval = match session.pending_approvals.remove(&tool_call_id) {
|
||||
Some(a) => a,
|
||||
None => {
|
||||
// F-260616-06: 幂等——内存无挂起审批时查审计表,若已处理则返回成功(非报错)
|
||||
if let Some(rec) = state.ai_tool_executions.find_by_tool_call_id(&tool_call_id).await
|
||||
.unwrap_or_default()
|
||||
{
|
||||
if rec.status == "executed" || rec.status == "rejected" || rec.status == "failed" {
|
||||
return Ok(format!("已处理({})", rec.status));
|
||||
}
|
||||
}
|
||||
return Err(format!("未找到挂起的审批: {}", tool_call_id));
|
||||
}
|
||||
};
|
||||
// recovered 字段保留读取(标记重启恢复来源,未来扩展用),本次修复移除 if !recovered 落库守卫。
|
||||
let _recovered = approval.recovered;
|
||||
|
||||
@@ -424,6 +438,7 @@ pub async fn ai_chat_edit(
|
||||
let knowledge_config = state.knowledge_config.lock().await.clone();
|
||||
let llm_concurrency = state.llm_concurrency.clone();
|
||||
let max_iterations = state.agent_max_iterations.load(Ordering::SeqCst);
|
||||
let max_retries = state.agent_max_retries.load(Ordering::SeqCst);
|
||||
|
||||
tauri::async_runtime::spawn(async move {
|
||||
run_agentic_loop(
|
||||
@@ -437,6 +452,7 @@ pub async fn ai_chat_edit(
|
||||
knowledge_config,
|
||||
llm_concurrency,
|
||||
max_iterations,
|
||||
max_retries,
|
||||
)
|
||||
.await;
|
||||
});
|
||||
@@ -472,6 +488,7 @@ pub async fn ai_chat_force_send(
|
||||
total_tokens: 0,
|
||||
prompt_tokens: 0,
|
||||
completion_tokens: 0,
|
||||
incomplete: None,
|
||||
conversation_id: Some(cid.clone()),
|
||||
});
|
||||
}
|
||||
@@ -499,7 +516,7 @@ pub async fn ai_chat_stop(state: State<'_, AppState>, app: AppHandle) -> Result<
|
||||
session.stop_flag.store(true, Ordering::SeqCst); // 双保险:防 try_continue 误判重启
|
||||
let conv_id = session.active_conversation_id.clone();
|
||||
drop(session);
|
||||
let _ = app.emit("ai-chat-event", AiChatEvent::AiCompleted { total_tokens: 0, prompt_tokens: 0, completion_tokens: 0, conversation_id: conv_id });
|
||||
let _ = app.emit("ai-chat-event", AiChatEvent::AiCompleted { total_tokens: 0, prompt_tokens: 0, completion_tokens: 0, incomplete: None, conversation_id: conv_id });
|
||||
return Ok(());
|
||||
}
|
||||
// 流式生成中:置位让 loop 自行收尾
|
||||
@@ -529,6 +546,7 @@ pub async fn ai_chat_stop(state: State<'_, AppState>, app: AppHandle) -> Result<
|
||||
total_tokens: 0,
|
||||
prompt_tokens: 0,
|
||||
completion_tokens: 0,
|
||||
incomplete: None,
|
||||
conversation_id: conv_id,
|
||||
},
|
||||
);
|
||||
@@ -601,6 +619,7 @@ pub async fn ai_stop_loop(
|
||||
total_tokens: 0,
|
||||
prompt_tokens: 0,
|
||||
completion_tokens: 0,
|
||||
incomplete: None,
|
||||
conversation_id: Some(conv_id),
|
||||
});
|
||||
Ok("ok".to_string())
|
||||
@@ -807,6 +826,7 @@ pub async fn ai_conversation_create(
|
||||
total_tokens: 0,
|
||||
prompt_tokens: 0,
|
||||
completion_tokens: 0,
|
||||
incomplete: None,
|
||||
conversation_id: Some(old_conv),
|
||||
});
|
||||
}
|
||||
@@ -1082,3 +1102,20 @@ pub async fn ai_set_agent_max_iterations(
|
||||
state.agent_max_iterations.store(clamped, Ordering::SeqCst);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 设置流式对话失败自动重试次数(F-260616-07 / 决策 a1:运行时调整,下次发消息生效)
|
||||
///
|
||||
/// 只重试流前失败(Init Err:未输出任何 token);流中途失败(MidStream Partial)保文不重试。
|
||||
/// 退避复用 retry::backoff_delay(1s→2s→4s+jitter) + is_status_retryable Fatal 分类 +
|
||||
/// 30s 总预算(详见 agentic.rs 流前重试循环)。
|
||||
/// 范围 clamp 0-10:0 表示不重试(直接报错),上限 10 防过度重试烧 token/拖慢体验。
|
||||
/// 默认 3(复用 retry.rs backoff_delay + 错误分类,详见 agentic.rs 重试循环)。
|
||||
#[tauri::command]
|
||||
pub async fn ai_set_agent_max_retries(
|
||||
state: State<'_, AppState>,
|
||||
value: u32,
|
||||
) -> Result<(), String> {
|
||||
let clamped = value.clamp(0, 10) as usize;
|
||||
state.agent_max_retries.store(clamped, Ordering::SeqCst);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user