perf: 优化 Agent 流式会话处理

- 合并流式通知与持久化写入,限制浏览器会话缓存

- 增量投影轮次事件并稳定关联异步工具任务
This commit is contained in:
2026-07-27 19:41:11 +08:00
parent 5497931abd
commit b08eb009bb
10 changed files with 539 additions and 47 deletions

View File

@@ -6,7 +6,7 @@ import type {
ChatTimelineToolApprovalPayload,
} from './types';
import { nextTick, onBeforeUnmount, ref, watch } from 'vue';
import { computed, nextTick, onBeforeUnmount, ref, watch } from 'vue';
import ChatTimelineItem from './ChatTimelineItem.vue';
@@ -38,6 +38,15 @@ let preservedScrollTop: number | undefined;
const bottomThreshold = 24;
let scrollFrame = 0;
const assistantActionAnchorIds = computed(() => {
const latestAssistantByRound = new Map<string, string>();
for (const item of props.items) {
if (item.type === 'message' && item.role === 'assistant' && item.roundId) {
latestAssistantByRound.set(item.roundId, item.id);
}
}
return new Set(latestAssistantByRound.values());
});
function isNearBottom(container: HTMLElement) {
return (
@@ -85,26 +94,13 @@ function canRegenerateMessage(item: ChatTimelineItemType) {
return item.type === 'message' && (props.regenerable?.(item) ?? false);
}
function isAssistantActionAnchor(
item: ChatTimelineItemType,
index: number,
items: ChatTimelineItemType[],
) {
if (item.type !== 'message' || item.role !== 'assistant' || !item.roundId) {
return false;
}
for (let cursor = items.length - 1; cursor >= 0; cursor -= 1) {
const current = items[cursor];
if (
current &&
current.type === 'message' &&
current.role === 'assistant' &&
current.roundId === item.roundId
) {
return cursor === index;
}
}
return false;
function isAssistantActionAnchor(item: ChatTimelineItemType) {
return (
item.type === 'message' &&
item.role === 'assistant' &&
Boolean(item.roundId) &&
assistantActionAnchorIds.value.has(item.id)
);
}
function isVariantLoading(item: ChatTimelineItemType) {
@@ -152,9 +148,9 @@ watch(
</div>
<template v-else>
<ChatTimelineItem
v-for="(item, index) in items"
v-for="item in items"
:key="item.id"
:assistant-actions-visible="isAssistantActionAnchor(item, index, items)"
:assistant-actions-visible="isAssistantActionAnchor(item)"
:item="item"
:image-loader="imageLoader"
:approval-loading="approvalLoading"