@assistant-ui/react 0.0.17 → 0.0.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -129,18 +129,22 @@ var ThreadViewport = forwardRef2(({ autoScroll = true, onScroll, children, ...re
129
129
  const ref = useComposedRefs(forwardedRef, divRef);
130
130
  const { useViewport } = useAssistantContext();
131
131
  const firstRenderRef = useRef(true);
132
+ const isScrollingToBottomRef = useRef(false);
132
133
  const lastScrollTop = useRef(0);
133
134
  const scrollToBottom = () => {
134
135
  const div = messagesEndRef.current;
135
136
  if (!div || !autoScroll) return;
136
137
  const behavior = firstRenderRef.current ? "instant" : "auto";
137
138
  firstRenderRef.current = false;
138
- useViewport.setState({ isAtBottom: true });
139
+ isScrollingToBottomRef.current = true;
139
140
  div.scrollIntoView({ behavior });
140
141
  };
141
142
  useOnResizeContent(divRef, () => {
142
- if (!useViewport.getState().isAtBottom) return;
143
- scrollToBottom();
143
+ if (!isScrollingToBottomRef.current && !useViewport.getState().isAtBottom) {
144
+ handleScroll();
145
+ } else {
146
+ scrollToBottom();
147
+ }
144
148
  });
145
149
  useOnScrollToBottom(() => {
146
150
  scrollToBottom();
@@ -152,6 +156,7 @@ var ThreadViewport = forwardRef2(({ autoScroll = true, onScroll, children, ...re
152
156
  const newIsAtBottom = div.scrollHeight - div.scrollTop <= div.clientHeight;
153
157
  if (!newIsAtBottom && lastScrollTop.current < div.scrollTop) {
154
158
  } else if (newIsAtBottom !== isAtBottom) {
159
+ isScrollingToBottomRef.current = false;
155
160
  useViewport.setState({ isAtBottom: newIsAtBottom });
156
161
  }
157
162
  lastScrollTop.current = div.scrollTop;
@@ -818,6 +823,7 @@ __export(branchPicker_exports, {
818
823
  });
819
824
 
820
825
  // src/actions/useGoToNextBranch.tsx
826
+ import { useCallback as useCallback2 } from "react";
821
827
  var useGoToNextBranch = () => {
822
828
  const { useThread } = useAssistantContext();
823
829
  const { useMessage, useComposer } = useMessageContext();
@@ -825,11 +831,12 @@ var useGoToNextBranch = () => {
825
831
  [useMessage, useComposer],
826
832
  (m, c) => c.isEditing || m.branches.indexOf(m.message.id) + 1 >= m.branches.length
827
833
  );
828
- if (disabled) return null;
829
- return () => {
834
+ const callback = useCallback2(() => {
830
835
  const { message, branches } = useMessage.getState();
831
836
  useThread.getState().switchToBranch(branches[branches.indexOf(message.id) + 1]);
832
- };
837
+ }, [useMessage, useThread]);
838
+ if (disabled) return null;
839
+ return callback;
833
840
  };
834
841
 
835
842
  // src/utils/createActionButton.tsx
@@ -861,6 +868,7 @@ var createActionButton = (useActionButton) => {
861
868
  var BranchPickerNext = createActionButton(useGoToNextBranch);
862
869
 
863
870
  // src/actions/useGoToPreviousBranch.tsx
871
+ import { useCallback as useCallback3 } from "react";
864
872
  var useGoToPreviousBranch = () => {
865
873
  const { useThread } = useAssistantContext();
866
874
  const { useMessage, useComposer } = useMessageContext();
@@ -868,13 +876,12 @@ var useGoToPreviousBranch = () => {
868
876
  [useMessage, useComposer],
869
877
  (m, c) => c.isEditing || m.branches.indexOf(m.message.id) <= 0
870
878
  );
871
- if (disabled) return null;
872
- return () => {
879
+ const callback = useCallback3(() => {
873
880
  const { message, branches } = useMessage.getState();
874
- useThread.getState().switchToBranch(
875
- branches[branches.indexOf(message.id) - 1]
876
- );
877
- };
881
+ useThread.getState().switchToBranch(branches[branches.indexOf(message.id) - 1]);
882
+ }, [useMessage, useThread]);
883
+ if (disabled) return null;
884
+ return callback;
878
885
  };
879
886
 
880
887
  // src/primitives/branchPicker/BranchPickerPrevious.tsx
@@ -948,6 +955,7 @@ var ActionBarRoot = forwardRef13(({ hideWhenRunning, autohide, autohideFloat, ..
948
955
  });
949
956
 
950
957
  // src/actions/useCopyMessage.tsx
958
+ import { useCallback as useCallback4 } from "react";
951
959
  var useCopyMessage = ({ copiedDuration = 3e3 }) => {
952
960
  const { useMessage, useComposer } = useMessageContext();
953
961
  const hasCopyableContent = useCombinedStore(
@@ -956,21 +964,23 @@ var useCopyMessage = ({ copiedDuration = 3e3 }) => {
956
964
  return c.isEditing || m.message.content.some((c2) => c2.type === "text");
957
965
  }
958
966
  );
959
- if (!hasCopyableContent) return null;
960
- return () => {
967
+ const callback = useCallback4(() => {
961
968
  const { isEditing, value: composerValue } = useComposer.getState();
962
969
  const { message, setIsCopied } = useMessage.getState();
963
970
  const valueToCopy = isEditing ? composerValue : getMessageText(message);
964
971
  navigator.clipboard.writeText(valueToCopy);
965
972
  setIsCopied(true);
966
973
  setTimeout(() => setIsCopied(false), copiedDuration);
967
- };
974
+ }, [useComposer, useMessage, copiedDuration]);
975
+ if (!hasCopyableContent) return null;
976
+ return callback;
968
977
  };
969
978
 
970
979
  // src/primitives/actionBar/ActionBarCopy.tsx
971
980
  var ActionBarCopy = createActionButton(useCopyMessage);
972
981
 
973
982
  // src/actions/useReloadMessage.tsx
983
+ import { useCallback as useCallback5 } from "react";
974
984
  var useReloadMessage = () => {
975
985
  const { useThread, useViewport } = useAssistantContext();
976
986
  const { useMessage } = useMessageContext();
@@ -978,29 +988,32 @@ var useReloadMessage = () => {
978
988
  [useThread, useMessage],
979
989
  (t, m) => t.isRunning || m.message.role !== "assistant"
980
990
  );
981
- if (disabled) return null;
982
- return () => {
991
+ const callback = useCallback5(() => {
983
992
  const { parentId } = useMessage.getState();
984
993
  useThread.getState().startRun(parentId);
985
994
  useViewport.getState().scrollToBottom();
986
- };
995
+ }, [useMessage, useThread, useViewport]);
996
+ if (disabled) return null;
997
+ return callback;
987
998
  };
988
999
 
989
1000
  // src/primitives/actionBar/ActionBarReload.tsx
990
1001
  var ActionBarReload = createActionButton(useReloadMessage);
991
1002
 
992
1003
  // src/actions/useBeginMessageEdit.tsx
1004
+ import { useCallback as useCallback6 } from "react";
993
1005
  var useBeginMessageEdit = () => {
994
1006
  const { useMessage, useComposer } = useMessageContext();
995
1007
  const disabled = useCombinedStore(
996
1008
  [useMessage, useComposer],
997
1009
  (m, c) => m.message.role !== "user" || c.isEditing
998
1010
  );
999
- if (disabled) return null;
1000
- return () => {
1011
+ const callback = useCallback6(() => {
1001
1012
  const { edit } = useComposer.getState();
1002
1013
  edit();
1003
- };
1014
+ }, [useComposer]);
1015
+ if (disabled) return null;
1016
+ return callback;
1004
1017
  };
1005
1018
 
1006
1019
  // src/primitives/actionBar/ActionBarEdit.tsx
@@ -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
  ".": {