@marimo-team/frontend 0.19.3-dev42 → 0.19.3-dev45
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.html
CHANGED
|
@@ -66,7 +66,7 @@
|
|
|
66
66
|
<marimo-server-token data-token="{{ server_token }}" hidden></marimo-server-token>
|
|
67
67
|
<!-- /TODO -->
|
|
68
68
|
<title>{{ title }}</title>
|
|
69
|
-
<script type="module" crossorigin src="./assets/index-
|
|
69
|
+
<script type="module" crossorigin src="./assets/index-VUoDw_Qb.js"></script>
|
|
70
70
|
<link rel="modulepreload" crossorigin href="./assets/preload-helper-BW0IMuFq.js">
|
|
71
71
|
<link rel="modulepreload" crossorigin href="./assets/hotkeys-uKX61F1_.js">
|
|
72
72
|
<link rel="modulepreload" crossorigin href="./assets/defaultLocale-BLUna9fQ.js">
|
package/package.json
CHANGED
|
@@ -15,7 +15,7 @@ export type PluginFunctions = {
|
|
|
15
15
|
get_chat_history: (req: {}) => Promise<{ messages: UIMessage[] }>;
|
|
16
16
|
delete_chat_history: (req: {}) => Promise<null>;
|
|
17
17
|
delete_chat_message: (req: { index: number }) => Promise<null>;
|
|
18
|
-
send_prompt: (req: SendMessageRequest) => Promise<
|
|
18
|
+
send_prompt: (req: SendMessageRequest) => Promise<unknown>;
|
|
19
19
|
};
|
|
20
20
|
|
|
21
21
|
const messageSchema = z.array(
|
|
@@ -47,7 +47,6 @@ export const ChatPlugin = createPlugin<{ messages: UIMessage[] }>(
|
|
|
47
47
|
maxHeight: z.number().optional(),
|
|
48
48
|
config: configSchema,
|
|
49
49
|
allowAttachments: z.union([z.boolean(), z.string().array()]),
|
|
50
|
-
frontendManaged: z.boolean(),
|
|
51
50
|
}),
|
|
52
51
|
)
|
|
53
52
|
.withFunctions<PluginFunctions>({
|
|
@@ -67,7 +66,7 @@ export const ChatPlugin = createPlugin<{ messages: UIMessage[] }>(
|
|
|
67
66
|
config: configSchema,
|
|
68
67
|
}),
|
|
69
68
|
)
|
|
70
|
-
.output(z.
|
|
69
|
+
.output(z.unknown()),
|
|
71
70
|
})
|
|
72
71
|
.renderer((props) => (
|
|
73
72
|
<TooltipProvider>
|
|
@@ -77,7 +76,6 @@ export const ChatPlugin = createPlugin<{ messages: UIMessage[] }>(
|
|
|
77
76
|
showConfigurationControls={props.data.showConfigurationControls}
|
|
78
77
|
maxHeight={props.data.maxHeight}
|
|
79
78
|
allowAttachments={props.data.allowAttachments}
|
|
80
|
-
frontendManaged={props.data.frontendManaged}
|
|
81
79
|
config={props.data.config}
|
|
82
80
|
get_chat_history={props.functions.get_chat_history}
|
|
83
81
|
delete_chat_history={props.functions.delete_chat_history}
|
|
@@ -66,7 +66,6 @@ interface Props extends PluginFunctions {
|
|
|
66
66
|
showConfigurationControls: boolean;
|
|
67
67
|
maxHeight: number | undefined;
|
|
68
68
|
allowAttachments: boolean | string[];
|
|
69
|
-
frontendManaged: boolean;
|
|
70
69
|
value: UIMessage[];
|
|
71
70
|
setValue: (messages: UIMessage[]) => void;
|
|
72
71
|
host: HTMLElement;
|
|
@@ -166,113 +165,42 @@ export const Chatbot: React.FC<Props> = (props) => {
|
|
|
166
165
|
};
|
|
167
166
|
});
|
|
168
167
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
frontendStreamControllerRef.current = controller;
|
|
168
|
+
const stream = new ReadableStream<UIMessageChunk>({
|
|
169
|
+
start(controller) {
|
|
170
|
+
frontendStreamControllerRef.current = controller;
|
|
173
171
|
|
|
174
|
-
const abortHandler = () => {
|
|
175
|
-
try {
|
|
176
|
-
controller.close();
|
|
177
|
-
} catch (error) {
|
|
178
|
-
Logger.debug("Controller may already be closed", { error });
|
|
179
|
-
}
|
|
180
|
-
frontendStreamControllerRef.current = null;
|
|
181
|
-
};
|
|
182
|
-
signal?.addEventListener("abort", abortHandler);
|
|
183
|
-
|
|
184
|
-
return () => {
|
|
185
|
-
signal?.removeEventListener("abort", abortHandler);
|
|
186
|
-
};
|
|
187
|
-
},
|
|
188
|
-
cancel() {
|
|
189
|
-
frontendStreamControllerRef.current = null;
|
|
190
|
-
},
|
|
191
|
-
});
|
|
192
|
-
|
|
193
|
-
// Start the prompt, chunks will be sent via events
|
|
194
|
-
props
|
|
195
|
-
.send_prompt({
|
|
196
|
-
messages: messages,
|
|
197
|
-
config: chatConfig,
|
|
198
|
-
})
|
|
199
|
-
.catch((error: Error) => {
|
|
200
|
-
frontendStreamControllerRef.current?.error(error);
|
|
201
|
-
frontendStreamControllerRef.current = null;
|
|
202
|
-
});
|
|
203
|
-
|
|
204
|
-
return createUIMessageStreamResponse({ stream });
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
if (signal?.aborted) {
|
|
208
|
-
return new Response("Aborted", { status: 499 });
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
// Create a placeholder message for streaming (backend-managed)
|
|
212
|
-
const messageId = Date.now().toString();
|
|
213
|
-
|
|
214
|
-
setMessages((prev) => [
|
|
215
|
-
...prev,
|
|
216
|
-
{
|
|
217
|
-
id: messageId,
|
|
218
|
-
role: "assistant",
|
|
219
|
-
parts: [{ type: "text", text: "" }],
|
|
220
|
-
},
|
|
221
|
-
]);
|
|
222
|
-
|
|
223
|
-
// Create an abort-aware promise for the send_prompt call
|
|
224
|
-
const sendPromptPromise = props.send_prompt({
|
|
225
|
-
messages: messages,
|
|
226
|
-
config: chatConfig,
|
|
227
|
-
});
|
|
228
|
-
|
|
229
|
-
// Race the send_prompt with an abort signal
|
|
230
|
-
const response = await new Promise<string | null>(
|
|
231
|
-
(resolve, reject) => {
|
|
232
|
-
// Listen for abort
|
|
233
172
|
const abortHandler = () => {
|
|
234
|
-
|
|
173
|
+
try {
|
|
174
|
+
controller.close();
|
|
175
|
+
} catch (error) {
|
|
176
|
+
Logger.debug("Controller may already be closed", { error });
|
|
177
|
+
}
|
|
178
|
+
frontendStreamControllerRef.current = null;
|
|
235
179
|
};
|
|
236
180
|
signal?.addEventListener("abort", abortHandler);
|
|
237
181
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
.finally(() => {
|
|
242
|
-
signal?.removeEventListener("abort", abortHandler);
|
|
243
|
-
});
|
|
182
|
+
return () => {
|
|
183
|
+
signal?.removeEventListener("abort", abortHandler);
|
|
184
|
+
};
|
|
244
185
|
},
|
|
245
|
-
|
|
186
|
+
cancel() {
|
|
187
|
+
frontendStreamControllerRef.current = null;
|
|
188
|
+
},
|
|
189
|
+
});
|
|
246
190
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
191
|
+
// Start the prompt, chunks will be sent via events
|
|
192
|
+
void props
|
|
193
|
+
.send_prompt({
|
|
194
|
+
messages: messages,
|
|
195
|
+
config: chatConfig,
|
|
196
|
+
})
|
|
197
|
+
.catch((error: Error) => {
|
|
198
|
+
frontendStreamControllerRef.current?.error(error);
|
|
199
|
+
frontendStreamControllerRef.current = null;
|
|
250
200
|
});
|
|
251
|
-
return new Response("Internal server error", { status: 500 });
|
|
252
|
-
}
|
|
253
201
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
if (
|
|
257
|
-
streamingStateRef.current.backendMessageId === null &&
|
|
258
|
-
streamingStateRef.current.frontendMessageIndex === null
|
|
259
|
-
) {
|
|
260
|
-
setMessages((prev) => {
|
|
261
|
-
const updated = [...prev];
|
|
262
|
-
const index = updated.findIndex((m) => m.id === messageId);
|
|
263
|
-
if (index !== -1) {
|
|
264
|
-
updated[index] = {
|
|
265
|
-
...updated[index],
|
|
266
|
-
parts: [{ type: "text", text: response }],
|
|
267
|
-
};
|
|
268
|
-
}
|
|
269
|
-
return updated;
|
|
270
|
-
});
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
return new Response(response);
|
|
274
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
275
|
-
} catch (error: any) {
|
|
202
|
+
return createUIMessageStreamResponse({ stream });
|
|
203
|
+
} catch (error: unknown) {
|
|
276
204
|
// Clear streaming state on error
|
|
277
205
|
streamingStateRef.current = {
|
|
278
206
|
backendMessageId: null,
|
|
@@ -280,13 +208,13 @@ export const Chatbot: React.FC<Props> = (props) => {
|
|
|
280
208
|
};
|
|
281
209
|
|
|
282
210
|
// Handle abort gracefully without showing an error
|
|
283
|
-
if (error.name === "AbortError") {
|
|
211
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
284
212
|
return new Response("Aborted", { status: 499 });
|
|
285
213
|
}
|
|
286
214
|
|
|
287
215
|
// HACK: strip the error message to clean up the response
|
|
288
|
-
const strippedError = error.message
|
|
289
|
-
|
|
216
|
+
const strippedError = (error as Error).message
|
|
217
|
+
?.split("failed with exception ")
|
|
290
218
|
.pop();
|
|
291
219
|
return new Response(strippedError, { status: 400 });
|
|
292
220
|
}
|
|
@@ -307,11 +235,7 @@ export const Chatbot: React.FC<Props> = (props) => {
|
|
|
307
235
|
frontendMessageIndex: null,
|
|
308
236
|
};
|
|
309
237
|
|
|
310
|
-
|
|
311
|
-
// Because useChat creates the proper message structure for us.
|
|
312
|
-
if (props.frontendManaged) {
|
|
313
|
-
props.setValue(message.messages);
|
|
314
|
-
}
|
|
238
|
+
props.setValue(message.messages);
|
|
315
239
|
},
|
|
316
240
|
onError: (error) => {
|
|
317
241
|
Logger.error("An error occurred:", error);
|
|
@@ -338,90 +262,28 @@ export const Chatbot: React.FC<Props> = (props) => {
|
|
|
338
262
|
return;
|
|
339
263
|
}
|
|
340
264
|
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
if (!controller) {
|
|
345
|
-
return;
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
const frontendMessage = message as {
|
|
349
|
-
type: string;
|
|
350
|
-
message_id: string;
|
|
351
|
-
content?: UIMessageChunk;
|
|
352
|
-
is_final?: boolean;
|
|
353
|
-
};
|
|
354
|
-
|
|
355
|
-
if (frontendMessage.content) {
|
|
356
|
-
controller.enqueue(frontendMessage.content);
|
|
357
|
-
}
|
|
358
|
-
if (frontendMessage.is_final) {
|
|
359
|
-
controller.close();
|
|
360
|
-
frontendStreamControllerRef.current = null;
|
|
361
|
-
}
|
|
265
|
+
// Push to the stream for useChat to process
|
|
266
|
+
const controller = frontendStreamControllerRef.current;
|
|
267
|
+
if (!controller) {
|
|
362
268
|
return;
|
|
363
269
|
}
|
|
364
270
|
|
|
365
|
-
|
|
366
|
-
const chunkMessage = message as {
|
|
271
|
+
const frontendMessage = message as {
|
|
367
272
|
type: string;
|
|
368
273
|
message_id: string;
|
|
369
|
-
content
|
|
370
|
-
is_final
|
|
274
|
+
content?: UIMessageChunk;
|
|
275
|
+
is_final?: boolean;
|
|
371
276
|
};
|
|
372
277
|
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
// Find the last assistant message (which should be the placeholder we created)
|
|
376
|
-
setMessages((prev) => {
|
|
377
|
-
const updated = [...prev];
|
|
378
|
-
// Find the last assistant message
|
|
379
|
-
for (let i = updated.length - 1; i >= 0; i--) {
|
|
380
|
-
if (updated[i].role === "assistant") {
|
|
381
|
-
streamingStateRef.current = {
|
|
382
|
-
backendMessageId: chunkMessage.message_id,
|
|
383
|
-
frontendMessageIndex: i,
|
|
384
|
-
};
|
|
385
|
-
break;
|
|
386
|
-
}
|
|
387
|
-
}
|
|
388
|
-
return updated;
|
|
389
|
-
});
|
|
278
|
+
if (frontendMessage.content) {
|
|
279
|
+
controller.enqueue(frontendMessage.content);
|
|
390
280
|
}
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
if (
|
|
395
|
-
streamingStateRef.current.backendMessageId ===
|
|
396
|
-
chunkMessage.message_id &&
|
|
397
|
-
frontendIndex !== null
|
|
398
|
-
) {
|
|
399
|
-
setMessages((prev) => {
|
|
400
|
-
const updated = [...prev];
|
|
401
|
-
const index = frontendIndex;
|
|
402
|
-
|
|
403
|
-
// Update the message at the tracked index
|
|
404
|
-
if (index < updated.length) {
|
|
405
|
-
const messageToUpdate = updated[index];
|
|
406
|
-
if (messageToUpdate.role === "assistant") {
|
|
407
|
-
updated[index] = {
|
|
408
|
-
...messageToUpdate,
|
|
409
|
-
parts: [{ type: "text", text: chunkMessage.content }],
|
|
410
|
-
};
|
|
411
|
-
}
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
return updated;
|
|
415
|
-
});
|
|
416
|
-
|
|
417
|
-
// Clear streaming state when final chunk arrives
|
|
418
|
-
if (chunkMessage.is_final) {
|
|
419
|
-
streamingStateRef.current = {
|
|
420
|
-
backendMessageId: null,
|
|
421
|
-
frontendMessageIndex: null,
|
|
422
|
-
};
|
|
423
|
-
}
|
|
281
|
+
if (frontendMessage.is_final) {
|
|
282
|
+
controller.close();
|
|
283
|
+
frontendStreamControllerRef.current = null;
|
|
424
284
|
}
|
|
285
|
+
|
|
286
|
+
return;
|
|
425
287
|
},
|
|
426
288
|
);
|
|
427
289
|
|
|
@@ -434,10 +296,7 @@ export const Chatbot: React.FC<Props> = (props) => {
|
|
|
434
296
|
props.delete_chat_message({ index });
|
|
435
297
|
setMessages(newMessages);
|
|
436
298
|
|
|
437
|
-
|
|
438
|
-
if (props.frontendManaged) {
|
|
439
|
-
props.setValue(newMessages);
|
|
440
|
-
}
|
|
299
|
+
props.setValue(newMessages);
|
|
441
300
|
}
|
|
442
301
|
};
|
|
443
302
|
|
|
@@ -526,7 +385,7 @@ export const Chatbot: React.FC<Props> = (props) => {
|
|
|
526
385
|
|
|
527
386
|
return (
|
|
528
387
|
<div
|
|
529
|
-
key={message.id}
|
|
388
|
+
key={`${message.id}-${index}`}
|
|
530
389
|
className={cn(
|
|
531
390
|
"flex flex-col group gap-2",
|
|
532
391
|
message.role === "user" ? "items-end" : "items-start",
|