Skip to content

Commit 4e42332

Browse files
committed
webui: Client-side implementation of tool calling with calculator tool and (javascript) code interpreter tool
1 parent 96a181a commit 4e42332

Some content is hidden

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

49 files changed

+3370
-393
lines changed

tools/server/public/index.html.gz

43.4 KB
Binary file not shown.

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
@@ -41,6 +41,7 @@
4141
"@tailwindcss/forms": "^0.5.9",
4242
"@tailwindcss/typography": "^0.5.15",
4343
"@tailwindcss/vite": "^4.0.0",
44+
"@testing-library/svelte": "^5.2.9",
4445
"@types/node": "^22",
4546
"@vitest/browser": "^3.2.3",
4647
"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
@@ -4,6 +4,7 @@
44
import ChatMessageAssistant from './ChatMessageAssistant.svelte';
55
import ChatMessageUser from './ChatMessageUser.svelte';
66
import ChatMessageSystem from './ChatMessageSystem.svelte';
7+
import { conversationsStore } from '$lib/stores/conversations.svelte';
78
89
interface Props {
910
class?: string;
@@ -21,6 +22,7 @@
2122
onNavigateToSibling?: (siblingId: string) => void;
2223
onRegenerateWithBranching?: (message: DatabaseMessage, modelOverride?: string) => void;
2324
siblingInfo?: ChatMessageSiblingInfo | null;
25+
toolParentIds?: string[];
2426
}
2527
2628
let {
@@ -34,9 +36,21 @@
3436
onEditUserMessagePreserveResponses,
3537
onNavigateToSibling,
3638
onRegenerateWithBranching,
37-
siblingInfo = null
39+
siblingInfo = null,
40+
toolParentIds
3841
}: Props = $props();
3942
43+
type MessageWithToolExtras = DatabaseMessage & {
44+
_actionTargetId?: string;
45+
_toolMessagesCollected?: { toolCallId?: string | null; parsed: unknown }[];
46+
};
47+
48+
const actionTargetId = $derived((message as MessageWithToolExtras)._actionTargetId ?? message.id);
49+
50+
function getActionTarget(): DatabaseMessage {
51+
return conversationsStore.activeMessages.find((m) => m.id === actionTargetId) ?? message;
52+
}
53+
4054
let deletionInfo = $state<{
4155
totalCount: number;
4256
userMessages: number;
@@ -92,12 +106,14 @@
92106
}
93107
94108
function handleConfirmDelete() {
95-
onDelete?.(message);
109+
const target = getActionTarget();
110+
onDelete?.(target);
96111
showDeleteDialog = false;
97112
}
98113
99114
async function handleDelete() {
100-
deletionInfo = await chatStore.getDeletionInfo(message.id);
115+
const target = getActionTarget();
116+
deletionInfo = await chatStore.getDeletionInfo(target.id);
101117
showDeleteDialog = true;
102118
}
103119
@@ -133,11 +149,13 @@
133149
}
134150
135151
function handleRegenerate(modelOverride?: string) {
136-
onRegenerateWithBranching?.(message, modelOverride);
152+
const target = getActionTarget();
153+
onRegenerateWithBranching?.(target, modelOverride);
137154
}
138155
139156
function handleContinue() {
140-
onContinueAssistantMessage?.(message);
157+
const target = getActionTarget();
158+
onContinueAssistantMessage?.(target);
141159
}
142160
143161
function handleSaveEdit() {
@@ -210,7 +228,7 @@
210228
{showDeleteDialog}
211229
{siblingInfo}
212230
/>
213-
{:else}
231+
{:else if message.role === 'assistant'}
214232
<ChatMessageAssistant
215233
bind:textareaElement
216234
class={className}
@@ -237,5 +255,11 @@
237255
{siblingInfo}
238256
{thinkingContent}
239257
{toolCallContent}
258+
toolParentIds={toolParentIds ?? [message.id]}
259+
toolMessagesCollected={(message as MessageWithToolExtras)._toolMessagesCollected}
240260
/>
261+
{:else if message.role === 'tool'}
262+
<!-- Tool messages are rendered inline inside their parent assistant's reasoning block.
263+
Skip standalone rendering to avoid duplicate bubbles. -->
264+
<!-- Intentionally left blank -->
241265
{/if}

0 commit comments

Comments
 (0)