@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.js CHANGED
@@ -173,18 +173,22 @@ var ThreadViewport = (0, import_react5.forwardRef)(({ autoScroll = true, onScrol
173
173
  const ref = (0, import_react_compose_refs.useComposedRefs)(forwardedRef, divRef);
174
174
  const { useViewport } = useAssistantContext();
175
175
  const firstRenderRef = (0, import_react5.useRef)(true);
176
+ const isScrollingToBottomRef = (0, import_react5.useRef)(false);
176
177
  const lastScrollTop = (0, import_react5.useRef)(0);
177
178
  const scrollToBottom = () => {
178
179
  const div = messagesEndRef.current;
179
180
  if (!div || !autoScroll) return;
180
181
  const behavior = firstRenderRef.current ? "instant" : "auto";
181
182
  firstRenderRef.current = false;
182
- useViewport.setState({ isAtBottom: true });
183
+ isScrollingToBottomRef.current = true;
183
184
  div.scrollIntoView({ behavior });
184
185
  };
185
186
  useOnResizeContent(divRef, () => {
186
- if (!useViewport.getState().isAtBottom) return;
187
- scrollToBottom();
187
+ if (!isScrollingToBottomRef.current && !useViewport.getState().isAtBottom) {
188
+ handleScroll();
189
+ } else {
190
+ scrollToBottom();
191
+ }
188
192
  });
189
193
  useOnScrollToBottom(() => {
190
194
  scrollToBottom();
@@ -196,6 +200,7 @@ var ThreadViewport = (0, import_react5.forwardRef)(({ autoScroll = true, onScrol
196
200
  const newIsAtBottom = div.scrollHeight - div.scrollTop <= div.clientHeight;
197
201
  if (!newIsAtBottom && lastScrollTop.current < div.scrollTop) {
198
202
  } else if (newIsAtBottom !== isAtBottom) {
203
+ isScrollingToBottomRef.current = false;
199
204
  useViewport.setState({ isAtBottom: newIsAtBottom });
200
205
  }
201
206
  lastScrollTop.current = div.scrollTop;
@@ -841,6 +846,7 @@ __export(branchPicker_exports, {
841
846
  });
842
847
 
843
848
  // src/actions/useGoToNextBranch.tsx
849
+ var import_react21 = require("react");
844
850
  var useGoToNextBranch = () => {
845
851
  const { useThread } = useAssistantContext();
846
852
  const { useMessage, useComposer } = useMessageContext();
@@ -848,20 +854,21 @@ var useGoToNextBranch = () => {
848
854
  [useMessage, useComposer],
849
855
  (m, c) => c.isEditing || m.branches.indexOf(m.message.id) + 1 >= m.branches.length
850
856
  );
851
- if (disabled) return null;
852
- return () => {
857
+ const callback = (0, import_react21.useCallback)(() => {
853
858
  const { message, branches } = useMessage.getState();
854
859
  useThread.getState().switchToBranch(branches[branches.indexOf(message.id) + 1]);
855
- };
860
+ }, [useMessage, useThread]);
861
+ if (disabled) return null;
862
+ return callback;
856
863
  };
857
864
 
858
865
  // src/utils/createActionButton.tsx
859
866
  var import_primitive8 = require("@radix-ui/primitive");
860
867
  var import_react_primitive10 = require("@radix-ui/react-primitive");
861
- var import_react21 = require("react");
868
+ var import_react22 = require("react");
862
869
  var import_jsx_runtime16 = require("react/jsx-runtime");
863
870
  var createActionButton = (useActionButton) => {
864
- return (0, import_react21.forwardRef)(
871
+ return (0, import_react22.forwardRef)(
865
872
  (props, forwardedRef) => {
866
873
  const onClick = useActionButton(props);
867
874
  return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
@@ -882,6 +889,7 @@ var createActionButton = (useActionButton) => {
882
889
  var BranchPickerNext = createActionButton(useGoToNextBranch);
883
890
 
884
891
  // src/actions/useGoToPreviousBranch.tsx
892
+ var import_react23 = require("react");
885
893
  var useGoToPreviousBranch = () => {
886
894
  const { useThread } = useAssistantContext();
887
895
  const { useMessage, useComposer } = useMessageContext();
@@ -889,13 +897,12 @@ var useGoToPreviousBranch = () => {
889
897
  [useMessage, useComposer],
890
898
  (m, c) => c.isEditing || m.branches.indexOf(m.message.id) <= 0
891
899
  );
892
- if (disabled) return null;
893
- return () => {
900
+ const callback = (0, import_react23.useCallback)(() => {
894
901
  const { message, branches } = useMessage.getState();
895
- useThread.getState().switchToBranch(
896
- branches[branches.indexOf(message.id) - 1]
897
- );
898
- };
902
+ useThread.getState().switchToBranch(branches[branches.indexOf(message.id) - 1]);
903
+ }, [useMessage, useThread]);
904
+ if (disabled) return null;
905
+ return callback;
899
906
  };
900
907
 
901
908
  // src/primitives/branchPicker/BranchPickerPrevious.tsx
@@ -919,9 +926,9 @@ var BranchPickerNumber = () => {
919
926
 
920
927
  // src/primitives/branchPicker/BranchPickerRoot.tsx
921
928
  var import_react_primitive11 = require("@radix-ui/react-primitive");
922
- var import_react22 = require("react");
929
+ var import_react24 = require("react");
923
930
  var import_jsx_runtime19 = require("react/jsx-runtime");
924
- var BranchPickerRoot = (0, import_react22.forwardRef)(({ hideWhenSingleBranch, ...rest }, ref) => {
931
+ var BranchPickerRoot = (0, import_react24.forwardRef)(({ hideWhenSingleBranch, ...rest }, ref) => {
925
932
  return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(MessageIf, { hasBranches: hideWhenSingleBranch ? true : void 0, children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_react_primitive11.Primitive.div, { ...rest, ref }) });
926
933
  });
927
934
 
@@ -936,9 +943,9 @@ __export(actionBar_exports, {
936
943
 
937
944
  // src/primitives/actionBar/ActionBarRoot.tsx
938
945
  var import_react_primitive12 = require("@radix-ui/react-primitive");
939
- var import_react23 = require("react");
946
+ var import_react25 = require("react");
940
947
  var import_jsx_runtime20 = require("react/jsx-runtime");
941
- var ActionBarRoot = (0, import_react23.forwardRef)(({ hideWhenRunning, autohide, autohideFloat, ...rest }, ref) => {
948
+ var ActionBarRoot = (0, import_react25.forwardRef)(({ hideWhenRunning, autohide, autohideFloat, ...rest }, ref) => {
942
949
  const { useThread } = useAssistantContext();
943
950
  const { useMessage } = useMessageContext();
944
951
  const hideAndfloatStatus = useCombinedStore(
@@ -965,6 +972,7 @@ var ActionBarRoot = (0, import_react23.forwardRef)(({ hideWhenRunning, autohide,
965
972
  });
966
973
 
967
974
  // src/actions/useCopyMessage.tsx
975
+ var import_react26 = require("react");
968
976
  var useCopyMessage = ({ copiedDuration = 3e3 }) => {
969
977
  const { useMessage, useComposer } = useMessageContext();
970
978
  const hasCopyableContent = useCombinedStore(
@@ -973,21 +981,23 @@ var useCopyMessage = ({ copiedDuration = 3e3 }) => {
973
981
  return c.isEditing || m.message.content.some((c2) => c2.type === "text");
974
982
  }
975
983
  );
976
- if (!hasCopyableContent) return null;
977
- return () => {
984
+ const callback = (0, import_react26.useCallback)(() => {
978
985
  const { isEditing, value: composerValue } = useComposer.getState();
979
986
  const { message, setIsCopied } = useMessage.getState();
980
987
  const valueToCopy = isEditing ? composerValue : getMessageText(message);
981
988
  navigator.clipboard.writeText(valueToCopy);
982
989
  setIsCopied(true);
983
990
  setTimeout(() => setIsCopied(false), copiedDuration);
984
- };
991
+ }, [useComposer, useMessage, copiedDuration]);
992
+ if (!hasCopyableContent) return null;
993
+ return callback;
985
994
  };
986
995
 
987
996
  // src/primitives/actionBar/ActionBarCopy.tsx
988
997
  var ActionBarCopy = createActionButton(useCopyMessage);
989
998
 
990
999
  // src/actions/useReloadMessage.tsx
1000
+ var import_react27 = require("react");
991
1001
  var useReloadMessage = () => {
992
1002
  const { useThread, useViewport } = useAssistantContext();
993
1003
  const { useMessage } = useMessageContext();
@@ -995,29 +1005,32 @@ var useReloadMessage = () => {
995
1005
  [useThread, useMessage],
996
1006
  (t, m) => t.isRunning || m.message.role !== "assistant"
997
1007
  );
998
- if (disabled) return null;
999
- return () => {
1008
+ const callback = (0, import_react27.useCallback)(() => {
1000
1009
  const { parentId } = useMessage.getState();
1001
1010
  useThread.getState().startRun(parentId);
1002
1011
  useViewport.getState().scrollToBottom();
1003
- };
1012
+ }, [useMessage, useThread, useViewport]);
1013
+ if (disabled) return null;
1014
+ return callback;
1004
1015
  };
1005
1016
 
1006
1017
  // src/primitives/actionBar/ActionBarReload.tsx
1007
1018
  var ActionBarReload = createActionButton(useReloadMessage);
1008
1019
 
1009
1020
  // src/actions/useBeginMessageEdit.tsx
1021
+ var import_react28 = require("react");
1010
1022
  var useBeginMessageEdit = () => {
1011
1023
  const { useMessage, useComposer } = useMessageContext();
1012
1024
  const disabled = useCombinedStore(
1013
1025
  [useMessage, useComposer],
1014
1026
  (m, c) => m.message.role !== "user" || c.isEditing
1015
1027
  );
1016
- if (disabled) return null;
1017
- return () => {
1028
+ const callback = (0, import_react28.useCallback)(() => {
1018
1029
  const { edit } = useComposer.getState();
1019
1030
  edit();
1020
- };
1031
+ }, [useComposer]);
1032
+ if (disabled) return null;
1033
+ return callback;
1021
1034
  };
1022
1035
 
1023
1036
  // src/primitives/actionBar/ActionBarEdit.tsx
@@ -1031,10 +1044,10 @@ __export(contentPart_exports, {
1031
1044
  });
1032
1045
 
1033
1046
  // src/adapters/vercel/VercelAIAssistantProvider.tsx
1034
- var import_react26 = require("react");
1047
+ var import_react31 = require("react");
1035
1048
 
1036
- // src/adapters/vercel/useDummyAIAssistantContext.tsx
1037
- var import_react24 = require("react");
1049
+ // src/adapters/core/AssistantProvider.tsx
1050
+ var import_react29 = require("react");
1038
1051
  var import_zustand5 = require("zustand");
1039
1052
 
1040
1053
  // src/utils/context/stores/ViewportStore.tsx
@@ -1057,41 +1070,79 @@ var makeViewportStore = () => {
1057
1070
  }));
1058
1071
  };
1059
1072
 
1060
- // src/adapters/vercel/useDummyAIAssistantContext.tsx
1061
- var makeDummyThreadStore = () => {
1062
- return (0, import_zustand5.create)(() => ({
1063
- messages: [],
1064
- isRunning: false,
1065
- getBranches: () => {
1066
- return [];
1067
- },
1068
- switchToBranch: () => {
1069
- throw new Error("Not implemented");
1070
- },
1071
- append: () => {
1072
- throw new Error("Not implemented");
1073
- },
1074
- startRun: () => {
1075
- throw new Error("Not implemented");
1076
- },
1077
- cancelRun: () => {
1078
- throw new Error("Not implemented");
1079
- }
1073
+ // src/adapters/core/AssistantProvider.tsx
1074
+ var import_jsx_runtime21 = require("react/jsx-runtime");
1075
+ var makeThreadStore = (runtimeRef) => {
1076
+ const useThread = (0, import_zustand5.create)(() => ({
1077
+ messages: runtimeRef.current.messages,
1078
+ isRunning: runtimeRef.current.isRunning,
1079
+ getBranches: (messageId) => runtimeRef.current.getBranches(messageId),
1080
+ switchToBranch: (branchId) => runtimeRef.current.switchToBranch(branchId),
1081
+ startRun: (parentId) => runtimeRef.current.startRun(parentId),
1082
+ append: (message) => runtimeRef.current.append(message),
1083
+ cancelRun: () => runtimeRef.current.cancelRun()
1080
1084
  }));
1085
+ const onRuntimeUpdate = () => {
1086
+ useThread.setState({
1087
+ messages: runtimeRef.current.messages,
1088
+ isRunning: runtimeRef.current.isRunning
1089
+ });
1090
+ };
1091
+ return {
1092
+ useThread,
1093
+ onRuntimeUpdate
1094
+ };
1081
1095
  };
1082
- var useDummyAIAssistantContext = () => {
1083
- const [context] = (0, import_react24.useState)(() => {
1084
- const useThread = makeDummyThreadStore();
1096
+ var AssistantProvider = ({ children, runtime }) => {
1097
+ const runtimeRef = (0, import_react29.useRef)(runtime);
1098
+ (0, import_react29.useInsertionEffect)(() => {
1099
+ runtimeRef.current = runtime;
1100
+ });
1101
+ const [{ context, onRuntimeUpdate }] = (0, import_react29.useState)(() => {
1102
+ const { useThread, onRuntimeUpdate: onRuntimeUpdate2 } = makeThreadStore(runtimeRef);
1085
1103
  const useViewport = makeViewportStore();
1086
1104
  const useComposer = makeThreadComposerStore(useThread);
1087
- return { useThread, useViewport, useComposer };
1105
+ return {
1106
+ context: {
1107
+ useViewport,
1108
+ useThread,
1109
+ useComposer
1110
+ },
1111
+ onRuntimeUpdate: onRuntimeUpdate2
1112
+ };
1088
1113
  });
1089
- return context;
1114
+ (0, import_react29.useEffect)(() => {
1115
+ onRuntimeUpdate();
1116
+ return runtime.subscribe(onRuntimeUpdate);
1117
+ }, [onRuntimeUpdate, runtime]);
1118
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(AssistantContext.Provider, { value: context, children });
1090
1119
  };
1091
1120
 
1092
- // src/adapters/vercel/useVercelAIThreadState.tsx
1093
- var import_react_use_callback_ref3 = require("@radix-ui/react-use-callback-ref");
1094
- var import_react25 = require("react");
1121
+ // src/adapters/core/vercel-use-chat/useVercelUseChatRuntime.tsx
1122
+ var import_react30 = require("react");
1123
+
1124
+ // src/adapters/ThreadMessageConverter.ts
1125
+ var ThreadMessageConverter = class {
1126
+ cache = /* @__PURE__ */ new WeakMap();
1127
+ convertMessages(converter, messages) {
1128
+ return messages.map((m) => {
1129
+ const cached = this.cache.get(m);
1130
+ const newMessage = converter(m, cached);
1131
+ this.cache.set(m, newMessage);
1132
+ return newMessage;
1133
+ });
1134
+ }
1135
+ };
1136
+
1137
+ // src/adapters/vercel/VercelThreadMessage.tsx
1138
+ var symbolInnerMessage = Symbol("innerMessage");
1139
+ var symbolInnerRSCMessage = Symbol("innerRSCMessage");
1140
+ var getVercelMessage = (message) => {
1141
+ return message[symbolInnerMessage];
1142
+ };
1143
+ var getVercelRSCMessage = (message) => {
1144
+ return message[symbolInnerRSCMessage];
1145
+ };
1095
1146
 
1096
1147
  // src/adapters/idUtils.tsx
1097
1148
  var import_non_secure = require("nanoid/non-secure");
@@ -1253,30 +1304,104 @@ var MessageRepository = class {
1253
1304
  }
1254
1305
  };
1255
1306
 
1256
- // src/adapters/ThreadMessageConverter.ts
1257
- var ThreadMessageConverter = class {
1258
- cache = /* @__PURE__ */ new WeakMap();
1259
- convertMessages(converter, messages) {
1260
- return messages.map((m) => {
1261
- const cached = this.cache.get(m);
1262
- const newMessage = converter(m, cached);
1263
- this.cache.set(m, newMessage);
1264
- return newMessage;
1307
+ // src/adapters/core/vercel-use-chat/VercelUseChatRuntime.tsx
1308
+ var sliceMessagesUntil = (messages, messageId) => {
1309
+ if (messageId == null) return [];
1310
+ const messageIdx = messages.findIndex((m) => m.id === messageId);
1311
+ if (messageIdx === -1)
1312
+ throw new Error(
1313
+ "useVercelAIThreadState: Message not found. This is liekly an internal bug in assistant-ui."
1314
+ );
1315
+ return messages.slice(0, messageIdx + 1);
1316
+ };
1317
+ var hasUpcomingMessage = (isRunning, messages) => {
1318
+ return isRunning && messages[messages.length - 1]?.role !== "assistant";
1319
+ };
1320
+ var VercelUseChatRuntime = class {
1321
+ constructor(vercel) {
1322
+ this.vercel = vercel;
1323
+ }
1324
+ _subscriptions = /* @__PURE__ */ new Set();
1325
+ repository = new MessageRepository();
1326
+ assistantOptimisticId = null;
1327
+ messages = [];
1328
+ isRunning = false;
1329
+ getBranches(messageId) {
1330
+ return this.repository.getBranches(messageId);
1331
+ }
1332
+ switchToBranch(branchId) {
1333
+ this.repository.switchToBranch(branchId);
1334
+ this.vercel.setMessages(
1335
+ this.repository.getMessages().map(getVercelMessage).filter((m) => m != null)
1336
+ );
1337
+ }
1338
+ async append(message) {
1339
+ if (message.content.length !== 1 || message.content[0]?.type !== "text")
1340
+ throw new Error("Only text content is supported by Vercel AI SDK.");
1341
+ const newMessages = sliceMessagesUntil(
1342
+ this.vercel.messages,
1343
+ message.parentId
1344
+ );
1345
+ this.vercel.setMessages(newMessages);
1346
+ await this.vercel.append({
1347
+ role: "user",
1348
+ content: message.content[0].text
1265
1349
  });
1266
1350
  }
1351
+ async startRun(parentId) {
1352
+ const reloadMaybe = "reload" in this.vercel ? this.vercel.reload : void 0;
1353
+ if (!reloadMaybe)
1354
+ throw new Error(
1355
+ "Reload is not supported by Vercel AI SDK's useAssistant."
1356
+ );
1357
+ const newMessages = sliceMessagesUntil(this.vercel.messages, parentId);
1358
+ this.vercel.setMessages(newMessages);
1359
+ await reloadMaybe();
1360
+ }
1361
+ cancelRun() {
1362
+ const lastMessage = this.vercel.messages.at(-1);
1363
+ this.vercel.stop();
1364
+ if (lastMessage?.role === "user") {
1365
+ this.vercel.setInput(lastMessage.content);
1366
+ }
1367
+ }
1368
+ updateData(isRunning, vm) {
1369
+ for (let i = 0; i < vm.length; i++) {
1370
+ const message = vm[i];
1371
+ const parent = vm[i - 1];
1372
+ this.repository.addOrUpdateMessage(parent?.id ?? null, message);
1373
+ }
1374
+ if (this.assistantOptimisticId) {
1375
+ this.repository.deleteMessage(this.assistantOptimisticId, null);
1376
+ this.assistantOptimisticId = null;
1377
+ }
1378
+ if (hasUpcomingMessage(isRunning, vm)) {
1379
+ this.assistantOptimisticId = this.repository.appendOptimisticMessage(
1380
+ vm.at(-1)?.id ?? null,
1381
+ {
1382
+ role: "assistant",
1383
+ content: [{ type: "text", text: "" }]
1384
+ }
1385
+ );
1386
+ }
1387
+ this.repository.resetHead(
1388
+ this.assistantOptimisticId ?? vm.at(-1)?.id ?? null
1389
+ );
1390
+ this.messages = this.repository.getMessages();
1391
+ this.isRunning = isRunning;
1392
+ for (const callback of this._subscriptions) callback();
1393
+ }
1394
+ subscribe(callback) {
1395
+ this._subscriptions.add(callback);
1396
+ return () => this._subscriptions.delete(callback);
1397
+ }
1267
1398
  };
1268
1399
 
1269
- // src/adapters/vercel/VercelThreadMessage.tsx
1270
- var symbolInnerMessage = Symbol("innerMessage");
1271
- var symbolInnerRSCMessage = Symbol("innerRSCMessage");
1272
- var getVercelMessage = (message) => {
1273
- return message[symbolInnerMessage];
1274
- };
1275
- var getVercelRSCMessage = (message) => {
1276
- return message[symbolInnerRSCMessage];
1400
+ // src/adapters/core/vercel-use-chat/useVercelUseChatRuntime.tsx
1401
+ var getIsRunning = (vercel) => {
1402
+ if ("isLoading" in vercel) return vercel.isLoading;
1403
+ return vercel.status === "in_progress";
1277
1404
  };
1278
-
1279
- // src/adapters/vercel/useVercelAIThreadState.tsx
1280
1405
  var vercelToThreadMessage = (message, status) => {
1281
1406
  const common = {
1282
1407
  id: message.id,
@@ -1313,28 +1438,14 @@ var vercelToThreadMessage = (message, status) => {
1313
1438
  );
1314
1439
  }
1315
1440
  };
1316
- var sliceMessagesUntil = (messages, messageId) => {
1317
- if (messageId == null) return [];
1318
- const messageIdx = messages.findIndex((m) => m.id === messageId);
1319
- if (messageIdx === -1)
1320
- throw new Error(
1321
- "useVercelAIThreadState: Message not found. This is liekly an internal bug in assistant-ui."
1322
- );
1323
- return messages.slice(0, messageIdx + 1);
1324
- };
1325
- var hasUpcomingMessage = (isRunning, messages) => {
1326
- return isRunning && messages[messages.length - 1]?.role !== "assistant";
1327
- };
1328
- var getIsRunning = (vercel) => {
1329
- if ("isLoading" in vercel) return vercel.isLoading;
1330
- return vercel.status === "in_progress";
1331
- };
1332
- var useVercelAIThreadState = (vercel) => {
1333
- const [data] = (0, import_react25.useState)(() => new MessageRepository());
1441
+ var useVercelUseChatRuntime = (vercel) => {
1442
+ const [runtime] = (0, import_react30.useState)(() => new VercelUseChatRuntime(vercel));
1443
+ (0, import_react30.useInsertionEffect)(() => {
1444
+ runtime.vercel = vercel;
1445
+ });
1334
1446
  const isRunning = getIsRunning(vercel);
1335
- const converter = (0, import_react25.useMemo)(() => new ThreadMessageConverter(), []);
1336
- const assistantOptimisticIdRef = (0, import_react25.useRef)(null);
1337
- const messages = (0, import_react25.useMemo)(() => {
1447
+ const converter = (0, import_react30.useMemo)(() => new ThreadMessageConverter(), []);
1448
+ const messages = (0, import_react30.useMemo)(() => {
1338
1449
  const lastMessageId = vercel.messages.at(-1)?.id;
1339
1450
  const convertCallback = (message, cache) => {
1340
1451
  const status = lastMessageId === message.id && isRunning ? "in_progress" : "done";
@@ -1342,113 +1453,102 @@ var useVercelAIThreadState = (vercel) => {
1342
1453
  return cache;
1343
1454
  return vercelToThreadMessage(message, status);
1344
1455
  };
1345
- const vm = converter.convertMessages(convertCallback, vercel.messages);
1346
- for (let i = 0; i < vm.length; i++) {
1347
- const message = vm[i];
1348
- const parent = vm[i - 1];
1349
- data.addOrUpdateMessage(parent?.id ?? null, message);
1350
- }
1351
- if (assistantOptimisticIdRef.current) {
1352
- data.deleteMessage(assistantOptimisticIdRef.current, null);
1353
- assistantOptimisticIdRef.current = null;
1354
- }
1355
- if (hasUpcomingMessage(isRunning, vm)) {
1356
- assistantOptimisticIdRef.current = data.appendOptimisticMessage(
1357
- vm.at(-1)?.id ?? null,
1358
- {
1359
- role: "assistant",
1360
- content: [{ type: "text", text: "" }]
1361
- }
1362
- );
1363
- }
1364
- data.resetHead(assistantOptimisticIdRef.current ?? vm.at(-1)?.id ?? null);
1365
- return data.getMessages();
1366
- }, [converter, data, isRunning, vercel.messages]);
1367
- const getBranches2 = (0, import_react25.useCallback)(
1368
- (messageId) => {
1369
- return data.getBranches(messageId);
1370
- },
1371
- [data]
1372
- );
1373
- const switchToBranch2 = (0, import_react_use_callback_ref3.useCallbackRef)((messageId) => {
1374
- data.switchToBranch(messageId);
1375
- vercel.setMessages(
1376
- data.getMessages().map(getVercelMessage).filter((m) => m != null)
1377
- );
1378
- });
1379
- const startRun = (0, import_react_use_callback_ref3.useCallbackRef)(async (parentId) => {
1380
- const reloadMaybe = "reload" in vercel ? vercel.reload : void 0;
1381
- if (!reloadMaybe)
1382
- throw new Error(
1383
- "Reload is not supported by Vercel AI SDK's useAssistant."
1384
- );
1385
- const newMessages = sliceMessagesUntil(vercel.messages, parentId);
1386
- vercel.setMessages(newMessages);
1387
- await reloadMaybe();
1388
- });
1389
- const append = (0, import_react_use_callback_ref3.useCallbackRef)(async (message) => {
1390
- if (message.content.length !== 1 || message.content[0]?.type !== "text")
1391
- throw new Error("Only text content is supported by Vercel AI SDK.");
1392
- const newMessages = sliceMessagesUntil(vercel.messages, message.parentId);
1393
- vercel.setMessages(newMessages);
1394
- await vercel.append({
1395
- role: "user",
1396
- content: message.content[0].text
1397
- });
1398
- });
1399
- const cancelRun2 = (0, import_react_use_callback_ref3.useCallbackRef)(() => {
1400
- const lastMessage = vercel.messages.at(-1);
1401
- vercel.stop();
1402
- if (lastMessage?.role === "user") {
1403
- vercel.setInput(lastMessage.content);
1404
- }
1405
- });
1406
- return (0, import_react25.useMemo)(
1407
- () => ({
1408
- isRunning,
1409
- messages,
1410
- getBranches: getBranches2,
1411
- switchToBranch: switchToBranch2,
1412
- append,
1413
- startRun,
1414
- cancelRun: cancelRun2
1415
- }),
1416
- [
1417
- isRunning,
1418
- messages,
1419
- getBranches2,
1420
- switchToBranch2,
1421
- append,
1422
- startRun,
1423
- cancelRun2
1424
- ]
1425
- );
1456
+ return converter.convertMessages(convertCallback, vercel.messages);
1457
+ }, [isRunning, vercel.messages, converter]);
1458
+ (0, import_react30.useEffect)(() => {
1459
+ runtime.updateData(isRunning, messages);
1460
+ }, [runtime, isRunning, messages]);
1461
+ return runtime;
1426
1462
  };
1427
1463
 
1428
1464
  // src/adapters/vercel/VercelAIAssistantProvider.tsx
1429
- var import_jsx_runtime21 = require("react/jsx-runtime");
1465
+ var import_jsx_runtime22 = require("react/jsx-runtime");
1430
1466
  var VercelAIAssistantProvider = ({
1431
1467
  children,
1432
1468
  ...rest
1433
1469
  }) => {
1434
- const context = useDummyAIAssistantContext();
1435
1470
  const vercel = "chat" in rest ? rest.chat : rest.assistant;
1436
- const threadState = useVercelAIThreadState(vercel);
1437
- (0, import_react26.useMemo)(() => {
1438
- context.useThread.setState(threadState, true);
1439
- }, [context, threadState]);
1440
- (0, import_react26.useMemo)(() => {
1441
- context.useComposer.setState({
1471
+ const runtime = useVercelUseChatRuntime(vercel);
1472
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(AssistantProvider, { runtime, children: [
1473
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ComposerSync, { vercel }),
1474
+ children
1475
+ ] });
1476
+ };
1477
+ var ComposerSync = ({
1478
+ vercel
1479
+ }) => {
1480
+ const { useComposer } = useAssistantContext();
1481
+ (0, import_react31.useEffect)(() => {
1482
+ useComposer.setState({
1442
1483
  value: vercel.input,
1443
1484
  setValue: vercel.setInput
1444
1485
  });
1445
- }, [context, vercel.input, vercel.setInput]);
1446
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(AssistantContext.Provider, { value: context, children });
1486
+ }, [useComposer, vercel.input, vercel.setInput]);
1487
+ return null;
1447
1488
  };
1448
1489
 
1449
- // src/adapters/vercel/VercelRSCAssistantProvider.tsx
1450
- var import_react27 = require("react");
1451
- var import_jsx_runtime22 = require("react/jsx-runtime");
1490
+ // src/adapters/core/vercel-rsc/useVercelRSCRuntime.tsx
1491
+ var import_react32 = require("react");
1492
+
1493
+ // src/adapters/core/vercel-rsc/VercelRSCRuntime.tsx
1494
+ var EMPTY_BRANCHES = Object.freeze([]);
1495
+ var VercelRSCRuntime = class {
1496
+ constructor(adapter) {
1497
+ this.adapter = adapter;
1498
+ }
1499
+ _subscriptions = /* @__PURE__ */ new Set();
1500
+ isRunning = false;
1501
+ messages = [];
1502
+ withRunning = (callback) => {
1503
+ this.isRunning = true;
1504
+ return callback.finally(() => {
1505
+ this.isRunning = false;
1506
+ });
1507
+ };
1508
+ getBranches() {
1509
+ return EMPTY_BRANCHES;
1510
+ }
1511
+ switchToBranch() {
1512
+ throw new Error(
1513
+ "Branch switching is not supported by VercelRSCAssistantProvider."
1514
+ );
1515
+ }
1516
+ async append(message) {
1517
+ if (message.parentId !== (this.messages.at(-1)?.id ?? null)) {
1518
+ if (!this.adapter.edit)
1519
+ throw new Error(
1520
+ "Message editing is not enabled, please provide an edit callback to VercelRSCAssistantProvider."
1521
+ );
1522
+ await this.withRunning(this.adapter.edit(message));
1523
+ } else {
1524
+ await this.withRunning(this.adapter.append(message));
1525
+ }
1526
+ }
1527
+ async startRun(parentId) {
1528
+ if (!this.adapter.reload)
1529
+ throw new Error(
1530
+ "Message reloading is not enabled, please provide a reload callback to VercelRSCAssistantProvider."
1531
+ );
1532
+ await this.withRunning(this.adapter.reload(parentId));
1533
+ }
1534
+ cancelRun() {
1535
+ if (process.env["NODE_ENV"] === "development") {
1536
+ console.warn(
1537
+ "Run cancellation is not supported by VercelRSCAssistantProvider."
1538
+ );
1539
+ }
1540
+ }
1541
+ updateData(messages) {
1542
+ this.messages = messages;
1543
+ for (const callback of this._subscriptions) callback();
1544
+ }
1545
+ subscribe(callback) {
1546
+ this._subscriptions.add(callback);
1547
+ return () => this._subscriptions.delete(callback);
1548
+ }
1549
+ };
1550
+
1551
+ // src/adapters/core/vercel-rsc/useVercelRSCRuntime.tsx
1452
1552
  var vercelToThreadMessage2 = (converter, rawMessage) => {
1453
1553
  const message = converter(rawMessage);
1454
1554
  return {
@@ -1460,220 +1560,61 @@ var vercelToThreadMessage2 = (converter, rawMessage) => {
1460
1560
  [symbolInnerRSCMessage]: rawMessage
1461
1561
  };
1462
1562
  };
1463
- var EMPTY_BRANCHES = [];
1464
- var getBranches = () => {
1465
- return EMPTY_BRANCHES;
1466
- };
1467
- var switchToBranch = () => {
1468
- throw new Error(
1469
- "Branch switching is not supported by VercelRSCAssistantProvider."
1470
- );
1471
- };
1472
- var cancelRun = () => {
1473
- if (process.env["NODE_ENV"] === "development") {
1474
- console.warn(
1475
- "Run cancellation is not supported by VercelRSCAssistantProvider."
1476
- );
1477
- }
1478
- };
1479
- var VercelRSCAssistantProvider = ({
1480
- children,
1481
- convertMessage,
1482
- messages: vercelMessages,
1483
- append: appendCallback,
1484
- edit,
1485
- reload
1486
- }) => {
1487
- const context = useDummyAIAssistantContext();
1488
- const [isRunning, setIsRunning] = (0, import_react27.useState)(false);
1489
- const withRunning = (0, import_react27.useCallback)((callback) => {
1490
- setIsRunning(true);
1491
- return callback.finally(() => setIsRunning(false));
1492
- }, []);
1493
- const [converter, convertCallback] = (0, import_react27.useMemo)(() => {
1494
- const rscConverter = convertMessage ?? ((m) => m);
1563
+ var useVercelRSCRuntime = (adapter) => {
1564
+ const [runtime] = (0, import_react32.useState)(() => new VercelRSCRuntime(adapter));
1565
+ (0, import_react32.useInsertionEffect)(() => {
1566
+ runtime.adapter = adapter;
1567
+ });
1568
+ const [converter, convertCallback] = (0, import_react32.useMemo)(() => {
1569
+ const rscConverter = adapter.convertMessage ?? ((m) => m);
1495
1570
  const convertCallback2 = (m, cache) => {
1496
1571
  if (cache) return cache;
1497
1572
  return vercelToThreadMessage2(rscConverter, m);
1498
1573
  };
1499
1574
  return [new ThreadMessageConverter(), convertCallback2];
1500
- }, [convertMessage]);
1501
- const messages = (0, import_react27.useMemo)(() => {
1502
- return converter.convertMessages(convertCallback, vercelMessages);
1503
- }, [converter, convertCallback, vercelMessages]);
1504
- const append = (0, import_react27.useCallback)(
1505
- async (message) => {
1506
- if (message.parentId !== (context.useThread.getState().messages.at(-1)?.id ?? null)) {
1507
- if (!edit)
1508
- throw new Error(
1509
- "Message editing is not enabled, please provide an edit callback to VercelRSCAssistantProvider."
1510
- );
1511
- await withRunning(edit(message));
1512
- } else {
1513
- await withRunning(appendCallback(message));
1514
- }
1515
- },
1516
- [context, withRunning, appendCallback, edit]
1517
- );
1518
- const startRun = (0, import_react27.useCallback)(
1519
- async (parentId) => {
1520
- if (!reload)
1521
- throw new Error(
1522
- "Message reloading is not enabled, please provide a reload callback to VercelRSCAssistantProvider."
1523
- );
1524
- await withRunning(reload(parentId));
1525
- },
1526
- [withRunning, reload]
1527
- );
1528
- (0, import_react27.useMemo)(() => {
1529
- context.useThread.setState(
1530
- {
1531
- messages,
1532
- isRunning,
1533
- getBranches,
1534
- switchToBranch,
1535
- append,
1536
- startRun,
1537
- cancelRun
1538
- },
1539
- true
1575
+ }, [adapter.convertMessage]);
1576
+ (0, import_react32.useEffect)(() => {
1577
+ runtime.updateData(
1578
+ converter.convertMessages(convertCallback, adapter.messages)
1540
1579
  );
1541
- }, [context, messages, isRunning, append, startRun]);
1542
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(AssistantContext.Provider, { value: context, children });
1543
- };
1544
-
1545
- // src/adapters/core/utils/useAssistantContext.tsx
1546
- var import_react28 = require("react");
1547
- var import_zustand6 = require("zustand");
1548
-
1549
- // src/adapters/core/utils/AssistantMessageRepository.tsx
1550
- var AssistantMessageRepository = class {
1551
- constructor(flushCallback) {
1552
- this.flushCallback = flushCallback;
1553
- }
1554
- repository = new MessageRepository();
1555
- getBranches(messageId) {
1556
- return this.repository.getBranches(messageId);
1557
- }
1558
- withModifications(callback) {
1559
- const res = callback(this.repository);
1560
- this.flushCallback(this.repository.getMessages());
1561
- return res;
1562
- }
1563
- };
1564
-
1565
- // src/adapters/core/utils/useAssistantContext.tsx
1566
- var makeThreadStore = (runtimeRef) => {
1567
- const repository = new AssistantMessageRepository((messages) => {
1568
- useThread.setState({ messages });
1569
- });
1570
- const useThread = (0, import_zustand6.create)(() => ({
1571
- messages: [],
1572
- isRunning: false,
1573
- getBranches: (messageId) => repository.getBranches(messageId),
1574
- switchToBranch: (branchId) => {
1575
- repository.withModifications((repository2) => {
1576
- repository2.switchToBranch(branchId);
1577
- });
1578
- },
1579
- startRun: async (parentId) => {
1580
- const optimisticId = repository.withModifications((repository2) => {
1581
- const optimisticId2 = repository2.appendOptimisticMessage(parentId, {
1582
- role: "assistant",
1583
- content: [{ type: "text", text: "" }]
1584
- });
1585
- repository2.resetHead(optimisticId2);
1586
- return optimisticId2;
1587
- });
1588
- const { id } = await runtimeRef.current.startRun(parentId);
1589
- repository.withModifications((repository2) => {
1590
- repository2.deleteMessage(optimisticId, id);
1591
- });
1592
- },
1593
- append: async (message) => {
1594
- const [parentOptimisticId, optimisticId] = repository.withModifications(
1595
- (repository2) => {
1596
- const parentOptimisticId2 = repository2.appendOptimisticMessage(
1597
- message.parentId,
1598
- {
1599
- role: "user",
1600
- content: message.content
1601
- }
1602
- );
1603
- const optimisticId2 = repository2.appendOptimisticMessage(
1604
- parentOptimisticId2,
1605
- {
1606
- role: "assistant",
1607
- content: [{ type: "text", text: "" }]
1608
- }
1609
- );
1610
- repository2.resetHead(optimisticId2);
1611
- return [parentOptimisticId2, optimisticId2];
1612
- }
1613
- );
1614
- const { parentId, id } = await runtimeRef.current.append(message);
1615
- repository.withModifications((repository2) => {
1616
- repository2.deleteMessage(parentOptimisticId, parentId);
1617
- repository2.deleteMessage(optimisticId, id);
1618
- });
1619
- },
1620
- cancelRun: () => runtimeRef.current.cancelRun()
1621
- }));
1622
- const onNewMessage = (parentId, message) => {
1623
- repository.withModifications((repository2) => {
1624
- repository2.addOrUpdateMessage(parentId, message);
1625
- });
1626
- };
1627
- const onRunningChange = (isRunning) => {
1628
- useThread.setState({ isRunning });
1629
- };
1630
- return {
1631
- useThread,
1632
- onNewMessage,
1633
- onRunningChange
1634
- };
1635
- };
1636
- var useAssistantContext2 = (runtime) => {
1637
- const runtimeRef = (0, import_react28.useRef)(runtime);
1638
- runtimeRef.current = runtime;
1639
- const [{ context, onNewMessage, onRunningChange }] = (0, import_react28.useState)(() => {
1640
- const { useThread, onNewMessage: onNewMessage2, onRunningChange: onRunningChange2 } = makeThreadStore(runtimeRef);
1641
- const useViewport = makeViewportStore();
1642
- const useComposer = makeThreadComposerStore(useThread);
1643
- return {
1644
- context: { useViewport, useThread, useComposer },
1645
- onNewMessage: onNewMessage2,
1646
- onRunningChange: onRunningChange2
1647
- };
1648
- });
1649
- (0, import_react28.useEffect)(() => {
1650
- return runtime.subscribeToMessageUpdates(onNewMessage);
1651
- }, [runtime, onNewMessage]);
1652
- (0, import_react28.useEffect)(() => {
1653
- return runtime.subscribeToStatusUpdates(onRunningChange);
1654
- }, [runtime, onRunningChange]);
1655
- return context;
1580
+ }, [runtime, converter, convertCallback, adapter.messages]);
1581
+ return runtime;
1656
1582
  };
1657
1583
 
1658
- // src/adapters/core/AssistantProvider.tsx
1584
+ // src/adapters/vercel/VercelRSCAssistantProvider.tsx
1659
1585
  var import_jsx_runtime23 = require("react/jsx-runtime");
1660
- var AssistantProvider = ({ children, runtime }) => {
1661
- const context = useAssistantContext2(runtime);
1662
- return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(AssistantContext.Provider, { value: context, children });
1586
+ var VercelRSCAssistantProvider = ({
1587
+ children,
1588
+ ...adapter
1589
+ }) => {
1590
+ const runtime = useVercelRSCRuntime(adapter);
1591
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(AssistantProvider, { runtime, children });
1663
1592
  };
1664
1593
 
1665
1594
  // src/adapters/core/local/useLocalRuntime.tsx
1666
- var import_react29 = require("react");
1595
+ var import_react33 = require("react");
1667
1596
 
1668
1597
  // src/adapters/core/local/LocalRuntime.tsx
1669
1598
  var LocalRuntime = class {
1670
1599
  constructor(adapter) {
1671
1600
  this.adapter = adapter;
1672
1601
  }
1673
- _messageUpdateCallbacks = /* @__PURE__ */ new Set();
1674
- _statusUpdateCallbacks = /* @__PURE__ */ new Set();
1602
+ _subscriptions = /* @__PURE__ */ new Set();
1675
1603
  abortController = null;
1676
1604
  repository = new MessageRepository();
1605
+ get messages() {
1606
+ return this.repository.getMessages();
1607
+ }
1608
+ get isRunning() {
1609
+ return this.abortController != null;
1610
+ }
1611
+ getBranches(messageId) {
1612
+ return this.repository.getBranches(messageId);
1613
+ }
1614
+ switchToBranch(branchId) {
1615
+ this.repository.switchToBranch(branchId);
1616
+ this.notifySubscribers();
1617
+ }
1677
1618
  async append(message) {
1678
1619
  const userMessageId = generateId();
1679
1620
  const userMessage = {
@@ -1682,9 +1623,8 @@ var LocalRuntime = class {
1682
1623
  content: message.content,
1683
1624
  createdAt: /* @__PURE__ */ new Date()
1684
1625
  };
1685
- this.addOrUpdateMessage(message.parentId, userMessage);
1686
- const { id } = await this.startRun(userMessageId);
1687
- return { parentId: userMessageId, id };
1626
+ this.repository.addOrUpdateMessage(message.parentId, userMessage);
1627
+ await this.startRun(userMessageId);
1688
1628
  }
1689
1629
  async startRun(parentId) {
1690
1630
  const id = generateId();
@@ -1697,59 +1637,52 @@ var LocalRuntime = class {
1697
1637
  content: [{ type: "text", text: "" }],
1698
1638
  createdAt: /* @__PURE__ */ new Date()
1699
1639
  };
1700
- this.addOrUpdateMessage(parentId, message);
1701
- void this.run(parentId, messages, message);
1702
- return { id };
1703
- }
1704
- addOrUpdateMessage(parentId, message) {
1705
- const clone = { ...message };
1706
- this.repository.addOrUpdateMessage(parentId, clone);
1707
- for (const callback of this._messageUpdateCallbacks)
1708
- callback(parentId, clone);
1709
- }
1710
- async run(parentId, messages, message) {
1711
- this.cancelRun();
1712
- for (const callback of this._statusUpdateCallbacks) callback(true);
1640
+ this.repository.addOrUpdateMessage(parentId, { ...message });
1641
+ this.abortController?.abort();
1713
1642
  this.abortController = new AbortController();
1643
+ this.notifySubscribers();
1714
1644
  try {
1715
1645
  await this.adapter.run({
1716
1646
  messages,
1717
1647
  abortSignal: this.abortController.signal,
1718
1648
  onUpdate: ({ content }) => {
1719
1649
  message.content = content;
1720
- this.addOrUpdateMessage(parentId, message);
1650
+ this.repository.addOrUpdateMessage(parentId, { ...message });
1651
+ this.notifySubscribers();
1721
1652
  }
1722
1653
  });
1723
1654
  message.status = "done";
1724
- this.addOrUpdateMessage(parentId, message);
1655
+ this.repository.addOrUpdateMessage(parentId, { ...message });
1725
1656
  } catch (e) {
1726
1657
  message.status = "error";
1727
- this.addOrUpdateMessage(parentId, message);
1658
+ this.repository.addOrUpdateMessage(parentId, { ...message });
1728
1659
  console.error(e);
1729
1660
  } finally {
1730
- this.cancelRun();
1661
+ this.abortController = null;
1662
+ this.notifySubscribers();
1731
1663
  }
1732
1664
  }
1733
1665
  cancelRun() {
1734
1666
  if (!this.abortController) return;
1735
1667
  this.abortController.abort();
1736
1668
  this.abortController = null;
1737
- for (const callback of this._statusUpdateCallbacks) callback(false);
1669
+ this.notifySubscribers();
1738
1670
  }
1739
- subscribeToMessageUpdates(callback) {
1740
- this._messageUpdateCallbacks.add(callback);
1741
- return () => this._messageUpdateCallbacks.delete(callback);
1671
+ notifySubscribers() {
1672
+ for (const callback of this._subscriptions) callback();
1742
1673
  }
1743
- subscribeToStatusUpdates(callback) {
1744
- this._statusUpdateCallbacks.add(callback);
1745
- return () => this._statusUpdateCallbacks.delete(callback);
1674
+ subscribe(callback) {
1675
+ this._subscriptions.add(callback);
1676
+ return () => this._subscriptions.delete(callback);
1746
1677
  }
1747
1678
  };
1748
1679
 
1749
1680
  // src/adapters/core/local/useLocalRuntime.tsx
1750
1681
  var useLocalRuntime = (adapter) => {
1751
- const [runtime] = (0, import_react29.useState)(() => new LocalRuntime(adapter));
1752
- runtime.adapter = adapter;
1682
+ const [runtime] = (0, import_react33.useState)(() => new LocalRuntime(adapter));
1683
+ (0, import_react33.useInsertionEffect)(() => {
1684
+ runtime.adapter = adapter;
1685
+ });
1753
1686
  return runtime;
1754
1687
  };
1755
1688