重构: AI聊天可靠性批次(审批累计计数/write_file diff预览/会话隔离补清/token缓存)

This commit is contained in:
2026-06-16 19:56:29 +08:00
parent ba7f35552b
commit d00b30f0ba
12 changed files with 238 additions and 45 deletions

View File

@@ -195,7 +195,7 @@ export type AiChatEvent = ({
} | {
type: 'AiToolCallCompleted'; id: string; result: unknown
} | {
type: 'AiApprovalRequired'; id: string; name: string; args: unknown; reason: string
type: 'AiApprovalRequired'; id: string; name: string; args: unknown; reason: string; diff?: string
} | {
type: 'AiApprovalResult'; id: string; approved: boolean
} | {
@@ -239,6 +239,9 @@ export interface AiToolCallInfo {
result?: unknown
/** 审批风险说明AiApprovalRequired 时填充) */
reason?: string
/** AE-2025-03write_file 行级 unified diff旧文件 vs 新内容),审批卡即时预览。
* 旧文件不存在(新建)或非 write_file 为 undefined前端回退显新 content。 */
diff?: string
}
/** 对话列表摘要 */

View File

@@ -36,6 +36,10 @@
</div>
</div>
<div v-if="tc.reason" class="ai-tool-approval-reason"> {{ tc.reason }}</div>
<!-- AE-2025-03: write_file 审批 diff 预览红删绿增行级旧文件不存在tc.diff 为空回退显上方 args content -->
<div v-if="tc.diff" class="ai-tool-approval-diff">
<pre class="ai-tool-diff-pre"><code><span v-for="(ln, idx) in diffLines" :key="idx" class="ai-tool-diff-line" :class="'ai-tool-diff-line--' + ln.kind">{{ ln.text }}{{ '\n' }}</span></code></pre>
</div>
<div class="ai-tool-actions">
<button class="ai-tool-btn ai-tool-btn--approve"
:disabled="approving"
@@ -382,6 +386,23 @@ onBeforeUnmount(() => {
/** 一次解析结果供模板多次读(模板引用 6 次,computed 避免每次 patch 重 parse) */
const parsed = computed(() => parseResult(props.tc.result))
/**
* AE-2025-03: write_file 审批 diff 行解析。
* 后端 generate_diff 输出 '+新行/-旧行/ 上下文行',逐行拆 → {kind, text} 供模板按 kind 着色。
* kind: 'add'(+, 绿)/'del'(-, 红)/'ctx'(空格, 灰)。computed 在 diff 变化(新审批)时重算。
*/
const diffLines = computed(() => {
const d = props.tc.diff
if (!d) return []
return d.split('\n').map(line => {
// 注意 generate_diff 末尾保留单尾换行 → split 末尾有空串元素,过滤
if (line === '') return null
if (line.startsWith('+')) return { kind: 'add', text: line }
if (line.startsWith('-')) return { kind: 'del', text: line }
return { kind: 'ctx', text: line }
}).filter((x): x is { kind: string; text: string } => x !== null)
})
/**
* 按 id 查项目名(覆盖活跃 + 回收站):审批 delete/restore/purge 各阶段都可能引用项目,
* 故先查 projects 再查 deletedProjects,找到即返回(提前退出),无则返回 undefined(调用方走 fallback)。
@@ -652,6 +673,37 @@ function toolResultSummary(tc: AiToolCallInfo): string {
border-radius: var(--df-radius-sm, 4px);
}
/* AE-2025-03: write_file 审批 diff 预览区(红删绿增行级,复用 --df-danger/--df-success token */
.ai-tool-approval-diff {
margin: 0 10px 8px;
border: 0.5px solid var(--df-border);
border-radius: var(--df-radius-sm, 4px);
overflow: hidden;
}
.ai-tool-diff-pre {
margin: 0;
padding: 6px 0;
max-height: 240px;
overflow: auto;
font-family: var(--df-font-mono);
font-size: 11px;
line-height: 1.5;
background: var(--df-bg-card);
}
.ai-tool-diff-pre code {
display: block;
white-space: pre;
}
.ai-tool-diff-line {
display: block;
padding: 0 10px;
white-space: pre-wrap;
word-break: break-all;
}
.ai-tool-diff-line--add { color: var(--df-success, #3ddb9c); background: var(--df-success-bg, rgba(61, 219, 160, 0.08)); }
.ai-tool-diff-line--del { color: var(--df-danger, #f06565); background: var(--df-danger-bg, rgba(240, 101, 101, 0.08)); }
.ai-tool-diff-line--ctx { color: var(--df-text-dim, #888); }
/* -- 审批按钮 -- */
.ai-tool-actions {
display: flex;

View File

@@ -36,6 +36,13 @@ async function newConversation() {
state.currentText = ''
state.pendingApprovals = []
state.streaming = false
// F-260616-09(A 路线):补漏清字段维持单例软隔离,解「新建会话上下文残留」。
// queue 清空防旧会话排队消息被带进新会话 drain;generatingConvId 清空防后台事件
// 错误路由;agentRound 复位 0;searchQuery 清空防新会话侧栏被旧搜索过滤。
state.queue = []
state.generatingConvId = null
state.agentRound = 0
state.searchQuery = ''
await loadConversations()
notifyConversationChanged()
}

View File

@@ -235,6 +235,8 @@ export function handleEvent(event: AiChatEvent) {
if (tc) {
tc.status = 'pending_approval'
tc.reason = event.reason
// AE-2025-03: write_file 审批注入行级 diff旧文件 vs 新内容),前端审批卡预览
if (event.diff) tc.diff = event.diff
}
// B-260616-12: 进入审批等待→取消该工具慢执行计时器(审批耗时由用户主导,非执行慢)
clearToolSlowTimer(event.id)