webui: remove client-side context pre-check and rely on backend for limits (#16506)

* fix: make SSE client robust to premature [DONE] in agentic proxy chains

* webui: remove client-side context pre-check and rely on backend for limits

Removed the client-side context window pre-check and now simply sends messages
while keeping the dialog imports limited to core components, eliminating the
maximum context alert path

Simplified streaming and non-streaming chat error handling to surface a generic
'No response received from server' error whenever the backend returns no content

Removed the obsolete maxContextError plumbing from the chat store so state
management now focuses on the core message flow without special context-limit cases

* webui: cosmetic rename of error messages

* Update tools/server/webui/src/lib/stores/chat.svelte.ts

Co-authored-by: Aleksander Grygier <aleksander.grygier@gmail.com>

* Update tools/server/webui/src/lib/stores/chat.svelte.ts

Co-authored-by: Aleksander Grygier <aleksander.grygier@gmail.com>

* Update tools/server/webui/src/lib/components/app/chat/ChatScreen/ChatScreen.svelte

Co-authored-by: Aleksander Grygier <aleksander.grygier@gmail.com>

* Update tools/server/webui/src/lib/components/app/chat/ChatScreen/ChatScreen.svelte

Co-authored-by: Aleksander Grygier <aleksander.grygier@gmail.com>

* chore: update webui build output

---------

Co-authored-by: Aleksander Grygier <aleksander.grygier@gmail.com>
This commit is contained in:
Pascal
2025-10-12 18:06:41 +02:00
committed by GitHub
parent c7be9febcb
commit 81d54bbfd5
11 changed files with 141 additions and 366 deletions
@@ -7,6 +7,7 @@
ChatMessages,
ChatProcessingInfo,
EmptyFileAlertDialog,
ChatErrorDialog,
ServerErrorSplash,
ServerInfo,
ServerLoadingSplash,
@@ -22,10 +23,11 @@
activeMessages,
activeConversation,
deleteConversation,
dismissErrorDialog,
errorDialog,
isLoading,
sendMessage,
stopGeneration,
setMaxContextError
stopGeneration
} from '$lib/stores/chat.svelte';
import {
supportsVision,
@@ -34,7 +36,6 @@
serverWarning,
serverStore
} from '$lib/stores/server.svelte';
import { contextService } from '$lib/services';
import { parseFilesToMessageExtras } from '$lib/utils/convert-files-to-extra';
import { isFileTypeSupported } from '$lib/utils/file-type';
import { filterFilesByModalities } from '$lib/utils/modality-file-validation';
@@ -79,6 +80,7 @@
showCenteredEmpty && !activeConversation() && activeMessages().length === 0 && !isLoading()
);
let activeErrorDialog = $derived(errorDialog());
let isServerLoading = $derived(serverLoading());
async function handleDeleteConfirm() {
@@ -105,6 +107,12 @@
}
}
function handleErrorDialogOpenChange(open: boolean) {
if (!open) {
dismissErrorDialog();
}
}
function handleDragOver(event: DragEvent) {
event.preventDefault();
}
@@ -183,21 +191,6 @@
const extras = result?.extras;
// Check context limit using real-time slots data
const contextCheck = await contextService.checkContextLimit();
if (contextCheck && contextCheck.wouldExceed) {
const errorMessage = contextService.getContextErrorMessage(contextCheck);
setMaxContextError({
message: errorMessage,
estimatedTokens: contextCheck.currentUsage,
maxContext: contextCheck.maxContext
});
return false;
}
// Enable autoscroll for user-initiated message sending
userScrolledUp = false;
autoScrollEnabled = true;
@@ -461,6 +454,13 @@
}}
/>
<ChatErrorDialog
message={activeErrorDialog?.message ?? ''}
onOpenChange={handleErrorDialogOpenChange}
open={Boolean(activeErrorDialog)}
type={activeErrorDialog?.type ?? 'server'}
/>
<style>
.conversation-chat-form {
position: relative;
@@ -0,0 +1,60 @@
<script lang="ts">
import * as AlertDialog from '$lib/components/ui/alert-dialog';
import { AlertTriangle, TimerOff } from '@lucide/svelte';
interface Props {
open: boolean;
type: 'timeout' | 'server';
message: string;
onOpenChange?: (open: boolean) => void;
}
let { open = $bindable(), type, message, onOpenChange }: Props = $props();
const isTimeout = $derived(type === 'timeout');
const title = $derived(isTimeout ? 'TCP Timeout' : 'Server Error');
const description = $derived(
isTimeout
? 'The request did not receive a response from the server before timing out.'
: 'The server responded with an error message. Review the details below.'
);
const iconClass = $derived(isTimeout ? 'text-destructive' : 'text-amber-500');
const badgeClass = $derived(
isTimeout
? 'border-destructive/40 bg-destructive/10 text-destructive'
: 'border-amber-500/40 bg-amber-500/10 text-amber-600 dark:text-amber-400'
);
function handleOpenChange(newOpen: boolean) {
open = newOpen;
onOpenChange?.(newOpen);
}
</script>
<AlertDialog.Root {open} onOpenChange={handleOpenChange}>
<AlertDialog.Content>
<AlertDialog.Header>
<AlertDialog.Title class="flex items-center gap-2">
{#if isTimeout}
<TimerOff class={`h-5 w-5 ${iconClass}`} />
{:else}
<AlertTriangle class={`h-5 w-5 ${iconClass}`} />
{/if}
{title}
</AlertDialog.Title>
<AlertDialog.Description>
{description}
</AlertDialog.Description>
</AlertDialog.Header>
<div class={`rounded-lg border px-4 py-3 text-sm ${badgeClass}`}>
<p class="font-medium">{message}</p>
</div>
<AlertDialog.Footer>
<AlertDialog.Action onclick={() => handleOpenChange(false)}>Close</AlertDialog.Action>
</AlertDialog.Footer>
</AlertDialog.Content>
</AlertDialog.Root>
@@ -1,66 +0,0 @@
<script lang="ts">
import { AlertTriangle } from '@lucide/svelte';
import * as AlertDialog from '$lib/components/ui/alert-dialog';
import { maxContextError, clearMaxContextError } from '$lib/stores/chat.svelte';
</script>
<AlertDialog.Root
open={maxContextError() !== null}
onOpenChange={(open) => !open && clearMaxContextError()}
>
<AlertDialog.Content>
<AlertDialog.Header>
<AlertDialog.Title class="flex items-center gap-2">
<AlertTriangle class="h-5 w-5 text-destructive" />
Message Too Long
</AlertDialog.Title>
<AlertDialog.Description>
Your message exceeds the model's context window and cannot be processed.
</AlertDialog.Description>
</AlertDialog.Header>
{#if maxContextError()}
<div class="space-y-3 text-sm">
<div class="rounded-lg bg-muted p-3">
<div class="mb-2 font-medium">Token Usage:</div>
<div class="space-y-1 text-muted-foreground">
<div>
Estimated tokens:
<span class="font-mono">
{maxContextError()?.estimatedTokens.toLocaleString()}
</span>
</div>
<div>
Context window:
<span class="font-mono">
{maxContextError()?.maxContext.toLocaleString()}
</span>
</div>
</div>
</div>
<div>
<div class="mb-2 font-medium">Suggestions:</div>
<ul class="list-inside list-disc space-y-1 text-muted-foreground">
<li>Shorten your message</li>
<li>Remove some file attachments</li>
<li>Start a new conversation</li>
</ul>
</div>
</div>
{/if}
<AlertDialog.Footer>
<AlertDialog.Action onclick={() => clearMaxContextError()}>Got it</AlertDialog.Action>
</AlertDialog.Footer>
</AlertDialog.Content>
</AlertDialog.Root>
@@ -30,12 +30,11 @@ export { default as ChatSidebar } from './chat/ChatSidebar/ChatSidebar.svelte';
export { default as ChatSidebarConversationItem } from './chat/ChatSidebar/ChatSidebarConversationItem.svelte';
export { default as ChatSidebarSearch } from './chat/ChatSidebar/ChatSidebarSearch.svelte';
export { default as ChatErrorDialog } from './dialogs/ChatErrorDialog.svelte';
export { default as EmptyFileAlertDialog } from './dialogs/EmptyFileAlertDialog.svelte';
export { default as ConversationTitleUpdateDialog } from './dialogs/ConversationTitleUpdateDialog.svelte';
export { default as MaximumContextAlertDialog } from './dialogs/MaximumContextAlertDialog.svelte';
export { default as KeyboardShortcutInfo } from './misc/KeyboardShortcutInfo.svelte';
export { default as MarkdownContent } from './misc/MarkdownContent.svelte';