@assistant-ui/ai-sdk 0.0.11 → 0.0.12

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 (119) hide show
  1. package/dist/converters/convertMessage.d.ts +8 -6
  2. package/dist/converters/convertMessage.d.ts.map +1 -1
  3. package/dist/converters/convertMessage.js +48 -22
  4. package/dist/converters/convertMessage.js.map +1 -1
  5. package/dist/index.d.ts +1 -1
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +1 -1
  8. package/dist/index.native.d.ts +1 -1
  9. package/dist/index.native.d.ts.map +1 -1
  10. package/dist/index.native.js +1 -1
  11. package/dist/model-context/injectInteractableContext.d.ts +3 -0
  12. package/dist/model-context/injectInteractableContext.d.ts.map +1 -1
  13. package/dist/model-context/injectInteractableContext.js +3 -0
  14. package/dist/model-context/injectInteractableContext.js.map +1 -1
  15. package/dist/model-context/injectQuoteContext.d.ts +1 -0
  16. package/dist/model-context/injectQuoteContext.d.ts.map +1 -1
  17. package/dist/model-context/injectQuoteContext.js +1 -0
  18. package/dist/model-context/injectQuoteContext.js.map +1 -1
  19. package/dist/runtime/AISDKChat.js +1 -1
  20. package/dist/runtime/AISDKThreads.d.ts +5 -0
  21. package/dist/runtime/AISDKThreads.d.ts.map +1 -1
  22. package/dist/runtime/AISDKThreads.js +37 -4
  23. package/dist/runtime/AISDKThreads.js.map +1 -1
  24. package/dist/runtime/DynamicChatTransport.d.ts +49 -0
  25. package/dist/runtime/DynamicChatTransport.d.ts.map +1 -0
  26. package/dist/runtime/DynamicChatTransport.js +147 -0
  27. package/dist/runtime/DynamicChatTransport.js.map +1 -0
  28. package/dist/runtime/getResumableAdapter.d.ts +5 -0
  29. package/dist/runtime/getResumableAdapter.d.ts.map +1 -0
  30. package/dist/runtime/getResumableAdapter.js +12 -0
  31. package/dist/runtime/getResumableAdapter.js.map +1 -0
  32. package/dist/runtime/sdkIdentity.js +1 -1
  33. package/dist/runtime/toolHistoryCodec.d.ts +20 -0
  34. package/dist/runtime/toolHistoryCodec.d.ts.map +1 -0
  35. package/dist/runtime/toolHistoryCodec.js +107 -0
  36. package/dist/runtime/toolHistoryCodec.js.map +1 -0
  37. package/dist/runtime/useAISDKRuntime.d.ts +4 -1
  38. package/dist/runtime/useAISDKRuntime.d.ts.map +1 -1
  39. package/dist/runtime/useAISDKRuntime.js +154 -155
  40. package/dist/runtime/useAISDKRuntime.js.map +1 -1
  41. package/dist/runtime/useChatRuntime.d.ts +14 -2
  42. package/dist/runtime/useChatRuntime.d.ts.map +1 -1
  43. package/dist/runtime/useChatRuntime.js +12 -3
  44. package/dist/runtime/useChatRuntime.js.map +1 -1
  45. package/dist/runtime/useChatThread.d.ts +3 -2
  46. package/dist/runtime/useChatThread.d.ts.map +1 -1
  47. package/dist/runtime/useChatThread.js +56 -36
  48. package/dist/runtime/useChatThread.js.map +1 -1
  49. package/dist/runtime/useDynamicChatTransport.d.ts +4 -0
  50. package/dist/runtime/useDynamicChatTransport.d.ts.map +1 -0
  51. package/dist/runtime/useDynamicChatTransport.js +64 -0
  52. package/dist/runtime/useDynamicChatTransport.js.map +1 -0
  53. package/dist/runtime/useExternalHistory.d.ts.map +1 -1
  54. package/dist/runtime/useExternalHistory.js +11 -109
  55. package/dist/runtime/useExternalHistory.js.map +1 -1
  56. package/dist/runtime/useResourceCleanup.js +1 -1
  57. package/dist/runtime/useStreamingTiming.js +2 -2
  58. package/dist/runtime/useStreamingTiming.js.map +1 -1
  59. package/dist/tools/generativeTools.d.ts +2 -1
  60. package/dist/tools/generativeTools.d.ts.map +1 -1
  61. package/dist/tools/generativeTools.js +5 -5
  62. package/dist/tools/generativeTools.js.map +1 -1
  63. package/dist/usage.js +1 -1
  64. package/dist/utils/sliceMessagesUntil.d.ts.map +1 -1
  65. package/dist/utils/sliceMessagesUntil.js +1 -2
  66. package/dist/utils/sliceMessagesUntil.js.map +1 -1
  67. package/package.json +11 -10
  68. package/src/converters/convertMessage.test.ts +120 -2
  69. package/src/converters/convertMessage.tool-args-status.test.tsx +185 -0
  70. package/src/converters/convertMessage.ts +60 -19
  71. package/src/converters/toCreateMessage.test.ts +27 -0
  72. package/src/index.native.ts +1 -1
  73. package/src/index.ts +1 -1
  74. package/src/model-context/injectInteractableContext.ts +3 -0
  75. package/src/model-context/injectQuoteContext.ts +1 -0
  76. package/src/runtime/AISDKChat.integration.test.tsx +57 -2
  77. package/src/runtime/AISDKThreads.cloud.test.ts +3 -0
  78. package/src/runtime/AISDKThreads.test.ts +181 -0
  79. package/src/runtime/AISDKThreads.ts +31 -4
  80. package/src/runtime/DynamicChatTransport.test.ts +203 -0
  81. package/src/runtime/DynamicChatTransport.ts +273 -0
  82. package/src/runtime/__tests__/controlled-transport.ts +3 -0
  83. package/src/runtime/getResumableAdapter.ts +16 -0
  84. package/src/runtime/toolHistoryCodec.test.ts +161 -0
  85. package/src/runtime/toolHistoryCodec.ts +207 -0
  86. package/src/runtime/useAISDKRuntime.approval.test.tsx +12 -0
  87. package/src/runtime/useAISDKRuntime.reload.test.tsx +219 -0
  88. package/src/runtime/useAISDKRuntime.test.ts +538 -5
  89. package/src/runtime/useAISDKRuntime.ts +161 -51
  90. package/src/runtime/useChatRuntime.integration.test.tsx +319 -4
  91. package/src/runtime/useChatRuntime.local-storage.test.tsx +123 -0
  92. package/src/runtime/useChatRuntime.test.ts +107 -1
  93. package/src/runtime/useChatRuntime.ts +26 -4
  94. package/src/runtime/useChatThread.binding.test.tsx +143 -0
  95. package/src/runtime/useChatThread.ts +98 -81
  96. package/src/runtime/useDynamicChatTransport.ts +26 -0
  97. package/src/runtime/useExternalHistory.test.ts +111 -0
  98. package/src/runtime/useExternalHistory.ts +14 -210
  99. package/src/runtime/useStreamingTiming.ts +2 -2
  100. package/src/tools/generativeTools.test.ts +1 -1
  101. package/src/tools/generativeTools.ts +5 -6
  102. package/src/utils/sliceMessagesUntil.test.ts +2 -6
  103. package/src/utils/sliceMessagesUntil.ts +1 -5
  104. package/dist/converters/modelContentEnvelope.d.ts +0 -14
  105. package/dist/converters/modelContentEnvelope.d.ts.map +0 -1
  106. package/dist/converters/modelContentEnvelope.js +0 -22
  107. package/dist/converters/modelContentEnvelope.js.map +0 -1
  108. package/dist/converters/toolOutputConversion.d.ts +0 -26
  109. package/dist/converters/toolOutputConversion.d.ts.map +0 -1
  110. package/dist/converters/toolOutputConversion.js +0 -31
  111. package/dist/converters/toolOutputConversion.js.map +0 -1
  112. package/dist/tools/frontendTools.d.ts +0 -30
  113. package/dist/tools/frontendTools.d.ts.map +0 -1
  114. package/dist/tools/frontendTools.js +0 -33
  115. package/dist/tools/frontendTools.js.map +0 -1
  116. package/src/converters/modelContentEnvelope.ts +0 -41
  117. package/src/converters/toolOutputConversion.ts +0 -26
  118. package/src/tools/frontendTools.test.ts +0 -205
  119. package/src/tools/frontendTools.ts +0 -83
@@ -0,0 +1,219 @@
1
+ // @vitest-environment jsdom
2
+ import { act, renderHook, waitFor } from "@testing-library/react";
3
+ import { describe, expect, it, vi } from "vitest";
4
+ import { useChat } from "@ai-sdk/react";
5
+ import type { ChatTransport, UIMessage, UIMessageChunk } from "ai";
6
+
7
+ vi.mock("./useExternalHistory", async (importOriginal) => {
8
+ const original =
9
+ await importOriginal<typeof import("./useExternalHistory")>();
10
+ return {
11
+ ...original,
12
+ useExternalHistory: vi.fn(() => ({
13
+ isLoading: false,
14
+ deleteMessage: vi.fn().mockResolvedValue(undefined),
15
+ persistToolInteractions: vi.fn().mockResolvedValue(undefined),
16
+ persistToolApprovalResponses: vi.fn().mockResolvedValue(undefined),
17
+ })),
18
+ };
19
+ });
20
+
21
+ import { useAISDKRuntime } from "./useAISDKRuntime";
22
+
23
+ const text = (id: string, role: UIMessage["role"], value: string) =>
24
+ ({ id, role, parts: [{ type: "text", text: value }] }) as UIMessage;
25
+
26
+ const makeTransport = () => {
27
+ const sent: string[][] = [];
28
+ const transport: ChatTransport<UIMessage> = {
29
+ async sendMessages({ messages }) {
30
+ sent.push(messages.map((message) => message.id));
31
+ const chunks: UIMessageChunk[] = [
32
+ { type: "start", messageId: "regenerated" },
33
+ { type: "text-start", id: "t" },
34
+ { type: "text-delta", id: "t", delta: "new" },
35
+ { type: "text-end", id: "t" },
36
+ { type: "finish" },
37
+ ];
38
+ return new ReadableStream({
39
+ start(controller) {
40
+ for (const chunk of chunks) controller.enqueue(chunk);
41
+ controller.close();
42
+ },
43
+ });
44
+ },
45
+ async reconnectToStream() {
46
+ return null;
47
+ },
48
+ };
49
+ return { transport, sent };
50
+ };
51
+
52
+ const setup = (
53
+ initial: UIMessage[],
54
+ options: Parameters<typeof useAISDKRuntime>[1] = {},
55
+ ) => {
56
+ const { transport, sent } = makeTransport();
57
+ const { result } = renderHook(() => {
58
+ const chat = useChat({ messages: initial, transport });
59
+ const runtime = useAISDKRuntime(chat, options);
60
+ return { chat, runtime };
61
+ });
62
+ return { result, sent };
63
+ };
64
+
65
+ describe("useAISDKRuntime reload and edit of a joined response", () => {
66
+ it("does not send any part of the response being reloaded", async () => {
67
+ const { result, sent } = setup([
68
+ text("u1", "user", "hi"),
69
+ text("a1", "assistant", "old-1"),
70
+ text("a2", "assistant", "old-2"),
71
+ ]);
72
+ await waitFor(() =>
73
+ expect(result.current.runtime.thread.getState().messages).toHaveLength(2),
74
+ );
75
+ const assistant = result.current.runtime.thread.getState().messages[1]!;
76
+
77
+ act(() => {
78
+ result.current.runtime.thread.getMessageById(assistant.id).reload();
79
+ });
80
+
81
+ await waitFor(() => expect(sent).toHaveLength(1));
82
+ await waitFor(() => expect(result.current.chat.status).toBe("ready"));
83
+ expect(sent[0]).toEqual(["u1"]);
84
+ expect(result.current.chat.messages.map((m) => m.id)).toEqual([
85
+ "u1",
86
+ "regenerated",
87
+ ]);
88
+ });
89
+
90
+ it('does not send the reloaded message with joinStrategy "none"', async () => {
91
+ const { result, sent } = setup(
92
+ [
93
+ text("u1", "user", "hi"),
94
+ text("a1", "assistant", "old-1"),
95
+ text("a2", "assistant", "old-2"),
96
+ ],
97
+ { joinStrategy: "none" },
98
+ );
99
+ await waitFor(() =>
100
+ expect(result.current.runtime.thread.getState().messages).toHaveLength(3),
101
+ );
102
+
103
+ act(() => {
104
+ result.current.runtime.thread.getMessageById("a1").reload();
105
+ });
106
+
107
+ await waitFor(() => expect(sent).toHaveLength(1));
108
+ await waitFor(() => expect(result.current.chat.status).toBe("ready"));
109
+ expect(sent[0]).toEqual(["u1"]);
110
+ });
111
+
112
+ it("keeps an assistant parent when reloading the message after it", async () => {
113
+ const { result, sent } = setup(
114
+ [
115
+ text("u1", "user", "hi"),
116
+ text("a1", "assistant", "first"),
117
+ text("a2", "assistant", "second"),
118
+ ],
119
+ { joinStrategy: "none" },
120
+ );
121
+ await waitFor(() =>
122
+ expect(result.current.runtime.thread.getState().messages).toHaveLength(3),
123
+ );
124
+
125
+ act(() => {
126
+ result.current.runtime.thread.getMessageById("a2").reload();
127
+ });
128
+
129
+ await waitFor(() => expect(sent).toHaveLength(1));
130
+ await waitFor(() => expect(result.current.chat.status).toBe("ready"));
131
+ expect(sent[0]).toEqual(["u1", "a1"]);
132
+ });
133
+
134
+ it("keeps a voice reply when reloading the text reply after it", async () => {
135
+ const { result, sent } = setup([
136
+ text("u1", "user", "hi"),
137
+ { ...text("v1", "assistant", "spoken"), metadata: { modality: "voice" } },
138
+ text("a1", "assistant", "written"),
139
+ ]);
140
+ await waitFor(() =>
141
+ expect(result.current.runtime.thread.getState().messages).toHaveLength(3),
142
+ );
143
+ const reply = result.current.runtime.thread.getState().messages[2]!;
144
+
145
+ act(() => {
146
+ result.current.runtime.thread.getMessageById(reply.id).reload();
147
+ });
148
+
149
+ await waitFor(() => expect(sent).toHaveLength(1));
150
+ await waitFor(() => expect(result.current.chat.status).toBe("ready"));
151
+ expect(sent[0]).toEqual(["u1", "v1"]);
152
+ });
153
+
154
+ it("replaces every part of a joined response when it is edited", async () => {
155
+ const { result, sent } = setup([
156
+ text("u1", "user", "hi"),
157
+ text("a1", "assistant", "old-1"),
158
+ text("a2", "assistant", "old-2"),
159
+ ]);
160
+ await waitFor(() =>
161
+ expect(result.current.runtime.thread.getState().messages).toHaveLength(2),
162
+ );
163
+ const assistant = result.current.runtime.thread.getState().messages[1]!;
164
+ const composer = result.current.runtime.thread.getMessageById(
165
+ assistant.id,
166
+ ).composer;
167
+
168
+ act(() => {
169
+ composer.beginEdit();
170
+ composer.setText("edited");
171
+ });
172
+ await act(async () => {
173
+ await composer.send();
174
+ });
175
+
176
+ await waitFor(() =>
177
+ expect(result.current.chat.messages.map((m) => m.role)).toEqual([
178
+ "user",
179
+ "assistant",
180
+ ]),
181
+ );
182
+ expect(result.current.chat.messages[1]!.parts).toEqual([
183
+ expect.objectContaining({ type: "text", text: "edited" }),
184
+ ]);
185
+ expect(sent).toEqual([]);
186
+ });
187
+
188
+ it("keeps every part of a joined response that a new branch follows", async () => {
189
+ const { result, sent } = setup([
190
+ text("u1", "user", "hi"),
191
+ text("a1", "assistant", "part-1"),
192
+ text("a2", "assistant", "part-2"),
193
+ text("u2", "user", "next"),
194
+ text("a3", "assistant", "answer"),
195
+ ]);
196
+ await waitFor(() =>
197
+ expect(result.current.runtime.thread.getState().messages).toHaveLength(4),
198
+ );
199
+ const assistant = result.current.runtime.thread.getState().messages[1]!;
200
+
201
+ act(() => {
202
+ result.current.runtime.thread.append({
203
+ role: "user",
204
+ parentId: assistant.id,
205
+ content: [{ type: "text", text: "rewritten" }],
206
+ });
207
+ });
208
+
209
+ await waitFor(() => expect(sent).toHaveLength(1));
210
+ expect(sent[0]!.slice(0, 3)).toEqual(["u1", "a1", "a2"]);
211
+ expect(sent[0]).toHaveLength(4);
212
+ expect(result.current.chat.messages[3]).toEqual(
213
+ expect.objectContaining({
214
+ role: "user",
215
+ parts: [expect.objectContaining({ type: "text", text: "rewritten" })],
216
+ }),
217
+ );
218
+ });
219
+ });