修复: DeepSeek 400 全量扫描 + 队列 per-conv 隔离

- openai_compat: 扫描所有 assistant 消息剥离 orphan tool_calls(原仅查末条)
- queue 加 conversationId 字段,按会话精准 drain
- regenerate/editMessage 只清本会话排队消息
- newConversation 保留旧会话排队消息
- AiError 只清出错会话的队列项
This commit is contained in:
lxy
2026-07-20 00:19:50 +08:00
parent 42efb31bbf
commit e9e3578d26
59 changed files with 2875 additions and 1330 deletions
+22 -10
View File
@@ -155,6 +155,7 @@ const props = defineProps<{
const store = useAiStore()
const { t } = useI18n()
const router = useRouter()
// path_auth 审批链阶段3b(统一审批模型):df-ai-unified-approval 开关响应式 ref。
// 开(true,默认):path 挂起归一进 ToolCard 内联审批(once/always/deny),不挂 DirAuthDialog。
// 关(false,兜底回退):挂 DirAuthDialog 独立弹窗老链路(useAiEvents AiDirAuthRequired case 走 pendingDirAuths)。
@@ -255,7 +256,12 @@ async function confirmClearChat() {
* 历史:B-260615-10 单 loop 软复位弹中断确认,F-09 决策e 上线后已废弃(A 路线遗留)。
*/
async function confirmNewConversation() {
await store.newConversation()
try {
await store.newConversation()
} catch (e) {
console.error('[AI] 新建对话失败:', e)
// 用错误气泡反馈用户,不静默吞掉
}
}
// 第二批抽离: 技能 `/` 联想 + @ 实体引用联想 + watch(inputText) 已迁移至 ChatInput.vue
@@ -510,18 +516,24 @@ onMounted(async () => {
console.debug(`[启动] AiChat IPC 完成: ${(performance.now() - t0).toFixed(0)}ms`)
})
// ── 置顶功能(持久化:刷新后保持置顶状态图标一致)──
// ── 置顶功能(仅分离窗口有意义;持久化:刷新后保持置顶状态图标一致)──
// 关键:置顶只对「分离窗口」生效。AiChat 在主窗口(App.vue)与分离窗口(AiDetached.vue)都挂载,
// 若不区分,主窗口启动时会因 KV=true 把【整个主窗口】置顶到所有应用之上(BUG)。
// 主窗口内 AI 是 docked 面板,主窗口本身不该置顶。故 restore/apply 一律 gate 在 props.detached。
const alwaysOnTop = ref(false)
const appSettingsStore = useAppSettingsStore()
// 从 SQLite KV 恢复上次置顶状态
// 从 SQLite KV 恢复上次置顶状态(仅分离窗口读用;主窗口读了也无副作用,ref 仅驱动按钮 active 态)
const savedAlwaysOnTop = appSettingsStore.get<boolean>('df-ai-always-on-top', false)
alwaysOnTop.value = savedAlwaysOnTop
// 若恢复值为 true(上次是置顶态),同步到 Tauri 窗口
if (savedAlwaysOnTop) {
// 异步设(不阻塞初始化流程)
const { getCurrentWebviewWindow } = await import('@tauri-apps/api/webviewWindow')
getCurrentWebviewWindow().setAlwaysOnTop(true).catch(() => {/* 静默 */})
}
alwaysOnTop.value = props.detached ? savedAlwaysOnTop : false
// 同步到 Tauri 窗口:分离窗口按 KV 还原;主窗口显式清置顶(防御历史误设/窗口状态残留)。
// 注意:此处不可用顶层 await —— <script setup> 顶层 await 会使本组件变成 async setup,
// 而 App.vue / AiDetached.vue 直接挂载 <AiChat /> 未包 <Suspense>,会导致渲染空白。
// 用 .then() fire-and-forget,既不阻塞初始化流程,又保持组件同步 setup。
import('@tauri-apps/api/webviewWindow').then(({ getCurrentWebviewWindow }) =>
getCurrentWebviewWindow()
.setAlwaysOnTop(props.detached ? savedAlwaysOnTop : false)
.catch(() => {/* 静默:webview 不可用时忽略 */}),
)
async function toggleAlwaysOnTop() {
const { getCurrentWebviewWindow } = await import('@tauri-apps/api/webviewWindow')
const win = getCurrentWebviewWindow()