diff --git a/src/lib/components/chat/Messages/StructuredOutputRenderer.svelte b/src/lib/components/chat/Messages/StructuredOutputRenderer.svelte
index f4ae7dcf81..e5d17b79ab 100644
--- a/src/lib/components/chat/Messages/StructuredOutputRenderer.svelte
+++ b/src/lib/components/chat/Messages/StructuredOutputRenderer.svelte
@@ -8,6 +8,7 @@
import Markdown from './Markdown.svelte';
import ConsecutiveDetailsGroup from './Markdown/ConsecutiveDetailsGroup.svelte';
+ import ContextCompactionLabel from './ContextCompactionLabel.svelte';
import {
buildOutputDisplayItems,
type OutputDetailToken,
@@ -170,6 +171,8 @@
{#if displayItem.item?.displayed || $settings?.terminalFileDisplay === 'inline'}
{/if}
+ {:else if displayItem.type === 'context_compaction'}
+
{:else}
{@const detailToken = displayItem.token}
{#if detailToken.attributes?.type === 'tool_calls'}
diff --git a/src/lib/components/chat/Messages/UserMessage.svelte b/src/lib/components/chat/Messages/UserMessage.svelte
index 5908522b0c..816568bc43 100644
--- a/src/lib/components/chat/Messages/UserMessage.svelte
+++ b/src/lib/components/chat/Messages/UserMessage.svelte
@@ -18,6 +18,7 @@
import equal from 'fast-deep-equal';
import Name from './Name.svelte';
+ import ContextCompactionLabel from './ContextCompactionLabel.svelte';
import ProfileImage from './ProfileImage.svelte';
import ProfilePreview from '$lib/components/channel/Messages/Message/ProfilePreview.svelte';
import Tooltip from '$lib/components/common/Tooltip.svelte';
@@ -195,6 +196,10 @@
{/if}
+ {#if message.contextSummary}
+
+ {/if}
+
{#if edit !== true}
{#if message.files}
diff --git a/src/lib/components/chat/Messages/structuredOutput.ts b/src/lib/components/chat/Messages/structuredOutput.ts
index e8b86964fd..df7dfef139 100644
--- a/src/lib/components/chat/Messages/structuredOutput.ts
+++ b/src/lib/components/chat/Messages/structuredOutput.ts
@@ -14,7 +14,7 @@ export type OutputItem = {
status?: string;
arguments?: unknown;
content?: OutputContentPart[];
- summary?: OutputContentPart[];
+ summary?: OutputContentPart[] | string;
output?: OutputContentPart[];
files?: unknown;
embeds?: unknown;
@@ -64,6 +64,11 @@ export type OutputDisplayItem =
type: 'file';
id: string;
item: Record;
+ }
+ | {
+ type: 'context_compaction';
+ id: string;
+ summary: string;
};
type ResponseStreamEvent = {
@@ -409,6 +414,16 @@ export function buildOutputDisplayItems(output: OutputItem[] = []): OutputDispla
return;
}
+ if (item.type === 'open_webui:context_compaction') {
+ flushDetails();
+ displayItems.push({
+ type: 'context_compaction',
+ id: item.id ?? `context-compaction-${index}`,
+ summary: typeof item.summary === 'string' ? item.summary : ''
+ });
+ return;
+ }
+
if (item.type === 'message') {
const text = getMessageText(item);
if (text.trim()) {
@@ -618,7 +633,7 @@ export function applyResponseStreamEvent(
if (!event.part) {
return nextOutput;
}
- item.summary = [...(item.summary ?? [])];
+ item.summary = Array.isArray(item.summary) ? [...item.summary] : [];
const summaryIndex = event.summary_index ?? item.summary.length;
setPart(item.summary, summaryIndex, { ...event.part }, { type: 'summary_text', text: '' });
return nextOutput;
@@ -633,7 +648,7 @@ export function applyResponseStreamEvent(
if (deltaType === 'reasoning_summary_text') {
const summaryIndex = event.summary_index ?? 0;
- item.summary = [...(item.summary ?? [])];
+ item.summary = Array.isArray(item.summary) ? [...item.summary] : [];
const part = ensurePart(item.summary, summaryIndex, { type: 'summary_text', text: '' });
part.text = appendDelta(part.text ?? '', event.delta);
return nextOutput;
diff --git a/src/lib/components/chat/Overview/Node.svelte b/src/lib/components/chat/Overview/Node.svelte
index 08b53edb66..e1654a7a18 100644
--- a/src/lib/components/chat/Overview/Node.svelte
+++ b/src/lib/components/chat/Overview/Node.svelte
@@ -18,6 +18,10 @@
getOutputText(nodeData?.message?.output) || nodeData?.message?.content || '';
$: messageContent = getMessageContent(data);
+ $: isCompacted =
+ data?.message?.contextSummary ||
+ data?.message?.output?.some((item) => item?.type === 'open_webui:context_compaction');
+ $: previewClass = `text-xs mt-0.5 ${isCompacted ? 'line-clamp-1' : 'line-clamp-2'}`;
$: modelName = data?.model
? resolveLocalizedModelName(data.model, $i18n.language)
: (data?.message?.model ?? 'Assistant');
@@ -45,9 +49,13 @@
{#if data?.message?.error}
-
{data.message.error.content}
+
+ {data.message.error.content}
+
{:else}
-
{messageContent}
+
+ {messageContent}
+
{/if}