mirror of
https://github.com/open-webui/open-webui.git
synced 2026-10-28 06:22:33 +00:00
refac
This commit is contained in:
parent
f465a4be11
commit
c8786f426a
5 changed files with 90 additions and 64 deletions
|
|
@ -3,14 +3,14 @@
|
|||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { toast } from 'svelte-sonner';
|
||||
|
||||
import { getContext, onDestroy, onMount, tick } from 'svelte';
|
||||
import { getContext, setContext, onDestroy, onMount, tick } from 'svelte';
|
||||
import { fade } from 'svelte/transition';
|
||||
const i18n: Writable<i18nType> = getContext('i18n');
|
||||
|
||||
import { goto, beforeNavigate } from '$app/navigation';
|
||||
import { page } from '$app/stores';
|
||||
|
||||
import { get, type Unsubscriber, type Writable } from 'svelte/store';
|
||||
import { get, writable, type Unsubscriber, type Writable } from 'svelte/store';
|
||||
import type { i18n as i18nType } from 'i18next';
|
||||
import { WEBUI_BASE_URL } from '$lib/constants';
|
||||
import equal from 'fast-deep-equal';
|
||||
|
|
@ -407,7 +407,13 @@
|
|||
let generating = false;
|
||||
let dragged = false;
|
||||
let generationController = null;
|
||||
let contextCompactionToastId = null;
|
||||
const contextCompactions = writable<Record<string, 'in_progress' | { error: string }>>({});
|
||||
setContext('contextCompactions', contextCompactions);
|
||||
let compactionChatId = '';
|
||||
$: if (compactionChatId !== $chatId) {
|
||||
contextCompactions.set({});
|
||||
compactionChatId = $chatId;
|
||||
}
|
||||
|
||||
let chat: any = null;
|
||||
let tags = [];
|
||||
|
|
@ -1310,40 +1316,21 @@
|
|||
}
|
||||
};
|
||||
|
||||
const dismissContextCompactionToast = () => {
|
||||
if (contextCompactionToastId !== null) {
|
||||
toast.dismiss(contextCompactionToastId);
|
||||
contextCompactionToastId = null;
|
||||
}
|
||||
const clearContextCompaction = (messageId: string) => {
|
||||
const next = { ...$contextCompactions };
|
||||
delete next[messageId];
|
||||
contextCompactions.set(next);
|
||||
};
|
||||
|
||||
const handleContextCompactionStatus = (status) => {
|
||||
if (status?.action !== 'context_compaction') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (status?.done) {
|
||||
if (contextCompactionToastId !== null) {
|
||||
if (status?.error) {
|
||||
toast.error($i18n.t('Context compaction failed'), {
|
||||
id: contextCompactionToastId,
|
||||
duration: 3000
|
||||
});
|
||||
} else {
|
||||
toast.success($i18n.t('Context compacted'), {
|
||||
id: contextCompactionToastId,
|
||||
duration: 1800
|
||||
});
|
||||
}
|
||||
contextCompactionToastId = null;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (contextCompactionToastId === null) {
|
||||
contextCompactionToastId = toast.loading($i18n.t('Compacting context'), {
|
||||
duration: Infinity
|
||||
});
|
||||
const handleContextCompactionStatus = (status, messageId: string) => {
|
||||
if (status?.action !== 'context_compaction') return;
|
||||
if (status.done && !status.error) {
|
||||
clearContextCompaction(messageId);
|
||||
} else {
|
||||
$contextCompactions = {
|
||||
...$contextCompactions,
|
||||
[messageId]: status.error ? { error: $i18n.t('Context compaction failed') } : 'in_progress'
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -1607,7 +1594,8 @@
|
|||
if (data?.checkpoint_message_id && history.messages[data.checkpoint_message_id]) {
|
||||
history.messages[data.checkpoint_message_id].contextSummary = data.context_summary;
|
||||
}
|
||||
handleContextCompactionStatus(data);
|
||||
handleContextCompactionStatus(data, event.message_id);
|
||||
autoScrollToBottom();
|
||||
} else if (type === 'chat:active') {
|
||||
if (!data?.active) {
|
||||
taskIds = null;
|
||||
|
|
@ -1637,7 +1625,7 @@
|
|||
}
|
||||
message.bridgeCancelled = true;
|
||||
bridgeCancellations.get(event.message_id)?.();
|
||||
if (!event.shared) dismissContextCompactionToast();
|
||||
clearContextCompaction(event.message_id);
|
||||
if (data?.output) {
|
||||
message.output = data.output;
|
||||
}
|
||||
|
|
@ -1646,6 +1634,7 @@
|
|||
// Set all response messages to done
|
||||
for (const messageId of history.messages[message.parentId].childrenIds) {
|
||||
history.messages[messageId].done = true;
|
||||
clearContextCompaction(messageId);
|
||||
}
|
||||
message.done = true;
|
||||
await processNextInQueue($chatId);
|
||||
|
|
@ -1682,7 +1671,7 @@
|
|||
message.done = true;
|
||||
} else handleOpenAIError(data.error, message);
|
||||
if (!event.shared && !responseCompleted) {
|
||||
dismissContextCompactionToast();
|
||||
clearContextCompaction(message.id);
|
||||
if (event.message_id === history.currentId) {
|
||||
await processNextInQueue(event.chat_id);
|
||||
}
|
||||
|
|
@ -2042,7 +2031,7 @@
|
|||
$socket?.off('disconnect', clearTyping);
|
||||
$socket?.off('disconnect', clearPresence);
|
||||
$socket?.off('disconnect', cancelBrowserInteractions);
|
||||
dismissContextCompactionToast();
|
||||
contextCompactions.set({});
|
||||
audioQueueInstance?.destroy();
|
||||
audioQueue.set(null);
|
||||
} catch (e) {
|
||||
|
|
@ -3554,9 +3543,10 @@
|
|||
|
||||
if (done) {
|
||||
message.done = true;
|
||||
if ($contextCompactions[message.id] === 'in_progress') clearContextCompaction(message.id);
|
||||
if (shared) return;
|
||||
if (message.error) {
|
||||
dismissContextCompactionToast();
|
||||
clearContextCompaction(message.id);
|
||||
bridge?.update();
|
||||
await processNextInQueue(chatId);
|
||||
return;
|
||||
|
|
@ -3692,15 +3682,17 @@
|
|||
}
|
||||
|
||||
const model = atSelectedModel?.id ?? selectedModels.find((modelId) => modelId);
|
||||
const toastId = toast.loading($i18n.t('Compacting context...'));
|
||||
const compactChatId = $chatId;
|
||||
const compactMessageId = history.currentId;
|
||||
if ($contextCompactions[compactMessageId] === 'in_progress') return;
|
||||
$contextCompactions = { ...$contextCompactions, [compactMessageId]: 'in_progress' };
|
||||
|
||||
try {
|
||||
const result = await compactChatById(localStorage.token, $chatId, model);
|
||||
const result = await compactChatById(localStorage.token, compactChatId, model);
|
||||
if ($chatId !== compactChatId) return;
|
||||
serverContextUsage = result?.context_usage ?? serverContextUsage;
|
||||
|
||||
if (result?.compacted) {
|
||||
toast.success($i18n.t('Context compacted'), { id: toastId });
|
||||
} else {
|
||||
if (!result?.compacted) {
|
||||
const skippedReason =
|
||||
result?.reason === 'too_short'
|
||||
? $i18n.t('Chat is too short to compact')
|
||||
|
|
@ -3709,13 +3701,20 @@
|
|||
: result?.reason === 'disabled'
|
||||
? $i18n.t('Context compaction is disabled')
|
||||
: $i18n.t('Nothing to compact');
|
||||
toast.message(skippedReason, { id: toastId });
|
||||
toast.message(skippedReason);
|
||||
}
|
||||
|
||||
await loadChat();
|
||||
clearContextCompaction(compactMessageId);
|
||||
} catch (error) {
|
||||
const message = error?.detail ?? error?.message ?? $i18n.t('Context compaction failed');
|
||||
toast.error(message, { id: toastId });
|
||||
if ($chatId === compactChatId) {
|
||||
$contextCompactions = {
|
||||
...$contextCompactions,
|
||||
[compactMessageId]: {
|
||||
error: String(error?.detail ?? error?.message ?? $i18n.t('Context compaction failed'))
|
||||
}
|
||||
};
|
||||
}
|
||||
} finally {
|
||||
messageInput?.focus({ preventScroll: true });
|
||||
}
|
||||
|
|
@ -4616,6 +4615,7 @@
|
|||
if (responseMessage?.parentId && history.messages[responseMessage.parentId]) {
|
||||
for (const messageId of history.messages[responseMessage.parentId].childrenIds) {
|
||||
history.messages[messageId].done = true;
|
||||
clearContextCompaction(messageId);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,21 +1,35 @@
|
|||
<script lang="ts">
|
||||
import { getContext } from 'svelte';
|
||||
import Spinner from '$lib/components/common/Spinner.svelte';
|
||||
|
||||
const i18n = getContext<typeof import('$lib/i18n').default>('i18n');
|
||||
|
||||
export let summary = '';
|
||||
export let status: 'in_progress' | { error: string } | undefined = undefined;
|
||||
export let compactPreview = false;
|
||||
|
||||
$: rowClass = `flex w-full min-w-0 items-center gap-1.5 py-0.5 ${
|
||||
compactPreview ? 'text-xs' : 'text-[0.9375rem]'
|
||||
} text-gray-400 dark:text-gray-500`;
|
||||
</script>
|
||||
|
||||
{#if summary.trim()}
|
||||
{#if status}
|
||||
<div role="status" class={rowClass}>
|
||||
{#if status === 'in_progress'}
|
||||
<Spinner className="size-4 shrink-0" />
|
||||
<span class="shimmer">{$i18n.t('Compacting context')}</span>
|
||||
{:else}
|
||||
<span>{status.error}</span>
|
||||
{/if}
|
||||
</div>
|
||||
{:else if summary.trim()}
|
||||
<!-- Only stop the response card's click handler; summary provides keyboard interaction. -->
|
||||
<!-- svelte-ignore a11y_click_events_have_key_events a11y_no_noninteractive_element_interactions -->
|
||||
<details class="my-1" on:click|stopPropagation>
|
||||
<details on:click|stopPropagation>
|
||||
<summary
|
||||
class="flex w-full cursor-pointer items-center gap-2 text-xs leading-5 text-gray-500 outline-offset-4 transition-colors before:flex-1 before:border-t before:border-dashed before:border-current before:opacity-25 after:flex-1 after:border-t after:border-dashed after:border-current after:opacity-25 hover:text-gray-700 focus-visible:outline-2 focus-visible:outline-gray-400 dark:text-gray-400 dark:hover:text-gray-200 [&::-webkit-details-marker]:hidden"
|
||||
class="{rowClass} cursor-pointer outline-offset-4 transition-colors hover:text-gray-500 focus-visible:outline-2 focus-visible:outline-gray-400 dark:hover:text-gray-400 [&::-webkit-details-marker]:hidden"
|
||||
>
|
||||
<span class="min-w-0 px-2 text-center">
|
||||
{$i18n.t('Context compacted')}
|
||||
</span>
|
||||
<span>{$i18n.t('Context compacted')}</span>
|
||||
</summary>
|
||||
<div
|
||||
role="region"
|
||||
|
|
@ -26,9 +40,7 @@
|
|||
</div>
|
||||
</details>
|
||||
{:else}
|
||||
<div
|
||||
class="my-1 flex w-full items-center gap-2 text-xs leading-5 text-gray-500 before:flex-1 before:border-t before:border-dashed before:border-current before:opacity-25 after:flex-1 after:border-t after:border-dashed after:border-current after:opacity-25 dark:text-gray-400"
|
||||
>
|
||||
<span class="min-w-0 px-2 text-center">{$i18n.t('Context compacted')}</span>
|
||||
<div class={rowClass}>
|
||||
<span>{$i18n.t('Context compacted')}</span>
|
||||
</div>
|
||||
{/if}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@
|
|||
import type { i18n as i18nType, t } from 'i18next';
|
||||
|
||||
const i18n = getContext<Writable<i18nType>>('i18n');
|
||||
const contextCompactions =
|
||||
getContext<Writable<Record<string, 'in_progress' | { error: string }>>>('contextCompactions');
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
|
|
@ -130,6 +132,7 @@
|
|||
export let chatId = '';
|
||||
export let history;
|
||||
export let messageId;
|
||||
$: compactionStatus = $contextCompactions?.[messageId];
|
||||
export let selectedModels = [];
|
||||
|
||||
let messageSource = history.messages[messageId];
|
||||
|
|
@ -714,8 +717,8 @@
|
|||
</Name>
|
||||
{/if}
|
||||
|
||||
{#if message.contextSummary}
|
||||
<ContextCompactionLabel summary={message.contextSummary} />
|
||||
{#if message.contextSummary && !compactionStatus}
|
||||
<ContextCompactionLabel summary={message.contextSummary} {compactPreview} />
|
||||
{/if}
|
||||
|
||||
<div>
|
||||
|
|
@ -922,7 +925,11 @@
|
|||
/>
|
||||
{/if}
|
||||
|
||||
{#if !message.done && !message.error && (hasResponseContent || !hasVisibleStatus)}
|
||||
{#if compactionStatus}
|
||||
<ContextCompactionLabel status={compactionStatus} {compactPreview} />
|
||||
{/if}
|
||||
|
||||
{#if !compactionStatus && !message.done && !message.error && (hasResponseContent || !hasVisibleStatus)}
|
||||
<div class="text-[0.9375rem] leading-relaxed">
|
||||
<span
|
||||
class="inline-block w-[0.125rem] h-3.5 bg-gray-400 dark:bg-gray-500 ml-0.5 animate-cursor-pulse align-text-bottom"
|
||||
|
|
|
|||
|
|
@ -172,7 +172,7 @@
|
|||
<TerminalOutputFile item={displayItem.item} {chatId} />
|
||||
{/if}
|
||||
{:else if displayItem.type === 'context_compaction'}
|
||||
<ContextCompactionLabel summary={displayItem.summary} />
|
||||
<ContextCompactionLabel summary={displayItem.summary} {compactPreview} />
|
||||
{:else}
|
||||
{@const detailToken = displayItem.token}
|
||||
{#if detailToken.attributes?.type === 'tool_calls'}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
import ChevronRightIcon from '$lib/components/layout/Sidebar/icons/ChevronRight.svelte';
|
||||
import { toast } from 'svelte-sonner';
|
||||
import { tick, getContext, onMount } from 'svelte';
|
||||
import type { Writable } from 'svelte/store';
|
||||
|
||||
import { models, settings } from '$lib/stores';
|
||||
import { user as _user } from '$lib/stores';
|
||||
|
|
@ -29,6 +30,8 @@
|
|||
import SubagentResultRow from './SubagentResultRow.svelte';
|
||||
|
||||
const i18n = getContext<typeof import('$lib/i18n').default>('i18n');
|
||||
const contextCompactions =
|
||||
getContext<Writable<Record<string, 'in_progress' | { error: string }>>>('contextCompactions');
|
||||
export let user;
|
||||
|
||||
export let chatId;
|
||||
|
|
@ -196,8 +199,12 @@
|
|||
</div>
|
||||
{/if}
|
||||
|
||||
{#if message.contextSummary}
|
||||
<ContextCompactionLabel summary={message.contextSummary} />
|
||||
{#if message.contextSummary || $contextCompactions?.[messageId]}
|
||||
<ContextCompactionLabel
|
||||
summary={message.contextSummary}
|
||||
status={$contextCompactions?.[messageId]}
|
||||
{compactPreview}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
<div class="chat-{message.role} w-full min-w-full">
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue