Commit
·
b2847f1
1
Parent(s):
832660f
small improvements
Browse files- src/chat/Chat.tsx +20 -8
- src/chat/Message.tsx +19 -22
- src/chat/MessageToolCall.tsx +11 -4
- src/textGeneration/TextGeneration.ts +8 -9
- src/textGeneration/types.ts +5 -49
- src/textGeneration/worker/textGenerationWorker.ts +2 -2
- src/utils/format.ts +5 -4
- src/utils/webMcp.ts +6 -2
src/chat/Chat.tsx
CHANGED
|
@@ -3,7 +3,7 @@ import { useChatSettings } from "@utils/context/chatSettings/useChatSettings.ts"
|
|
| 3 |
import { formatBytes } from "@utils/format.ts";
|
| 4 |
import { CONVERSATION_STARTERS, MODELS } from "@utils/models.ts";
|
| 5 |
import { TOOLS } from "@utils/tools.ts";
|
| 6 |
-
import { Settings } from "lucide-react";
|
| 7 |
import {
|
| 8 |
useEffect,
|
| 9 |
useMemo,
|
|
@@ -48,12 +48,7 @@ export default function Chat({ className = "" }: { className?: string }) {
|
|
| 48 |
const newSettingsKey = JSON.stringify(settings);
|
| 49 |
if (!settings || settingsKey.current === newSettingsKey) return;
|
| 50 |
settingsKey.current = newSettingsKey;
|
| 51 |
-
|
| 52 |
-
TOOLS.filter((tool) => settings.tools.includes(tool.name)),
|
| 53 |
-
settings.temperature,
|
| 54 |
-
settings.enableThinking,
|
| 55 |
-
settings.systemPrompt
|
| 56 |
-
);
|
| 57 |
}, [settings]);
|
| 58 |
|
| 59 |
useEffect(() => {
|
|
@@ -64,6 +59,14 @@ export default function Chat({ className = "" }: { className?: string }) {
|
|
| 64 |
|
| 65 |
const modelDownloaded = downloadedModels.includes(settings.modelKey);
|
| 66 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 67 |
const initializeModel = async () => {
|
| 68 |
setState(State.INITIALIZING);
|
| 69 |
setDownloadProgress(0);
|
|
@@ -185,7 +188,7 @@ export default function Chat({ className = "" }: { className?: string }) {
|
|
| 185 |
</div>
|
| 186 |
)}
|
| 187 |
</div>
|
| 188 |
-
<div className="fixed right-0 bottom-6 left-0 mx-auto w-full max-w-4xl">
|
| 189 |
<Card className="mt-auto">
|
| 190 |
<ChatForm
|
| 191 |
disabled={!ready}
|
|
@@ -206,6 +209,15 @@ export default function Chat({ className = "" }: { className?: string }) {
|
|
| 206 |
{settings?.tools.length}/{TOOLS.length} tool
|
| 207 |
{settings?.tools.length !== 1 ? "s" : ""} active
|
| 208 |
</Button>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 209 |
</div>
|
| 210 |
</div>
|
| 211 |
</>
|
|
|
|
| 3 |
import { formatBytes } from "@utils/format.ts";
|
| 4 |
import { CONVERSATION_STARTERS, MODELS } from "@utils/models.ts";
|
| 5 |
import { TOOLS } from "@utils/tools.ts";
|
| 6 |
+
import { RotateCcw, Settings } from "lucide-react";
|
| 7 |
import {
|
| 8 |
useEffect,
|
| 9 |
useMemo,
|
|
|
|
| 48 |
const newSettingsKey = JSON.stringify(settings);
|
| 49 |
if (!settings || settingsKey.current === newSettingsKey) return;
|
| 50 |
settingsKey.current = newSettingsKey;
|
| 51 |
+
initializeConversation();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 52 |
}, [settings]);
|
| 53 |
|
| 54 |
useEffect(() => {
|
|
|
|
| 59 |
|
| 60 |
const modelDownloaded = downloadedModels.includes(settings.modelKey);
|
| 61 |
|
| 62 |
+
const initializeConversation = () =>
|
| 63 |
+
generator.initializeConversation(
|
| 64 |
+
TOOLS.filter((tool) => settings.tools.includes(tool.name)),
|
| 65 |
+
settings.temperature,
|
| 66 |
+
settings.enableThinking,
|
| 67 |
+
settings.systemPrompt
|
| 68 |
+
);
|
| 69 |
+
|
| 70 |
const initializeModel = async () => {
|
| 71 |
setState(State.INITIALIZING);
|
| 72 |
setDownloadProgress(0);
|
|
|
|
| 188 |
</div>
|
| 189 |
)}
|
| 190 |
</div>
|
| 191 |
+
<div className="fixed right-0 bottom-6 left-0 mx-auto w-full max-w-4xl space-y-2">
|
| 192 |
<Card className="mt-auto">
|
| 193 |
<ChatForm
|
| 194 |
disabled={!ready}
|
|
|
|
| 209 |
{settings?.tools.length}/{TOOLS.length} tool
|
| 210 |
{settings?.tools.length !== 1 ? "s" : ""} active
|
| 211 |
</Button>
|
| 212 |
+
<Button
|
| 213 |
+
iconLeft={<RotateCcw />}
|
| 214 |
+
size="xs"
|
| 215 |
+
variant="ghost"
|
| 216 |
+
color="mono"
|
| 217 |
+
onClick={initializeConversation}
|
| 218 |
+
>
|
| 219 |
+
new conversation
|
| 220 |
+
</Button>
|
| 221 |
</div>
|
| 222 |
</div>
|
| 223 |
</>
|
src/chat/Message.tsx
CHANGED
|
@@ -1,5 +1,6 @@
|
|
| 1 |
import { Modal } from "@theme";
|
| 2 |
import cn from "@utils/classnames.ts";
|
|
|
|
| 3 |
import { SquareCode, StopCircle } from "lucide-react";
|
| 4 |
import { useState } from "react";
|
| 5 |
|
|
@@ -15,25 +16,21 @@ export default function Message({
|
|
| 15 |
className?: string;
|
| 16 |
}) {
|
| 17 |
const [templateOpen, setTemplateOpen] = useState<boolean>(false);
|
| 18 |
-
const formatDuration = (ms: number) => {
|
| 19 |
-
if (ms < 1000) {
|
| 20 |
-
return `${Math.round(ms)}ms`;
|
| 21 |
-
}
|
| 22 |
-
return `${(ms / 1000).toFixed(2)}s`;
|
| 23 |
-
};
|
| 24 |
|
| 25 |
return (
|
| 26 |
<div className={cn(className, "group relative flex flex-col gap-1")}>
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
|
|
|
|
|
|
|
| 37 |
{message.content.map((part) =>
|
| 38 |
part.type === "tool" ? (
|
| 39 |
<MessageToolCall tool={part} />
|
|
@@ -47,18 +44,18 @@ export default function Message({
|
|
| 47 |
<span>Response interrupted by the user</span>
|
| 48 |
</div>
|
| 49 |
)}
|
| 50 |
-
{message.
|
| 51 |
<div className="mt-1 inline-flex flex-wrap items-center gap-1.5 self-end rounded-lg bg-gray-200 px-2.5 py-1 text-xs font-medium text-gray-700 opacity-0 transition-opacity group-hover:opacity-100 dark:bg-gray-700 dark:text-gray-300">
|
| 52 |
-
<span className="font-semibold">{message.
|
| 53 |
<span className="text-gray-400 dark:text-gray-500">•</span>
|
| 54 |
<span>
|
| 55 |
-
Output: {Math.round(message.
|
| 56 |
-
{message.
|
| 57 |
-
{formatDuration(message.
|
| 58 |
</span>
|
| 59 |
<span className="text-gray-400 dark:text-gray-500">•</span>
|
| 60 |
<span title="Input processing time">
|
| 61 |
-
Prefill: {formatDuration(message.
|
| 62 |
</span>
|
| 63 |
<span className="text-gray-400 dark:text-gray-500">•</span>
|
| 64 |
<button
|
|
|
|
| 1 |
import { Modal } from "@theme";
|
| 2 |
import cn from "@utils/classnames.ts";
|
| 3 |
+
import { formatDuration } from "@utils/format.ts";
|
| 4 |
import { SquareCode, StopCircle } from "lucide-react";
|
| 5 |
import { useState } from "react";
|
| 6 |
|
|
|
|
| 16 |
className?: string;
|
| 17 |
}) {
|
| 18 |
const [templateOpen, setTemplateOpen] = useState<boolean>(false);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 19 |
|
| 20 |
return (
|
| 21 |
<div className={cn(className, "group relative flex flex-col gap-1")}>
|
| 22 |
+
{message.metadata && (
|
| 23 |
+
<Modal
|
| 24 |
+
title="Full Template"
|
| 25 |
+
isOpen={templateOpen}
|
| 26 |
+
onClose={() => setTemplateOpen(false)}
|
| 27 |
+
size="xl"
|
| 28 |
+
>
|
| 29 |
+
<pre className="max-h-[70vh] overflow-auto rounded-lg border border-gray-300 bg-gray-50 p-4 text-sm leading-relaxed text-gray-900 dark:border-gray-600 dark:bg-gray-900 dark:text-gray-100">
|
| 30 |
+
{message.metadata.template}
|
| 31 |
+
</pre>
|
| 32 |
+
</Modal>
|
| 33 |
+
)}
|
| 34 |
{message.content.map((part) =>
|
| 35 |
part.type === "tool" ? (
|
| 36 |
<MessageToolCall tool={part} />
|
|
|
|
| 44 |
<span>Response interrupted by the user</span>
|
| 45 |
</div>
|
| 46 |
)}
|
| 47 |
+
{message.metadata && (
|
| 48 |
<div className="mt-1 inline-flex flex-wrap items-center gap-1.5 self-end rounded-lg bg-gray-200 px-2.5 py-1 text-xs font-medium text-gray-700 opacity-0 transition-opacity group-hover:opacity-100 dark:bg-gray-700 dark:text-gray-300">
|
| 49 |
+
<span className="font-semibold">{message.metadata.model}</span>
|
| 50 |
<span className="text-gray-400 dark:text-gray-500">•</span>
|
| 51 |
<span>
|
| 52 |
+
Output: {Math.round(message.metadata.outputTps)} tok/s (
|
| 53 |
+
{message.metadata.outputTokens} tokens in{" "}
|
| 54 |
+
{formatDuration(message.metadata.outputDurationMs)})
|
| 55 |
</span>
|
| 56 |
<span className="text-gray-400 dark:text-gray-500">•</span>
|
| 57 |
<span title="Input processing time">
|
| 58 |
+
Prefill: {formatDuration(message.metadata.inputDurationMs)}
|
| 59 |
</span>
|
| 60 |
<span className="text-gray-400 dark:text-gray-500">•</span>
|
| 61 |
<button
|
src/chat/MessageToolCall.tsx
CHANGED
|
@@ -1,4 +1,5 @@
|
|
| 1 |
-
import {
|
|
|
|
| 2 |
import { useState } from "react";
|
| 3 |
|
| 4 |
import type { ChatMessageAssistantTool } from "../textGeneration/types.ts";
|
|
@@ -25,11 +26,17 @@ export default function MessageToolCall({
|
|
| 25 |
>
|
| 26 |
<div className="flex items-center justify-between gap-3">
|
| 27 |
<button
|
| 28 |
-
className="flex cursor-pointer items-center gap-2 text-xs text-gray-700 hover:text-gray-900 dark:text-gray-300 dark:hover:text-gray-100"
|
| 29 |
onClick={() => setExpanded(!expanded)}
|
| 30 |
>
|
| 31 |
-
|
| 32 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 33 |
</button>
|
| 34 |
</div>
|
| 35 |
{expanded && (
|
|
|
|
| 1 |
+
import { formatDuration } from "@utils/format.ts";
|
| 2 |
+
import { Timer, Wrench } from "lucide-react";
|
| 3 |
import { useState } from "react";
|
| 4 |
|
| 5 |
import type { ChatMessageAssistantTool } from "../textGeneration/types.ts";
|
|
|
|
| 26 |
>
|
| 27 |
<div className="flex items-center justify-between gap-3">
|
| 28 |
<button
|
| 29 |
+
className="flex w-full cursor-pointer items-center justify-between gap-2 text-xs text-gray-700 hover:text-gray-900 dark:text-gray-300 dark:hover:text-gray-100"
|
| 30 |
onClick={() => setExpanded(!expanded)}
|
| 31 |
>
|
| 32 |
+
<span className="flex items-center gap-2">
|
| 33 |
+
{isLoading ? <Loader size="xs" /> : <Wrench className="h-3 w-3" />}
|
| 34 |
+
{isLoading ? "calling tool" : "called tool"} <b>{tool.name}</b>
|
| 35 |
+
</span>
|
| 36 |
+
<span className="flex items-center gap-1 opacity-60">
|
| 37 |
+
<Timer className="h-3 w-3" />
|
| 38 |
+
{formatDuration(tool.time)}
|
| 39 |
+
</span>
|
| 40 |
</button>
|
| 41 |
</div>
|
| 42 |
{expanded && (
|
src/textGeneration/TextGeneration.ts
CHANGED
|
@@ -11,7 +11,7 @@ import {
|
|
| 11 |
type ChatMessageAssistant,
|
| 12 |
type ChatMessageAssistantResponse,
|
| 13 |
type ChatMessageAssistantTool,
|
| 14 |
-
type
|
| 15 |
type Request,
|
| 16 |
RequestType,
|
| 17 |
type Response,
|
|
@@ -143,9 +143,8 @@ export default class TextGeneration {
|
|
| 143 |
) => {
|
| 144 |
return new Promise<{
|
| 145 |
response: string;
|
| 146 |
-
|
| 147 |
interrupted: boolean;
|
| 148 |
-
template: string;
|
| 149 |
}>((resolve, reject) => {
|
| 150 |
if (this.modelKey === null) {
|
| 151 |
reject("Model not initialized");
|
|
@@ -177,9 +176,8 @@ export default class TextGeneration {
|
|
| 177 |
this.messages.push({ role: "assistant", content: data.response });
|
| 178 |
resolve({
|
| 179 |
response: data.response,
|
| 180 |
-
|
| 181 |
interrupted: data.interrupted,
|
| 182 |
-
template: data.template,
|
| 183 |
});
|
| 184 |
}
|
| 185 |
if (data.type === ResponseType.GENERATE_TEXT_CHUNK) {
|
|
@@ -219,7 +217,7 @@ export default class TextGeneration {
|
|
| 219 |
|
| 220 |
this.chatMessages = [...prevChatMessages, assistantMessage];
|
| 221 |
|
| 222 |
-
const {
|
| 223 |
prompt,
|
| 224 |
isUser ? "user" : "tool",
|
| 225 |
(partialResponse) => {
|
|
@@ -233,6 +231,7 @@ export default class TextGeneration {
|
|
| 233 |
: ({
|
| 234 |
type: "tool",
|
| 235 |
result: null,
|
|
|
|
| 236 |
functionSignature: `${part.name}(${JSON.stringify(
|
| 237 |
part.arguments
|
| 238 |
)})`,
|
|
@@ -244,8 +243,7 @@ export default class TextGeneration {
|
|
| 244 |
);
|
| 245 |
isUser = false;
|
| 246 |
|
| 247 |
-
assistantMessage.
|
| 248 |
-
assistantMessage.modelUsage = modelUsage;
|
| 249 |
assistantMessage.interrupted = interrupted;
|
| 250 |
this.chatMessages = [...prevChatMessages, assistantMessage];
|
| 251 |
|
|
@@ -270,7 +268,7 @@ export default class TextGeneration {
|
|
| 270 |
)
|
| 271 |
);
|
| 272 |
|
| 273 |
-
assistantMessage.
|
| 274 |
assistantMessage.content = assistantMessage.content.map((message) => {
|
| 275 |
if (message.type === "tool") {
|
| 276 |
const toolResponse = toolResponses.find(
|
|
@@ -280,6 +278,7 @@ export default class TextGeneration {
|
|
| 280 |
return {
|
| 281 |
...message,
|
| 282 |
result: toolResponse.result,
|
|
|
|
| 283 |
};
|
| 284 |
}
|
| 285 |
return message;
|
|
|
|
| 11 |
type ChatMessageAssistant,
|
| 12 |
type ChatMessageAssistantResponse,
|
| 13 |
type ChatMessageAssistantTool,
|
| 14 |
+
type GenerationMetadata,
|
| 15 |
type Request,
|
| 16 |
RequestType,
|
| 17 |
type Response,
|
|
|
|
| 143 |
) => {
|
| 144 |
return new Promise<{
|
| 145 |
response: string;
|
| 146 |
+
metadata: GenerationMetadata;
|
| 147 |
interrupted: boolean;
|
|
|
|
| 148 |
}>((resolve, reject) => {
|
| 149 |
if (this.modelKey === null) {
|
| 150 |
reject("Model not initialized");
|
|
|
|
| 176 |
this.messages.push({ role: "assistant", content: data.response });
|
| 177 |
resolve({
|
| 178 |
response: data.response,
|
| 179 |
+
metadata: data.metadata,
|
| 180 |
interrupted: data.interrupted,
|
|
|
|
| 181 |
});
|
| 182 |
}
|
| 183 |
if (data.type === ResponseType.GENERATE_TEXT_CHUNK) {
|
|
|
|
| 217 |
|
| 218 |
this.chatMessages = [...prevChatMessages, assistantMessage];
|
| 219 |
|
| 220 |
+
const { interrupted, metadata } = await this.generateText(
|
| 221 |
prompt,
|
| 222 |
isUser ? "user" : "tool",
|
| 223 |
(partialResponse) => {
|
|
|
|
| 231 |
: ({
|
| 232 |
type: "tool",
|
| 233 |
result: null,
|
| 234 |
+
time: null,
|
| 235 |
functionSignature: `${part.name}(${JSON.stringify(
|
| 236 |
part.arguments
|
| 237 |
)})`,
|
|
|
|
| 243 |
);
|
| 244 |
isUser = false;
|
| 245 |
|
| 246 |
+
assistantMessage.metadata = metadata;
|
|
|
|
| 247 |
assistantMessage.interrupted = interrupted;
|
| 248 |
this.chatMessages = [...prevChatMessages, assistantMessage];
|
| 249 |
|
|
|
|
| 268 |
)
|
| 269 |
);
|
| 270 |
|
| 271 |
+
assistantMessage.metadata = metadata;
|
| 272 |
assistantMessage.content = assistantMessage.content.map((message) => {
|
| 273 |
if (message.type === "tool") {
|
| 274 |
const toolResponse = toolResponses.find(
|
|
|
|
| 278 |
return {
|
| 279 |
...message,
|
| 280 |
result: toolResponse.result,
|
| 281 |
+
time: toolResponse.time,
|
| 282 |
};
|
| 283 |
}
|
| 284 |
return message;
|
src/textGeneration/types.ts
CHANGED
|
@@ -58,8 +58,7 @@ interface ResponseGenerateTextChunk {
|
|
| 58 |
interface ResponseGenerateTextDone {
|
| 59 |
type: ResponseType.GENERATE_TEXT_DONE;
|
| 60 |
response: string;
|
| 61 |
-
|
| 62 |
-
template: string;
|
| 63 |
interrupted: boolean;
|
| 64 |
requestId: number;
|
| 65 |
}
|
|
@@ -76,7 +75,7 @@ interface ResponseInitializeModel {
|
|
| 76 |
requestId: number;
|
| 77 |
}
|
| 78 |
|
| 79 |
-
export interface
|
| 80 |
inputDurationMs: number;
|
| 81 |
outputTokens: number;
|
| 82 |
outputDurationMs: number;
|
|
@@ -84,6 +83,7 @@ export interface ModelUsage {
|
|
| 84 |
doneMs: number;
|
| 85 |
modelKey: keyof typeof MODELS;
|
| 86 |
model: string;
|
|
|
|
| 87 |
}
|
| 88 |
|
| 89 |
interface ResponseGenerateTextAborted {
|
|
@@ -107,8 +107,7 @@ export interface ChatMessageAssistant {
|
|
| 107 |
role: "assistant";
|
| 108 |
content: Array<ChatMessageAssistantResponse | ChatMessageAssistantTool>;
|
| 109 |
interrupted: boolean;
|
| 110 |
-
|
| 111 |
-
template?: string;
|
| 112 |
}
|
| 113 |
|
| 114 |
export interface ChatMessageSystem {
|
|
@@ -125,53 +124,10 @@ export interface ChatMessageAssistantTool extends ToolCallPayload {
|
|
| 125 |
type: "tool";
|
| 126 |
functionSignature: string;
|
| 127 |
result: string;
|
|
|
|
| 128 |
}
|
| 129 |
|
| 130 |
export type ChatMessage =
|
| 131 |
| ChatMessageUser
|
| 132 |
| ChatMessageAssistant
|
| 133 |
| ChatMessageSystem;
|
| 134 |
-
|
| 135 |
-
/*
|
| 136 |
-
interface ResponseGenerateTextDone {}
|
| 137 |
-
|
| 138 |
-
export enum ResponseStatus {
|
| 139 |
-
SUCCESS,
|
| 140 |
-
ERROR,
|
| 141 |
-
STARTED,
|
| 142 |
-
}
|
| 143 |
-
|
| 144 |
-
export enum BackgroundTasks {
|
| 145 |
-
EXTRACT_FEATURES,
|
| 146 |
-
INITIALIZE_MODELS,
|
| 147 |
-
AGENT_GENERATE_TEXT,
|
| 148 |
-
AGENT_GET_MESSAGES,
|
| 149 |
-
AGENT_CLEAR,
|
| 150 |
-
}
|
| 151 |
-
|
| 152 |
-
export enum BackgroundMessages {
|
| 153 |
-
DOWNLOAD_PROGRESS,
|
| 154 |
-
MESSAGES_UPDATE,
|
| 155 |
-
}
|
| 156 |
-
|
| 157 |
-
export type Dtype = "fp32" | "fp16" | "q4" | "q4f16";
|
| 158 |
-
|
| 159 |
-
export interface ChatMessageUser {
|
| 160 |
-
role: "user";
|
| 161 |
-
content: string;
|
| 162 |
-
}
|
| 163 |
-
|
| 164 |
-
export interface ChatMessageTool {
|
| 165 |
-
name: string;
|
| 166 |
-
functionSignature: string;
|
| 167 |
-
id: string;
|
| 168 |
-
result: string;
|
| 169 |
-
}
|
| 170 |
-
|
| 171 |
-
export interface ChatMessageAssistant {
|
| 172 |
-
role: "assistant";
|
| 173 |
-
content: string;
|
| 174 |
-
tools: Array<ChatMessageTool>;
|
| 175 |
-
}
|
| 176 |
-
|
| 177 |
-
export type ChatMessage = ChatMessageUser | ChatMessageAssistant;*/
|
|
|
|
| 58 |
interface ResponseGenerateTextDone {
|
| 59 |
type: ResponseType.GENERATE_TEXT_DONE;
|
| 60 |
response: string;
|
| 61 |
+
metadata: GenerationMetadata;
|
|
|
|
| 62 |
interrupted: boolean;
|
| 63 |
requestId: number;
|
| 64 |
}
|
|
|
|
| 75 |
requestId: number;
|
| 76 |
}
|
| 77 |
|
| 78 |
+
export interface GenerationMetadata {
|
| 79 |
inputDurationMs: number;
|
| 80 |
outputTokens: number;
|
| 81 |
outputDurationMs: number;
|
|
|
|
| 83 |
doneMs: number;
|
| 84 |
modelKey: keyof typeof MODELS;
|
| 85 |
model: string;
|
| 86 |
+
template: string;
|
| 87 |
}
|
| 88 |
|
| 89 |
interface ResponseGenerateTextAborted {
|
|
|
|
| 107 |
role: "assistant";
|
| 108 |
content: Array<ChatMessageAssistantResponse | ChatMessageAssistantTool>;
|
| 109 |
interrupted: boolean;
|
| 110 |
+
metadata?: GenerationMetadata;
|
|
|
|
| 111 |
}
|
| 112 |
|
| 113 |
export interface ChatMessageSystem {
|
|
|
|
| 124 |
type: "tool";
|
| 125 |
functionSignature: string;
|
| 126 |
result: string;
|
| 127 |
+
time: number;
|
| 128 |
}
|
| 129 |
|
| 130 |
export type ChatMessage =
|
| 131 |
| ChatMessageUser
|
| 132 |
| ChatMessageAssistant
|
| 133 |
| ChatMessageSystem;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/textGeneration/worker/textGenerationWorker.ts
CHANGED
|
@@ -189,7 +189,7 @@ self.onmessage = async ({ data }: MessageEvent<Request>) => {
|
|
| 189 |
postMessage({
|
| 190 |
type: ResponseType.GENERATE_TEXT_DONE,
|
| 191 |
response,
|
| 192 |
-
|
| 193 |
inputDurationMs: firstTokenTime - started,
|
| 194 |
outputTokens: numTokens,
|
| 195 |
outputDurationMs: ended - firstTokenTime,
|
|
@@ -197,8 +197,8 @@ self.onmessage = async ({ data }: MessageEvent<Request>) => {
|
|
| 197 |
doneMs: ended - started,
|
| 198 |
modelKey: MODEL.modelId,
|
| 199 |
model: MODEL.title,
|
|
|
|
| 200 |
},
|
| 201 |
-
template,
|
| 202 |
interrupted: stoppingCriteria.interrupted,
|
| 203 |
requestId,
|
| 204 |
});
|
|
|
|
| 189 |
postMessage({
|
| 190 |
type: ResponseType.GENERATE_TEXT_DONE,
|
| 191 |
response,
|
| 192 |
+
metadata: {
|
| 193 |
inputDurationMs: firstTokenTime - started,
|
| 194 |
outputTokens: numTokens,
|
| 195 |
outputDurationMs: ended - firstTokenTime,
|
|
|
|
| 197 |
doneMs: ended - started,
|
| 198 |
modelKey: MODEL.modelId,
|
| 199 |
model: MODEL.title,
|
| 200 |
+
template,
|
| 201 |
},
|
|
|
|
| 202 |
interrupted: stoppingCriteria.interrupted,
|
| 203 |
requestId,
|
| 204 |
});
|
src/utils/format.ts
CHANGED
|
@@ -74,10 +74,11 @@ export const formatResult = (data: any, indent = 0): string => {
|
|
| 74 |
}
|
| 75 |
};
|
| 76 |
|
| 77 |
-
export const
|
| 78 |
-
|
| 79 |
-
|
| 80 |
-
|
|
|
|
| 81 |
};
|
| 82 |
|
| 83 |
export const round = (input: number, decimals: number): number => {
|
|
|
|
| 74 |
}
|
| 75 |
};
|
| 76 |
|
| 77 |
+
export const formatDuration = (ms: number) => {
|
| 78 |
+
if (ms < 1000) {
|
| 79 |
+
return `${Math.round(ms)}ms`;
|
| 80 |
+
}
|
| 81 |
+
return `${(ms / 1000).toFixed(2)}s`;
|
| 82 |
};
|
| 83 |
|
| 84 |
export const round = (input: number, decimals: number): number => {
|
src/utils/webMcp.ts
CHANGED
|
@@ -199,13 +199,17 @@ export const splitResponse = (
|
|
| 199 |
export const executeToolCall = async (
|
| 200 |
toolCall: ToolCallPayload,
|
| 201 |
tools: Array<WebMCPTool>
|
| 202 |
-
): Promise<{ id: string; result: string }> => {
|
|
|
|
| 203 |
const toolToUse = tools.find((t) => t.name === toolCall.name);
|
| 204 |
if (!toolToUse)
|
| 205 |
throw new Error(`Tool '${toolCall.name}' not found or is disabled.`);
|
| 206 |
|
|
|
|
|
|
|
| 207 |
return {
|
| 208 |
id: toolCall.id,
|
| 209 |
-
result
|
|
|
|
| 210 |
};
|
| 211 |
};
|
|
|
|
| 199 |
export const executeToolCall = async (
|
| 200 |
toolCall: ToolCallPayload,
|
| 201 |
tools: Array<WebMCPTool>
|
| 202 |
+
): Promise<{ id: string; result: string; time: number }> => {
|
| 203 |
+
const started = performance.now();
|
| 204 |
const toolToUse = tools.find((t) => t.name === toolCall.name);
|
| 205 |
if (!toolToUse)
|
| 206 |
throw new Error(`Tool '${toolCall.name}' not found or is disabled.`);
|
| 207 |
|
| 208 |
+
const result = await executeWebMCPTool(toolToUse, toolCall.arguments);
|
| 209 |
+
const ended = performance.now();
|
| 210 |
return {
|
| 211 |
id: toolCall.id,
|
| 212 |
+
result,
|
| 213 |
+
time: ended - started,
|
| 214 |
};
|
| 215 |
};
|