优化: aichat 工具体验补强(切生成会话占位气泡治丢首响应/read_symbol符号级提示/失败引导/审批失败toast) + 修复 WorkflowDagDisplay 崩页与 aiChat i18n 缺key
This commit is contained in:
@@ -13,6 +13,7 @@
|
||||
//! - _pendingApprovalIds:审批按钮防抖守卫(同 useAiSend 原实现)
|
||||
|
||||
import { ref } from 'vue'
|
||||
import { emit } from '@tauri-apps/api/event'
|
||||
import { aiApi } from '@/api'
|
||||
import { state } from '@/stores/ai'
|
||||
import { t } from '@/i18n/i18n-helpers'
|
||||
@@ -99,9 +100,14 @@ async function approveToolCall(toolCallId: string, approved: boolean, decision?:
|
||||
// 仅 IPC 真失败(审批已处理/网络断)走到这里:后端工具失败已改走 emit completed,不进此分支
|
||||
// 不回滚 pending_approval(会卡死按钮),改设 completed + 错误提示,让用户知晓失败
|
||||
console.error('[AI] 审批操作未送达后端:', e)
|
||||
// P1-e-d:审批失败给用户可见反馈——composable 无组件上下文,经 Tauri 事件总线
|
||||
// emit('ai-toast'),由 AiChat.vue listen 弹本地 toast(对齐 ai-tool-slow-toast 先例)。
|
||||
// 文案复用 ai.approvalNotDelivered(与下方 tc.result 同源)。
|
||||
const errMsg = e instanceof Error ? e.message : String(e)
|
||||
void emit('ai-toast', { msg: t('ai.approvalNotDelivered', { error: errMsg }), type: 'error' })
|
||||
if (tc) {
|
||||
tc.status = 'completed'
|
||||
tc.result = t('ai.approvalNotDelivered', { error: e instanceof Error ? e.message : String(e) })
|
||||
tc.result = t('ai.approvalNotDelivered', { error: errMsg })
|
||||
}
|
||||
throw e
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ import { persistUiState } from './useAiPanel'
|
||||
import { setStreaming } from './streamingGuard'
|
||||
import { nextMsgId, getConvState, clearConvStreamState, clearLastDelta, startApprovalTimer, clearAllApprovalTimers, switchingConvs, getConvStreamState, setConvCurrentText, notifyConversationChanged } from './aiShared'
|
||||
import { t } from '@/i18n/i18n-helpers'
|
||||
import type { AiConversationDetail, AiMessage, AiToolCallInfo, ConvId } from '@/api/types'
|
||||
import type { AiConversationDetail, AiMessage, AiToolCallInfo, ConvId, MessageId } from '@/api/types'
|
||||
|
||||
const appSettings = useAppSettingsStore()
|
||||
|
||||
@@ -333,6 +333,28 @@ export async function switchConversation(id: string, force = false) {
|
||||
state.messages = parseConvMessages(rawMsgs, 'loaded')
|
||||
// parse 成功才置 active(保证 active 与 messages 一致)
|
||||
state.activeConversationId = id
|
||||
// CSW-P1-4: 切到 round0 进行中的会话时,后端该轮 per_conv 只有 user 消息(assistant 占位
|
||||
// 轮末才入),末条是 user → 流式 currentText 无 assistant 承载(MessageList isLastAi 不命中,
|
||||
// flushCurrentText 遇 user break)整轮回复丢失。补前端占位 assistant 气泡承接 currentText,
|
||||
// 生成完成由 AiCompleted/AiAgentRound 收尾 flushCurrentText 回填 content。
|
||||
// targetGen(非终止三态 generating/stopping/compressed)在上文已算,直接复用;
|
||||
// round0-pending- 前缀防御判重(force 重刷同会话时 parseConvMessages 已整体重建,不叠加)。
|
||||
// 字段满足 AiMessage 必填(id/role/content/timestamp),仅 id 因 branded MessageId 逐字段 cast。
|
||||
const lastMsg = state.messages[state.messages.length - 1]
|
||||
if (
|
||||
targetGen
|
||||
&& lastMsg
|
||||
&& lastMsg.role === 'user'
|
||||
&& !state.messages.some((m) => m.role === 'assistant' && m.id.startsWith('round0-pending-'))
|
||||
) {
|
||||
state.messages.push({
|
||||
id: `round0-pending-${id}` as MessageId,
|
||||
role: 'assistant',
|
||||
content: '',
|
||||
isError: false,
|
||||
timestamp: Date.now(),
|
||||
})
|
||||
}
|
||||
} catch (e) {
|
||||
// 历史消息解析/映射失败原仅 state.messages=[] → 切换后空白用户不知原因。
|
||||
// 改为推错误气泡提示 + 控制台日志(保留 state.messages 不再清空,避免空白无反馈)。
|
||||
|
||||
Reference in New Issue
Block a user