@dudousxd/nestjs-agent-react 0.3.2 → 0.4.0
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.cjs +279 -37
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +80 -8
- package/dist/index.d.ts +80 -8
- package/dist/index.js +285 -44
- package/dist/index.js.map +1 -1
- package/package.json +7 -2
package/dist/index.js
CHANGED
|
@@ -101,18 +101,26 @@ var AgentChatTransport = class {
|
|
|
101
101
|
this.options.onMeta?.(meta);
|
|
102
102
|
}
|
|
103
103
|
/**
|
|
104
|
-
* Parse the backend's SSE byte stream and
|
|
105
|
-
*
|
|
106
|
-
* - `event: meta` `data: {"runId","threadId"}`
|
|
107
|
-
* - `data:
|
|
108
|
-
* - `event: done` `data: {}`
|
|
104
|
+
* Parse the backend's SSE byte stream and re-emit it as a valid v7 UI-message chunk stream.
|
|
105
|
+
* Recognized frames:
|
|
106
|
+
* - `event: meta` `data: {"runId","threadId"}` → records identity
|
|
107
|
+
* - `data: <AgentStreamEvent JSON>` → mapped to UI chunks (text / reasoning / tool)
|
|
108
|
+
* - `event: done` `data: {}` → terminates
|
|
109
|
+
* - `event: error` `data: {code,message}` → error chunk
|
|
110
|
+
*
|
|
111
|
+
* A run is ONE UI message with N steps. Each `step-start`/`step-finish` pair brackets a model
|
|
112
|
+
* call plus its tool execution; text and reasoning open lazily and close at the step boundary, so
|
|
113
|
+
* tool-call cards (input streaming → output) render live between the prose.
|
|
109
114
|
*/
|
|
110
115
|
toChunkStream(source) {
|
|
111
116
|
const reader = source.getReader();
|
|
112
117
|
const decoder = new TextDecoder();
|
|
113
|
-
const textId = `txt-${Math.random().toString(36).slice(2)}`;
|
|
114
118
|
let buffer = "";
|
|
115
119
|
let started = false;
|
|
120
|
+
let stepOpen = false;
|
|
121
|
+
let stepIndex = 0;
|
|
122
|
+
let textId = null;
|
|
123
|
+
let reasoningId = null;
|
|
116
124
|
const record = /* @__PURE__ */ __name((meta) => this.recordMeta(meta), "record");
|
|
117
125
|
return new ReadableStream({
|
|
118
126
|
async pull(controller) {
|
|
@@ -122,24 +130,154 @@ var AgentChatTransport = class {
|
|
|
122
130
|
controller.enqueue({
|
|
123
131
|
type: "start"
|
|
124
132
|
});
|
|
125
|
-
controller.enqueue({
|
|
126
|
-
type: "start-step"
|
|
127
|
-
});
|
|
128
|
-
controller.enqueue({
|
|
129
|
-
type: "text-start",
|
|
130
|
-
id: textId
|
|
131
|
-
});
|
|
132
133
|
}
|
|
133
134
|
__name(ensureStarted, "ensureStarted");
|
|
134
|
-
function
|
|
135
|
+
function openStep() {
|
|
135
136
|
ensureStarted();
|
|
137
|
+
if (stepOpen) closeStep();
|
|
138
|
+
stepOpen = true;
|
|
139
|
+
stepIndex += 1;
|
|
140
|
+
textId = null;
|
|
141
|
+
reasoningId = null;
|
|
136
142
|
controller.enqueue({
|
|
137
|
-
type: "
|
|
138
|
-
id: textId
|
|
143
|
+
type: "start-step"
|
|
139
144
|
});
|
|
145
|
+
}
|
|
146
|
+
__name(openStep, "openStep");
|
|
147
|
+
function ensureStep() {
|
|
148
|
+
if (!stepOpen) openStep();
|
|
149
|
+
}
|
|
150
|
+
__name(ensureStep, "ensureStep");
|
|
151
|
+
function closeStep() {
|
|
152
|
+
if (!stepOpen) return;
|
|
153
|
+
if (textId !== null) {
|
|
154
|
+
controller.enqueue({
|
|
155
|
+
type: "text-end",
|
|
156
|
+
id: textId
|
|
157
|
+
});
|
|
158
|
+
textId = null;
|
|
159
|
+
}
|
|
160
|
+
if (reasoningId !== null) {
|
|
161
|
+
controller.enqueue({
|
|
162
|
+
type: "reasoning-end",
|
|
163
|
+
id: reasoningId
|
|
164
|
+
});
|
|
165
|
+
reasoningId = null;
|
|
166
|
+
}
|
|
140
167
|
controller.enqueue({
|
|
141
168
|
type: "finish-step"
|
|
142
169
|
});
|
|
170
|
+
stepOpen = false;
|
|
171
|
+
}
|
|
172
|
+
__name(closeStep, "closeStep");
|
|
173
|
+
function emit(event) {
|
|
174
|
+
switch (event.kind) {
|
|
175
|
+
case "step-start":
|
|
176
|
+
openStep();
|
|
177
|
+
break;
|
|
178
|
+
case "step-finish":
|
|
179
|
+
closeStep();
|
|
180
|
+
if (event.costUsd !== void 0) {
|
|
181
|
+
controller.enqueue({
|
|
182
|
+
type: "message-metadata",
|
|
183
|
+
messageMetadata: {
|
|
184
|
+
costUsd: event.costUsd
|
|
185
|
+
}
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
break;
|
|
189
|
+
case "text":
|
|
190
|
+
ensureStep();
|
|
191
|
+
if (textId === null) {
|
|
192
|
+
textId = `txt-${stepIndex}`;
|
|
193
|
+
controller.enqueue({
|
|
194
|
+
type: "text-start",
|
|
195
|
+
id: textId
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
controller.enqueue({
|
|
199
|
+
type: "text-delta",
|
|
200
|
+
id: textId,
|
|
201
|
+
delta: event.text
|
|
202
|
+
});
|
|
203
|
+
break;
|
|
204
|
+
case "reasoning":
|
|
205
|
+
ensureStep();
|
|
206
|
+
if (reasoningId === null) {
|
|
207
|
+
reasoningId = `rsn-${stepIndex}`;
|
|
208
|
+
controller.enqueue({
|
|
209
|
+
type: "reasoning-start",
|
|
210
|
+
id: reasoningId
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
controller.enqueue({
|
|
214
|
+
type: "reasoning-delta",
|
|
215
|
+
id: reasoningId,
|
|
216
|
+
delta: event.text
|
|
217
|
+
});
|
|
218
|
+
break;
|
|
219
|
+
case "tool-input-start":
|
|
220
|
+
ensureStep();
|
|
221
|
+
controller.enqueue({
|
|
222
|
+
type: "tool-input-start",
|
|
223
|
+
toolCallId: event.id,
|
|
224
|
+
toolName: event.name,
|
|
225
|
+
// `toolKind` is absent on older backends — omitted (not `undefined`-valued) so
|
|
226
|
+
// `toolMetadata` itself is only present when there's something to say, letting a
|
|
227
|
+
// UI gate approval affordances on `kind === 'action'` without hardcoding tool names.
|
|
228
|
+
...event.toolKind !== void 0 ? {
|
|
229
|
+
toolMetadata: {
|
|
230
|
+
toolKind: event.toolKind
|
|
231
|
+
}
|
|
232
|
+
} : {}
|
|
233
|
+
});
|
|
234
|
+
break;
|
|
235
|
+
case "tool-input-delta":
|
|
236
|
+
ensureStep();
|
|
237
|
+
controller.enqueue({
|
|
238
|
+
type: "tool-input-delta",
|
|
239
|
+
toolCallId: event.id,
|
|
240
|
+
inputTextDelta: event.delta
|
|
241
|
+
});
|
|
242
|
+
break;
|
|
243
|
+
case "tool-input-available":
|
|
244
|
+
ensureStep();
|
|
245
|
+
controller.enqueue({
|
|
246
|
+
type: "tool-input-available",
|
|
247
|
+
toolCallId: event.id,
|
|
248
|
+
toolName: event.name,
|
|
249
|
+
input: event.input,
|
|
250
|
+
...event.toolKind !== void 0 ? {
|
|
251
|
+
toolMetadata: {
|
|
252
|
+
toolKind: event.toolKind
|
|
253
|
+
}
|
|
254
|
+
} : {}
|
|
255
|
+
});
|
|
256
|
+
break;
|
|
257
|
+
case "tool-output":
|
|
258
|
+
ensureStep();
|
|
259
|
+
controller.enqueue({
|
|
260
|
+
type: "tool-output-available",
|
|
261
|
+
toolCallId: event.id,
|
|
262
|
+
output: event.output
|
|
263
|
+
});
|
|
264
|
+
break;
|
|
265
|
+
case "tool-output-error":
|
|
266
|
+
ensureStep();
|
|
267
|
+
controller.enqueue({
|
|
268
|
+
type: "tool-output-error",
|
|
269
|
+
toolCallId: event.id,
|
|
270
|
+
errorText: event.error
|
|
271
|
+
});
|
|
272
|
+
break;
|
|
273
|
+
default:
|
|
274
|
+
break;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
__name(emit, "emit");
|
|
278
|
+
function finish() {
|
|
279
|
+
ensureStarted();
|
|
280
|
+
closeStep();
|
|
143
281
|
controller.enqueue({
|
|
144
282
|
type: "finish"
|
|
145
283
|
});
|
|
@@ -178,15 +316,8 @@ var AgentChatTransport = class {
|
|
|
178
316
|
const meta = parseMeta(frame.data);
|
|
179
317
|
if (meta) record(meta);
|
|
180
318
|
} else if (frame.data) {
|
|
181
|
-
const
|
|
182
|
-
if (
|
|
183
|
-
ensureStarted();
|
|
184
|
-
controller.enqueue({
|
|
185
|
-
type: "text-delta",
|
|
186
|
-
id: textId,
|
|
187
|
-
delta
|
|
188
|
-
});
|
|
189
|
-
}
|
|
319
|
+
const event = parseEvent(frame.data);
|
|
320
|
+
if (event) emit(event);
|
|
190
321
|
}
|
|
191
322
|
separator = buffer.indexOf("\n\n");
|
|
192
323
|
}
|
|
@@ -248,17 +379,17 @@ function parseErrorText(data) {
|
|
|
248
379
|
return "Agent run failed";
|
|
249
380
|
}
|
|
250
381
|
__name(parseErrorText, "parseErrorText");
|
|
251
|
-
function
|
|
382
|
+
function parseEvent(data) {
|
|
252
383
|
try {
|
|
253
384
|
const parsed = JSON.parse(data);
|
|
254
|
-
if (parsed !== null && typeof parsed === "object" && "
|
|
255
|
-
return parsed
|
|
385
|
+
if (parsed !== null && typeof parsed === "object" && "kind" in parsed) {
|
|
386
|
+
return parsed;
|
|
256
387
|
}
|
|
257
388
|
} catch {
|
|
258
389
|
}
|
|
259
390
|
return null;
|
|
260
391
|
}
|
|
261
|
-
__name(
|
|
392
|
+
__name(parseEvent, "parseEvent");
|
|
262
393
|
function extractText(message) {
|
|
263
394
|
let text = "";
|
|
264
395
|
for (const part of message.parts ?? []) {
|
|
@@ -302,10 +433,39 @@ var AgentClient = class {
|
|
|
302
433
|
return this.request("POST", `/agent/threads/${encodeURIComponent(threadId)}/fork-from/${encodeURIComponent(messageId)}`);
|
|
303
434
|
}
|
|
304
435
|
renameThread(id, title) {
|
|
305
|
-
return this.
|
|
436
|
+
return this.updateThread(id, {
|
|
306
437
|
title
|
|
307
438
|
});
|
|
308
439
|
}
|
|
440
|
+
/** General `PATCH /agent/threads/:threadId` — title and/or the thread's pinned default agent. */
|
|
441
|
+
updateThread(id, patch) {
|
|
442
|
+
return this.request("PATCH", `/agent/threads/${encodeURIComponent(id)}`, patch);
|
|
443
|
+
}
|
|
444
|
+
/**
|
|
445
|
+
* Uploads a file (image/PDF) for a vision-capable model turn. Multipart, field name `file` —
|
|
446
|
+
* mirrors the backend's `POST /agent/attachments`. The returned {@link MessageAttachment} is
|
|
447
|
+
* what a caller then rides on `sendMessage({ text }, { body: { attachments: [...] } })`.
|
|
448
|
+
*/
|
|
449
|
+
async uploadAttachment(file) {
|
|
450
|
+
const fetchImpl = this.options.fetch ?? globalThis.fetch;
|
|
451
|
+
const baseUrl = (this.options.baseUrl ?? "").replace(/\/$/, "");
|
|
452
|
+
const dynamic = await this.options.getHeaders?.() ?? {};
|
|
453
|
+
const formData = new FormData();
|
|
454
|
+
formData.append("file", file);
|
|
455
|
+
const response = await fetchImpl(`${baseUrl}/agent/attachments`, {
|
|
456
|
+
method: "POST",
|
|
457
|
+
headers: {
|
|
458
|
+
accept: "application/json",
|
|
459
|
+
...this.options.headers,
|
|
460
|
+
...dynamic
|
|
461
|
+
},
|
|
462
|
+
body: formData,
|
|
463
|
+
...this.options.credentials !== void 0 ? {
|
|
464
|
+
credentials: this.options.credentials
|
|
465
|
+
} : {}
|
|
466
|
+
});
|
|
467
|
+
return this.handleResponse(response, "POST", "/agent/attachments");
|
|
468
|
+
}
|
|
309
469
|
promoteThread(id) {
|
|
310
470
|
return this.request("POST", `/agent/threads/${encodeURIComponent(id)}/promote`);
|
|
311
471
|
}
|
|
@@ -345,6 +505,9 @@ var AgentClient = class {
|
|
|
345
505
|
credentials: this.options.credentials
|
|
346
506
|
} : {}
|
|
347
507
|
});
|
|
508
|
+
return this.handleResponse(response, method, path);
|
|
509
|
+
}
|
|
510
|
+
async handleResponse(response, method, path) {
|
|
348
511
|
if (!response.ok) {
|
|
349
512
|
throw new AgentHttpError(response.status, method, path, response.statusText);
|
|
350
513
|
}
|
|
@@ -355,16 +518,66 @@ var AgentClient = class {
|
|
|
355
518
|
}
|
|
356
519
|
};
|
|
357
520
|
|
|
521
|
+
// src/stored-message-to-ui-message.ts
|
|
522
|
+
function storedMessageToUiMessage(message) {
|
|
523
|
+
const parts = [];
|
|
524
|
+
if (message.content) {
|
|
525
|
+
parts.push({
|
|
526
|
+
type: "text",
|
|
527
|
+
text: message.content
|
|
528
|
+
});
|
|
529
|
+
}
|
|
530
|
+
for (const attachment of message.attachments ?? []) {
|
|
531
|
+
parts.push({
|
|
532
|
+
type: "file",
|
|
533
|
+
mediaType: attachment.contentType,
|
|
534
|
+
filename: attachment.name,
|
|
535
|
+
url: attachment.url
|
|
536
|
+
});
|
|
537
|
+
}
|
|
538
|
+
for (const call of message.toolCalls ?? []) {
|
|
539
|
+
const result = message.toolResults?.find((candidate) => candidate.id === call.id);
|
|
540
|
+
const toolKind = call.kind;
|
|
541
|
+
parts.push({
|
|
542
|
+
type: `tool-${call.name}`,
|
|
543
|
+
toolCallId: call.id,
|
|
544
|
+
...toolKind !== void 0 ? {
|
|
545
|
+
toolMetadata: {
|
|
546
|
+
toolKind
|
|
547
|
+
}
|
|
548
|
+
} : {},
|
|
549
|
+
...result !== void 0 ? {
|
|
550
|
+
state: "output-available",
|
|
551
|
+
input: call.input,
|
|
552
|
+
output: result.output
|
|
553
|
+
} : {
|
|
554
|
+
state: "input-available",
|
|
555
|
+
input: call.input
|
|
556
|
+
}
|
|
557
|
+
});
|
|
558
|
+
}
|
|
559
|
+
return {
|
|
560
|
+
id: message.id,
|
|
561
|
+
role: message.role,
|
|
562
|
+
parts
|
|
563
|
+
};
|
|
564
|
+
}
|
|
565
|
+
__name(storedMessageToUiMessage, "storedMessageToUiMessage");
|
|
566
|
+
|
|
358
567
|
// src/use-agent-chat.ts
|
|
359
568
|
import { useChat } from "@ai-sdk/react";
|
|
360
|
-
import { useCallback, useMemo, useRef, useState } from "react";
|
|
569
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
361
570
|
function useAgentChat(options) {
|
|
362
571
|
const latest = useRef(options);
|
|
363
572
|
latest.current = options;
|
|
364
573
|
const [runId, setRunId] = useState(options.resumeRunId);
|
|
365
574
|
const runIdRef = useRef(runId);
|
|
366
575
|
runIdRef.current = runId;
|
|
576
|
+
const autoResumeRunIdRef = useRef(void 0);
|
|
577
|
+
const [autoResume, setAutoResume] = useState(false);
|
|
578
|
+
const [activeRunId, setActiveRunId] = useState(null);
|
|
367
579
|
const regenerateNext = useRef(false);
|
|
580
|
+
const createdThreadId = useRef(void 0);
|
|
368
581
|
const client = useMemo(() => {
|
|
369
582
|
if (options.client) return options.client;
|
|
370
583
|
return new AgentClient({
|
|
@@ -380,9 +593,33 @@ function useAgentChat(options) {
|
|
|
380
593
|
getHeaders: /* @__PURE__ */ __name(async () => mergeHeaders(latest.current), "getHeaders")
|
|
381
594
|
});
|
|
382
595
|
}, []);
|
|
596
|
+
useEffect(() => {
|
|
597
|
+
if (!options.resume || options.threadId === void 0) return;
|
|
598
|
+
let cancelled = false;
|
|
599
|
+
const threadId = options.threadId;
|
|
600
|
+
void client.getThread(threadId).then((thread) => {
|
|
601
|
+
if (cancelled) return;
|
|
602
|
+
setActiveRunId(thread.activeRunId ?? null);
|
|
603
|
+
if (thread.activeRunId != null) {
|
|
604
|
+
autoResumeRunIdRef.current = thread.activeRunId;
|
|
605
|
+
setAutoResume(true);
|
|
606
|
+
}
|
|
607
|
+
});
|
|
608
|
+
return () => {
|
|
609
|
+
cancelled = true;
|
|
610
|
+
};
|
|
611
|
+
}, [
|
|
612
|
+
client,
|
|
613
|
+
options.resume,
|
|
614
|
+
options.threadId
|
|
615
|
+
]);
|
|
383
616
|
const transport = useMemo(() => {
|
|
384
617
|
function onMeta(meta) {
|
|
385
618
|
setRunId(meta.runId);
|
|
619
|
+
if (latest.current.threadId === void 0 && meta.threadId && createdThreadId.current !== meta.threadId) {
|
|
620
|
+
createdThreadId.current = meta.threadId;
|
|
621
|
+
latest.current.onThreadCreated?.(meta.threadId);
|
|
622
|
+
}
|
|
386
623
|
}
|
|
387
624
|
__name(onMeta, "onMeta");
|
|
388
625
|
return new AgentChatTransport({
|
|
@@ -404,9 +641,10 @@ function useAgentChat(options) {
|
|
|
404
641
|
const pageContext = current.getPageContext?.() ?? null;
|
|
405
642
|
const regenerate2 = regenerateNext.current;
|
|
406
643
|
regenerateNext.current = false;
|
|
644
|
+
const threadId = current.threadId ?? createdThreadId.current;
|
|
407
645
|
return {
|
|
408
|
-
...
|
|
409
|
-
threadId
|
|
646
|
+
...threadId !== void 0 ? {
|
|
647
|
+
threadId
|
|
410
648
|
} : {},
|
|
411
649
|
...pageContext ? {
|
|
412
650
|
pageContext
|
|
@@ -416,13 +654,13 @@ function useAgentChat(options) {
|
|
|
416
654
|
} : {}
|
|
417
655
|
};
|
|
418
656
|
}, "getBody"),
|
|
419
|
-
getResumeRunId: /* @__PURE__ */ __name(() => latest.current.resumeRunId, "getResumeRunId"),
|
|
657
|
+
getResumeRunId: /* @__PURE__ */ __name(() => latest.current.resumeRunId ?? autoResumeRunIdRef.current, "getResumeRunId"),
|
|
420
658
|
onMeta
|
|
421
659
|
});
|
|
422
660
|
}, []);
|
|
423
661
|
const chat = useChat({
|
|
424
662
|
transport,
|
|
425
|
-
resume: options.resumeRunId !== void 0,
|
|
663
|
+
resume: options.resumeRunId !== void 0 || autoResume,
|
|
426
664
|
...options.threadId !== void 0 ? {
|
|
427
665
|
id: options.threadId
|
|
428
666
|
} : {},
|
|
@@ -489,10 +727,10 @@ function useAgentChat(options) {
|
|
|
489
727
|
]);
|
|
490
728
|
const cancel = useCallback(async () => {
|
|
491
729
|
chatRef.current.stop();
|
|
492
|
-
const
|
|
493
|
-
if (
|
|
730
|
+
const activeRunId2 = runIdRef.current;
|
|
731
|
+
if (activeRunId2) {
|
|
494
732
|
try {
|
|
495
|
-
await client.cancelStream(
|
|
733
|
+
await client.cancelStream(activeRunId2);
|
|
496
734
|
} catch {
|
|
497
735
|
}
|
|
498
736
|
}
|
|
@@ -524,6 +762,8 @@ function useAgentChat(options) {
|
|
|
524
762
|
...chat,
|
|
525
763
|
addToolResult,
|
|
526
764
|
runId,
|
|
765
|
+
/** The `resume`-fetched thread's active run id, or `null` once resolved with none. */
|
|
766
|
+
activeRunId,
|
|
527
767
|
client,
|
|
528
768
|
threads,
|
|
529
769
|
loadThreads,
|
|
@@ -552,10 +792,10 @@ async function mergeHeaders(options) {
|
|
|
552
792
|
__name(mergeHeaders, "mergeHeaders");
|
|
553
793
|
|
|
554
794
|
// src/components/chat-input.tsx
|
|
555
|
-
import React, { useEffect as
|
|
795
|
+
import React, { useEffect as useEffect3, useState as useState3 } from "react";
|
|
556
796
|
|
|
557
797
|
// src/components/use-speech-recognition.ts
|
|
558
|
-
import { useCallback as useCallback2, useEffect, useMemo as useMemo2, useRef as useRef2, useState as useState2 } from "react";
|
|
798
|
+
import { useCallback as useCallback2, useEffect as useEffect2, useMemo as useMemo2, useRef as useRef2, useState as useState2 } from "react";
|
|
559
799
|
function getRecognitionCtor() {
|
|
560
800
|
if (typeof window === "undefined") return null;
|
|
561
801
|
const scope = window;
|
|
@@ -571,7 +811,7 @@ function useSpeechRecognition({ lang = "en-US", onFinalTranscript, onInterimTran
|
|
|
571
811
|
const interimRef = useRef2(onInterimTranscript);
|
|
572
812
|
finalRef.current = onFinalTranscript;
|
|
573
813
|
interimRef.current = onInterimTranscript;
|
|
574
|
-
|
|
814
|
+
useEffect2(() => {
|
|
575
815
|
if (!ctor) return;
|
|
576
816
|
const recognition = new ctor();
|
|
577
817
|
recognition.lang = lang;
|
|
@@ -651,7 +891,7 @@ function ChatInput({ onSubmit, disabled, placeholder, rows = 2, enableSpeech = t
|
|
|
651
891
|
}, "onFinalTranscript"),
|
|
652
892
|
onInterimTranscript: /* @__PURE__ */ __name((text) => setInterim(text), "onInterimTranscript")
|
|
653
893
|
});
|
|
654
|
-
|
|
894
|
+
useEffect3(() => {
|
|
655
895
|
if (!speech.isListening) setInterim("");
|
|
656
896
|
}, [
|
|
657
897
|
speech.isListening
|
|
@@ -710,7 +950,7 @@ __name(ChatInput, "ChatInput");
|
|
|
710
950
|
|
|
711
951
|
// src/components/message-item.tsx
|
|
712
952
|
import { isTextUIPart, isToolUIPart } from "ai";
|
|
713
|
-
import React2, { useEffect as
|
|
953
|
+
import React2, { useEffect as useEffect4, useRef as useRef3, useState as useState4 } from "react";
|
|
714
954
|
function MessageItem({ message, renderToolPart, renderToolGroup, renderText, classNames, isStreaming = false, editable, onEditSubmit, regeneratable, onRegenerate, onFork, usage, createdAt }) {
|
|
715
955
|
const isUser = message.role === "user";
|
|
716
956
|
const parts = message.parts ?? [];
|
|
@@ -814,7 +1054,7 @@ function EditableUserBubble({ message, onSubmit, renderText, classNames, actions
|
|
|
814
1054
|
const [isEditing, setIsEditing] = useState4(false);
|
|
815
1055
|
const [draft, setDraft] = useState4(initialText);
|
|
816
1056
|
const textareaRef = useRef3(null);
|
|
817
|
-
|
|
1057
|
+
useEffect4(() => {
|
|
818
1058
|
if (isEditing && textareaRef.current) {
|
|
819
1059
|
const el = textareaRef.current;
|
|
820
1060
|
el.focus();
|
|
@@ -1132,6 +1372,7 @@ export {
|
|
|
1132
1372
|
formatRelativeTime,
|
|
1133
1373
|
loadStoredSpeechLang,
|
|
1134
1374
|
persistSpeechLang,
|
|
1375
|
+
storedMessageToUiMessage,
|
|
1135
1376
|
useAgentChat,
|
|
1136
1377
|
useSpeechRecognition
|
|
1137
1378
|
};
|