@ai-sdk/react 0.0.0-9477ebb9-20250403064906

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.mjs ADDED
@@ -0,0 +1,646 @@
1
+ // src/use-chat.ts
2
+ import {
3
+ callChatApi,
4
+ extractMaxToolInvocationStep,
5
+ fillMessageParts,
6
+ generateId as generateIdFunc,
7
+ getMessageParts,
8
+ isAssistantMessageWithCompletedToolCalls,
9
+ prepareAttachmentsForRequest,
10
+ shouldResubmitMessages,
11
+ updateToolCallResult
12
+ } from "@ai-sdk/ui-utils";
13
+ import { useCallback, useEffect as useEffect2, useMemo, useRef, useState as useState2 } from "react";
14
+ import useSWR from "swr";
15
+
16
+ // src/throttle.ts
17
+ import throttleFunction from "throttleit";
18
+ function throttle(fn, waitMs) {
19
+ return waitMs != null ? throttleFunction(fn, waitMs) : fn;
20
+ }
21
+
22
+ // src/util/use-stable-value.ts
23
+ import { isDeepEqualData } from "@ai-sdk/ui-utils";
24
+ import { useEffect, useState } from "react";
25
+ function useStableValue(latestValue) {
26
+ const [value, setValue] = useState(latestValue);
27
+ useEffect(() => {
28
+ if (!isDeepEqualData(latestValue, value)) {
29
+ setValue(latestValue);
30
+ }
31
+ }, [latestValue, value]);
32
+ return value;
33
+ }
34
+
35
+ // src/use-chat.ts
36
+ function useChat({
37
+ api = "/api/chat",
38
+ id,
39
+ initialMessages,
40
+ initialInput = "",
41
+ sendExtraMessageFields,
42
+ onToolCall,
43
+ experimental_prepareRequestBody,
44
+ maxSteps = 1,
45
+ streamProtocol = "data",
46
+ onResponse,
47
+ onFinish,
48
+ onError,
49
+ credentials,
50
+ headers,
51
+ body,
52
+ generateId = generateIdFunc,
53
+ fetch: fetch2,
54
+ keepLastMessageOnError = true,
55
+ experimental_throttle: throttleWaitMs
56
+ } = {}) {
57
+ const [hookId] = useState2(generateId);
58
+ const chatId = id != null ? id : hookId;
59
+ const chatKey = typeof api === "string" ? [api, chatId] : chatId;
60
+ const stableInitialMessages = useStableValue(initialMessages != null ? initialMessages : []);
61
+ const processedInitialMessages = useMemo(
62
+ () => fillMessageParts(stableInitialMessages),
63
+ [stableInitialMessages]
64
+ );
65
+ const { data: messages, mutate } = useSWR(
66
+ [chatKey, "messages"],
67
+ null,
68
+ { fallbackData: processedInitialMessages }
69
+ );
70
+ const messagesRef = useRef(messages || []);
71
+ useEffect2(() => {
72
+ messagesRef.current = messages || [];
73
+ }, [messages]);
74
+ const { data: streamData, mutate: mutateStreamData } = useSWR([chatKey, "streamData"], null);
75
+ const streamDataRef = useRef(streamData);
76
+ useEffect2(() => {
77
+ streamDataRef.current = streamData;
78
+ }, [streamData]);
79
+ const { data: status = "ready", mutate: mutateStatus } = useSWR([chatKey, "status"], null);
80
+ const { data: error = void 0, mutate: setError } = useSWR([chatKey, "error"], null);
81
+ const abortControllerRef = useRef(null);
82
+ const extraMetadataRef = useRef({
83
+ credentials,
84
+ headers,
85
+ body
86
+ });
87
+ useEffect2(() => {
88
+ extraMetadataRef.current = {
89
+ credentials,
90
+ headers,
91
+ body
92
+ };
93
+ }, [credentials, headers, body]);
94
+ const triggerRequest = useCallback(
95
+ async (chatRequest) => {
96
+ var _a, _b;
97
+ mutateStatus("submitted");
98
+ setError(void 0);
99
+ const chatMessages = fillMessageParts(chatRequest.messages);
100
+ const messageCount = chatMessages.length;
101
+ const maxStep = extractMaxToolInvocationStep(
102
+ (_a = chatMessages[chatMessages.length - 1]) == null ? void 0 : _a.toolInvocations
103
+ );
104
+ try {
105
+ const abortController = new AbortController();
106
+ abortControllerRef.current = abortController;
107
+ const throttledMutate = throttle(mutate, throttleWaitMs);
108
+ const throttledMutateStreamData = throttle(
109
+ mutateStreamData,
110
+ throttleWaitMs
111
+ );
112
+ const previousMessages = messagesRef.current;
113
+ throttledMutate(chatMessages, false);
114
+ const constructedMessagesPayload = sendExtraMessageFields ? chatMessages : chatMessages.map(
115
+ ({
116
+ role,
117
+ content,
118
+ experimental_attachments,
119
+ data,
120
+ annotations,
121
+ toolInvocations,
122
+ parts
123
+ }) => ({
124
+ role,
125
+ content,
126
+ ...experimental_attachments !== void 0 && {
127
+ experimental_attachments
128
+ },
129
+ ...data !== void 0 && { data },
130
+ ...annotations !== void 0 && { annotations },
131
+ ...toolInvocations !== void 0 && { toolInvocations },
132
+ ...parts !== void 0 && { parts }
133
+ })
134
+ );
135
+ const existingData = streamDataRef.current;
136
+ await callChatApi({
137
+ api,
138
+ body: (_b = experimental_prepareRequestBody == null ? void 0 : experimental_prepareRequestBody({
139
+ id: chatId,
140
+ messages: chatMessages,
141
+ requestData: chatRequest.data,
142
+ requestBody: chatRequest.body
143
+ })) != null ? _b : {
144
+ id: chatId,
145
+ messages: constructedMessagesPayload,
146
+ data: chatRequest.data,
147
+ ...extraMetadataRef.current.body,
148
+ ...chatRequest.body
149
+ },
150
+ streamProtocol,
151
+ credentials: extraMetadataRef.current.credentials,
152
+ headers: {
153
+ ...extraMetadataRef.current.headers,
154
+ ...chatRequest.headers
155
+ },
156
+ abortController: () => abortControllerRef.current,
157
+ restoreMessagesOnFailure() {
158
+ if (!keepLastMessageOnError) {
159
+ throttledMutate(previousMessages, false);
160
+ }
161
+ },
162
+ onResponse,
163
+ onUpdate({ message, data, replaceLastMessage }) {
164
+ mutateStatus("streaming");
165
+ throttledMutate(
166
+ [
167
+ ...replaceLastMessage ? chatMessages.slice(0, chatMessages.length - 1) : chatMessages,
168
+ message
169
+ ],
170
+ false
171
+ );
172
+ if (data == null ? void 0 : data.length) {
173
+ throttledMutateStreamData(
174
+ [...existingData != null ? existingData : [], ...data],
175
+ false
176
+ );
177
+ }
178
+ },
179
+ onToolCall,
180
+ onFinish,
181
+ generateId,
182
+ fetch: fetch2,
183
+ lastMessage: chatMessages[chatMessages.length - 1]
184
+ });
185
+ abortControllerRef.current = null;
186
+ mutateStatus("ready");
187
+ } catch (err) {
188
+ if (err.name === "AbortError") {
189
+ abortControllerRef.current = null;
190
+ mutateStatus("ready");
191
+ return null;
192
+ }
193
+ if (onError && err instanceof Error) {
194
+ onError(err);
195
+ }
196
+ setError(err);
197
+ mutateStatus("error");
198
+ }
199
+ const messages2 = messagesRef.current;
200
+ if (shouldResubmitMessages({
201
+ originalMaxToolInvocationStep: maxStep,
202
+ originalMessageCount: messageCount,
203
+ maxSteps,
204
+ messages: messages2
205
+ })) {
206
+ await triggerRequest({ messages: messages2 });
207
+ }
208
+ },
209
+ [
210
+ mutate,
211
+ mutateStatus,
212
+ api,
213
+ extraMetadataRef,
214
+ onResponse,
215
+ onFinish,
216
+ onError,
217
+ setError,
218
+ mutateStreamData,
219
+ streamDataRef,
220
+ streamProtocol,
221
+ sendExtraMessageFields,
222
+ experimental_prepareRequestBody,
223
+ onToolCall,
224
+ maxSteps,
225
+ messagesRef,
226
+ abortControllerRef,
227
+ generateId,
228
+ fetch2,
229
+ keepLastMessageOnError,
230
+ throttleWaitMs,
231
+ chatId
232
+ ]
233
+ );
234
+ const append = useCallback(
235
+ async (message, {
236
+ data,
237
+ headers: headers2,
238
+ body: body2,
239
+ experimental_attachments
240
+ } = {}) => {
241
+ var _a, _b;
242
+ const attachmentsForRequest = await prepareAttachmentsForRequest(
243
+ experimental_attachments
244
+ );
245
+ const messages2 = messagesRef.current.concat({
246
+ ...message,
247
+ id: (_a = message.id) != null ? _a : generateId(),
248
+ createdAt: (_b = message.createdAt) != null ? _b : /* @__PURE__ */ new Date(),
249
+ experimental_attachments: attachmentsForRequest.length > 0 ? attachmentsForRequest : void 0,
250
+ parts: getMessageParts(message)
251
+ });
252
+ return triggerRequest({ messages: messages2, headers: headers2, body: body2, data });
253
+ },
254
+ [triggerRequest, generateId]
255
+ );
256
+ const reload = useCallback(
257
+ async ({ data, headers: headers2, body: body2 } = {}) => {
258
+ const messages2 = messagesRef.current;
259
+ if (messages2.length === 0) {
260
+ return null;
261
+ }
262
+ const lastMessage = messages2[messages2.length - 1];
263
+ return triggerRequest({
264
+ messages: lastMessage.role === "assistant" ? messages2.slice(0, -1) : messages2,
265
+ headers: headers2,
266
+ body: body2,
267
+ data
268
+ });
269
+ },
270
+ [triggerRequest]
271
+ );
272
+ const stop = useCallback(() => {
273
+ if (abortControllerRef.current) {
274
+ abortControllerRef.current.abort();
275
+ abortControllerRef.current = null;
276
+ }
277
+ }, []);
278
+ const setMessages = useCallback(
279
+ (messages2) => {
280
+ if (typeof messages2 === "function") {
281
+ messages2 = messages2(messagesRef.current);
282
+ }
283
+ const messagesWithParts = fillMessageParts(messages2);
284
+ mutate(messagesWithParts, false);
285
+ messagesRef.current = messagesWithParts;
286
+ },
287
+ [mutate]
288
+ );
289
+ const setData = useCallback(
290
+ (data) => {
291
+ if (typeof data === "function") {
292
+ data = data(streamDataRef.current);
293
+ }
294
+ mutateStreamData(data, false);
295
+ streamDataRef.current = data;
296
+ },
297
+ [mutateStreamData]
298
+ );
299
+ const [input, setInput] = useState2(initialInput);
300
+ const handleSubmit = useCallback(
301
+ async (event, options = {}, metadata) => {
302
+ var _a;
303
+ (_a = event == null ? void 0 : event.preventDefault) == null ? void 0 : _a.call(event);
304
+ if (!input && !options.allowEmptySubmit)
305
+ return;
306
+ if (metadata) {
307
+ extraMetadataRef.current = {
308
+ ...extraMetadataRef.current,
309
+ ...metadata
310
+ };
311
+ }
312
+ const attachmentsForRequest = await prepareAttachmentsForRequest(
313
+ options.experimental_attachments
314
+ );
315
+ const messages2 = messagesRef.current.concat({
316
+ id: generateId(),
317
+ createdAt: /* @__PURE__ */ new Date(),
318
+ role: "user",
319
+ content: input,
320
+ experimental_attachments: attachmentsForRequest.length > 0 ? attachmentsForRequest : void 0,
321
+ parts: [{ type: "text", text: input }]
322
+ });
323
+ const chatRequest = {
324
+ messages: messages2,
325
+ headers: options.headers,
326
+ body: options.body,
327
+ data: options.data
328
+ };
329
+ triggerRequest(chatRequest);
330
+ setInput("");
331
+ },
332
+ [input, generateId, triggerRequest]
333
+ );
334
+ const handleInputChange = (e) => {
335
+ setInput(e.target.value);
336
+ };
337
+ const addToolResult = useCallback(
338
+ ({ toolCallId, result }) => {
339
+ const currentMessages = messagesRef.current;
340
+ updateToolCallResult({
341
+ messages: currentMessages,
342
+ toolCallId,
343
+ toolResult: result
344
+ });
345
+ mutate(
346
+ [
347
+ ...currentMessages.slice(0, currentMessages.length - 1),
348
+ { ...currentMessages[currentMessages.length - 1] }
349
+ ],
350
+ false
351
+ );
352
+ if (status === "submitted" || status === "streaming") {
353
+ return;
354
+ }
355
+ const lastMessage = currentMessages[currentMessages.length - 1];
356
+ if (isAssistantMessageWithCompletedToolCalls(lastMessage)) {
357
+ triggerRequest({ messages: currentMessages });
358
+ }
359
+ },
360
+ [mutate, status, triggerRequest]
361
+ );
362
+ return {
363
+ messages: messages != null ? messages : [],
364
+ id: chatId,
365
+ setMessages,
366
+ data: streamData,
367
+ setData,
368
+ error,
369
+ append,
370
+ reload,
371
+ stop,
372
+ input,
373
+ setInput,
374
+ handleInputChange,
375
+ handleSubmit,
376
+ isLoading: status === "submitted" || status === "streaming",
377
+ status,
378
+ addToolResult
379
+ };
380
+ }
381
+
382
+ // src/use-completion.ts
383
+ import {
384
+ callCompletionApi
385
+ } from "@ai-sdk/ui-utils";
386
+ import { useCallback as useCallback2, useEffect as useEffect3, useId, useRef as useRef2, useState as useState3 } from "react";
387
+ import useSWR2 from "swr";
388
+ function useCompletion({
389
+ api = "/api/completion",
390
+ id,
391
+ initialCompletion = "",
392
+ initialInput = "",
393
+ credentials,
394
+ headers,
395
+ body,
396
+ streamProtocol = "data",
397
+ fetch: fetch2,
398
+ onResponse,
399
+ onFinish,
400
+ onError,
401
+ experimental_throttle: throttleWaitMs
402
+ } = {}) {
403
+ const hookId = useId();
404
+ const completionId = id || hookId;
405
+ const { data, mutate } = useSWR2([api, completionId], null, {
406
+ fallbackData: initialCompletion
407
+ });
408
+ const { data: isLoading = false, mutate: mutateLoading } = useSWR2(
409
+ [completionId, "loading"],
410
+ null
411
+ );
412
+ const { data: streamData, mutate: mutateStreamData } = useSWR2([completionId, "streamData"], null);
413
+ const [error, setError] = useState3(void 0);
414
+ const completion = data;
415
+ const [abortController, setAbortController] = useState3(null);
416
+ const extraMetadataRef = useRef2({
417
+ credentials,
418
+ headers,
419
+ body
420
+ });
421
+ useEffect3(() => {
422
+ extraMetadataRef.current = {
423
+ credentials,
424
+ headers,
425
+ body
426
+ };
427
+ }, [credentials, headers, body]);
428
+ const triggerRequest = useCallback2(
429
+ async (prompt, options) => callCompletionApi({
430
+ api,
431
+ prompt,
432
+ credentials: extraMetadataRef.current.credentials,
433
+ headers: { ...extraMetadataRef.current.headers, ...options == null ? void 0 : options.headers },
434
+ body: {
435
+ ...extraMetadataRef.current.body,
436
+ ...options == null ? void 0 : options.body
437
+ },
438
+ streamProtocol,
439
+ fetch: fetch2,
440
+ // throttle streamed ui updates:
441
+ setCompletion: throttle(
442
+ (completion2) => mutate(completion2, false),
443
+ throttleWaitMs
444
+ ),
445
+ onData: throttle(
446
+ (data2) => mutateStreamData([...streamData != null ? streamData : [], ...data2 != null ? data2 : []], false),
447
+ throttleWaitMs
448
+ ),
449
+ setLoading: mutateLoading,
450
+ setError,
451
+ setAbortController,
452
+ onResponse,
453
+ onFinish,
454
+ onError
455
+ }),
456
+ [
457
+ mutate,
458
+ mutateLoading,
459
+ api,
460
+ extraMetadataRef,
461
+ setAbortController,
462
+ onResponse,
463
+ onFinish,
464
+ onError,
465
+ setError,
466
+ streamData,
467
+ streamProtocol,
468
+ fetch2,
469
+ mutateStreamData,
470
+ throttleWaitMs
471
+ ]
472
+ );
473
+ const stop = useCallback2(() => {
474
+ if (abortController) {
475
+ abortController.abort();
476
+ setAbortController(null);
477
+ }
478
+ }, [abortController]);
479
+ const setCompletion = useCallback2(
480
+ (completion2) => {
481
+ mutate(completion2, false);
482
+ },
483
+ [mutate]
484
+ );
485
+ const complete = useCallback2(
486
+ async (prompt, options) => {
487
+ return triggerRequest(prompt, options);
488
+ },
489
+ [triggerRequest]
490
+ );
491
+ const [input, setInput] = useState3(initialInput);
492
+ const handleSubmit = useCallback2(
493
+ (event) => {
494
+ var _a;
495
+ (_a = event == null ? void 0 : event.preventDefault) == null ? void 0 : _a.call(event);
496
+ return input ? complete(input) : void 0;
497
+ },
498
+ [input, complete]
499
+ );
500
+ const handleInputChange = useCallback2(
501
+ (e) => {
502
+ setInput(e.target.value);
503
+ },
504
+ [setInput]
505
+ );
506
+ return {
507
+ completion,
508
+ complete,
509
+ error,
510
+ setCompletion,
511
+ stop,
512
+ input,
513
+ setInput,
514
+ handleInputChange,
515
+ handleSubmit,
516
+ isLoading,
517
+ data: streamData
518
+ };
519
+ }
520
+
521
+ // src/use-object.ts
522
+ import {
523
+ isAbortError,
524
+ safeValidateTypes
525
+ } from "@ai-sdk/provider-utils";
526
+ import {
527
+ asSchema,
528
+ isDeepEqualData as isDeepEqualData2,
529
+ parsePartialJson
530
+ } from "@ai-sdk/ui-utils";
531
+ import { useCallback as useCallback3, useId as useId2, useRef as useRef3, useState as useState4 } from "react";
532
+ import useSWR3 from "swr";
533
+ var getOriginalFetch = () => fetch;
534
+ function useObject({
535
+ api,
536
+ id,
537
+ schema,
538
+ // required, in the future we will use it for validation
539
+ initialValue,
540
+ fetch: fetch2,
541
+ onError,
542
+ onFinish,
543
+ headers,
544
+ credentials
545
+ }) {
546
+ const hookId = useId2();
547
+ const completionId = id != null ? id : hookId;
548
+ const { data, mutate } = useSWR3(
549
+ [api, completionId],
550
+ null,
551
+ { fallbackData: initialValue }
552
+ );
553
+ const [error, setError] = useState4(void 0);
554
+ const [isLoading, setIsLoading] = useState4(false);
555
+ const abortControllerRef = useRef3(null);
556
+ const stop = useCallback3(() => {
557
+ var _a;
558
+ try {
559
+ (_a = abortControllerRef.current) == null ? void 0 : _a.abort();
560
+ } catch (ignored) {
561
+ } finally {
562
+ setIsLoading(false);
563
+ abortControllerRef.current = null;
564
+ }
565
+ }, []);
566
+ const submit = async (input) => {
567
+ var _a;
568
+ try {
569
+ mutate(void 0);
570
+ setIsLoading(true);
571
+ setError(void 0);
572
+ const abortController = new AbortController();
573
+ abortControllerRef.current = abortController;
574
+ const actualFetch = fetch2 != null ? fetch2 : getOriginalFetch();
575
+ const response = await actualFetch(api, {
576
+ method: "POST",
577
+ headers: {
578
+ "Content-Type": "application/json",
579
+ ...headers
580
+ },
581
+ credentials,
582
+ signal: abortController.signal,
583
+ body: JSON.stringify(input)
584
+ });
585
+ if (!response.ok) {
586
+ throw new Error(
587
+ (_a = await response.text()) != null ? _a : "Failed to fetch the response."
588
+ );
589
+ }
590
+ if (response.body == null) {
591
+ throw new Error("The response body is empty.");
592
+ }
593
+ let accumulatedText = "";
594
+ let latestObject = void 0;
595
+ await response.body.pipeThrough(new TextDecoderStream()).pipeTo(
596
+ new WritableStream({
597
+ write(chunk) {
598
+ accumulatedText += chunk;
599
+ const { value } = parsePartialJson(accumulatedText);
600
+ const currentObject = value;
601
+ if (!isDeepEqualData2(latestObject, currentObject)) {
602
+ latestObject = currentObject;
603
+ mutate(currentObject);
604
+ }
605
+ },
606
+ close() {
607
+ setIsLoading(false);
608
+ abortControllerRef.current = null;
609
+ if (onFinish != null) {
610
+ const validationResult = safeValidateTypes({
611
+ value: latestObject,
612
+ schema: asSchema(schema)
613
+ });
614
+ onFinish(
615
+ validationResult.success ? { object: validationResult.value, error: void 0 } : { object: void 0, error: validationResult.error }
616
+ );
617
+ }
618
+ }
619
+ })
620
+ );
621
+ } catch (error2) {
622
+ if (isAbortError(error2)) {
623
+ return;
624
+ }
625
+ if (onError && error2 instanceof Error) {
626
+ onError(error2);
627
+ }
628
+ setIsLoading(false);
629
+ setError(error2 instanceof Error ? error2 : new Error(String(error2)));
630
+ }
631
+ };
632
+ return {
633
+ submit,
634
+ object: data,
635
+ error,
636
+ isLoading,
637
+ stop
638
+ };
639
+ }
640
+ var experimental_useObject = useObject;
641
+ export {
642
+ experimental_useObject,
643
+ useChat,
644
+ useCompletion
645
+ };
646
+ //# sourceMappingURL=index.mjs.map