@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.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