Skip to content

Commit 0a428ff

Browse files
committed
webui: Client-side implementation of tool calling with calculator tool and (javascript) code interpreter tool
1 parent ec98e20 commit 0a428ff

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+3385
-408
lines changed

tools/server/webui/package-lock.json

Lines changed: 27 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tools/server/webui/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
"@tailwindcss/forms": "^0.5.9",
4141
"@tailwindcss/typography": "^0.5.15",
4242
"@tailwindcss/vite": "^4.0.0",
43+
"@testing-library/svelte": "^5.2.9",
4344
"@types/node": "^22",
4445
"@vitest/browser": "^3.2.3",
4546
"bits-ui": "^2.14.4",

tools/server/webui/playwright.config.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ import { defineConfig } from '@playwright/test';
22

33
export default defineConfig({
44
webServer: {
5-
command: 'npm run build && http-server ../public -p 8181',
5+
command:
6+
'npm run build && gzip -dc ../public/index.html.gz > ../public/index.html && http-server ../public -p 8181',
67
port: 8181,
78
timeout: 120000,
89
reuseExistingServer: false

tools/server/webui/src/lib/components/app/chat/ChatMessages/ChatMessage.svelte

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import ChatMessageAssistant from './ChatMessageAssistant.svelte';
66
import ChatMessageUser from './ChatMessageUser.svelte';
77
import ChatMessageSystem from './ChatMessageSystem.svelte';
8+
import { conversationsStore } from '$lib/stores/conversations.svelte';
89
910
interface Props {
1011
class?: string;
@@ -22,6 +23,7 @@
2223
onNavigateToSibling?: (siblingId: string) => void;
2324
onRegenerateWithBranching?: (message: DatabaseMessage, modelOverride?: string) => void;
2425
siblingInfo?: ChatMessageSiblingInfo | null;
26+
toolParentIds?: string[];
2527
}
2628
2729
let {
@@ -35,9 +37,21 @@
3537
onEditUserMessagePreserveResponses,
3638
onNavigateToSibling,
3739
onRegenerateWithBranching,
38-
siblingInfo = null
40+
siblingInfo = null,
41+
toolParentIds
3942
}: Props = $props();
4043
44+
type MessageWithToolExtras = DatabaseMessage & {
45+
_actionTargetId?: string;
46+
_toolMessagesCollected?: { toolCallId?: string | null; parsed: unknown }[];
47+
};
48+
49+
const actionTargetId = $derived((message as MessageWithToolExtras)._actionTargetId ?? message.id);
50+
51+
function getActionTarget(): DatabaseMessage {
52+
return conversationsStore.activeMessages.find((m) => m.id === actionTargetId) ?? message;
53+
}
54+
4155
let deletionInfo = $state<{
4256
totalCount: number;
4357
userMessages: number;
@@ -95,12 +109,14 @@
95109
}
96110
97111
function handleConfirmDelete() {
98-
onDelete?.(message);
112+
const target = getActionTarget();
113+
onDelete?.(target);
99114
showDeleteDialog = false;
100115
}
101116
102117
async function handleDelete() {
103-
deletionInfo = await chatStore.getDeletionInfo(message.id);
118+
const target = getActionTarget();
119+
deletionInfo = await chatStore.getDeletionInfo(target.id);
104120
showDeleteDialog = true;
105121
}
106122
@@ -136,11 +152,13 @@
136152
}
137153
138154
function handleRegenerate(modelOverride?: string) {
139-
onRegenerateWithBranching?.(message, modelOverride);
155+
const target = getActionTarget();
156+
onRegenerateWithBranching?.(target, modelOverride);
140157
}
141158
142159
function handleContinue() {
143-
onContinueAssistantMessage?.(message);
160+
const target = getActionTarget();
161+
onContinueAssistantMessage?.(target);
144162
}
145163
146164
function handleSaveEdit() {
@@ -213,7 +231,7 @@
213231
{showDeleteDialog}
214232
{siblingInfo}
215233
/>
216-
{:else}
234+
{:else if message.role === 'assistant'}
217235
<ChatMessageAssistant
218236
bind:textareaElement
219237
class={className}
@@ -240,5 +258,11 @@
240258
{siblingInfo}
241259
{thinkingContent}
242260
{toolCallContent}
261+
toolParentIds={toolParentIds ?? [message.id]}
262+
toolMessagesCollected={(message as MessageWithToolExtras)._toolMessagesCollected}
243263
/>
264+
{:else if message.role === 'tool'}
265+
<!-- Tool messages are rendered inline inside their parent assistant's reasoning block.
266+
Skip standalone rendering to avoid duplicate bubbles. -->
267+
<!-- Intentionally left blank -->
244268
{/if}

0 commit comments

Comments
 (0)