新增: 压缩摘要折叠进所属轮次轨迹卡

- 压缩摘要进轮次桶随轨迹卡收起(旧口径原样展开作废)
- 轨迹卡内渲染为摘要 pill 单行, 点击展开全文(复用思考展开态)
- 压缩摘要不竞选最终答复气泡, 过程记录语义
This commit is contained in:
lxy
2026-10-11 08:53:36 +08:00
parent b4b18e351b
commit c2809bd927
3 changed files with 32 additions and 4 deletions
+30 -4
View File
@@ -971,6 +971,11 @@ function thinkPlainText(m: AiMessage): string {
const src = m.reasoning_content || ''
return src.replace(/\s+/g, ' ').trim()
}
/** 压缩摘要 pill 单行:纯文本空白归一截断(961 轮次记录形态,title 承载全文) */
function compactSnippet(content: string | undefined): string {
const txt = (content || '').replace(/\s+/g, ' ').trim()
return txt.length > 80 ? txt.slice(0, 80) + '…' : txt
}
/** pill 单行文本:纯文本截断 + 空轮回退文案(title 承载全文,此处只管单行) */
function thinkSnippet(m: AiMessage): string {
const txt = thinkPlainText(m)
@@ -1137,7 +1142,8 @@ function genBucketItems(
let fi = -1
for (let i = msgs.length - 1; i >= 0; i--) {
const c = msgs[i].content
if (c && c.trim() && !isToolResultJson(c)) { fi = i; break }
// compressed(压缩摘要)不竞选最终答复:摘要是过程记录,轨迹卡内 pill 呈现
if (c && c.trim() && !isToolResultJson(c) && msgs[i].status !== 'compressed') { fi = i; break }
}
const finalMsg = fi >= 0 ? msgs[fi] : msgs[msgs.length - 1]
// 过程消息 = finalMsg 之外全部(含其后尾气泡):异常收尾(错误/停止/流中断)时末轮气泡
@@ -1248,8 +1254,9 @@ const renderItems = computed<RenderItem[]>(() => {
items.push({ kind: 'msg', key: 'm-' + m.id, msg: m })
continue
}
// assistant 非错误非压缩进轮次桶(compressed 用户诉求不折叠,原样展开)
if (m.role === 'assistant' && !m.isError && m.status !== 'compressed') {
// assistant 非错误进轮次桶——compressed(压缩摘要)也进桶(用户令 10-11:压缩摘要
// 的动作作为轮次记录折叠,随轨迹卡收起;旧口径「原样展开」作废,961② 已定其归属当前轮)
if (m.role === 'assistant' && !m.isError) {
bucket.push(m)
} else {
flushBucket()
@@ -1649,8 +1656,27 @@ defineExpose({
<!-- 展开态 rail 面板:每轮=思考单行 pill(点击展开全文)+工具卡,图标锚定左轨连线贯通 -->
<div v-if="item.expanded" class="ai-trace-body">
<div v-for="m in item.traceMsgs" :key="m.id" class="ai-trace-round">
<!-- 压缩摘要(用户令 10-11):轮次过程记录形态,pill 单行+点击展开全文,
复用思考 pill 展开态(expandedThinkIds/toggleThink 同交互语义零新状态) -->
<template v-if="m.status === 'compressed'">
<button
class="ai-think-pill"
:title="m.content"
@click="toggleThink(m.id)"
>
<span class="ai-think-ic">
<svg aria-hidden="true" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12a9 9 0 11-9-9"/><path d="M21 3l-9 9"/></svg>
</span>
<span class="ai-think-text">{{ $t('ai.trace.compressed') }} · {{ compactSnippet(m.content) }}</span>
</button>
<div
v-if="expandedThinkIds.has(m.id) && m.content?.trim()"
class="ai-think-full ai-md"
v-html="renderMdDecorated(m.content)"
></div>
</template>
<!-- 空内容轮(只剩「过程记录」占位)不渲染思考 pill,降噪;工具卡照常 -->
<template v-if="hasThinkBody(m)">
<template v-else-if="hasThinkBody(m)">
<button
class="ai-think-pill"
:title="thinkFullText(m)"