修复: 队列发送失败回填不丢消息+池权重revert+lang解析DRY+注释变量名修正
This commit is contained in:
@@ -21,7 +21,7 @@ import { ref } from 'vue'
|
||||
import { aiApi } from '@/api'
|
||||
import { useAppSettingsStore } from '@/stores/appSettings'
|
||||
import { state } from '@/stores/ai'
|
||||
import type { ContentPart } from '@/api/types'
|
||||
import type { ContentPart, AiMessage } from '@/api/types'
|
||||
import i18n from '@/i18n'
|
||||
import { resetStreamWatchdog, clearStreamWatchdog } from './useAiStream'
|
||||
import { startListener } from './useAiEvents'
|
||||
@@ -31,6 +31,19 @@ const t = ((i18n as any).global.t as (k: string, named?: Record<string, unknown>
|
||||
|
||||
const appSettings = useAppSettingsStore()
|
||||
|
||||
/**
|
||||
* B-260617-03 DRY: 解析 AI 回复语言。
|
||||
*
|
||||
* df-ai-language='auto' → 回落到 df-language;否则用 df-ai-language 指定值。
|
||||
* doSend/regenerate/editMessage 三处复用(原各内联同一段表达式)。
|
||||
*/
|
||||
function resolveLang(): string {
|
||||
const raw = appSettings.get<string>('df-ai-language', 'auto')
|
||||
return raw === 'auto'
|
||||
? appSettings.get<string>('df-language', 'zh-CN')
|
||||
: raw
|
||||
}
|
||||
|
||||
/// 待发送队列上限(超过抛错提示用户)
|
||||
const QUEUE_LIMIT = 10
|
||||
|
||||
@@ -101,10 +114,7 @@ async function doSend(text: string, skill?: string, force = false, parts?: Conte
|
||||
resetStreamWatchdog() // 启动流式看门狗,无数据超时兜底
|
||||
|
||||
await startListener()
|
||||
const raw = appSettings.get<string>('df-ai-language', 'auto')
|
||||
const lang = raw === 'auto'
|
||||
? appSettings.get<string>('df-language', 'zh-CN')
|
||||
: raw
|
||||
const lang = resolveLang()
|
||||
try {
|
||||
// force=true 走 force_send IPC(复位残留 generating),否则走正常 send
|
||||
// F-01 阶段6: 透传 modelOverride(主对话专用,后端兜底校验池内才用)
|
||||
@@ -159,10 +169,7 @@ async function regenerate() {
|
||||
resetStreamWatchdog()
|
||||
|
||||
await startListener()
|
||||
const raw = appSettings.get<string>('df-ai-language', 'auto')
|
||||
const lang = raw === 'auto'
|
||||
? appSettings.get<string>('df-language', 'zh-CN')
|
||||
: raw
|
||||
const lang = resolveLang()
|
||||
const convId = state.activeConversationId
|
||||
if (!convId) {
|
||||
// 无活跃对话:回滚占位,报错
|
||||
@@ -232,10 +239,7 @@ async function editMessage(newMessage: string) {
|
||||
resetStreamWatchdog()
|
||||
|
||||
await startListener()
|
||||
const raw = appSettings.get<string>('df-ai-language', 'auto')
|
||||
const lang = raw === 'auto'
|
||||
? appSettings.get<string>('df-language', 'zh-CN')
|
||||
: raw
|
||||
const lang = resolveLang()
|
||||
const convId = state.activeConversationId
|
||||
if (!convId) {
|
||||
state.streaming = false
|
||||
@@ -254,11 +258,36 @@ async function editMessage(newMessage: string) {
|
||||
}
|
||||
}
|
||||
|
||||
/** 取出队首并发送(AiCompleted 触发,此时 streaming 已 false) */
|
||||
/**
|
||||
* 取出队首并发送(AiCompleted 触发,此时 streaming 已 false)。
|
||||
*
|
||||
* B-260617-02: 原 void sendMessage(...) fire-and-forget 吞没了 doSend IPC 失败 throw,
|
||||
* 既无 AiCompleted 触发下次 drain(队列永久卡死),也无任何用户反馈。
|
||||
* 现显式 catch:复位 streaming + 推错误气泡(复用 AiError 的 isError 气泡模式) +
|
||||
* 回填失败消息到队首并停 drain(不静默丢用户输入,保留剩余队列供手动重试/编辑/取消)。
|
||||
* 成功路径不受影响——doSend 成功后端会再 emit AiCompleted 续 drain 链路。
|
||||
*/
|
||||
export function drainQueue() {
|
||||
if (state.queue.length === 0) return
|
||||
const next = state.queue.shift()!
|
||||
void sendMessage(next.text, next.skill, false, next.parts)
|
||||
void (async () => {
|
||||
try {
|
||||
await sendMessage(next.text, next.skill, false, next.parts)
|
||||
} catch (e) {
|
||||
state.streaming = false
|
||||
clearStreamWatchdog()
|
||||
const errMsg = e instanceof Error ? e.message : String(e)
|
||||
state.queue.unshift(next) // 回填失败消息到队首,保留剩余队列(不静默丢用户输入)
|
||||
state.messages.push({
|
||||
id: `err-${nextMsgId()}`,
|
||||
role: 'assistant',
|
||||
content: t('ai.queuedSendFailed', { error: errMsg }),
|
||||
isError: true,
|
||||
timestamp: Date.now(),
|
||||
} as AiMessage)
|
||||
console.error('[AI] drainQueue 续发失败:', e)
|
||||
}
|
||||
})()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -314,7 +343,14 @@ export function isQueueTimedOut(): boolean {
|
||||
return first !== undefined && (Date.now() - first.enqueuedAt > QUEUE_TIMEOUT_MS)
|
||||
}
|
||||
|
||||
/** 超时时弹 confirm,用户确认后以 force_mode 重发队首消息;取消则保持排队 */
|
||||
/**
|
||||
* 超时时弹 confirm,用户确认后以 force_mode 重发队首消息;取消则保持排队。
|
||||
*
|
||||
* B-260617-04: force_send 失败时,原实现队首已 shift 致消息静默丢失。
|
||||
* 现失败把队首 unshift 回队列(保留 UI 队列可见,用户可改普通发送/编辑/取消),
|
||||
* 并复位 streaming/clearStreamWatchdog(对齐 doSend 失败回填模式)让 UI 脱卡死态。
|
||||
* 不自动重试 force(避免循环),交用户决定下一步。
|
||||
*/
|
||||
export async function tryForceSend(confirmFn: (msg: string) => Promise<boolean>): Promise<boolean> {
|
||||
const first = state.queue[0]
|
||||
if (!first) return false
|
||||
@@ -328,8 +364,17 @@ export async function tryForceSend(confirmFn: (msg: string) => Promise<boolean>)
|
||||
try {
|
||||
await sendMessage(first.text, first.skill, true, first.parts)
|
||||
return true
|
||||
} catch {
|
||||
// force_send 也失败:消息不回队列(避免循环),让用户看到错误
|
||||
} catch (e) {
|
||||
// force_send 也失败:回填队首保消息不丢,复位 streaming 让 UI 可继续操作
|
||||
state.queue.unshift({
|
||||
text: first.text,
|
||||
skill: first.skill,
|
||||
enqueuedAt: first.enqueuedAt,
|
||||
parts: first.parts,
|
||||
})
|
||||
state.streaming = false
|
||||
clearStreamWatchdog()
|
||||
console.error('[AI] tryForceSend force_send 失败,消息已回填队首:', e)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user