diff --git a/tools/server/webui/src/lib/agentic/openai-sse-client.ts b/tools/server/webui/src/lib/clients/openai-sse.ts
similarity index 98%
rename from tools/server/webui/src/lib/agentic/openai-sse-client.ts
rename to tools/server/webui/src/lib/clients/openai-sse.ts
index 8f9c2b8ac5..2de9533d1d 100644
--- a/tools/server/webui/src/lib/agentic/openai-sse-client.ts
+++ b/tools/server/webui/src/lib/clients/openai-sse.ts
@@ -5,7 +5,7 @@ import type {
} from '$lib/types/api';
import type { ChatMessagePromptProgress, ChatMessageTimings } from '$lib/types/chat';
import { mergeToolCallDeltas, extractModelName } from '$lib/utils/chat-stream';
-import type { AgenticChatCompletionRequest } from './types';
+import type { AgenticChatCompletionRequest } from '$lib/types/agentic';
export type OpenAISseCallbacks = {
onChunk?: (chunk: string) => void;
diff --git a/tools/server/webui/src/lib/components/app/chat/ChatMessages/AgenticContent.svelte b/tools/server/webui/src/lib/components/app/chat/ChatMessages/AgenticContent.svelte
index cb55c12327..f1f8b2362d 100644
--- a/tools/server/webui/src/lib/components/app/chat/ChatMessages/AgenticContent.svelte
+++ b/tools/server/webui/src/lib/components/app/chat/ChatMessages/AgenticContent.svelte
@@ -14,7 +14,7 @@
} from '$lib/components/app';
import { config } from '$lib/stores/settings.svelte';
import { Wrench, Loader2 } from '@lucide/svelte';
- import { AgenticSectionType } from '$lib/types/agentic';
+ import { AgenticSectionType } from '$lib/enums';
import { AGENTIC_TAGS, AGENTIC_REGEX } from '$lib/constants/agentic';
import { formatJsonPretty } from '$lib/utils/formatters';
@@ -38,16 +38,20 @@
const showToolCallInProgress = $derived(config().showToolCallInProgress as boolean);
- function isExpanded(index: number, isPending: boolean): boolean {
- if (showToolCallInProgress && isPending) {
- return true;
- }
+ function getDefaultExpanded(isPending: boolean): boolean {
+ return showToolCallInProgress && isPending;
+ }
- return expandedStates[index] ?? showToolCallInProgress;
+ function isExpanded(index: number, isPending: boolean): boolean {
+ if (expandedStates[index] !== undefined) {
+ return expandedStates[index];
+ }
+ return getDefaultExpanded(isPending);
}
function toggleExpanded(index: number, isPending: boolean) {
- expandedStates[index] = !isExpanded(index, isPending);
+ const currentState = isExpanded(index, isPending);
+ expandedStates[index] = !currentState;
}
function parseAgenticContent(rawContent: string): AgenticSection[] {
diff --git a/tools/server/webui/src/lib/components/app/chat/ChatMessages/ChatMessage.svelte b/tools/server/webui/src/lib/components/app/chat/ChatMessages/ChatMessage.svelte
index 3ded592344..47bb81a6a7 100644
--- a/tools/server/webui/src/lib/components/app/chat/ChatMessages/ChatMessage.svelte
+++ b/tools/server/webui/src/lib/components/app/chat/ChatMessages/ChatMessage.svelte
@@ -10,6 +10,7 @@
import { DatabaseService } from '$lib/services';
import { config } from '$lib/stores/settings.svelte';
import { SYSTEM_MESSAGE_PLACEHOLDER } from '$lib/constants/ui';
+ import { MessageRole } from '$lib/enums';
import { copyToClipboard, isIMEComposing, formatMessageForClipboard } from '$lib/utils';
import ChatMessageAssistant from './ChatMessageAssistant.svelte';
import ChatMessageUser from './ChatMessageUser.svelte';
@@ -70,7 +71,7 @@
let textareaElement: HTMLTextAreaElement | undefined = $state();
let thinkingContent = $derived.by(() => {
- if (message.role === 'assistant') {
+ if (message.role === MessageRole.ASSISTANT) {
const trimmedThinking = message.thinking?.trim();
return trimmedThinking ? trimmedThinking : null;
@@ -92,7 +93,7 @@
isEditing = false;
// If canceling a new system message with placeholder content, remove it without deleting children
- if (message.role === 'system') {
+ if (message.role === MessageRole.SYSTEM) {
const conversationDeleted = await removeSystemPromptPlaceholder(message.id);
if (conversationDeleted) {
@@ -136,7 +137,7 @@
isEditing = true;
// Clear temporary placeholder content for system messages
editedContent =
- message.role === 'system' && message.content === SYSTEM_MESSAGE_PLACEHOLDER
+ message.role === MessageRole.SYSTEM && message.content === SYSTEM_MESSAGE_PLACEHOLDER
? ''
: message.content;
textareaElement?.focus();
@@ -179,7 +180,7 @@
}
async function handleSaveEdit() {
- if (message.role === 'system') {
+ if (message.role === MessageRole.SYSTEM) {
// System messages: update in place without branching
const newContent = editedContent.trim();
@@ -198,7 +199,7 @@
if (index !== -1) {
conversationsStore.updateMessageAtIndex(index, { content: newContent });
}
- } else if (message.role === 'user') {
+ } else if (message.role === MessageRole.USER) {
const finalExtras = await getMergedExtras();
onEditWithBranching?.(message, editedContent.trim(), finalExtras);
} else {
@@ -213,7 +214,7 @@
}
async function handleSaveEditOnly() {
- if (message.role === 'user') {
+ if (message.role === MessageRole.USER) {
// For user messages, trim to avoid accidental whitespace
const finalExtras = await getMergedExtras();
onEditUserMessagePreserveResponses?.(message, editedContent.trim(), finalExtras);
@@ -240,7 +241,7 @@
}
-{#if message.role === 'system'}
+{#if message.role === MessageRole.SYSTEM}
-{:else if message.role === 'user'}
+{:else if message.role === MessageRole.USER}
{/if}
- {#if role === 'assistant' && onRegenerate}
+ {#if role === MessageRole.ASSISTANT && onRegenerate}
onRegenerate()} />
{/if}
- {#if role === 'assistant' && onContinue}
+ {#if role === MessageRole.ASSISTANT && onContinue}
{/if}
diff --git a/tools/server/webui/src/lib/components/app/chat/ChatMessages/ChatMessageAssistant.svelte b/tools/server/webui/src/lib/components/app/chat/ChatMessages/ChatMessageAssistant.svelte
index f5c9a37bef..d36ea5aaa7 100644
--- a/tools/server/webui/src/lib/components/app/chat/ChatMessages/ChatMessageAssistant.svelte
+++ b/tools/server/webui/src/lib/components/app/chat/ChatMessages/ChatMessageAssistant.svelte
@@ -17,6 +17,7 @@
import { Button } from '$lib/components/ui/button';
import { Checkbox } from '$lib/components/ui/checkbox';
import { INPUT_CLASSES } from '$lib/constants/css-classes';
+ import { MessageRole } from '$lib/enums';
import Label from '$lib/components/ui/label/label.svelte';
import { config } from '$lib/stores/settings.svelte';
import { conversationsStore } from '$lib/stores/conversations.svelte';
@@ -245,7 +246,7 @@
{#if message.timestamp && !isEditing}
{/if}
diff --git a/tools/server/webui/src/lib/components/app/chat/ChatMessages/ChatMessageUser.svelte b/tools/server/webui/src/lib/components/app/chat/ChatMessages/ChatMessageUser.svelte
index 0bf8cde390..5c301eb8b2 100644
--- a/tools/server/webui/src/lib/components/app/chat/ChatMessages/ChatMessageUser.svelte
+++ b/tools/server/webui/src/lib/components/app/chat/ChatMessages/ChatMessageUser.svelte
@@ -4,6 +4,7 @@
import { config } from '$lib/stores/settings.svelte';
import ChatMessageActions from './ChatMessageActions.svelte';
import ChatMessageEditForm from './ChatMessageEditForm.svelte';
+ import { MessageRole } from '$lib/enums';
interface Props {
class?: string;
@@ -156,7 +157,7 @@
{onShowDeleteDialogChange}
{siblingInfo}
{showDeleteDialog}
- role="user"
+ role={MessageRole.USER}
/>
{/if}
diff --git a/tools/server/webui/src/lib/components/app/dialogs/DialogMcpServersSettings.svelte b/tools/server/webui/src/lib/components/app/dialogs/DialogMcpServersSettings.svelte
index 504c882d0f..5620417891 100644
--- a/tools/server/webui/src/lib/components/app/dialogs/DialogMcpServersSettings.svelte
+++ b/tools/server/webui/src/lib/components/app/dialogs/DialogMcpServersSettings.svelte
@@ -1,9 +1,8 @@