@assistant-ui/react 0.0.18 → 0.0.20

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
@@ -129,18 +129,22 @@ var ThreadViewport = forwardRef2(({ autoScroll = true, onScroll, children, ...re
129
129
  const ref = useComposedRefs(forwardedRef, divRef);
130
130
  const { useViewport } = useAssistantContext();
131
131
  const firstRenderRef = useRef(true);
132
+ const isScrollingToBottomRef = useRef(false);
132
133
  const lastScrollTop = useRef(0);
133
134
  const scrollToBottom = () => {
134
135
  const div = messagesEndRef.current;
135
136
  if (!div || !autoScroll) return;
136
137
  const behavior = firstRenderRef.current ? "instant" : "auto";
137
138
  firstRenderRef.current = false;
138
- useViewport.setState({ isAtBottom: true });
139
+ isScrollingToBottomRef.current = true;
139
140
  div.scrollIntoView({ behavior });
140
141
  };
141
142
  useOnResizeContent(divRef, () => {
142
- if (!useViewport.getState().isAtBottom) return;
143
- scrollToBottom();
143
+ if (!isScrollingToBottomRef.current && !useViewport.getState().isAtBottom) {
144
+ handleScroll();
145
+ } else {
146
+ scrollToBottom();
147
+ }
144
148
  });
145
149
  useOnScrollToBottom(() => {
146
150
  scrollToBottom();
@@ -152,6 +156,7 @@ var ThreadViewport = forwardRef2(({ autoScroll = true, onScroll, children, ...re
152
156
  const newIsAtBottom = div.scrollHeight - div.scrollTop <= div.clientHeight;
153
157
  if (!newIsAtBottom && lastScrollTop.current < div.scrollTop) {
154
158
  } else if (newIsAtBottom !== isAtBottom) {
159
+ isScrollingToBottomRef.current = false;
155
160
  useViewport.setState({ isAtBottom: newIsAtBottom });
156
161
  }
157
162
  lastScrollTop.current = div.scrollTop;
@@ -818,6 +823,7 @@ __export(branchPicker_exports, {
818
823
  });
819
824
 
820
825
  // src/actions/useGoToNextBranch.tsx
826
+ import { useCallback as useCallback2 } from "react";
821
827
  var useGoToNextBranch = () => {
822
828
  const { useThread } = useAssistantContext();
823
829
  const { useMessage, useComposer } = useMessageContext();
@@ -825,11 +831,12 @@ var useGoToNextBranch = () => {
825
831
  [useMessage, useComposer],
826
832
  (m, c) => c.isEditing || m.branches.indexOf(m.message.id) + 1 >= m.branches.length
827
833
  );
828
- if (disabled) return null;
829
- return () => {
834
+ const callback = useCallback2(() => {
830
835
  const { message, branches } = useMessage.getState();
831
836
  useThread.getState().switchToBranch(branches[branches.indexOf(message.id) + 1]);
832
- };
837
+ }, [useMessage, useThread]);
838
+ if (disabled) return null;
839
+ return callback;
833
840
  };
834
841
 
835
842
  // src/utils/createActionButton.tsx
@@ -861,6 +868,7 @@ var createActionButton = (useActionButton) => {
861
868
  var BranchPickerNext = createActionButton(useGoToNextBranch);
862
869
 
863
870
  // src/actions/useGoToPreviousBranch.tsx
871
+ import { useCallback as useCallback3 } from "react";
864
872
  var useGoToPreviousBranch = () => {
865
873
  const { useThread } = useAssistantContext();
866
874
  const { useMessage, useComposer } = useMessageContext();
@@ -868,13 +876,12 @@ var useGoToPreviousBranch = () => {
868
876
  [useMessage, useComposer],
869
877
  (m, c) => c.isEditing || m.branches.indexOf(m.message.id) <= 0
870
878
  );
871
- if (disabled) return null;
872
- return () => {
879
+ const callback = useCallback3(() => {
873
880
  const { message, branches } = useMessage.getState();
874
- useThread.getState().switchToBranch(
875
- branches[branches.indexOf(message.id) - 1]
876
- );
877
- };
881
+ useThread.getState().switchToBranch(branches[branches.indexOf(message.id) - 1]);
882
+ }, [useMessage, useThread]);
883
+ if (disabled) return null;
884
+ return callback;
878
885
  };
879
886
 
880
887
  // src/primitives/branchPicker/BranchPickerPrevious.tsx
@@ -948,6 +955,7 @@ var ActionBarRoot = forwardRef13(({ hideWhenRunning, autohide, autohideFloat, ..
948
955
  });
949
956
 
950
957
  // src/actions/useCopyMessage.tsx
958
+ import { useCallback as useCallback4 } from "react";
951
959
  var useCopyMessage = ({ copiedDuration = 3e3 }) => {
952
960
  const { useMessage, useComposer } = useMessageContext();
953
961
  const hasCopyableContent = useCombinedStore(
@@ -956,21 +964,23 @@ var useCopyMessage = ({ copiedDuration = 3e3 }) => {
956
964
  return c.isEditing || m.message.content.some((c2) => c2.type === "text");
957
965
  }
958
966
  );
959
- if (!hasCopyableContent) return null;
960
- return () => {
967
+ const callback = useCallback4(() => {
961
968
  const { isEditing, value: composerValue } = useComposer.getState();
962
969
  const { message, setIsCopied } = useMessage.getState();
963
970
  const valueToCopy = isEditing ? composerValue : getMessageText(message);
964
971
  navigator.clipboard.writeText(valueToCopy);
965
972
  setIsCopied(true);
966
973
  setTimeout(() => setIsCopied(false), copiedDuration);
967
- };
974
+ }, [useComposer, useMessage, copiedDuration]);
975
+ if (!hasCopyableContent) return null;
976
+ return callback;
968
977
  };
969
978
 
970
979
  // src/primitives/actionBar/ActionBarCopy.tsx
971
980
  var ActionBarCopy = createActionButton(useCopyMessage);
972
981
 
973
982
  // src/actions/useReloadMessage.tsx
983
+ import { useCallback as useCallback5 } from "react";
974
984
  var useReloadMessage = () => {
975
985
  const { useThread, useViewport } = useAssistantContext();
976
986
  const { useMessage } = useMessageContext();
@@ -978,29 +988,32 @@ var useReloadMessage = () => {
978
988
  [useThread, useMessage],
979
989
  (t, m) => t.isRunning || m.message.role !== "assistant"
980
990
  );
981
- if (disabled) return null;
982
- return () => {
991
+ const callback = useCallback5(() => {
983
992
  const { parentId } = useMessage.getState();
984
993
  useThread.getState().startRun(parentId);
985
994
  useViewport.getState().scrollToBottom();
986
- };
995
+ }, [useMessage, useThread, useViewport]);
996
+ if (disabled) return null;
997
+ return callback;
987
998
  };
988
999
 
989
1000
  // src/primitives/actionBar/ActionBarReload.tsx
990
1001
  var ActionBarReload = createActionButton(useReloadMessage);
991
1002
 
992
1003
  // src/actions/useBeginMessageEdit.tsx
1004
+ import { useCallback as useCallback6 } from "react";
993
1005
  var useBeginMessageEdit = () => {
994
1006
  const { useMessage, useComposer } = useMessageContext();
995
1007
  const disabled = useCombinedStore(
996
1008
  [useMessage, useComposer],
997
1009
  (m, c) => m.message.role !== "user" || c.isEditing
998
1010
  );
999
- if (disabled) return null;
1000
- return () => {
1011
+ const callback = useCallback6(() => {
1001
1012
  const { edit } = useComposer.getState();
1002
1013
  edit();
1003
- };
1014
+ }, [useComposer]);
1015
+ if (disabled) return null;
1016
+ return callback;
1004
1017
  };
1005
1018
 
1006
1019
  // src/primitives/actionBar/ActionBarEdit.tsx
@@ -1014,10 +1027,15 @@ __export(contentPart_exports, {
1014
1027
  });
1015
1028
 
1016
1029
  // src/adapters/vercel/VercelAIAssistantProvider.tsx
1017
- import { useMemo as useMemo6 } from "react";
1030
+ import { useEffect as useEffect6 } from "react";
1018
1031
 
1019
- // src/adapters/vercel/useDummyAIAssistantContext.tsx
1020
- import { useState as useState3 } from "react";
1032
+ // src/adapters/core/AssistantProvider.tsx
1033
+ import {
1034
+ useEffect as useEffect4,
1035
+ useInsertionEffect,
1036
+ useRef as useRef4,
1037
+ useState as useState3
1038
+ } from "react";
1021
1039
  import { create as create5 } from "zustand";
1022
1040
 
1023
1041
  // src/utils/context/stores/ViewportStore.tsx
@@ -1040,41 +1058,79 @@ var makeViewportStore = () => {
1040
1058
  }));
1041
1059
  };
1042
1060
 
1043
- // src/adapters/vercel/useDummyAIAssistantContext.tsx
1044
- var makeDummyThreadStore = () => {
1045
- return create5(() => ({
1046
- messages: [],
1047
- isRunning: false,
1048
- getBranches: () => {
1049
- return [];
1050
- },
1051
- switchToBranch: () => {
1052
- throw new Error("Not implemented");
1053
- },
1054
- append: () => {
1055
- throw new Error("Not implemented");
1056
- },
1057
- startRun: () => {
1058
- throw new Error("Not implemented");
1059
- },
1060
- cancelRun: () => {
1061
- throw new Error("Not implemented");
1062
- }
1061
+ // src/adapters/core/AssistantProvider.tsx
1062
+ import { jsx as jsx21 } from "react/jsx-runtime";
1063
+ var makeThreadStore = (runtimeRef) => {
1064
+ const useThread = create5(() => ({
1065
+ messages: runtimeRef.current.messages,
1066
+ isRunning: runtimeRef.current.isRunning,
1067
+ getBranches: (messageId) => runtimeRef.current.getBranches(messageId),
1068
+ switchToBranch: (branchId) => runtimeRef.current.switchToBranch(branchId),
1069
+ startRun: (parentId) => runtimeRef.current.startRun(parentId),
1070
+ append: (message) => runtimeRef.current.append(message),
1071
+ cancelRun: () => runtimeRef.current.cancelRun()
1063
1072
  }));
1073
+ const onRuntimeUpdate = () => {
1074
+ useThread.setState({
1075
+ messages: runtimeRef.current.messages,
1076
+ isRunning: runtimeRef.current.isRunning
1077
+ });
1078
+ };
1079
+ return {
1080
+ useThread,
1081
+ onRuntimeUpdate
1082
+ };
1064
1083
  };
1065
- var useDummyAIAssistantContext = () => {
1066
- const [context] = useState3(() => {
1067
- const useThread = makeDummyThreadStore();
1084
+ var AssistantProvider = ({ children, runtime }) => {
1085
+ const runtimeRef = useRef4(runtime);
1086
+ useInsertionEffect(() => {
1087
+ runtimeRef.current = runtime;
1088
+ });
1089
+ const [{ context, onRuntimeUpdate }] = useState3(() => {
1090
+ const { useThread, onRuntimeUpdate: onRuntimeUpdate2 } = makeThreadStore(runtimeRef);
1068
1091
  const useViewport = makeViewportStore();
1069
1092
  const useComposer = makeThreadComposerStore(useThread);
1070
- return { useThread, useViewport, useComposer };
1093
+ return {
1094
+ context: {
1095
+ useViewport,
1096
+ useThread,
1097
+ useComposer
1098
+ },
1099
+ onRuntimeUpdate: onRuntimeUpdate2
1100
+ };
1071
1101
  });
1072
- return context;
1102
+ useEffect4(() => {
1103
+ onRuntimeUpdate();
1104
+ return runtime.subscribe(onRuntimeUpdate);
1105
+ }, [onRuntimeUpdate, runtime]);
1106
+ return /* @__PURE__ */ jsx21(AssistantContext.Provider, { value: context, children });
1073
1107
  };
1074
1108
 
1075
- // src/adapters/vercel/useVercelAIThreadState.tsx
1076
- import { useCallbackRef as useCallbackRef3 } from "@radix-ui/react-use-callback-ref";
1077
- import { useCallback as useCallback2, useMemo as useMemo5, useRef as useRef4, useState as useState4 } from "react";
1109
+ // src/adapters/core/vercel-use-chat/useVercelUseChatRuntime.tsx
1110
+ import { useEffect as useEffect5, useInsertionEffect as useInsertionEffect2, useMemo as useMemo5, useState as useState4 } from "react";
1111
+
1112
+ // src/adapters/ThreadMessageConverter.ts
1113
+ var ThreadMessageConverter = class {
1114
+ cache = /* @__PURE__ */ new WeakMap();
1115
+ convertMessages(converter, messages) {
1116
+ return messages.map((m) => {
1117
+ const cached = this.cache.get(m);
1118
+ const newMessage = converter(m, cached);
1119
+ this.cache.set(m, newMessage);
1120
+ return newMessage;
1121
+ });
1122
+ }
1123
+ };
1124
+
1125
+ // src/adapters/vercel/VercelThreadMessage.tsx
1126
+ var symbolInnerMessage = Symbol("innerMessage");
1127
+ var symbolInnerRSCMessage = Symbol("innerRSCMessage");
1128
+ var getVercelMessage = (message) => {
1129
+ return message[symbolInnerMessage];
1130
+ };
1131
+ var getVercelRSCMessage = (message) => {
1132
+ return message[symbolInnerRSCMessage];
1133
+ };
1078
1134
 
1079
1135
  // src/adapters/idUtils.tsx
1080
1136
  import { customAlphabet } from "nanoid/non-secure";
@@ -1236,30 +1292,104 @@ var MessageRepository = class {
1236
1292
  }
1237
1293
  };
1238
1294
 
1239
- // src/adapters/ThreadMessageConverter.ts
1240
- var ThreadMessageConverter = class {
1241
- cache = /* @__PURE__ */ new WeakMap();
1242
- convertMessages(converter, messages) {
1243
- return messages.map((m) => {
1244
- const cached = this.cache.get(m);
1245
- const newMessage = converter(m, cached);
1246
- this.cache.set(m, newMessage);
1247
- return newMessage;
1295
+ // src/adapters/core/vercel-use-chat/VercelUseChatRuntime.tsx
1296
+ var sliceMessagesUntil = (messages, messageId) => {
1297
+ if (messageId == null) return [];
1298
+ const messageIdx = messages.findIndex((m) => m.id === messageId);
1299
+ if (messageIdx === -1)
1300
+ throw new Error(
1301
+ "useVercelAIThreadState: Message not found. This is liekly an internal bug in assistant-ui."
1302
+ );
1303
+ return messages.slice(0, messageIdx + 1);
1304
+ };
1305
+ var hasUpcomingMessage = (isRunning, messages) => {
1306
+ return isRunning && messages[messages.length - 1]?.role !== "assistant";
1307
+ };
1308
+ var VercelUseChatRuntime = class {
1309
+ constructor(vercel) {
1310
+ this.vercel = vercel;
1311
+ }
1312
+ _subscriptions = /* @__PURE__ */ new Set();
1313
+ repository = new MessageRepository();
1314
+ assistantOptimisticId = null;
1315
+ messages = [];
1316
+ isRunning = false;
1317
+ getBranches(messageId) {
1318
+ return this.repository.getBranches(messageId);
1319
+ }
1320
+ switchToBranch(branchId) {
1321
+ this.repository.switchToBranch(branchId);
1322
+ this.vercel.setMessages(
1323
+ this.repository.getMessages().map(getVercelMessage).filter((m) => m != null)
1324
+ );
1325
+ }
1326
+ async append(message) {
1327
+ if (message.content.length !== 1 || message.content[0]?.type !== "text")
1328
+ throw new Error("Only text content is supported by Vercel AI SDK.");
1329
+ const newMessages = sliceMessagesUntil(
1330
+ this.vercel.messages,
1331
+ message.parentId
1332
+ );
1333
+ this.vercel.setMessages(newMessages);
1334
+ await this.vercel.append({
1335
+ role: "user",
1336
+ content: message.content[0].text
1248
1337
  });
1249
1338
  }
1339
+ async startRun(parentId) {
1340
+ const reloadMaybe = "reload" in this.vercel ? this.vercel.reload : void 0;
1341
+ if (!reloadMaybe)
1342
+ throw new Error(
1343
+ "Reload is not supported by Vercel AI SDK's useAssistant."
1344
+ );
1345
+ const newMessages = sliceMessagesUntil(this.vercel.messages, parentId);
1346
+ this.vercel.setMessages(newMessages);
1347
+ await reloadMaybe();
1348
+ }
1349
+ cancelRun() {
1350
+ const lastMessage = this.vercel.messages.at(-1);
1351
+ this.vercel.stop();
1352
+ if (lastMessage?.role === "user") {
1353
+ this.vercel.setInput(lastMessage.content);
1354
+ }
1355
+ }
1356
+ updateData(isRunning, vm) {
1357
+ for (let i = 0; i < vm.length; i++) {
1358
+ const message = vm[i];
1359
+ const parent = vm[i - 1];
1360
+ this.repository.addOrUpdateMessage(parent?.id ?? null, message);
1361
+ }
1362
+ if (this.assistantOptimisticId) {
1363
+ this.repository.deleteMessage(this.assistantOptimisticId, null);
1364
+ this.assistantOptimisticId = null;
1365
+ }
1366
+ if (hasUpcomingMessage(isRunning, vm)) {
1367
+ this.assistantOptimisticId = this.repository.appendOptimisticMessage(
1368
+ vm.at(-1)?.id ?? null,
1369
+ {
1370
+ role: "assistant",
1371
+ content: [{ type: "text", text: "" }]
1372
+ }
1373
+ );
1374
+ }
1375
+ this.repository.resetHead(
1376
+ this.assistantOptimisticId ?? vm.at(-1)?.id ?? null
1377
+ );
1378
+ this.messages = this.repository.getMessages();
1379
+ this.isRunning = isRunning;
1380
+ for (const callback of this._subscriptions) callback();
1381
+ }
1382
+ subscribe(callback) {
1383
+ this._subscriptions.add(callback);
1384
+ return () => this._subscriptions.delete(callback);
1385
+ }
1250
1386
  };
1251
1387
 
1252
- // src/adapters/vercel/VercelThreadMessage.tsx
1253
- var symbolInnerMessage = Symbol("innerMessage");
1254
- var symbolInnerRSCMessage = Symbol("innerRSCMessage");
1255
- var getVercelMessage = (message) => {
1256
- return message[symbolInnerMessage];
1257
- };
1258
- var getVercelRSCMessage = (message) => {
1259
- return message[symbolInnerRSCMessage];
1388
+ // src/adapters/core/vercel-use-chat/useVercelUseChatRuntime.tsx
1389
+ var getIsRunning = (vercel) => {
1390
+ if ("isLoading" in vercel) return vercel.isLoading;
1391
+ return vercel.status === "in_progress";
1260
1392
  };
1261
-
1262
- // src/adapters/vercel/useVercelAIThreadState.tsx
1263
1393
  var vercelToThreadMessage = (message, status) => {
1264
1394
  const common = {
1265
1395
  id: message.id,
@@ -1296,27 +1426,13 @@ var vercelToThreadMessage = (message, status) => {
1296
1426
  );
1297
1427
  }
1298
1428
  };
1299
- var sliceMessagesUntil = (messages, messageId) => {
1300
- if (messageId == null) return [];
1301
- const messageIdx = messages.findIndex((m) => m.id === messageId);
1302
- if (messageIdx === -1)
1303
- throw new Error(
1304
- "useVercelAIThreadState: Message not found. This is liekly an internal bug in assistant-ui."
1305
- );
1306
- return messages.slice(0, messageIdx + 1);
1307
- };
1308
- var hasUpcomingMessage = (isRunning, messages) => {
1309
- return isRunning && messages[messages.length - 1]?.role !== "assistant";
1310
- };
1311
- var getIsRunning = (vercel) => {
1312
- if ("isLoading" in vercel) return vercel.isLoading;
1313
- return vercel.status === "in_progress";
1314
- };
1315
- var useVercelAIThreadState = (vercel) => {
1316
- const [data] = useState4(() => new MessageRepository());
1429
+ var useVercelUseChatRuntime = (vercel) => {
1430
+ const [runtime] = useState4(() => new VercelUseChatRuntime(vercel));
1431
+ useInsertionEffect2(() => {
1432
+ runtime.vercel = vercel;
1433
+ });
1317
1434
  const isRunning = getIsRunning(vercel);
1318
1435
  const converter = useMemo5(() => new ThreadMessageConverter(), []);
1319
- const assistantOptimisticIdRef = useRef4(null);
1320
1436
  const messages = useMemo5(() => {
1321
1437
  const lastMessageId = vercel.messages.at(-1)?.id;
1322
1438
  const convertCallback = (message, cache) => {
@@ -1325,117 +1441,102 @@ var useVercelAIThreadState = (vercel) => {
1325
1441
  return cache;
1326
1442
  return vercelToThreadMessage(message, status);
1327
1443
  };
1328
- const vm = converter.convertMessages(convertCallback, vercel.messages);
1329
- for (let i = 0; i < vm.length; i++) {
1330
- const message = vm[i];
1331
- const parent = vm[i - 1];
1332
- data.addOrUpdateMessage(parent?.id ?? null, message);
1333
- }
1334
- if (assistantOptimisticIdRef.current) {
1335
- data.deleteMessage(assistantOptimisticIdRef.current, null);
1336
- assistantOptimisticIdRef.current = null;
1337
- }
1338
- if (hasUpcomingMessage(isRunning, vm)) {
1339
- assistantOptimisticIdRef.current = data.appendOptimisticMessage(
1340
- vm.at(-1)?.id ?? null,
1341
- {
1342
- role: "assistant",
1343
- content: [{ type: "text", text: "" }]
1344
- }
1345
- );
1346
- }
1347
- data.resetHead(assistantOptimisticIdRef.current ?? vm.at(-1)?.id ?? null);
1348
- return data.getMessages();
1349
- }, [converter, data, isRunning, vercel.messages]);
1350
- const getBranches2 = useCallback2(
1351
- (messageId) => {
1352
- return data.getBranches(messageId);
1353
- },
1354
- [data]
1355
- );
1356
- const switchToBranch2 = useCallbackRef3((messageId) => {
1357
- data.switchToBranch(messageId);
1358
- vercel.setMessages(
1359
- data.getMessages().map(getVercelMessage).filter((m) => m != null)
1360
- );
1361
- });
1362
- const startRun = useCallbackRef3(async (parentId) => {
1363
- const reloadMaybe = "reload" in vercel ? vercel.reload : void 0;
1364
- if (!reloadMaybe)
1365
- throw new Error(
1366
- "Reload is not supported by Vercel AI SDK's useAssistant."
1367
- );
1368
- const newMessages = sliceMessagesUntil(vercel.messages, parentId);
1369
- vercel.setMessages(newMessages);
1370
- await reloadMaybe();
1371
- });
1372
- const append = useCallbackRef3(async (message) => {
1373
- if (message.content.length !== 1 || message.content[0]?.type !== "text")
1374
- throw new Error("Only text content is supported by Vercel AI SDK.");
1375
- const newMessages = sliceMessagesUntil(vercel.messages, message.parentId);
1376
- vercel.setMessages(newMessages);
1377
- await vercel.append({
1378
- role: "user",
1379
- content: message.content[0].text
1380
- });
1381
- });
1382
- const cancelRun2 = useCallbackRef3(() => {
1383
- const lastMessage = vercel.messages.at(-1);
1384
- vercel.stop();
1385
- if (lastMessage?.role === "user") {
1386
- vercel.setInput(lastMessage.content);
1387
- }
1388
- });
1389
- return useMemo5(
1390
- () => ({
1391
- isRunning,
1392
- messages,
1393
- getBranches: getBranches2,
1394
- switchToBranch: switchToBranch2,
1395
- append,
1396
- startRun,
1397
- cancelRun: cancelRun2
1398
- }),
1399
- [
1400
- isRunning,
1401
- messages,
1402
- getBranches2,
1403
- switchToBranch2,
1404
- append,
1405
- startRun,
1406
- cancelRun2
1407
- ]
1408
- );
1444
+ return converter.convertMessages(convertCallback, vercel.messages);
1445
+ }, [isRunning, vercel.messages, converter]);
1446
+ useEffect5(() => {
1447
+ runtime.updateData(isRunning, messages);
1448
+ }, [runtime, isRunning, messages]);
1449
+ return runtime;
1409
1450
  };
1410
1451
 
1411
1452
  // src/adapters/vercel/VercelAIAssistantProvider.tsx
1412
- import { jsx as jsx21 } from "react/jsx-runtime";
1453
+ import { jsx as jsx22, jsxs as jsxs4 } from "react/jsx-runtime";
1413
1454
  var VercelAIAssistantProvider = ({
1414
1455
  children,
1415
1456
  ...rest
1416
1457
  }) => {
1417
- const context = useDummyAIAssistantContext();
1418
1458
  const vercel = "chat" in rest ? rest.chat : rest.assistant;
1419
- const threadState = useVercelAIThreadState(vercel);
1420
- useMemo6(() => {
1421
- context.useThread.setState(threadState, true);
1422
- }, [context, threadState]);
1423
- useMemo6(() => {
1424
- context.useComposer.setState({
1459
+ const runtime = useVercelUseChatRuntime(vercel);
1460
+ return /* @__PURE__ */ jsxs4(AssistantProvider, { runtime, children: [
1461
+ /* @__PURE__ */ jsx22(ComposerSync, { vercel }),
1462
+ children
1463
+ ] });
1464
+ };
1465
+ var ComposerSync = ({
1466
+ vercel
1467
+ }) => {
1468
+ const { useComposer } = useAssistantContext();
1469
+ useEffect6(() => {
1470
+ useComposer.setState({
1425
1471
  value: vercel.input,
1426
1472
  setValue: vercel.setInput
1427
1473
  });
1428
- }, [context, vercel.input, vercel.setInput]);
1429
- return /* @__PURE__ */ jsx21(AssistantContext.Provider, { value: context, children });
1474
+ }, [useComposer, vercel.input, vercel.setInput]);
1475
+ return null;
1430
1476
  };
1431
1477
 
1432
- // src/adapters/vercel/VercelRSCAssistantProvider.tsx
1433
- import {
1434
- useCallback as useCallback3,
1435
- useMemo as useMemo7,
1436
- useState as useState5
1437
- } from "react";
1438
- import { jsx as jsx22 } from "react/jsx-runtime";
1478
+ // src/adapters/core/vercel-rsc/useVercelRSCRuntime.tsx
1479
+ import { useEffect as useEffect7, useInsertionEffect as useInsertionEffect3, useMemo as useMemo6, useState as useState5 } from "react";
1480
+
1481
+ // src/adapters/core/vercel-rsc/VercelRSCRuntime.tsx
1482
+ var EMPTY_BRANCHES = Object.freeze([]);
1483
+ var VercelRSCRuntime = class {
1484
+ constructor(adapter) {
1485
+ this.adapter = adapter;
1486
+ }
1487
+ _subscriptions = /* @__PURE__ */ new Set();
1488
+ isRunning = false;
1489
+ messages = [];
1490
+ withRunning = (callback) => {
1491
+ this.isRunning = true;
1492
+ return callback.finally(() => {
1493
+ this.isRunning = false;
1494
+ });
1495
+ };
1496
+ getBranches() {
1497
+ return EMPTY_BRANCHES;
1498
+ }
1499
+ switchToBranch() {
1500
+ throw new Error(
1501
+ "Branch switching is not supported by VercelRSCAssistantProvider."
1502
+ );
1503
+ }
1504
+ async append(message) {
1505
+ if (message.parentId !== (this.messages.at(-1)?.id ?? null)) {
1506
+ if (!this.adapter.edit)
1507
+ throw new Error(
1508
+ "Message editing is not enabled, please provide an edit callback to VercelRSCAssistantProvider."
1509
+ );
1510
+ await this.withRunning(this.adapter.edit(message));
1511
+ } else {
1512
+ await this.withRunning(this.adapter.append(message));
1513
+ }
1514
+ }
1515
+ async startRun(parentId) {
1516
+ if (!this.adapter.reload)
1517
+ throw new Error(
1518
+ "Message reloading is not enabled, please provide a reload callback to VercelRSCAssistantProvider."
1519
+ );
1520
+ await this.withRunning(this.adapter.reload(parentId));
1521
+ }
1522
+ cancelRun() {
1523
+ if (process.env["NODE_ENV"] === "development") {
1524
+ console.warn(
1525
+ "Run cancellation is not supported by VercelRSCAssistantProvider."
1526
+ );
1527
+ }
1528
+ }
1529
+ updateData(messages) {
1530
+ this.messages = messages;
1531
+ for (const callback of this._subscriptions) callback();
1532
+ }
1533
+ subscribe(callback) {
1534
+ this._subscriptions.add(callback);
1535
+ return () => this._subscriptions.delete(callback);
1536
+ }
1537
+ };
1538
+
1539
+ // src/adapters/core/vercel-rsc/useVercelRSCRuntime.tsx
1439
1540
  var vercelToThreadMessage2 = (converter, rawMessage) => {
1440
1541
  const message = converter(rawMessage);
1441
1542
  return {
@@ -1447,220 +1548,61 @@ var vercelToThreadMessage2 = (converter, rawMessage) => {
1447
1548
  [symbolInnerRSCMessage]: rawMessage
1448
1549
  };
1449
1550
  };
1450
- var EMPTY_BRANCHES = [];
1451
- var getBranches = () => {
1452
- return EMPTY_BRANCHES;
1453
- };
1454
- var switchToBranch = () => {
1455
- throw new Error(
1456
- "Branch switching is not supported by VercelRSCAssistantProvider."
1457
- );
1458
- };
1459
- var cancelRun = () => {
1460
- if (process.env["NODE_ENV"] === "development") {
1461
- console.warn(
1462
- "Run cancellation is not supported by VercelRSCAssistantProvider."
1463
- );
1464
- }
1465
- };
1466
- var VercelRSCAssistantProvider = ({
1467
- children,
1468
- convertMessage,
1469
- messages: vercelMessages,
1470
- append: appendCallback,
1471
- edit,
1472
- reload
1473
- }) => {
1474
- const context = useDummyAIAssistantContext();
1475
- const [isRunning, setIsRunning] = useState5(false);
1476
- const withRunning = useCallback3((callback) => {
1477
- setIsRunning(true);
1478
- return callback.finally(() => setIsRunning(false));
1479
- }, []);
1480
- const [converter, convertCallback] = useMemo7(() => {
1481
- const rscConverter = convertMessage ?? ((m) => m);
1551
+ var useVercelRSCRuntime = (adapter) => {
1552
+ const [runtime] = useState5(() => new VercelRSCRuntime(adapter));
1553
+ useInsertionEffect3(() => {
1554
+ runtime.adapter = adapter;
1555
+ });
1556
+ const [converter, convertCallback] = useMemo6(() => {
1557
+ const rscConverter = adapter.convertMessage ?? ((m) => m);
1482
1558
  const convertCallback2 = (m, cache) => {
1483
1559
  if (cache) return cache;
1484
1560
  return vercelToThreadMessage2(rscConverter, m);
1485
1561
  };
1486
1562
  return [new ThreadMessageConverter(), convertCallback2];
1487
- }, [convertMessage]);
1488
- const messages = useMemo7(() => {
1489
- return converter.convertMessages(convertCallback, vercelMessages);
1490
- }, [converter, convertCallback, vercelMessages]);
1491
- const append = useCallback3(
1492
- async (message) => {
1493
- if (message.parentId !== (context.useThread.getState().messages.at(-1)?.id ?? null)) {
1494
- if (!edit)
1495
- throw new Error(
1496
- "Message editing is not enabled, please provide an edit callback to VercelRSCAssistantProvider."
1497
- );
1498
- await withRunning(edit(message));
1499
- } else {
1500
- await withRunning(appendCallback(message));
1501
- }
1502
- },
1503
- [context, withRunning, appendCallback, edit]
1504
- );
1505
- const startRun = useCallback3(
1506
- async (parentId) => {
1507
- if (!reload)
1508
- throw new Error(
1509
- "Message reloading is not enabled, please provide a reload callback to VercelRSCAssistantProvider."
1510
- );
1511
- await withRunning(reload(parentId));
1512
- },
1513
- [withRunning, reload]
1514
- );
1515
- useMemo7(() => {
1516
- context.useThread.setState(
1517
- {
1518
- messages,
1519
- isRunning,
1520
- getBranches,
1521
- switchToBranch,
1522
- append,
1523
- startRun,
1524
- cancelRun
1525
- },
1526
- true
1563
+ }, [adapter.convertMessage]);
1564
+ useEffect7(() => {
1565
+ runtime.updateData(
1566
+ converter.convertMessages(convertCallback, adapter.messages)
1527
1567
  );
1528
- }, [context, messages, isRunning, append, startRun]);
1529
- return /* @__PURE__ */ jsx22(AssistantContext.Provider, { value: context, children });
1530
- };
1531
-
1532
- // src/adapters/core/utils/useAssistantContext.tsx
1533
- import { useEffect as useEffect4, useRef as useRef5, useState as useState6 } from "react";
1534
- import { create as create6 } from "zustand";
1535
-
1536
- // src/adapters/core/utils/AssistantMessageRepository.tsx
1537
- var AssistantMessageRepository = class {
1538
- constructor(flushCallback) {
1539
- this.flushCallback = flushCallback;
1540
- }
1541
- repository = new MessageRepository();
1542
- getBranches(messageId) {
1543
- return this.repository.getBranches(messageId);
1544
- }
1545
- withModifications(callback) {
1546
- const res = callback(this.repository);
1547
- this.flushCallback(this.repository.getMessages());
1548
- return res;
1549
- }
1550
- };
1551
-
1552
- // src/adapters/core/utils/useAssistantContext.tsx
1553
- var makeThreadStore = (runtimeRef) => {
1554
- const repository = new AssistantMessageRepository((messages) => {
1555
- useThread.setState({ messages });
1556
- });
1557
- const useThread = create6(() => ({
1558
- messages: [],
1559
- isRunning: false,
1560
- getBranches: (messageId) => repository.getBranches(messageId),
1561
- switchToBranch: (branchId) => {
1562
- repository.withModifications((repository2) => {
1563
- repository2.switchToBranch(branchId);
1564
- });
1565
- },
1566
- startRun: async (parentId) => {
1567
- const optimisticId = repository.withModifications((repository2) => {
1568
- const optimisticId2 = repository2.appendOptimisticMessage(parentId, {
1569
- role: "assistant",
1570
- content: [{ type: "text", text: "" }]
1571
- });
1572
- repository2.resetHead(optimisticId2);
1573
- return optimisticId2;
1574
- });
1575
- const { id } = await runtimeRef.current.startRun(parentId);
1576
- repository.withModifications((repository2) => {
1577
- repository2.deleteMessage(optimisticId, id);
1578
- });
1579
- },
1580
- append: async (message) => {
1581
- const [parentOptimisticId, optimisticId] = repository.withModifications(
1582
- (repository2) => {
1583
- const parentOptimisticId2 = repository2.appendOptimisticMessage(
1584
- message.parentId,
1585
- {
1586
- role: "user",
1587
- content: message.content
1588
- }
1589
- );
1590
- const optimisticId2 = repository2.appendOptimisticMessage(
1591
- parentOptimisticId2,
1592
- {
1593
- role: "assistant",
1594
- content: [{ type: "text", text: "" }]
1595
- }
1596
- );
1597
- repository2.resetHead(optimisticId2);
1598
- return [parentOptimisticId2, optimisticId2];
1599
- }
1600
- );
1601
- const { parentId, id } = await runtimeRef.current.append(message);
1602
- repository.withModifications((repository2) => {
1603
- repository2.deleteMessage(parentOptimisticId, parentId);
1604
- repository2.deleteMessage(optimisticId, id);
1605
- });
1606
- },
1607
- cancelRun: () => runtimeRef.current.cancelRun()
1608
- }));
1609
- const onNewMessage = (parentId, message) => {
1610
- repository.withModifications((repository2) => {
1611
- repository2.addOrUpdateMessage(parentId, message);
1612
- });
1613
- };
1614
- const onRunningChange = (isRunning) => {
1615
- useThread.setState({ isRunning });
1616
- };
1617
- return {
1618
- useThread,
1619
- onNewMessage,
1620
- onRunningChange
1621
- };
1622
- };
1623
- var useAssistantContext2 = (runtime) => {
1624
- const runtimeRef = useRef5(runtime);
1625
- runtimeRef.current = runtime;
1626
- const [{ context, onNewMessage, onRunningChange }] = useState6(() => {
1627
- const { useThread, onNewMessage: onNewMessage2, onRunningChange: onRunningChange2 } = makeThreadStore(runtimeRef);
1628
- const useViewport = makeViewportStore();
1629
- const useComposer = makeThreadComposerStore(useThread);
1630
- return {
1631
- context: { useViewport, useThread, useComposer },
1632
- onNewMessage: onNewMessage2,
1633
- onRunningChange: onRunningChange2
1634
- };
1635
- });
1636
- useEffect4(() => {
1637
- return runtime.subscribeToMessageUpdates(onNewMessage);
1638
- }, [runtime, onNewMessage]);
1639
- useEffect4(() => {
1640
- return runtime.subscribeToStatusUpdates(onRunningChange);
1641
- }, [runtime, onRunningChange]);
1642
- return context;
1568
+ }, [runtime, converter, convertCallback, adapter.messages]);
1569
+ return runtime;
1643
1570
  };
1644
1571
 
1645
- // src/adapters/core/AssistantProvider.tsx
1572
+ // src/adapters/vercel/VercelRSCAssistantProvider.tsx
1646
1573
  import { jsx as jsx23 } from "react/jsx-runtime";
1647
- var AssistantProvider = ({ children, runtime }) => {
1648
- const context = useAssistantContext2(runtime);
1649
- return /* @__PURE__ */ jsx23(AssistantContext.Provider, { value: context, children });
1574
+ var VercelRSCAssistantProvider = ({
1575
+ children,
1576
+ ...adapter
1577
+ }) => {
1578
+ const runtime = useVercelRSCRuntime(adapter);
1579
+ return /* @__PURE__ */ jsx23(AssistantProvider, { runtime, children });
1650
1580
  };
1651
1581
 
1652
1582
  // src/adapters/core/local/useLocalRuntime.tsx
1653
- import { useState as useState7 } from "react";
1583
+ import { useInsertionEffect as useInsertionEffect4, useState as useState6 } from "react";
1654
1584
 
1655
1585
  // src/adapters/core/local/LocalRuntime.tsx
1656
1586
  var LocalRuntime = class {
1657
1587
  constructor(adapter) {
1658
1588
  this.adapter = adapter;
1659
1589
  }
1660
- _messageUpdateCallbacks = /* @__PURE__ */ new Set();
1661
- _statusUpdateCallbacks = /* @__PURE__ */ new Set();
1590
+ _subscriptions = /* @__PURE__ */ new Set();
1662
1591
  abortController = null;
1663
1592
  repository = new MessageRepository();
1593
+ get messages() {
1594
+ return this.repository.getMessages();
1595
+ }
1596
+ get isRunning() {
1597
+ return this.abortController != null;
1598
+ }
1599
+ getBranches(messageId) {
1600
+ return this.repository.getBranches(messageId);
1601
+ }
1602
+ switchToBranch(branchId) {
1603
+ this.repository.switchToBranch(branchId);
1604
+ this.notifySubscribers();
1605
+ }
1664
1606
  async append(message) {
1665
1607
  const userMessageId = generateId();
1666
1608
  const userMessage = {
@@ -1669,9 +1611,8 @@ var LocalRuntime = class {
1669
1611
  content: message.content,
1670
1612
  createdAt: /* @__PURE__ */ new Date()
1671
1613
  };
1672
- this.addOrUpdateMessage(message.parentId, userMessage);
1673
- const { id } = await this.startRun(userMessageId);
1674
- return { parentId: userMessageId, id };
1614
+ this.repository.addOrUpdateMessage(message.parentId, userMessage);
1615
+ await this.startRun(userMessageId);
1675
1616
  }
1676
1617
  async startRun(parentId) {
1677
1618
  const id = generateId();
@@ -1684,59 +1625,52 @@ var LocalRuntime = class {
1684
1625
  content: [{ type: "text", text: "" }],
1685
1626
  createdAt: /* @__PURE__ */ new Date()
1686
1627
  };
1687
- this.addOrUpdateMessage(parentId, message);
1688
- void this.run(parentId, messages, message);
1689
- return { id };
1690
- }
1691
- addOrUpdateMessage(parentId, message) {
1692
- const clone = { ...message };
1693
- this.repository.addOrUpdateMessage(parentId, clone);
1694
- for (const callback of this._messageUpdateCallbacks)
1695
- callback(parentId, clone);
1696
- }
1697
- async run(parentId, messages, message) {
1698
- this.cancelRun();
1699
- for (const callback of this._statusUpdateCallbacks) callback(true);
1628
+ this.repository.addOrUpdateMessage(parentId, { ...message });
1629
+ this.abortController?.abort();
1700
1630
  this.abortController = new AbortController();
1631
+ this.notifySubscribers();
1701
1632
  try {
1702
1633
  await this.adapter.run({
1703
1634
  messages,
1704
1635
  abortSignal: this.abortController.signal,
1705
1636
  onUpdate: ({ content }) => {
1706
1637
  message.content = content;
1707
- this.addOrUpdateMessage(parentId, message);
1638
+ this.repository.addOrUpdateMessage(parentId, { ...message });
1639
+ this.notifySubscribers();
1708
1640
  }
1709
1641
  });
1710
1642
  message.status = "done";
1711
- this.addOrUpdateMessage(parentId, message);
1643
+ this.repository.addOrUpdateMessage(parentId, { ...message });
1712
1644
  } catch (e) {
1713
1645
  message.status = "error";
1714
- this.addOrUpdateMessage(parentId, message);
1646
+ this.repository.addOrUpdateMessage(parentId, { ...message });
1715
1647
  console.error(e);
1716
1648
  } finally {
1717
- this.cancelRun();
1649
+ this.abortController = null;
1650
+ this.notifySubscribers();
1718
1651
  }
1719
1652
  }
1720
1653
  cancelRun() {
1721
1654
  if (!this.abortController) return;
1722
1655
  this.abortController.abort();
1723
1656
  this.abortController = null;
1724
- for (const callback of this._statusUpdateCallbacks) callback(false);
1657
+ this.notifySubscribers();
1725
1658
  }
1726
- subscribeToMessageUpdates(callback) {
1727
- this._messageUpdateCallbacks.add(callback);
1728
- return () => this._messageUpdateCallbacks.delete(callback);
1659
+ notifySubscribers() {
1660
+ for (const callback of this._subscriptions) callback();
1729
1661
  }
1730
- subscribeToStatusUpdates(callback) {
1731
- this._statusUpdateCallbacks.add(callback);
1732
- return () => this._statusUpdateCallbacks.delete(callback);
1662
+ subscribe(callback) {
1663
+ this._subscriptions.add(callback);
1664
+ return () => this._subscriptions.delete(callback);
1733
1665
  }
1734
1666
  };
1735
1667
 
1736
1668
  // src/adapters/core/local/useLocalRuntime.tsx
1737
1669
  var useLocalRuntime = (adapter) => {
1738
- const [runtime] = useState7(() => new LocalRuntime(adapter));
1739
- runtime.adapter = adapter;
1670
+ const [runtime] = useState6(() => new LocalRuntime(adapter));
1671
+ useInsertionEffect4(() => {
1672
+ runtime.adapter = adapter;
1673
+ });
1740
1674
  return runtime;
1741
1675
  };
1742
1676