@assistant-ui/react 0.0.18 → 0.0.20

Sign up to get free protection for your applications and to get access to all the features.
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