@assistant-ui/react 0.5.42 → 0.5.46

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