优化: 对话执行轨迹聚合与工具卡交互
前端聚合当前轮次工具调用数,替代每轮清零的完成计数,进度文案改轮次+累计口径 完成聚合语义由执行轨迹卡承接,工具卡复读修复与默认折叠收敛 流式与工具事件配套调整,新增轮次轨迹收集 composable
This commit is contained in:
@@ -14,12 +14,12 @@
|
||||
</div>
|
||||
|
||||
<template v-for="(group, gi) in groupedToolCalls" :key="group.name">
|
||||
<!-- 分组标题:同名工具 >=2 个时显示 -->
|
||||
<!-- 分组标题:同名工具 >=2 个时显示(B 聚合卡统一:默认行「N 次调用 · 成功 x 失败 y」,
|
||||
点开逐条;成功/失败与状态点同一套 isToolFailure 判定,文案走 ai.trace 同源 key) -->
|
||||
<div v-if="group.calls.length >= 2" class="ai-tool-group-header" @click="toggleGroupCollapse(gi)">
|
||||
<span class="ai-tool-group-icon" v-html="groupIcon(group.name)"></span>
|
||||
<span class="ai-tool-group-name">{{ groupDisplayName(group) }}</span>
|
||||
<span class="ai-tool-group-count">{{ group.calls.length }}</span>
|
||||
<span class="ai-tool-group-files">{{ groupFileSummary(group) }}</span>
|
||||
<span class="ai-tool-group-agg">{{ groupAggText(group) }}</span>
|
||||
<span class="ai-tool-group-chevron" :class="{ 'is-open': !isGroupCollapsed(gi) }">▸</span>
|
||||
<!-- 全部展开/收起(U-260618:从 topbar 下沉到首个可折叠分组标题行尾巴,与 run command 同行) -->
|
||||
<div
|
||||
@@ -52,6 +52,7 @@ import { ref, computed, watch, nextTick, onMounted, onBeforeUnmount } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import ToolCard from './ToolCard.vue'
|
||||
import { listen, type UnlistenFn } from '@tauri-apps/api/event'
|
||||
import { aggregateCalls, statsTail } from '@/composables/ai/useTurnTrace'
|
||||
import type { AiToolCallInfo } from '@/api/types'
|
||||
|
||||
// useI18n needed for $t() in template (collapseAll / expandAll) + groupDisplayName(i18n key)
|
||||
@@ -227,24 +228,12 @@ function groupDisplayName(group: ToolCallGroup): string {
|
||||
return group.name.replace(/_/g, ' ')
|
||||
}
|
||||
|
||||
/** 从工具调用参数中提取路径摘要(取前 3 个文件名)。
|
||||
* 供分组标题行展示,让用户一眼知道影响了哪些文件。*/
|
||||
function groupFileSummary(group: ToolCallGroup): string {
|
||||
const paths: string[] = []
|
||||
for (const tc of group.calls) {
|
||||
const args = tc.args as Record<string, unknown> | undefined
|
||||
const p = args?.path
|
||||
if (typeof p === 'string' && p.trim()) {
|
||||
// 取最后一段文件名(或最后两级目录)
|
||||
const parts = p.trim().split(/[/\\]/)
|
||||
const short = parts.length >= 2
|
||||
? parts.slice(-2).join('/')
|
||||
: parts[parts.length - 1]
|
||||
if (!paths.includes(short)) paths.push(short)
|
||||
if (paths.length >= 3) break
|
||||
}
|
||||
}
|
||||
return paths.length ? ' · ' + paths.join(' · ') : ''
|
||||
/** 分组聚合行文案:「N 次调用 · 成功 x 失败 y」(零值项省略;与单卡状态点同源 isToolFailure 判定) */
|
||||
function groupAggText(group: ToolCallGroup): string {
|
||||
const s = aggregateCalls(group.calls)
|
||||
const tail = statsTail(s)
|
||||
const head = t('ai.trace.calls', { n: s.total })
|
||||
return tail ? `${head} · ${tail}` : head
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -478,18 +467,14 @@ function groupIcon(name: string): string {
|
||||
font-weight: 500;
|
||||
color: var(--df-text-secondary);
|
||||
}
|
||||
.ai-tool-group-count {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-width: 16px;
|
||||
height: 16px;
|
||||
padding: 0 4px;
|
||||
border-radius: 8px;
|
||||
/* 聚合行「N 次调用 · 成功 x 失败 y」:弱化等宽,与单卡耗时同视觉层级 */
|
||||
.ai-tool-group-agg {
|
||||
font-family: var(--df-font-mono);
|
||||
font-size: 10px;
|
||||
font-weight: 600;
|
||||
background: var(--df-accent-bg);
|
||||
color: var(--df-accent);
|
||||
color: var(--df-text-dim);
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.ai-tool-group-chevron {
|
||||
/* 移除 margin-left:auto,让全部展开/收起按钮(global-toggle)占据行尾右对齐 */
|
||||
|
||||
Reference in New Issue
Block a user