From 0eb81b4ecb64822d5bf1c1a4da88f13a51f8d55c Mon Sep 17 00:00:00 2001 From: jxxghp Date: Fri, 7 Aug 2026 12:42:57 +0800 Subject: [PATCH] perf(agent): smooth SSE streaming updates --- public/nginx.conf | 5 +- src/components/agent/AgentAssistantEntry.vue | 13 +- src/components/agent/AgentAssistantPanel.vue | 140 ++++++++++++------ src/components/agent/AgentAssistantWidget.vue | 38 ++++- src/components/agent/AgentMarkdownContent.vue | 73 +++++++++ .../__tests__/AgentAssistantEntry.spec.ts | 40 +++++ .../__tests__/AgentAssistantPanel.spec.ts | 52 +++++++ .../__tests__/AgentAssistantWidget.spec.ts | 43 +++++- .../__tests__/AgentMarkdownContent.spec.ts | 38 +++++ src/utils/agentMarkdown.ts | 21 +++ 10 files changed, 408 insertions(+), 55 deletions(-) create mode 100644 src/components/agent/AgentMarkdownContent.vue create mode 100644 src/components/agent/__tests__/AgentMarkdownContent.spec.ts create mode 100644 src/utils/agentMarkdown.ts diff --git a/public/nginx.conf b/public/nginx.conf index c96a4ef6..91c8acc4 100644 --- a/public/nginx.conf +++ b/public/nginx.conf @@ -65,13 +65,14 @@ http { root html; } - location ~ ^/api/v1/(system/(message|progress/|logging)|search/.*/stream$) { + location ~ ^/api/v1/(system/(message|progress/|logging)|search/.*/stream$|message/agent/stream$) { # SSE MIME类型设置 default_type text/event-stream; # 禁用缓存 - add_header Cache-Control no-cache; + add_header Cache-Control "no-cache, no-transform"; add_header X-Accel-Buffering no; + gzip off; proxy_buffering off; proxy_cache off; diff --git a/src/components/agent/AgentAssistantEntry.vue b/src/components/agent/AgentAssistantEntry.vue index 8a1b76b7..1c75ee53 100644 --- a/src/components/agent/AgentAssistantEntry.vue +++ b/src/components/agent/AgentAssistantEntry.vue @@ -47,6 +47,8 @@ const props = withDefaults( }, ) +const ASSISTANT_PREVIEW_MAX_LENGTH = 480 + const emit = defineEmits<{ open: [] }>() @@ -1014,6 +1016,15 @@ function scheduleFabBubbleRemoval(id: string, duration = FAB_NOTIFICATION_BUBBLE function upsertFabBubble(bubble: AgentAssistantEntryBubble, options: { autoClose?: boolean; duration?: number } = {}) { if (!props.active || !bubble.text) return + const existingIndex = fabBubbles.value.findIndex(item => item.id === bubble.id) + if (existingIndex >= 0) { + fabBubbles.value[existingIndex] = bubble + setFabDocked(false) + nextTick(scheduleFabBubblePositionUpdate) + if (options.autoClose) scheduleFabBubbleRemoval(bubble.id, options.duration) + return + } + const hadBubbles = hasFabBubbles.value const wasDocked = fabDocked.value const existingBubbles = fabBubbles.value.filter(item => item.id !== bubble.id) @@ -1061,7 +1072,7 @@ function showAssistantReplyPreview(value: string) { showBubble({ id: 'assistant-preview', kind: 'assistant', - text: value, + text: value.slice(0, ASSISTANT_PREVIEW_MAX_LENGTH), }) } diff --git a/src/components/agent/AgentAssistantPanel.vue b/src/components/agent/AgentAssistantPanel.vue index 0859979f..fcf75d06 100644 --- a/src/components/agent/AgentAssistantPanel.vue +++ b/src/components/agent/AgentAssistantPanel.vue @@ -1,11 +1,10 @@