@assistant-ui/react-ai-sdk 1.3.32 → 1.3.33

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.
Files changed (47) hide show
  1. package/dist/assistant-stream/dist/core/tool/schema-utils.js.map +1 -1
  2. package/dist/client.d.ts +10 -0
  3. package/dist/client.js +9 -0
  4. package/dist/frontendTools.js.map +1 -1
  5. package/dist/generativeTools.d.ts +17 -3
  6. package/dist/generativeTools.d.ts.map +1 -1
  7. package/dist/generativeTools.js +14 -5
  8. package/dist/generativeTools.js.map +1 -1
  9. package/dist/index.d.ts +2 -2
  10. package/dist/index.js +2 -2
  11. package/dist/index.native.d.ts +10 -0
  12. package/dist/index.native.js +9 -0
  13. package/dist/injectQuoteContext.js.map +1 -1
  14. package/dist/mcp-stdio.node.d.ts +2 -0
  15. package/dist/mcp-stdio.node.js +2 -0
  16. package/dist/mcp-stdio.unsupported.d.ts +7 -0
  17. package/dist/mcp-stdio.unsupported.d.ts.map +1 -0
  18. package/dist/mcp-stdio.unsupported.js +11 -0
  19. package/dist/mcp-stdio.unsupported.js.map +1 -0
  20. package/dist/ui/use-chat/AssistantChatTransport.js.map +1 -1
  21. package/dist/ui/use-chat/useAISDKRuntime.d.ts +10 -0
  22. package/dist/ui/use-chat/useAISDKRuntime.d.ts.map +1 -1
  23. package/dist/ui/use-chat/useAISDKRuntime.js +4 -3
  24. package/dist/ui/use-chat/useAISDKRuntime.js.map +1 -1
  25. package/dist/ui/use-chat/useChatRuntime.d.ts +1 -0
  26. package/dist/ui/use-chat/useChatRuntime.d.ts.map +1 -1
  27. package/dist/ui/use-chat/useChatRuntime.js +3 -2
  28. package/dist/ui/use-chat/useChatRuntime.js.map +1 -1
  29. package/dist/ui/use-chat/useExternalHistory.js.map +1 -1
  30. package/dist/ui/use-chat/useStreamingTiming.js +1 -1
  31. package/dist/ui/utils/convertMessage.d.ts +1 -1
  32. package/dist/ui/utils/convertMessage.d.ts.map +1 -1
  33. package/dist/ui/utils/convertMessage.js.map +1 -1
  34. package/dist/ui/utils/sliceMessagesUntil.js.map +1 -1
  35. package/dist/ui/utils/toCreateMessage.js.map +1 -1
  36. package/dist/usage.js.map +1 -1
  37. package/package.json +27 -11
  38. package/src/client.ts +18 -0
  39. package/src/generativeTools.test.ts +29 -47
  40. package/src/generativeTools.ts +18 -4
  41. package/src/index.native.ts +3 -0
  42. package/src/index.ts +1 -16
  43. package/src/mcp-stdio.node.ts +1 -0
  44. package/src/mcp-stdio.unsupported.ts +12 -0
  45. package/src/ui/use-chat/useAISDKRuntime.test.ts +56 -0
  46. package/src/ui/use-chat/useAISDKRuntime.ts +12 -0
  47. package/src/ui/use-chat/useChatRuntime.ts +3 -0
@@ -37,6 +37,12 @@ const createChatHelpers = (messages: any[] = []) => {
37
37
  return chatHelpers;
38
38
  };
39
39
 
40
+ const textOf = (message: any): string =>
41
+ message.content
42
+ .filter((part: any) => part.type === "text")
43
+ .map((part: any) => part.text)
44
+ .join("|");
45
+
40
46
  describe("useAISDKRuntime", () => {
41
47
  beforeEach(() => {
42
48
  vi.clearAllMocks();
@@ -439,4 +445,54 @@ describe("useAISDKRuntime", () => {
439
445
  const { result } = renderHook(() => useAISDKRuntime(chat, { suggestions }));
440
446
  expect(result.current.thread.getState().suggestions).toEqual(suggestions);
441
447
  });
448
+
449
+ it("merges consecutive assistant messages into one turn by default", async () => {
450
+ const chat = createChatHelpers([
451
+ { id: "u1", role: "user", parts: [{ type: "text", text: "hi" }] },
452
+ { id: "a1", role: "assistant", parts: [{ type: "text", text: "first" }] },
453
+ {
454
+ id: "a2",
455
+ role: "assistant",
456
+ parts: [{ type: "text", text: "second" }],
457
+ },
458
+ ]);
459
+
460
+ const { result } = renderHook(() => useAISDKRuntime(chat));
461
+
462
+ await waitFor(() => {
463
+ expect(result.current.thread.getState().messages.length).toBe(2);
464
+ });
465
+
466
+ const messages = result.current.thread.getState().messages;
467
+ expect(messages.map((m: any) => m.role)).toEqual(["user", "assistant"]);
468
+ expect(textOf(messages[1])).toBe("first|second");
469
+ });
470
+
471
+ it('keeps consecutive assistant messages separate when joinStrategy is "none"', async () => {
472
+ const chat = createChatHelpers([
473
+ { id: "u1", role: "user", parts: [{ type: "text", text: "hi" }] },
474
+ { id: "a1", role: "assistant", parts: [{ type: "text", text: "first" }] },
475
+ {
476
+ id: "a2",
477
+ role: "assistant",
478
+ parts: [{ type: "text", text: "second" }],
479
+ },
480
+ ]);
481
+
482
+ const { result } = renderHook(() =>
483
+ useAISDKRuntime(chat, { joinStrategy: "none" }),
484
+ );
485
+
486
+ await waitFor(() => {
487
+ expect(result.current.thread.getState().messages.length).toBe(3);
488
+ });
489
+
490
+ const messages = result.current.thread.getState().messages;
491
+ expect(messages.map((m: any) => m.role)).toEqual([
492
+ "user",
493
+ "assistant",
494
+ "assistant",
495
+ ]);
496
+ expect(messages.slice(1).map(textOf)).toEqual(["first", "second"]);
497
+ });
442
498
  });
@@ -6,6 +6,7 @@ import { isToolUIPart, generateId } from "ai";
6
6
  import {
7
7
  useExternalStoreRuntime,
8
8
  useRuntimeAdapters,
9
+ type JoinStrategy,
9
10
  } from "@assistant-ui/core/react";
10
11
  import type { ToolExecutionStatus } from "@assistant-ui/core";
11
12
  import type {
@@ -82,6 +83,15 @@ export type AISDKRuntimeAdapter = ExternalStoreSharedOptions & {
82
83
  * (for example, an SSE reconnect endpoint keyed by turn id).
83
84
  */
84
85
  onResume?: ExternalStoreAdapter["onResume"];
86
+ /**
87
+ * How consecutive assistant messages are rendered.
88
+ *
89
+ * `"concat-content"` (the default) merges them into a single thread message.
90
+ * `"none"` keeps each assistant message as its own thread message, which is
91
+ * useful when a backend persists proactive or consecutive assistant messages
92
+ * as separate entries.
93
+ */
94
+ joinStrategy?: JoinStrategy | undefined;
85
95
  };
86
96
 
87
97
  export const useAISDKRuntime = <UI_MESSAGE extends UIMessage = UIMessage>(
@@ -93,6 +103,7 @@ export const useAISDKRuntime = <UI_MESSAGE extends UIMessage = UIMessage>(
93
103
  toCreateMessage: customToCreateMessage,
94
104
  cancelPendingToolCallsOnSend = true,
95
105
  onResume,
106
+ joinStrategy,
96
107
  } = adapter;
97
108
  const contextAdapters = useRuntimeAdapters();
98
109
  const [toolStatuses, setToolStatuses] = useState<
@@ -126,6 +137,7 @@ export const useAISDKRuntime = <UI_MESSAGE extends UIMessage = UIMessage>(
126
137
  const messages = AISDKMessageConverter.useThreadMessages({
127
138
  isRunning,
128
139
  messages: chatHelpers.messages,
140
+ joinStrategy,
129
141
  metadata: useMemo(
130
142
  () => ({
131
143
  toolStatuses,
@@ -29,6 +29,7 @@ export type UseChatRuntimeOptions<UI_MESSAGE extends UIMessage = UIMessage> =
29
29
  adapters?: AISDKRuntimeAdapter["adapters"] | undefined;
30
30
  toCreateMessage?: CustomToCreateMessageFunction;
31
31
  onResume?: AISDKRuntimeAdapter["onResume"];
32
+ joinStrategy?: AISDKRuntimeAdapter["joinStrategy"];
32
33
  };
33
34
 
34
35
  const useDynamicChatTransport = <UI_MESSAGE extends UIMessage = UIMessage>(
@@ -78,6 +79,7 @@ const useChatThreadRuntime = <UI_MESSAGE extends UIMessage = UIMessage>(
78
79
  unstable_capabilities: _unstable_capabilities,
79
80
  suggestions: _suggestions,
80
81
  onResume,
82
+ joinStrategy,
81
83
  ...chatOptions
82
84
  } = options ?? {};
83
85
  // peel guard: any shared key left in `chatOptions` collapses this to `never`
@@ -103,6 +105,7 @@ const useChatThreadRuntime = <UI_MESSAGE extends UIMessage = UIMessage>(
103
105
  ...pickExternalStoreSharedOptions(options ?? {}),
104
106
  ...(toCreateMessage && { toCreateMessage }),
105
107
  ...(onResume && { onResume }),
108
+ ...(joinStrategy && { joinStrategy }),
106
109
  });
107
110
 
108
111
  if (transport instanceof AssistantChatTransport) {