@assistant-ui/react 0.5.42 → 0.5.46

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
@@ -164,6 +164,7 @@ import { create as create4 } from "zustand";
164
164
  var getThreadStateFromRuntime = (runtime) => {
165
165
  const lastMessage = runtime.messages.at(-1);
166
166
  return Object.freeze({
167
+ threadId: runtime.threadId,
167
168
  capabilities: runtime.capabilities,
168
169
  isDisabled: runtime.isDisabled,
169
170
  isRunning: lastMessage?.role !== "assistant" ? false : lastMessage.status.type === "running"
@@ -636,6 +637,9 @@ var useSmooth = (state, smooth = false) => {
636
637
  setDisplayedText(text2);
637
638
  useSmoothStatus2?.setState(text2 !== state.part.text ? SMOOTH_STATUS : state.status);
638
639
  });
640
+ useEffect(() => {
641
+ useSmoothStatus2?.setState(text !== displayedText ? SMOOTH_STATUS : state.status);
642
+ }, [useSmoothStatus2, text, displayedText, state.status]);
639
643
  const [animatorRef] = useState2(
640
644
  new TextStreamAnimator(text, setText)
641
645
  );
@@ -1077,6 +1081,7 @@ var LocalThreadRuntime = class {
1077
1081
  constructor(configProvider, adapter, { initialMessages, ...options }) {
1078
1082
  this.configProvider = configProvider;
1079
1083
  this.adapter = adapter;
1084
+ this.threadId = generateId();
1080
1085
  this.options = options;
1081
1086
  if (initialMessages) {
1082
1087
  let parentId = null;
@@ -1098,6 +1103,7 @@ var LocalThreadRuntime = class {
1098
1103
  unstable_copy: true,
1099
1104
  speak: false
1100
1105
  };
1106
+ threadId;
1101
1107
  isDisabled = false;
1102
1108
  get messages() {
1103
1109
  return this.repository.getMessages();
@@ -1400,11 +1406,12 @@ var getAutoStatus = (isLast, isRunning) => isLast && isRunning ? AUTO_STATUS_RUN
1400
1406
 
1401
1407
  // src/runtimes/external-store/ThreadMessageLike.tsx
1402
1408
  var fromThreadMessageLike = (like, fallbackId, fallbackStatus) => {
1403
- const { role, content, id, createdAt, status } = like;
1409
+ const { role, id, createdAt, status } = like;
1404
1410
  const common = {
1405
1411
  id: id ?? fallbackId,
1406
1412
  createdAt: createdAt ?? /* @__PURE__ */ new Date()
1407
1413
  };
1414
+ const content = typeof like.content === "string" ? [{ type: "text", text: like.content }] : like.content;
1408
1415
  switch (role) {
1409
1416
  case "assistant":
1410
1417
  return {
@@ -1414,6 +1421,8 @@ var fromThreadMessageLike = (like, fallbackId, fallbackStatus) => {
1414
1421
  const type = part.type;
1415
1422
  switch (type) {
1416
1423
  case "text":
1424
+ if (part.text.trim().length === 0) return null;
1425
+ return part;
1417
1426
  case "ui":
1418
1427
  return part;
1419
1428
  case "tool-call": {
@@ -1428,7 +1437,7 @@ var fromThreadMessageLike = (like, fallbackId, fallbackStatus) => {
1428
1437
  throw new Error(`Unknown content part type: ${unhandledType}`);
1429
1438
  }
1430
1439
  }
1431
- }),
1440
+ }).filter((c) => !!c),
1432
1441
  status: status ?? fallbackStatus
1433
1442
  };
1434
1443
  case "user":
@@ -1493,8 +1502,9 @@ var ExternalStoreThreadRuntime = class {
1493
1502
  get capabilities() {
1494
1503
  return this._capabilities;
1495
1504
  }
1496
- messages = [];
1497
- isDisabled = false;
1505
+ threadId;
1506
+ messages;
1507
+ isDisabled;
1498
1508
  converter = new ThreadMessageConverter();
1499
1509
  _store;
1500
1510
  composer = {
@@ -1507,8 +1517,12 @@ var ExternalStoreThreadRuntime = class {
1507
1517
  constructor(store) {
1508
1518
  this.store = store;
1509
1519
  }
1520
+ get store() {
1521
+ return this._store;
1522
+ }
1510
1523
  set store(store) {
1511
1524
  if (this._store === store) return;
1525
+ this.threadId = store.threadId ?? this.threadId ?? generateId();
1512
1526
  const isRunning = store.isRunning ?? false;
1513
1527
  this.isDisabled = store.isDisabled ?? false;
1514
1528
  const oldStore = this._store;
@@ -1644,6 +1658,9 @@ var ExternalStoreRuntime = class extends BaseAssistantRuntime {
1644
1658
  constructor(store) {
1645
1659
  super(new ExternalStoreThreadRuntime(store));
1646
1660
  }
1661
+ get store() {
1662
+ return this.thread.store;
1663
+ }
1647
1664
  set store(store) {
1648
1665
  this.thread.store = store;
1649
1666
  }
@@ -1684,6 +1701,153 @@ var useExternalStoreRuntime = (store) => {
1684
1701
  return runtime;
1685
1702
  };
1686
1703
 
1704
+ // src/runtimes/external-store/external-message-converter.tsx
1705
+ import { useMemo as useMemo2 } from "react";
1706
+ var joinExternalMessages = (messages) => {
1707
+ const assistantMessage = {
1708
+ role: "assistant",
1709
+ content: []
1710
+ };
1711
+ for (const output of messages) {
1712
+ if (output.role === "tool") {
1713
+ const toolCallIdx = assistantMessage.content.findIndex(
1714
+ (c) => c.type === "tool-call" && c.toolCallId === output.toolCallId
1715
+ );
1716
+ if (toolCallIdx !== -1) {
1717
+ const toolCall = assistantMessage.content[toolCallIdx];
1718
+ if (output.toolName) {
1719
+ if (toolCall.toolName !== output.toolName)
1720
+ throw new Error(
1721
+ `Tool call name ${output.toolCallId} ${output.toolName} does not match existing tool call ${toolCall.toolName}`
1722
+ );
1723
+ }
1724
+ assistantMessage.content[toolCallIdx] = {
1725
+ ...toolCall,
1726
+ result: output.result
1727
+ };
1728
+ } else {
1729
+ throw new Error(
1730
+ `Tool call ${output.toolCallId} ${output.toolName} not found in assistant message`
1731
+ );
1732
+ }
1733
+ } else {
1734
+ const content = output.content;
1735
+ const role = output.role;
1736
+ switch (role) {
1737
+ case "system":
1738
+ case "user":
1739
+ return { role, content };
1740
+ case "assistant":
1741
+ if (assistantMessage.content.length === 0) {
1742
+ assistantMessage.id = output.id;
1743
+ assistantMessage.createdAt ??= output.createdAt;
1744
+ assistantMessage.status ??= output.status;
1745
+ }
1746
+ assistantMessage.content.push(...content);
1747
+ break;
1748
+ default: {
1749
+ const unsupportedRole = role;
1750
+ throw new Error(`Unknown message role: ${unsupportedRole}`);
1751
+ }
1752
+ }
1753
+ }
1754
+ }
1755
+ return assistantMessage;
1756
+ };
1757
+ var chunkExternalMessages = (callbackResults) => {
1758
+ const results = [];
1759
+ let isAssistant = false;
1760
+ let inputs = [];
1761
+ let outputs = [];
1762
+ const flush = () => {
1763
+ if (outputs.length) {
1764
+ results.push({
1765
+ inputs,
1766
+ outputs
1767
+ });
1768
+ }
1769
+ inputs = [];
1770
+ outputs = [];
1771
+ };
1772
+ for (const callbackResult of callbackResults) {
1773
+ for (const output of callbackResult.outputs) {
1774
+ if (!isAssistant || output.role === "user" || output.role === "system") {
1775
+ flush();
1776
+ }
1777
+ isAssistant = output.role === "assistant" || output.role === "tool";
1778
+ if (inputs.at(-1) !== callbackResult.input) {
1779
+ inputs.push(callbackResult.input);
1780
+ }
1781
+ outputs.push(output);
1782
+ }
1783
+ }
1784
+ flush();
1785
+ return results;
1786
+ };
1787
+ var useExternalMessageConverter = ({
1788
+ callback,
1789
+ messages,
1790
+ isRunning
1791
+ }) => {
1792
+ const state = useMemo2(
1793
+ () => ({
1794
+ callback,
1795
+ callbackCache: /* @__PURE__ */ new WeakMap(),
1796
+ chunkCache: /* @__PURE__ */ new WeakMap(),
1797
+ converterCache: new ThreadMessageConverter()
1798
+ }),
1799
+ [callback]
1800
+ );
1801
+ return useMemo2(() => {
1802
+ const callbackResults = [];
1803
+ for (const message of messages) {
1804
+ let result = state.callbackCache.get(message);
1805
+ if (!result) {
1806
+ const output = state.callback(message);
1807
+ const outputs = Array.isArray(output) ? output : [output];
1808
+ result = { input: message, outputs };
1809
+ state.callbackCache.set(message, result);
1810
+ }
1811
+ callbackResults.push(result);
1812
+ }
1813
+ const chunks = chunkExternalMessages(callbackResults).map((m) => {
1814
+ const key = m.outputs[0];
1815
+ if (!key) return m;
1816
+ const cached = state.chunkCache.get(key);
1817
+ if (cached && shallowArrayEqual(cached.outputs, m.outputs)) return cached;
1818
+ state.chunkCache.set(key, m);
1819
+ return m;
1820
+ });
1821
+ return state.converterCache.convertMessages(
1822
+ chunks,
1823
+ (cache, message, idx) => {
1824
+ const isLast = idx === chunks.length - 1;
1825
+ const autoStatus = getAutoStatus(isLast, isRunning);
1826
+ if (cache && (cache.role !== "assistant" || !isAutoStatus(cache.status) || cache.status === autoStatus)) {
1827
+ const inputs = getExternalStoreMessage(cache);
1828
+ if (shallowArrayEqual(inputs, message.inputs)) {
1829
+ return cache;
1830
+ }
1831
+ }
1832
+ const newMessage = fromThreadMessageLike(
1833
+ joinExternalMessages(message.outputs),
1834
+ idx.toString(),
1835
+ autoStatus
1836
+ );
1837
+ newMessage[symbolInnerMessage] = message.inputs;
1838
+ return newMessage;
1839
+ }
1840
+ );
1841
+ }, [state, messages, isRunning]);
1842
+ };
1843
+ var shallowArrayEqual = (a, b) => {
1844
+ if (a.length !== b.length) return false;
1845
+ for (let i = 0; i < a.length; i++) {
1846
+ if (a[i] !== b[i]) return false;
1847
+ }
1848
+ return true;
1849
+ };
1850
+
1687
1851
  // src/runtimes/dangerous-in-browser/useDangerousInBrowserRuntime.ts
1688
1852
  import { useState as useState6 } from "react";
1689
1853
 
@@ -1788,7 +1952,7 @@ var ThreadProvider = ({
1788
1952
  const thread = provider.thread;
1789
1953
  const oldState = context.useThread.getState();
1790
1954
  const state = getThreadStateFromRuntime(thread);
1791
- if (oldState.isDisabled !== state.isDisabled || oldState.isRunning !== state.isRunning || // TODO ensure capabilities is memoized
1955
+ if (oldState.threadId !== state.threadId || oldState.isDisabled !== state.isDisabled || oldState.isRunning !== state.isRunning || // TODO ensure capabilities is memoized
1792
1956
  oldState.capabilities !== state.capabilities) {
1793
1957
  context.useThread.setState(
1794
1958
  state,
@@ -1877,7 +2041,7 @@ var AssistantRuntimeProviderImpl = ({ children, runtime }) => {
1877
2041
  var AssistantRuntimeProvider = memo(AssistantRuntimeProviderImpl);
1878
2042
 
1879
2043
  // src/context/react/ComposerContext.ts
1880
- import { useMemo as useMemo2 } from "react";
2044
+ import { useMemo as useMemo3 } from "react";
1881
2045
 
1882
2046
  // src/context/react/MessageContext.ts
1883
2047
  import { createContext as createContext4, useContext as useContext4 } from "react";
@@ -1895,7 +2059,7 @@ function useMessageContext(options) {
1895
2059
  var useComposerContext = () => {
1896
2060
  const { useComposer } = useThreadContext();
1897
2061
  const { useEditComposer } = useMessageContext({ optional: true }) ?? {};
1898
- return useMemo2(
2062
+ return useMemo3(
1899
2063
  () => ({
1900
2064
  useComposer: useEditComposer ?? useComposer,
1901
2065
  type: useEditComposer ? "edit" : "new"
@@ -1992,6 +2156,7 @@ var makeAssistantTool = (tool) => {
1992
2156
  useAssistantTool(tool);
1993
2157
  return null;
1994
2158
  };
2159
+ Tool.unstable_tool = tool;
1995
2160
  return Tool;
1996
2161
  };
1997
2162
 
@@ -2013,6 +2178,7 @@ var makeAssistantToolUI = (tool) => {
2013
2178
  useAssistantToolUI(tool);
2014
2179
  return null;
2015
2180
  };
2181
+ ToolUI.unstable_tool = tool;
2016
2182
  return ToolUI;
2017
2183
  };
2018
2184
 
@@ -2035,7 +2201,7 @@ var useAssistantInstructions = (instruction) => {
2035
2201
  import { useCallback as useCallback3 } from "react";
2036
2202
 
2037
2203
  // src/utils/combined/useCombinedStore.ts
2038
- import { useMemo as useMemo3 } from "react";
2204
+ import { useMemo as useMemo4 } from "react";
2039
2205
 
2040
2206
  // src/utils/combined/createCombinedStore.ts
2041
2207
  import { useSyncExternalStore } from "react";
@@ -2056,7 +2222,7 @@ var createCombinedStore = (stores) => {
2056
2222
 
2057
2223
  // src/utils/combined/useCombinedStore.ts
2058
2224
  var useCombinedStore = (stores, selector) => {
2059
- const useCombined = useMemo3(() => createCombinedStore(stores), stores);
2225
+ const useCombined = useMemo4(() => createCombinedStore(stores), stores);
2060
2226
  return useCombined(selector);
2061
2227
  };
2062
2228
 
@@ -2869,21 +3035,22 @@ ContentPartPrimitiveInProgress.displayName = "ContentPartPrimitive.InProgress";
2869
3035
 
2870
3036
  // src/primitives/message/MessageContent.tsx
2871
3037
  import { jsx as jsx22, jsxs as jsxs3 } from "react/jsx-runtime";
3038
+ var ToolUIDisplay = ({
3039
+ UI,
3040
+ ...props
3041
+ }) => {
3042
+ const { useToolUIs } = useAssistantContext();
3043
+ const Render = useToolUIs((s) => s.getToolUI(props.part.toolName)) ?? UI;
3044
+ if (!Render) return null;
3045
+ return /* @__PURE__ */ jsx22(Render, { ...props });
3046
+ };
2872
3047
  var defaultComponents = {
2873
3048
  Text: () => /* @__PURE__ */ jsxs3("p", { style: { whiteSpace: "pre-line" }, children: [
2874
3049
  /* @__PURE__ */ jsx22(ContentPartPrimitiveText, {}),
2875
3050
  /* @__PURE__ */ jsx22(ContentPartPrimitiveInProgress, { children: /* @__PURE__ */ jsx22("span", { style: { fontFamily: "revert" }, children: " \u25CF" }) })
2876
3051
  ] }),
2877
3052
  Image: () => /* @__PURE__ */ jsx22(ContentPartPrimitiveImage, {}),
2878
- UI: () => /* @__PURE__ */ jsx22(ContentPartPrimitiveDisplay, {}),
2879
- tools: {
2880
- Fallback: (props) => {
2881
- const { useToolUIs } = useAssistantContext();
2882
- const Render = useToolUIs((s) => s.getToolUI(props.part.toolName));
2883
- if (!Render) return null;
2884
- return /* @__PURE__ */ jsx22(Render, { ...props });
2885
- }
2886
- }
3053
+ UI: () => /* @__PURE__ */ jsx22(ContentPartPrimitiveDisplay, {})
2887
3054
  };
2888
3055
  var MessageContentPartComponent = ({
2889
3056
  components: {
@@ -2891,7 +3058,7 @@ var MessageContentPartComponent = ({
2891
3058
  Text: Text2 = defaultComponents.Text,
2892
3059
  Image: Image2 = defaultComponents.Image,
2893
3060
  UI = defaultComponents.UI,
2894
- tools: { by_name = {}, Fallback: Fallback2 = defaultComponents.tools.Fallback } = {}
3061
+ tools: { by_name = {}, Fallback: Fallback2 = void 0 } = {}
2895
3062
  } = {}
2896
3063
  }) => {
2897
3064
  const { useThreadActions } = useThreadContext();
@@ -2918,10 +3085,19 @@ var MessageContentPartComponent = ({
2918
3085
  const Tool = by_name[part.toolName] || Fallback2;
2919
3086
  const addResult = (result) => addToolResult({
2920
3087
  messageId: useMessage.getState().message.id,
3088
+ toolName: part.toolName,
2921
3089
  toolCallId: part.toolCallId,
2922
3090
  result
2923
3091
  });
2924
- return /* @__PURE__ */ jsx22(Tool, { part, status, addResult });
3092
+ return /* @__PURE__ */ jsx22(
3093
+ ToolUIDisplay,
3094
+ {
3095
+ UI: Tool,
3096
+ part,
3097
+ status,
3098
+ addResult
3099
+ }
3100
+ );
2925
3101
  }
2926
3102
  default:
2927
3103
  const unhandledType = type;
@@ -3790,13 +3966,22 @@ var AssistantMessageContentWrapper = withDefaults("div", {
3790
3966
  className: "aui-assistant-message-content"
3791
3967
  });
3792
3968
  var AssistantMessageContent = forwardRef22(({ components: componentsProp, ...rest }, ref) => {
3793
- const { assistantMessage: { components = {} } = {} } = useThreadConfig();
3969
+ const { tools, assistantMessage: { components = {} } = {} } = useThreadConfig();
3794
3970
  return /* @__PURE__ */ jsx36(AssistantMessageContentWrapper, { ...rest, ref, children: /* @__PURE__ */ jsx36(
3795
3971
  message_exports.Content,
3796
3972
  {
3797
3973
  components: {
3798
3974
  ...componentsProp,
3799
- Text: componentsProp?.Text ?? components.Text ?? content_part_default.Text
3975
+ Text: componentsProp?.Text ?? components.Text ?? content_part_default.Text,
3976
+ tools: {
3977
+ by_name: !tools ? void 0 : Object.fromEntries(
3978
+ tools.map((t) => [
3979
+ t.unstable_tool.toolName,
3980
+ t.unstable_tool.render
3981
+ ])
3982
+ ),
3983
+ Fallback: components.ToolFallback
3984
+ }
3800
3985
  }
3801
3986
  }
3802
3987
  ) });
@@ -4327,6 +4512,7 @@ export {
4327
4512
  useContentPartText,
4328
4513
  useDangerousInBrowserRuntime,
4329
4514
  useEdgeRuntime,
4515
+ useExternalMessageConverter,
4330
4516
  useExternalStoreRuntime,
4331
4517
  useLocalRuntime,
4332
4518
  useMessageContext,