@assistant-ui/react-ai-sdk 0.5.15 → 0.6.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.mjs CHANGED
@@ -40,80 +40,56 @@ var getVercelRSCMessage = (message) => {
40
40
  return getExternalStoreMessage(message);
41
41
  };
42
42
 
43
- // src/ui/use-chat/useVercelUseChatRuntime.tsx
44
- import { useEffect as useEffect3, useInsertionEffect, useState } from "react";
45
-
46
- // src/ui/use-chat/VercelUseChatRuntime.tsx
47
- import { INTERNAL as INTERNAL2 } from "@assistant-ui/react";
48
-
49
- // src/ui/use-chat/VercelUseChatThreadRuntime.tsx
50
- import {
51
- INTERNAL
52
- } from "@assistant-ui/react";
53
- import { create } from "zustand";
54
-
55
- // src/ui/getVercelAIMessage.tsx
56
- var symbolInnerAIMessage = Symbol("innerVercelAIUIMessage");
57
- var getVercelAIMessage = (message) => {
58
- return message[symbolInnerAIMessage];
59
- };
60
-
61
- // src/ui/utils/sliceMessagesUntil.tsx
62
- var sliceMessagesUntil = (messages, messageId) => {
63
- if (messageId == null) return [];
64
- let messageIdx = messages.findIndex((m) => m.id === messageId);
65
- if (messageIdx === -1)
66
- throw new Error(
67
- "useVercelAIThreadState: Message not found. This is liekly an internal bug in assistant-ui."
68
- );
69
- while (messages[messageIdx + 1]?.role === "assistant") {
70
- messageIdx++;
43
+ // src/ui/utils/useCachedChunkedMessages.ts
44
+ import { useMemo } from "react";
45
+ var hasItems = (messages) => messages.length > 0;
46
+ var chunkedMessages = (messages) => {
47
+ const chunks = [];
48
+ let currentChunk = [];
49
+ for (const message of messages) {
50
+ if (message.role === "assistant" || message.role === "data") {
51
+ currentChunk.push(message);
52
+ } else {
53
+ if (hasItems(currentChunk)) {
54
+ chunks.push(currentChunk);
55
+ currentChunk = [];
56
+ }
57
+ chunks.push([message]);
58
+ }
71
59
  }
72
- return messages.slice(0, messageIdx + 1);
60
+ if (hasItems(currentChunk)) {
61
+ chunks.push(currentChunk);
62
+ }
63
+ return chunks;
73
64
  };
74
-
75
- // src/ui/utils/useVercelAIComposerSync.tsx
76
- import { useThreadContext } from "@assistant-ui/react";
77
- import { useEffect } from "react";
78
- var useVercelAIComposerSync = (vercel) => {
79
- const { useComposer } = useThreadContext();
80
- useEffect(() => {
81
- useComposer.setState({
82
- value: vercel.input,
83
- setValue: vercel.setInput
84
- });
85
- }, [useComposer, vercel.input, vercel.setInput]);
65
+ var shallowArrayEqual = (a, b) => {
66
+ if (a.length !== b.length) return false;
67
+ for (let i = 0; i < a.length; i++) {
68
+ if (a[i] !== b[i]) return false;
69
+ }
70
+ return true;
86
71
  };
87
-
88
- // src/ui/utils/useVercelAIThreadSync.tsx
89
- import { useEffect as useEffect2, useMemo } from "react";
90
-
91
- // src/utils/ThreadMessageConverter.ts
92
- var ThreadMessageConverter = class {
93
- cache = /* @__PURE__ */ new WeakMap();
94
- convertMessages(messages, converter, keyMapper = (key) => key) {
95
- return messages.map((m) => {
96
- const key = keyMapper(m);
97
- const cached = this.cache.get(key);
98
- const newMessage = converter(m, cached);
99
- this.cache.set(key, newMessage);
100
- return newMessage;
72
+ var useCachedChunkedMessages = (messages) => {
73
+ const cache = useMemo(() => /* @__PURE__ */ new WeakMap(), []);
74
+ return useMemo(() => {
75
+ return chunkedMessages(messages).map((m) => {
76
+ const key = m[0];
77
+ if (!key) return m;
78
+ const cached = cache.get(key);
79
+ if (cached && shallowArrayEqual(cached, m)) return cached;
80
+ cache.set(key, m);
81
+ return m;
101
82
  });
102
- }
83
+ }, [messages, cache]);
103
84
  };
104
85
 
105
- // src/ui/utils/useVercelAIThreadSync.tsx
106
- var getIsRunning = (vercel) => {
107
- if ("isLoading" in vercel) return vercel.isLoading;
108
- return vercel.status === "in_progress";
109
- };
110
- var vercelToThreadMessage2 = (messages, status) => {
86
+ // src/ui/utils/convertMessage.ts
87
+ var convertMessage = (messages) => {
111
88
  const firstMessage = messages[0];
112
89
  if (!firstMessage) throw new Error("No messages found");
113
90
  const common = {
114
91
  id: firstMessage.id,
115
- createdAt: firstMessage.createdAt ?? /* @__PURE__ */ new Date(),
116
- [symbolInnerAIMessage]: messages
92
+ createdAt: firstMessage.createdAt ?? /* @__PURE__ */ new Date()
117
93
  };
118
94
  switch (firstMessage.role) {
119
95
  case "user":
@@ -153,8 +129,7 @@ var vercelToThreadMessage2 = (messages, status) => {
153
129
  ) ?? [],
154
130
  ...typeof message.data === "object" && !Array.isArray(message.data) && message.data?.["type"] === "tool-call" ? [message.data] : []
155
131
  ];
156
- }),
157
- status
132
+ })
158
133
  };
159
134
  for (const message of messages) {
160
135
  if (typeof message.data === "object" && !Array.isArray(message.data) && message.data?.["type"] === "tool-result") {
@@ -175,369 +150,137 @@ var vercelToThreadMessage2 = (messages, status) => {
175
150
  );
176
151
  }
177
152
  };
178
- var hasItems = (messages) => messages.length > 0;
179
- var chunkedMessages = (messages) => {
180
- const chunks = [];
181
- let currentChunk = [];
182
- for (const message of messages) {
183
- if (message.role === "assistant" || message.role === "data") {
184
- currentChunk.push(message);
185
- } else {
186
- if (hasItems(currentChunk)) {
187
- chunks.push(currentChunk);
188
- currentChunk = [];
189
- }
190
- chunks.push([message]);
191
- }
192
- }
193
- if (hasItems(currentChunk)) {
194
- chunks.push(currentChunk);
195
- }
196
- return chunks;
197
- };
198
- var shallowArrayEqual = (a, b) => {
199
- if (a.length !== b.length) return false;
200
- for (let i = 0; i < a.length; i++) {
201
- if (a[i] !== b[i]) return false;
202
- }
203
- return true;
204
- };
205
- var useVercelAIThreadSync = (vercel, updateData) => {
206
- const isRunning = getIsRunning(vercel);
207
- const converter = useMemo(() => new ThreadMessageConverter(), []);
208
- useEffect2(() => {
209
- const lastMessageId = vercel.messages.at(-1)?.id;
210
- const convertCallback = (messages2, cache) => {
211
- const status = lastMessageId === messages2[0].id && isRunning ? {
212
- type: "running"
213
- } : {
214
- type: "complete",
215
- reason: "unknown"
216
- };
217
- if (cache && shallowArrayEqual(cache.content, messages2) && (cache.role !== "assistant" || cache.status.type === status.type))
218
- return cache;
219
- return vercelToThreadMessage2(messages2, status);
220
- };
221
- const messages = converter.convertMessages(
222
- chunkedMessages(vercel.messages),
223
- convertCallback,
224
- (m) => m[0]
225
- );
226
- updateData(isRunning, messages);
227
- }, [updateData, isRunning, vercel.messages, converter]);
228
- };
229
-
230
- // src/ui/use-chat/VercelUseChatThreadRuntime.tsx
231
- var { MessageRepository } = INTERNAL;
232
- var hasUpcomingMessage = (isRunning, messages) => {
233
- return isRunning && messages[messages.length - 1]?.role !== "assistant";
234
- };
235
- var CAPABILITIES = Object.freeze({
236
- switchToBranch: true,
237
- edit: true,
238
- reload: true,
239
- cancel: true,
240
- copy: true
241
- });
242
- var VercelUseChatThreadRuntime = class {
243
- constructor(vercel) {
244
- this.vercel = vercel;
245
- this.useVercel = create(() => ({
246
- vercel
247
- }));
248
- }
249
- _subscriptions = /* @__PURE__ */ new Set();
250
- repository = new MessageRepository();
251
- assistantOptimisticId = null;
252
- useVercel;
253
- capabilities = CAPABILITIES;
254
- messages = [];
255
- isDisabled = false;
256
- getBranches(messageId) {
257
- return this.repository.getBranches(messageId);
258
- }
259
- switchToBranch(branchId) {
260
- this.repository.switchToBranch(branchId);
261
- this.updateVercelMessages(this.repository.getMessages());
262
- }
263
- async append(message) {
264
- if (message.content.length !== 1 || message.content[0]?.type !== "text")
265
- throw new Error(
266
- "Only text content is supported by VercelUseChatRuntime. Use the Edge runtime for image support."
267
- );
268
- const newMessages = sliceMessagesUntil(
269
- this.vercel.messages,
270
- message.parentId
271
- );
272
- this.vercel.setMessages(newMessages);
273
- await this.vercel.append({
274
- role: message.role,
275
- content: message.content[0].text
276
- });
277
- }
278
- async startRun(parentId) {
279
- const newMessages = sliceMessagesUntil(this.vercel.messages, parentId);
280
- this.vercel.setMessages(newMessages);
281
- await this.vercel.reload();
282
- }
283
- cancelRun() {
284
- const previousMessage = this.vercel.messages.at(-1);
285
- this.vercel.stop();
286
- if (this.assistantOptimisticId) {
287
- this.repository.deleteMessage(this.assistantOptimisticId);
288
- this.assistantOptimisticId = null;
289
- }
290
- let messages = this.repository.getMessages();
291
- if (previousMessage?.role === "user" && previousMessage.id === messages.at(-1)?.id) {
292
- this.vercel.setInput(previousMessage.content);
293
- this.repository.deleteMessage(previousMessage.id);
294
- messages = this.repository.getMessages();
295
- }
296
- setTimeout(() => {
297
- this.updateVercelMessages(messages);
298
- }, 0);
299
- }
300
- subscribe(callback) {
301
- this._subscriptions.add(callback);
302
- return () => this._subscriptions.delete(callback);
303
- }
304
- updateVercelMessages = (messages) => {
305
- this.vercel.setMessages(
306
- messages.flatMap(getVercelAIMessage).filter((m) => m != null)
307
- );
308
- };
309
- onVercelUpdated() {
310
- if (this.useVercel.getState().vercel !== this.vercel) {
311
- this.useVercel.setState({ vercel: this.vercel });
312
- }
313
- }
314
- updateData = (isRunning, vm) => {
315
- for (let i = 0; i < vm.length; i++) {
316
- const message = vm[i];
317
- const parent = vm[i - 1];
318
- this.repository.addOrUpdateMessage(parent?.id ?? null, message);
319
- }
320
- if (this.assistantOptimisticId) {
321
- this.repository.deleteMessage(this.assistantOptimisticId);
322
- this.assistantOptimisticId = null;
323
- }
324
- if (hasUpcomingMessage(isRunning, vm)) {
325
- this.assistantOptimisticId = this.repository.appendOptimisticMessage(
326
- vm.at(-1)?.id ?? null,
327
- {
328
- role: "assistant",
329
- content: []
330
- }
331
- );
332
- }
333
- this.repository.resetHead(
334
- this.assistantOptimisticId ?? vm.at(-1)?.id ?? null
335
- );
336
- this.messages = this.repository.getMessages();
337
- for (const callback of this._subscriptions) callback();
338
- };
339
- unstable_synchronizer = () => {
340
- const { vercel } = this.useVercel();
341
- useVercelAIThreadSync(vercel, this.updateData);
342
- useVercelAIComposerSync(vercel);
343
- return null;
344
- };
345
- addToolResult({ toolCallId, result }) {
346
- this.vercel.addToolResult({ toolCallId, result });
347
- }
348
- };
349
-
350
- // src/ui/use-chat/VercelUseChatRuntime.tsx
351
- var { ProxyConfigProvider, BaseAssistantRuntime } = INTERNAL2;
352
- var VercelUseChatRuntime = class extends BaseAssistantRuntime {
353
- _proxyConfigProvider = new ProxyConfigProvider();
354
- constructor(vercel) {
355
- super(new VercelUseChatThreadRuntime(vercel));
356
- }
357
- set vercel(vercel) {
358
- this.thread.vercel = vercel;
359
- }
360
- onVercelUpdated() {
361
- return this.thread.onVercelUpdated();
362
- }
363
- getModelConfig() {
364
- return this._proxyConfigProvider.getModelConfig();
365
- }
366
- registerModelConfigProvider(provider) {
367
- return this._proxyConfigProvider.registerModelConfigProvider(provider);
368
- }
369
- switchToThread(threadId) {
370
- if (threadId) {
371
- throw new Error(
372
- "VercelAIRuntime does not yet support switching threads."
373
- );
374
- }
375
- this.thread.vercel.messages = [];
376
- this.thread.vercel.input = "";
377
- this.thread.vercel.setMessages([]);
378
- this.thread.vercel.setInput("");
379
- this.thread = new VercelUseChatThreadRuntime(this.thread.vercel);
380
- }
381
- };
382
153
 
383
154
  // src/ui/use-chat/useVercelUseChatRuntime.tsx
384
- var useVercelUseChatRuntime = (chatHelpers) => {
385
- const [runtime] = useState(() => new VercelUseChatRuntime(chatHelpers));
386
- useInsertionEffect(() => {
387
- runtime.vercel = chatHelpers;
388
- });
389
- useEffect3(() => {
390
- runtime.onVercelUpdated();
391
- });
392
- return runtime;
393
- };
394
-
395
- // src/ui/use-assistant/useVercelUseAssistantRuntime.tsx
396
- import { useEffect as useEffect4, useInsertionEffect as useInsertionEffect2, useState as useState2 } from "react";
155
+ import { useExternalStoreRuntime as useExternalStoreRuntime2 } from "@assistant-ui/react";
397
156
 
398
- // src/ui/use-assistant/VercelUseAssistantRuntime.tsx
157
+ // src/ui/utils/useInputSync.tsx
158
+ import { useRef, useEffect } from "react";
399
159
  import {
400
- INTERNAL as INTERNAL3
160
+ subscribeToMainThread
401
161
  } from "@assistant-ui/react";
402
-
403
- // src/ui/use-assistant/VercelUseAssistantThreadRuntime.tsx
404
- import { create as create2 } from "zustand";
405
- var EMPTY_BRANCHES = Object.freeze([]);
406
- var CAPABILITIES2 = Object.freeze({
407
- switchToBranch: false,
408
- edit: false,
409
- reload: false,
410
- cancel: false,
411
- copy: true
412
- });
413
- var VercelUseAssistantThreadRuntime = class {
414
- constructor(vercel) {
415
- this.vercel = vercel;
416
- this.useVercel = create2(() => ({
417
- vercel
418
- }));
419
- }
420
- _subscriptions = /* @__PURE__ */ new Set();
421
- capabilities = CAPABILITIES2;
422
- useVercel;
423
- messages = [];
424
- isDisabled = false;
425
- getBranches() {
426
- return EMPTY_BRANCHES;
427
- }
428
- switchToBranch() {
429
- throw new Error(
430
- "VercelUseAssistantRuntime does not support switching branches."
431
- );
432
- }
433
- async append(message) {
434
- if (message.role !== "user")
435
- throw new Error(
436
- "Only appending user messages are supported in VercelUseAssistantRuntime. This is likely an internal bug in assistant-ui."
437
- );
438
- if (message.content.length !== 1 || message.content[0]?.type !== "text")
439
- throw new Error("VercelUseAssistantRuntime only supports text content.");
440
- if (message.parentId !== (this.messages.at(-1)?.id ?? null))
441
- throw new Error(
442
- "VercelUseAssistantRuntime does not support editing messages."
443
- );
444
- await this.vercel.append({
445
- role: "user",
446
- content: message.content[0].text
447
- });
448
- }
449
- async startRun() {
450
- throw new Error("VercelUseAssistantRuntime does not support reloading.");
451
- }
452
- cancelRun() {
453
- const previousMessage = this.vercel.messages.at(-1);
454
- this.vercel.stop();
455
- if (previousMessage?.role === "user") {
456
- this.vercel.setInput(previousMessage.content);
457
- }
458
- }
459
- subscribe(callback) {
460
- this._subscriptions.add(callback);
461
- return () => this._subscriptions.delete(callback);
462
- }
463
- onVercelUpdated() {
464
- if (this.useVercel.getState().vercel !== this.vercel) {
465
- this.useVercel.setState({ vercel: this.vercel });
466
- }
467
- }
468
- updateData = (isRunning, vm) => {
469
- if (hasUpcomingMessage2(isRunning, vm)) {
470
- vm.push({
471
- id: "__optimistic__result",
472
- createdAt: /* @__PURE__ */ new Date(),
473
- status: { type: "running" },
474
- role: "assistant",
475
- content: []
476
- });
162
+ var useInputSync = (helpers, runtime) => {
163
+ const helpersRef = useRef(helpers);
164
+ useEffect(() => {
165
+ helpersRef.current = helpers;
166
+ if (runtime.thread.composer.text !== helpers.input) {
167
+ runtime.thread.composer.setText(helpers.input);
477
168
  }
478
- this.messages = vm;
479
- for (const callback of this._subscriptions) callback();
480
- };
481
- unstable_synchronizer = () => {
482
- const { vercel } = this.useVercel();
483
- useVercelAIThreadSync(vercel, this.updateData);
484
- useVercelAIComposerSync(vercel);
485
- return null;
486
- };
487
- addToolResult() {
169
+ }, [helpers, runtime]);
170
+ useEffect(() => {
171
+ return subscribeToMainThread(runtime, () => {
172
+ if (runtime.thread.composer.text !== helpersRef.current.input) {
173
+ helpersRef.current.setInput(runtime.thread.composer.text);
174
+ }
175
+ });
176
+ }, [runtime]);
177
+ };
178
+
179
+ // src/ui/utils/sliceMessagesUntil.tsx
180
+ var sliceMessagesUntil = (messages, messageId) => {
181
+ if (messageId == null) return [];
182
+ let messageIdx = messages.findIndex((m) => m.id === messageId);
183
+ if (messageIdx === -1)
488
184
  throw new Error(
489
- "VercelUseAssistantRuntime does not support adding tool results."
185
+ "useVercelAIThreadState: Message not found. This is liekly an internal bug in assistant-ui."
490
186
  );
187
+ while (messages[messageIdx + 1]?.role === "assistant") {
188
+ messageIdx++;
491
189
  }
190
+ return messages.slice(0, messageIdx + 1);
492
191
  };
493
192
 
494
- // src/ui/use-assistant/VercelUseAssistantRuntime.tsx
495
- var { ProxyConfigProvider: ProxyConfigProvider2, BaseAssistantRuntime: BaseAssistantRuntime2 } = INTERNAL3;
496
- var hasUpcomingMessage2 = (isRunning, messages) => {
497
- return isRunning && messages[messages.length - 1]?.role !== "assistant";
498
- };
499
- var VercelUseAssistantRuntime = class extends BaseAssistantRuntime2 {
500
- _proxyConfigProvider = new ProxyConfigProvider2();
501
- constructor(vercel) {
502
- super(new VercelUseAssistantThreadRuntime(vercel));
503
- }
504
- set vercel(vercel) {
505
- this.thread.vercel = vercel;
506
- }
507
- onVercelUpdated() {
508
- return this.thread.onVercelUpdated();
509
- }
510
- getModelConfig() {
511
- return this._proxyConfigProvider.getModelConfig();
512
- }
513
- registerModelConfigProvider(provider) {
514
- return this._proxyConfigProvider.registerModelConfigProvider(provider);
515
- }
516
- switchToThread(threadId) {
517
- if (threadId) {
518
- throw new Error("VercelAIRuntime does not yet support switching threads");
519
- }
520
- this.thread.vercel.messages = [];
521
- this.thread.vercel.input = "";
522
- this.thread.vercel.setMessages([]);
523
- this.thread.vercel.setInput("");
524
- this.thread = new VercelUseAssistantThreadRuntime(this.thread.vercel);
525
- }
193
+ // src/ui/use-chat/useVercelUseChatRuntime.tsx
194
+ var useVercelUseChatRuntime = (chatHelpers) => {
195
+ const messages = useCachedChunkedMessages(chatHelpers.messages);
196
+ const runtime = useExternalStoreRuntime2({
197
+ isRunning: chatHelpers.isLoading,
198
+ messages,
199
+ setMessages: (messages2) => chatHelpers.setMessages(messages2.flat()),
200
+ onCancel: async () => chatHelpers.stop(),
201
+ onNew: async (message) => {
202
+ if (message.content.length !== 1 || message.content[0]?.type !== "text")
203
+ throw new Error(
204
+ "Only text content is supported by VercelUseChatRuntime. Use the Edge runtime for image support."
205
+ );
206
+ await chatHelpers.append({
207
+ role: message.role,
208
+ content: message.content[0].text
209
+ });
210
+ },
211
+ onEdit: async (message) => {
212
+ if (message.content.length !== 1 || message.content[0]?.type !== "text")
213
+ throw new Error(
214
+ "Only text content is supported by VercelUseChatRuntime. Use the Edge runtime for image support."
215
+ );
216
+ const newMessages = sliceMessagesUntil(
217
+ chatHelpers.messages,
218
+ message.parentId
219
+ );
220
+ chatHelpers.setMessages(newMessages);
221
+ await chatHelpers.append({
222
+ role: message.role,
223
+ content: message.content[0].text
224
+ });
225
+ },
226
+ onReload: async (parentId) => {
227
+ const newMessages = sliceMessagesUntil(chatHelpers.messages, parentId);
228
+ chatHelpers.setMessages(newMessages);
229
+ await chatHelpers.reload();
230
+ },
231
+ onAddToolResult: ({ toolCallId, result }) => {
232
+ chatHelpers.addToolResult({ toolCallId, result });
233
+ },
234
+ // onCopy // TODO
235
+ onNewThread: () => {
236
+ chatHelpers.messages = [];
237
+ chatHelpers.input = "";
238
+ chatHelpers.setMessages([]);
239
+ chatHelpers.setInput("");
240
+ },
241
+ convertMessage
242
+ });
243
+ useInputSync(chatHelpers, runtime);
244
+ return runtime;
526
245
  };
527
246
 
528
247
  // src/ui/use-assistant/useVercelUseAssistantRuntime.tsx
248
+ import { useExternalStoreRuntime as useExternalStoreRuntime3 } from "@assistant-ui/react";
529
249
  var useVercelUseAssistantRuntime = (assistantHelpers) => {
530
- const [runtime] = useState2(
531
- () => new VercelUseAssistantRuntime(assistantHelpers)
532
- );
533
- useInsertionEffect2(() => {
534
- runtime.vercel = assistantHelpers;
535
- });
536
- useEffect4(() => {
537
- runtime.onVercelUpdated();
250
+ const messages = useCachedChunkedMessages(assistantHelpers.messages);
251
+ const runtime = useExternalStoreRuntime3({
252
+ isRunning: assistantHelpers.status === "in_progress",
253
+ messages,
254
+ onCancel: async () => assistantHelpers.stop(),
255
+ onNew: async (message) => {
256
+ if (message.content.length !== 1 || message.content[0]?.type !== "text")
257
+ throw new Error(
258
+ "VercelUseAssistantRuntime only supports text content."
259
+ );
260
+ await assistantHelpers.append({
261
+ role: message.role,
262
+ content: message.content[0].text
263
+ });
264
+ },
265
+ onNewThread: () => {
266
+ assistantHelpers.messages = [];
267
+ assistantHelpers.input = "";
268
+ assistantHelpers.setMessages([]);
269
+ assistantHelpers.setInput("");
270
+ },
271
+ convertMessage
538
272
  });
273
+ useInputSync(assistantHelpers, runtime);
539
274
  return runtime;
540
275
  };
276
+
277
+ // src/ui/getVercelAIMessage.tsx
278
+ import {
279
+ getExternalStoreMessage as getExternalStoreMessage2
280
+ } from "@assistant-ui/react";
281
+ var getVercelAIMessage = (message) => {
282
+ return getExternalStoreMessage2(message);
283
+ };
541
284
  export {
542
285
  getVercelAIMessage,
543
286
  getVercelRSCMessage,