修复: 对话轨迹生成锚点回归与耗时口径
生成中锚点改真实末条,修第二轮打字机与工具卡不渲染回归 轨迹总耗时计入末轮时长,单卡耗时剔除审批等待区间 聚合文案与截断常量下沉共用,时长进位边界钳制
This commit is contained in:
@@ -21,7 +21,7 @@ import { useMarkdown } from '../../composables/useMarkdown'
|
||||
import { useMessageScroll } from '../../composables/ai/useMessageScroll'
|
||||
import { useStreamRenderer } from '../../composables/ai/useStreamRenderer'
|
||||
import { loadMoreHistory, getLoadMoreState } from '../../composables/ai/useAiConversations'
|
||||
import { collectTurnStats, statsTail, formatDuration, type TurnCallStats } from '../../composables/ai/useTurnTrace'
|
||||
import { collectTurnStats, aggText, type TurnCallStats } from '../../composables/ai/useTurnTrace'
|
||||
import ToolCardList from '../ToolCardList.vue'
|
||||
import MessageItem from './MessageItem.vue'
|
||||
import EmptyState from './EmptyState.vue'
|
||||
@@ -741,15 +741,9 @@ function toggleTrace(key: string): void {
|
||||
expandedTraceIds.value = next
|
||||
}
|
||||
|
||||
/** 轨迹卡聚合行:「N 次调用 · 成功 x 失败 y · 总耗时 T」(零值段省略;无工具调用显「过程记录」) */
|
||||
/** 轨迹卡聚合行:「N 次调用 · 成功 x 失败 y · 总耗时 T」(拼装统一走 useTurnTrace.aggText) */
|
||||
function traceAggText(stats: TurnCallStats, startTs: number, endTs: number): string {
|
||||
const parts: string[] = []
|
||||
parts.push(stats.total > 0 ? t('ai.trace.calls', { n: stats.total }) : t('ai.trace.processOnly'))
|
||||
const tail = statsTail(stats)
|
||||
if (tail) parts.push(tail)
|
||||
const dur = formatDuration(endTs - startTs)
|
||||
if (dur) parts.push(t('ai.trace.duration', { time: dur }))
|
||||
return parts.join(' · ')
|
||||
return aggText(stats, endTs - startTs)
|
||||
}
|
||||
|
||||
/** 轨迹卡 hover:绝对起止时刻(相对时长之外可溯源具体时间) */
|
||||
@@ -793,6 +787,18 @@ const renderItems = computed<RenderItem[]>(() => {
|
||||
const msgs = bucket.filter(shouldRenderMsg)
|
||||
bucket = []
|
||||
if (msgs.length === 0) return
|
||||
|
||||
if (isViewingGenerating.value) {
|
||||
// 执行中:当前步骤锚定真实末条消息(agentic 新一轮 push 新空气泡,若按「末条有正文」
|
||||
// 找锚点会错挂上一轮,致当前轮打字机/光标/工具卡整条不渲染);先前步骤收进一行实时摘要
|
||||
const cur = msgs[msgs.length - 1]
|
||||
const prev = msgs.slice(0, -1)
|
||||
if (prev.length > 0) {
|
||||
items.push({ kind: 'step', key: 'step-' + prev[0].id, stats: collectTurnStats(prev) })
|
||||
}
|
||||
items.push({ kind: 'msg', key: 'm-' + cur.id, msg: cur })
|
||||
return
|
||||
}
|
||||
// 最终答复:末条有正文且非纯 JSON 工具结果的消息;全无正文时保底末条(工具轮可见)
|
||||
let fi = -1
|
||||
for (let i = msgs.length - 1; i >= 0; i--) {
|
||||
@@ -801,24 +807,22 @@ const renderItems = computed<RenderItem[]>(() => {
|
||||
}
|
||||
const finalMsg = fi >= 0 ? msgs[fi] : msgs[msgs.length - 1]
|
||||
const traceMsgs = msgs.slice(0, msgs.indexOf(finalMsg))
|
||||
|
||||
if (isViewingGenerating.value) {
|
||||
// 执行中:先前步骤收进一行实时摘要,仅当前步骤(末条)展开
|
||||
if (traceMsgs.length > 0) {
|
||||
items.push({ kind: 'step', key: 'step-' + traceMsgs[0].id, stats: collectTurnStats(traceMsgs) })
|
||||
}
|
||||
items.push({ kind: 'msg', key: 'm-' + finalMsg.id, msg: finalMsg })
|
||||
return
|
||||
}
|
||||
// 完成:轨迹折叠为一张卡,展开时再 emit 段内过程消息(与归档分段同一扁平化模式)
|
||||
if (traceMsgs.length > 0) {
|
||||
const key = 'trace-' + traceMsgs[0].id
|
||||
const expanded = expandedTraceIds.value.has(key)
|
||||
// 终点计入末轮时长:过程消息里最晚的工具完成时刻与末轮开始时刻取较大者
|
||||
let lastCompleted = 0
|
||||
for (const m of traceMsgs) {
|
||||
for (const tc of m.toolCalls || []) {
|
||||
if (tc.completedAt && tc.completedAt > lastCompleted) lastCompleted = tc.completedAt
|
||||
}
|
||||
}
|
||||
items.push({
|
||||
kind: 'trace', key,
|
||||
stats: collectTurnStats(traceMsgs),
|
||||
startTs: traceMsgs[0].timestamp,
|
||||
endTs: finalMsg.timestamp,
|
||||
endTs: Math.max(lastCompleted, finalMsg.timestamp),
|
||||
traceMsgs, expanded,
|
||||
})
|
||||
if (expanded) {
|
||||
|
||||
Reference in New Issue
Block a user