修复: B-22 前后端状态同步(ai_is_generating IPC+发送前查后端真值)

commands.rs 加 ai_is_generating IPC(读 session.generating)+lib.rs 注册;useAiSend sendMessage 预检先 invoke('ai_is_generating')查后端真值:后端 generating=true 时入队(撞 QUEUE_LIMIT 抛错)+本地 streaming=false 则复位 streaming=true 让用户感知,IPC 失败退化为 false 不阻断。消除前端 streaming 与后端 generating 不同步致预检放行撞拦截。批6 w8qbkxxvz,cargo+vue-tsc 0
This commit is contained in:
2026-06-15 05:48:47 +08:00
parent 19d64fc421
commit d809cf4b18
3 changed files with 40 additions and 0 deletions

View File

@@ -25,6 +25,18 @@ use super::AiChatEvent;
// 发送 / 审批 / 控制
// ============================================================
/// 查询后端真实 generating 状态(B-260615-22:方案 A 发送前 IPC 查后端真值)
///
/// 前端 `state.streaming` 与后端 `AiSession.generating` 各自维护:
/// 后端 loop 异常退出 / AiError 已复位 generating=false 时,前端若仅查本地 streaming
/// 可能仍为 true(或反之),预检放行后撞后端 `ai_chat_send` 的 generating 拦截。
/// 发送前调此命令取后端真值,据之与本地 streaming 对齐,消除状态不同步。
#[tauri::command]
pub async fn ai_is_generating(state: State<'_, AppState>) -> Result<bool, String> {
let session = state.ai_session.lock().await;
Ok(session.generating)
}
/// 发送消息并获取流式 AI 响应
///
/// 非阻塞:立即返回 "ok",通过 ai-chat-event 事件流式推送

View File

@@ -73,6 +73,7 @@ pub fn run() {
commands::ai::ai_approve,
commands::ai::ai_pending_tool_calls,
commands::ai::ai_chat_clear,
commands::ai::ai_is_generating,
commands::ai::ai_list_providers,
commands::ai::ai_save_provider,
commands::ai::ai_set_provider,

View File

@@ -9,6 +9,7 @@
//! - sendMessage 调 useAiStream.resetStreamWatchdog
//! - drainQueue 在 sendMessage 完成后由 handleEvent(AiCompleted) 调用 — 故 drainQueue 必须为模块级 export
import { invoke } from '@tauri-apps/api/core'
import { aiApi } from '../../api'
import { useAppSettingsStore } from '../../stores/appSettings'
import { state } from '../../stores/ai'
@@ -32,6 +33,32 @@ export function drainQueue() {
async function sendMessage(text: string, skill?: string) {
if (!text.trim()) return
// B-260615-22(方案 A):发送前 IPC 查后端真实 generating。
// 前端 state.streaming 与后端 AiSession.generating 各自维护:后端 loop 异常退出/
// AiError 已复位 generating=false 时,本地 streaming 可能仍为 false(状态不同步),
// 仅查本地预检会放行,随后撞 ai_chat_send:45 的 generating 拦截。此处对齐后端真值。
let backendGenerating = false
try {
backendGenerating = await invoke<boolean>('ai_is_generating')
} catch {
// IPC 失败不阻断发送:退化到仅靠本地 streaming 预检(原行为),让 ai_chat_send 兜底。
backendGenerating = false
}
if (backendGenerating) {
if (state.queue.length >= QUEUE_LIMIT) {
throw new Error(`待发送队列已满(最多 ${QUEUE_LIMIT} 条)`)
}
// 后端真在生成但本地 streaming 已复位(false):状态不同步——对齐本地 streaming=true
// 让用户感知(禁用输入框等),并入队等待 AiCompleted 续发;不入队则消息静默丢失。
if (!state.streaming) {
state.streaming = true
}
state.queue.push({ text: text.trim(), skill: skill || undefined })
return
}
// 本地 streaming=true 但后端 generating=false(看门狗/AiError 已复位本地,或反向不同步):
// 后端可接收,本地直接进入发送(无需复位 streaming)。
// 生成中:进入待发送队列,当前对话完成后(AiCompleted)由 drainQueue 自动续发
if (state.streaming) {
if (state.queue.length >= QUEUE_LIMIT) {