@assistant-ui/react 0.0.17 → 0.0.19

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
@@ -1074,9 +1087,9 @@ var useDummyAIAssistantContext = () => {
1074
1087
 
1075
1088
  // src/adapters/vercel/useVercelAIThreadState.tsx
1076
1089
  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";
1090
+ import { useCallback as useCallback7, useMemo as useMemo5, useRef as useRef4, useState as useState4 } from "react";
1078
1091
 
1079
- // src/adapters/MessageRepository.tsx
1092
+ // src/adapters/idUtils.tsx
1080
1093
  import { customAlphabet } from "nanoid/non-secure";
1081
1094
  var generateId = customAlphabet(
1082
1095
  "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",
@@ -1084,6 +1097,8 @@ var generateId = customAlphabet(
1084
1097
  );
1085
1098
  var optimisticPrefix = "__optimistic__";
1086
1099
  var generateOptimisticId = () => `${optimisticPrefix}${generateId()}`;
1100
+
1101
+ // src/adapters/MessageRepository.tsx
1087
1102
  var findHead = (message) => {
1088
1103
  if (message.next) return findHead(message.next);
1089
1104
  return message;
@@ -1095,15 +1110,6 @@ var MessageRepository = class {
1095
1110
  root = {
1096
1111
  children: []
1097
1112
  };
1098
- getFallbackChild(p) {
1099
- const childId = p.children.at(-1);
1100
- const child = childId ? this.messages.get(childId) : null;
1101
- if (child === void 0)
1102
- throw new Error(
1103
- "MessageRepository(getFallbackChild): Child message not found. This is likely an internal bug in assistant-ui."
1104
- );
1105
- return child;
1106
- }
1107
1113
  performOp(newParent, child, operation) {
1108
1114
  const parentOrRoot = child.prev ?? this.root;
1109
1115
  const newParentOrRoot = newParent ?? this.root;
@@ -1113,7 +1119,14 @@ var MessageRepository = class {
1113
1119
  (m) => m !== child.current.id
1114
1120
  );
1115
1121
  if (child.prev?.next === child) {
1116
- child.prev.next = this.getFallbackChild(child.prev);
1122
+ const fallbackId = child.prev.children.at(-1);
1123
+ const fallback = fallbackId ? this.messages.get(fallbackId) : null;
1124
+ if (fallback === void 0) {
1125
+ throw new Error(
1126
+ "MessageRepository(performOp/cut): Fallback sibling message not found. This is likely an internal bug in assistant-ui."
1127
+ );
1128
+ }
1129
+ child.prev.next = fallback;
1117
1130
  }
1118
1131
  }
1119
1132
  if (operation !== "cut") {
@@ -1172,14 +1185,14 @@ var MessageRepository = class {
1172
1185
  });
1173
1186
  return optimisticId;
1174
1187
  }
1175
- deleteMessage(messageId, newParentId) {
1188
+ deleteMessage(messageId, replacementId) {
1176
1189
  const message = this.messages.get(messageId);
1177
- const newParent = newParentId ? this.messages.get(newParentId) : null;
1190
+ const replacement = replacementId ? this.messages.get(replacementId) : null;
1178
1191
  if (!message)
1179
1192
  throw new Error(
1180
1193
  "MessageRepository(deleteMessage): Optimistic message not found. This is likely an internal bug in assistant-ui."
1181
1194
  );
1182
- if (newParent === void 0)
1195
+ if (replacement === void 0)
1183
1196
  throw new Error(
1184
1197
  "MessageRepository(deleteMessage): New message not found. This is likely an internal bug in assistant-ui."
1185
1198
  );
@@ -1189,11 +1202,11 @@ var MessageRepository = class {
1189
1202
  throw new Error(
1190
1203
  "MessageRepository(deleteMessage): Child message not found. This is likely an internal bug in assistant-ui."
1191
1204
  );
1192
- this.performOp(newParent, childMessage, "relink");
1205
+ this.performOp(replacement, childMessage, "relink");
1193
1206
  }
1194
1207
  this.messages.delete(messageId);
1195
1208
  if (this.head === message) {
1196
- this.head = this.getFallbackChild(message.prev ?? this.root);
1209
+ this.head = replacement;
1197
1210
  }
1198
1211
  this.performOp(null, message, "cut");
1199
1212
  }
@@ -1236,17 +1249,13 @@ var MessageRepository = class {
1236
1249
  }
1237
1250
  };
1238
1251
 
1239
- // src/adapters/ThreadMessageConverter.tsx
1252
+ // src/adapters/ThreadMessageConverter.ts
1240
1253
  var ThreadMessageConverter = class {
1241
- constructor(converter) {
1242
- this.converter = converter;
1243
- }
1244
1254
  cache = /* @__PURE__ */ new WeakMap();
1245
- convertMessages(messages) {
1255
+ convertMessages(converter, messages) {
1246
1256
  return messages.map((m) => {
1247
1257
  const cached = this.cache.get(m);
1248
- if (cached) return cached;
1249
- const newMessage = this.converter(m);
1258
+ const newMessage = converter(m, cached);
1250
1259
  this.cache.set(m, newMessage);
1251
1260
  return newMessage;
1252
1261
  });
@@ -1319,16 +1328,17 @@ var getIsRunning = (vercel) => {
1319
1328
  var useVercelAIThreadState = (vercel) => {
1320
1329
  const [data] = useState4(() => new MessageRepository());
1321
1330
  const isRunning = getIsRunning(vercel);
1322
- const convertCallback = useCallbackRef3((message) => {
1323
- return vercelToThreadMessage(
1324
- message,
1325
- vercel.messages.at(-1) === message && isRunning ? "in_progress" : "done"
1326
- );
1327
- });
1328
- const converter = new ThreadMessageConverter(convertCallback);
1331
+ const converter = useMemo5(() => new ThreadMessageConverter(), []);
1329
1332
  const assistantOptimisticIdRef = useRef4(null);
1330
1333
  const messages = useMemo5(() => {
1331
- const vm = converter.convertMessages(vercel.messages);
1334
+ const lastMessageId = vercel.messages.at(-1)?.id;
1335
+ const convertCallback = (message, cache) => {
1336
+ const status = lastMessageId === message.id && isRunning ? "in_progress" : "done";
1337
+ if (cache && (cache.role === "user" || cache.status === status))
1338
+ return cache;
1339
+ return vercelToThreadMessage(message, status);
1340
+ };
1341
+ const vm = converter.convertMessages(convertCallback, vercel.messages);
1332
1342
  for (let i = 0; i < vm.length; i++) {
1333
1343
  const message = vm[i];
1334
1344
  const parent = vm[i - 1];
@@ -1350,7 +1360,7 @@ var useVercelAIThreadState = (vercel) => {
1350
1360
  data.resetHead(assistantOptimisticIdRef.current ?? vm.at(-1)?.id ?? null);
1351
1361
  return data.getMessages();
1352
1362
  }, [converter, data, isRunning, vercel.messages]);
1353
- const getBranches2 = useCallback2(
1363
+ const getBranches2 = useCallback7(
1354
1364
  (messageId) => {
1355
1365
  return data.getBranches(messageId);
1356
1366
  },
@@ -1434,7 +1444,7 @@ var VercelAIAssistantProvider = ({
1434
1444
 
1435
1445
  // src/adapters/vercel/VercelRSCAssistantProvider.tsx
1436
1446
  import {
1437
- useCallback as useCallback3,
1447
+ useCallback as useCallback8,
1438
1448
  useMemo as useMemo7,
1439
1449
  useState as useState5
1440
1450
  } from "react";
@@ -1476,20 +1486,22 @@ var VercelRSCAssistantProvider = ({
1476
1486
  }) => {
1477
1487
  const context = useDummyAIAssistantContext();
1478
1488
  const [isRunning, setIsRunning] = useState5(false);
1479
- const withRunning = useCallback3((callback) => {
1489
+ const withRunning = useCallback8((callback) => {
1480
1490
  setIsRunning(true);
1481
1491
  return callback.finally(() => setIsRunning(false));
1482
1492
  }, []);
1483
- const converter = useMemo7(() => {
1493
+ const [converter, convertCallback] = useMemo7(() => {
1484
1494
  const rscConverter = convertMessage ?? ((m) => m);
1485
- return new ThreadMessageConverter((m) => {
1495
+ const convertCallback2 = (m, cache) => {
1496
+ if (cache) return cache;
1486
1497
  return vercelToThreadMessage2(rscConverter, m);
1487
- });
1498
+ };
1499
+ return [new ThreadMessageConverter(), convertCallback2];
1488
1500
  }, [convertMessage]);
1489
1501
  const messages = useMemo7(() => {
1490
- return converter.convertMessages(vercelMessages);
1491
- }, [converter, vercelMessages]);
1492
- const append = useCallback3(
1502
+ return converter.convertMessages(convertCallback, vercelMessages);
1503
+ }, [converter, convertCallback, vercelMessages]);
1504
+ const append = useCallback8(
1493
1505
  async (message) => {
1494
1506
  if (message.parentId !== (context.useThread.getState().messages.at(-1)?.id ?? null)) {
1495
1507
  if (!edit)
@@ -1503,7 +1515,7 @@ var VercelRSCAssistantProvider = ({
1503
1515
  },
1504
1516
  [context, withRunning, appendCallback, edit]
1505
1517
  );
1506
- const startRun = useCallback3(
1518
+ const startRun = useCallback8(
1507
1519
  async (parentId) => {
1508
1520
  if (!reload)
1509
1521
  throw new Error(
@@ -1529,6 +1541,266 @@ var VercelRSCAssistantProvider = ({
1529
1541
  }, [context, messages, isRunning, append, startRun]);
1530
1542
  return /* @__PURE__ */ jsx22(AssistantContext.Provider, { value: context, children });
1531
1543
  };
1544
+
1545
+ // src/adapters/core/utils/useAssistantContext.tsx
1546
+ import {
1547
+ useEffect as useEffect4,
1548
+ useInsertionEffect,
1549
+ useRef as useRef5,
1550
+ useState as useState6
1551
+ } from "react";
1552
+ import { create as create6 } from "zustand";
1553
+
1554
+ // src/adapters/core/utils/AssistantMessageRepository.tsx
1555
+ var AssistantMessageRepository = class {
1556
+ constructor(flushCallback) {
1557
+ this.flushCallback = flushCallback;
1558
+ }
1559
+ repository = new MessageRepository();
1560
+ getBranches(messageId) {
1561
+ return this.repository.getBranches(messageId);
1562
+ }
1563
+ withModifications(callback) {
1564
+ const res = callback(this.repository);
1565
+ this.flushCallback(this.repository.getMessages());
1566
+ return res;
1567
+ }
1568
+ };
1569
+
1570
+ // src/adapters/core/utils/useAssistantContext.tsx
1571
+ var makeThreadStore = (runtimeRef) => {
1572
+ const repository = new AssistantMessageRepository((messages) => {
1573
+ useThread.setState({ messages });
1574
+ });
1575
+ const useThread = create6(() => ({
1576
+ messages: [],
1577
+ isRunning: false,
1578
+ getBranches: (messageId) => repository.getBranches(messageId),
1579
+ switchToBranch: (branchId) => {
1580
+ repository.withModifications((repository2) => {
1581
+ repository2.switchToBranch(branchId);
1582
+ });
1583
+ },
1584
+ startRun: async (parentId) => {
1585
+ const optimisticId = repository.withModifications((repository2) => {
1586
+ const optimisticId2 = repository2.appendOptimisticMessage(parentId, {
1587
+ role: "assistant",
1588
+ content: [{ type: "text", text: "" }]
1589
+ });
1590
+ repository2.resetHead(optimisticId2);
1591
+ return optimisticId2;
1592
+ });
1593
+ const { id } = await runtimeRef.current.startRun(parentId);
1594
+ repository.withModifications((repository2) => {
1595
+ repository2.deleteMessage(optimisticId, id);
1596
+ });
1597
+ },
1598
+ append: async (message) => {
1599
+ const [parentOptimisticId, optimisticId] = repository.withModifications(
1600
+ (repository2) => {
1601
+ const parentOptimisticId2 = repository2.appendOptimisticMessage(
1602
+ message.parentId,
1603
+ {
1604
+ role: "user",
1605
+ content: message.content
1606
+ }
1607
+ );
1608
+ const optimisticId2 = repository2.appendOptimisticMessage(
1609
+ parentOptimisticId2,
1610
+ {
1611
+ role: "assistant",
1612
+ content: [{ type: "text", text: "" }]
1613
+ }
1614
+ );
1615
+ repository2.resetHead(optimisticId2);
1616
+ return [parentOptimisticId2, optimisticId2];
1617
+ }
1618
+ );
1619
+ const { parentId, id } = await runtimeRef.current.append(message);
1620
+ repository.withModifications((repository2) => {
1621
+ repository2.deleteMessage(parentOptimisticId, parentId);
1622
+ repository2.deleteMessage(optimisticId, id);
1623
+ });
1624
+ },
1625
+ cancelRun: () => runtimeRef.current.cancelRun()
1626
+ }));
1627
+ const onNewMessage = (parentId, message) => {
1628
+ repository.withModifications((repository2) => {
1629
+ repository2.addOrUpdateMessage(parentId, message);
1630
+ });
1631
+ };
1632
+ const onRunningChange = (isRunning) => {
1633
+ useThread.setState({ isRunning });
1634
+ };
1635
+ return {
1636
+ useThread,
1637
+ onNewMessage,
1638
+ onRunningChange
1639
+ };
1640
+ };
1641
+ var useAssistantContext2 = (runtime) => {
1642
+ const runtimeRef = useRef5(runtime);
1643
+ useInsertionEffect(() => {
1644
+ runtimeRef.current = runtime;
1645
+ });
1646
+ const [{ context, onNewMessage, onRunningChange }] = useState6(() => {
1647
+ const { useThread, onNewMessage: onNewMessage2, onRunningChange: onRunningChange2 } = makeThreadStore(runtimeRef);
1648
+ const useViewport = makeViewportStore();
1649
+ const useComposer = makeThreadComposerStore(useThread);
1650
+ return {
1651
+ context: { useViewport, useThread, useComposer },
1652
+ onNewMessage: onNewMessage2,
1653
+ onRunningChange: onRunningChange2
1654
+ };
1655
+ });
1656
+ useEffect4(() => {
1657
+ return runtime.subscribeToMessageUpdates(onNewMessage);
1658
+ }, [runtime, onNewMessage]);
1659
+ useEffect4(() => {
1660
+ return runtime.subscribeToStatusUpdates(onRunningChange);
1661
+ }, [runtime, onRunningChange]);
1662
+ return context;
1663
+ };
1664
+
1665
+ // src/adapters/core/AssistantProvider.tsx
1666
+ import { jsx as jsx23 } from "react/jsx-runtime";
1667
+ var AssistantProvider = ({ children, runtime }) => {
1668
+ const context = useAssistantContext2(runtime);
1669
+ return /* @__PURE__ */ jsx23(AssistantContext.Provider, { value: context, children });
1670
+ };
1671
+
1672
+ // src/adapters/core/local/useLocalRuntime.tsx
1673
+ import { useState as useState7 } from "react";
1674
+
1675
+ // src/adapters/core/local/LocalRuntime.tsx
1676
+ var LocalRuntime = class {
1677
+ constructor(adapter) {
1678
+ this.adapter = adapter;
1679
+ }
1680
+ _messageUpdateCallbacks = /* @__PURE__ */ new Set();
1681
+ _statusUpdateCallbacks = /* @__PURE__ */ new Set();
1682
+ abortController = null;
1683
+ repository = new MessageRepository();
1684
+ async append(message) {
1685
+ const userMessageId = generateId();
1686
+ const userMessage = {
1687
+ id: userMessageId,
1688
+ role: "user",
1689
+ content: message.content,
1690
+ createdAt: /* @__PURE__ */ new Date()
1691
+ };
1692
+ this.addOrUpdateMessage(message.parentId, userMessage);
1693
+ const { id } = await this.startRun(userMessageId);
1694
+ return { parentId: userMessageId, id };
1695
+ }
1696
+ async startRun(parentId) {
1697
+ const id = generateId();
1698
+ this.repository.resetHead(parentId);
1699
+ const messages = this.repository.getMessages();
1700
+ const message = {
1701
+ id,
1702
+ role: "assistant",
1703
+ status: "in_progress",
1704
+ content: [{ type: "text", text: "" }],
1705
+ createdAt: /* @__PURE__ */ new Date()
1706
+ };
1707
+ this.addOrUpdateMessage(parentId, message);
1708
+ void this.run(parentId, messages, message);
1709
+ return { id };
1710
+ }
1711
+ addOrUpdateMessage(parentId, message) {
1712
+ const clone = { ...message };
1713
+ this.repository.addOrUpdateMessage(parentId, clone);
1714
+ for (const callback of this._messageUpdateCallbacks)
1715
+ callback(parentId, clone);
1716
+ }
1717
+ async run(parentId, messages, message) {
1718
+ this.cancelRun();
1719
+ for (const callback of this._statusUpdateCallbacks) callback(true);
1720
+ this.abortController = new AbortController();
1721
+ try {
1722
+ await this.adapter.run({
1723
+ messages,
1724
+ abortSignal: this.abortController.signal,
1725
+ onUpdate: ({ content }) => {
1726
+ message.content = content;
1727
+ this.addOrUpdateMessage(parentId, message);
1728
+ }
1729
+ });
1730
+ message.status = "done";
1731
+ this.addOrUpdateMessage(parentId, message);
1732
+ } catch (e) {
1733
+ message.status = "error";
1734
+ this.addOrUpdateMessage(parentId, message);
1735
+ console.error(e);
1736
+ } finally {
1737
+ this.cancelRun();
1738
+ }
1739
+ }
1740
+ cancelRun() {
1741
+ if (!this.abortController) return;
1742
+ this.abortController.abort();
1743
+ this.abortController = null;
1744
+ for (const callback of this._statusUpdateCallbacks) callback(false);
1745
+ }
1746
+ subscribeToMessageUpdates(callback) {
1747
+ this._messageUpdateCallbacks.add(callback);
1748
+ return () => this._messageUpdateCallbacks.delete(callback);
1749
+ }
1750
+ subscribeToStatusUpdates(callback) {
1751
+ this._statusUpdateCallbacks.add(callback);
1752
+ return () => this._statusUpdateCallbacks.delete(callback);
1753
+ }
1754
+ };
1755
+
1756
+ // src/adapters/core/local/useLocalRuntime.tsx
1757
+ var useLocalRuntime = (adapter) => {
1758
+ const [runtime] = useState7(() => new LocalRuntime(adapter));
1759
+ runtime.adapter = adapter;
1760
+ return runtime;
1761
+ };
1762
+
1763
+ // src/adapters/core/local/vercel/VercelModelAdapter.tsx
1764
+ import { streamText } from "ai";
1765
+ var VercelModelAdapter = class {
1766
+ constructor(model) {
1767
+ this.model = model;
1768
+ }
1769
+ async run({ messages, abortSignal, onUpdate }) {
1770
+ const { fullStream } = await streamText({
1771
+ model: this.model,
1772
+ abortSignal,
1773
+ messages: messages.map((m) => ({
1774
+ role: m.role,
1775
+ content: m.content.filter((c) => c.type !== "ui")
1776
+ }))
1777
+ });
1778
+ const content = [];
1779
+ for await (const aiPart of fullStream) {
1780
+ switch (aiPart.type) {
1781
+ case "text-delta": {
1782
+ let part = content.at(-1);
1783
+ if (!part || part.type !== "text") {
1784
+ part = { type: "text", text: "" };
1785
+ content.push(part);
1786
+ }
1787
+ part.text += aiPart.textDelta;
1788
+ break;
1789
+ }
1790
+ case "tool-call": {
1791
+ content.push({
1792
+ type: "tool-call",
1793
+ name: aiPart.toolName,
1794
+ args: aiPart.args
1795
+ });
1796
+ break;
1797
+ }
1798
+ }
1799
+ onUpdate({ content });
1800
+ }
1801
+ return { content };
1802
+ }
1803
+ };
1532
1804
  export {
1533
1805
  actionBar_exports as ActionBarPrimitive,
1534
1806
  branchPicker_exports as BranchPickerPrimitive,
@@ -1538,8 +1810,11 @@ export {
1538
1810
  thread_exports as ThreadPrimitive,
1539
1811
  VercelAIAssistantProvider,
1540
1812
  VercelRSCAssistantProvider,
1813
+ AssistantProvider as unstable_AssistantProvider,
1814
+ VercelModelAdapter as unstable_VercelModelAdapter,
1541
1815
  getVercelMessage as unstable_getVercelMessage,
1542
1816
  getVercelRSCMessage as unstable_getVercelRSCMessage,
1817
+ useLocalRuntime as unstable_useLocalRuntime,
1543
1818
  useMessageContext as unstable_useMessageContext,
1544
1819
  useBeginMessageEdit,
1545
1820
  useCopyMessage,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@assistant-ui/react",
3
- "version": "0.0.17",
3
+ "version": "0.0.19",
4
4
  "license": "MIT",
5
5
  "exports": {
6
6
  ".": {