重构: AI聊天可靠性批次(审批累计计数/write_file diff预览/会话隔离补清/token缓存)
This commit is contained in:
@@ -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-03:write_file 行级 unified diff(旧文件 vs 新内容),审批卡即时预览。
|
||||
* 旧文件不存在(新建)或非 write_file 为 undefined,前端回退显新 content。 */
|
||||
diff?: string
|
||||
}
|
||||
|
||||
/** 对话列表摘要 */
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user