优化: aichat 体验收尾(useAiEvents拆分4子域 + AIC-FIX P1队列/mutex/缓存 + i18n残留抽取)
This commit is contained in:
@@ -23,8 +23,8 @@ import type { ContentPart, AiMessage, MentionSpan, MessageId } from '@/api/types
|
||||
import { t } from '@/i18n/i18n-helpers'
|
||||
import { resetStreamWatchdog, clearStreamWatchdog } from './useAiStream'
|
||||
import { setStreaming } from './streamingGuard'
|
||||
import { startListener, flushCurrentText } from './useAiEvents'
|
||||
import { nextMsgId, startApprovalTimer, clearApprovalTimer, clearAllApprovalTimers, resolveAiLang, convStates, setConvCurrentText, setConvStreaming } from './aiShared'
|
||||
import { startListener } from './useAiEvents'
|
||||
import { nextMsgId, startApprovalTimer, clearApprovalTimer, clearAllApprovalTimers, resolveAiLang, convStates, setConvCurrentText, setConvStreaming, flushCurrentText } from './aiShared'
|
||||
|
||||
/// 待发送队列上限(超过抛错提示用户)
|
||||
const QUEUE_LIMIT = 10
|
||||
@@ -33,18 +33,18 @@ const QUEUE_LIMIT = 10
|
||||
const QUEUE_TIMEOUT_MS = 30_000
|
||||
|
||||
/**
|
||||
* F-01 阶段6: 用户指定模型 override(主对话专用,模块级单例)。
|
||||
* 用户指定模型 override(主对话专用,模块级单例)。
|
||||
*
|
||||
* null=自动模式(路由器选);非空字符串=用户从下拉指定的 model_id。
|
||||
* 仅主对话(agentic)生效:doSend/sendMessage/regenerate/editMessage 透传给后端,
|
||||
* 标题/扫描/灵感等内部调用不读此字段(后端各自独立 IPC 不带 override)。
|
||||
* 标题/扫描/灵感等内部调用不读此字段。
|
||||
*
|
||||
* 后端兜底(agentic.rs):override 非空且在该 provider model_configs 池中才用,
|
||||
* 否则落回路由结果——绝不让 override 导致无模型。
|
||||
*
|
||||
* 模块级(非组件级):AiChat.vue 顶部下拉读写此 ref,发送链路读此 ref 透传,
|
||||
* 单实例 AiChat 不需 props/inject 串联。切换对话时后端清 session.model_override,
|
||||
* 前端应同步清(modelOverride.value = null)保持 UI 与后端一致。
|
||||
* 模块级 ref(非组件级):AiChat 顶部下拉读写,发送链路读此 ref 透传,单实例不需 props/inject。
|
||||
* 切换会话后端会清自身 session.model_override;前端保留用户手选(每会话默认权重最高
|
||||
* 由 TopBar 兜底:仅当 override 空/对目标模型池无效时才重置),避免切会话往返丢失手选模型。
|
||||
*/
|
||||
const modelOverride = ref<string | null>(null)
|
||||
|
||||
@@ -278,9 +278,8 @@ export function drainQueue(convId?: string | null) {
|
||||
const next = state.queue.splice(idx, 1)[0]!
|
||||
void (async () => {
|
||||
try {
|
||||
// 用目标会话 id 发送(而非当前活跃会话)——若已切到别的会话,原实现会把 A 的队首消息
|
||||
// 误发到当前活跃会话 B。队列项无 mentionSpans 字段,传 undefined。
|
||||
await sendMessage(next.text, next.skill, false, next.parts, undefined, targetId)
|
||||
// 用目标会话 id 发送(而非当前活跃会话),避免切走后把 A 的队首消息误发到 B;spans 一并透传
|
||||
await sendMessage(next.text, next.skill, false, next.parts, next.spans, targetId)
|
||||
} catch (e) {
|
||||
// 失败时彻底收尾:带目标会话 id 精准清理 per-conv 看门狗 + 会话状态
|
||||
setConvStreaming(targetId, false)
|
||||
@@ -352,6 +351,7 @@ async function sendMessage(text: string, skill?: string, forceMode = false, part
|
||||
skill: skill || undefined,
|
||||
enqueuedAt: Date.now(),
|
||||
parts: parts && parts.length > 0 ? parts : undefined,
|
||||
spans: spans && spans.length > 0 ? spans : undefined,
|
||||
conversationId: targetConvId,
|
||||
})
|
||||
return
|
||||
@@ -388,7 +388,7 @@ export async function tryForceSend(confirmFn: (msg: string) => Promise<boolean>,
|
||||
// 此处无需前置 false(前置 false 会触发消息列表 watch 清流式块再重建,产生瞬态抖动)
|
||||
clearStreamWatchdog()
|
||||
try {
|
||||
await sendMessage(first.text, first.skill, true, first.parts, undefined, first.conversationId)
|
||||
await sendMessage(first.text, first.skill, true, first.parts, first.spans, first.conversationId)
|
||||
return true
|
||||
} catch (e) {
|
||||
// force_send 也失败:回填该会话队首保消息不丢,复位 streaming 让 UI 可继续操作
|
||||
@@ -398,6 +398,7 @@ export async function tryForceSend(confirmFn: (msg: string) => Promise<boolean>,
|
||||
skill: first.skill,
|
||||
enqueuedAt: first.enqueuedAt,
|
||||
parts: first.parts,
|
||||
spans: first.spans,
|
||||
conversationId: first.conversationId,
|
||||
})
|
||||
setStreaming(false, { convId: targetConv ?? state.activeConversationId, reason: 'tryForceSend-fail' })
|
||||
@@ -481,7 +482,7 @@ async function sendQueuedNow(index: number) {
|
||||
// backendGenerating(后端真值仍 true)→ spliced 被入队而非立即发,与"立即发送"语义不符。
|
||||
// force_send 原子复位 generating=false 再发,无竞态窗口;stop_flag 让旧 loop 在下个检测点退出。
|
||||
// 显式传 spliced 所属会话 id(当前活跃视图内操作,即当前会话)。
|
||||
await sendMessage(spliced.text, spliced.skill, true, spliced.parts, undefined, spliced.conversationId)
|
||||
await sendMessage(spliced.text, spliced.skill, true, spliced.parts, spliced.spans, spliced.conversationId)
|
||||
}
|
||||
|
||||
/** 停止当前生成:本地先复位 streaming,再发停止信号。
|
||||
|
||||
Reference in New Issue
Block a user