@assistant-ui/ai-sdk 0.0.7 → 0.0.9
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/LICENSE +1 -1
- package/README.md +1 -1
- package/dist/adapters/aiSDKFormatAdapter.d.ts +2 -2
- package/dist/adapters/aiSDKFormatAdapter.d.ts.map +1 -0
- package/dist/adapters/vercelAttachmentAdapter.d.ts +1 -3
- package/dist/adapters/vercelAttachmentAdapter.d.ts.map +1 -1
- package/dist/adapters/vercelAttachmentAdapter.js +3 -3
- package/dist/adapters/vercelAttachmentAdapter.js.map +1 -1
- package/dist/aiSDKExtras.d.ts +4 -6
- package/dist/aiSDKExtras.d.ts.map +1 -1
- package/dist/converters/convertMessage.d.ts +34 -24
- package/dist/converters/convertMessage.d.ts.map +1 -1
- package/dist/converters/convertMessage.js +56 -12
- package/dist/converters/convertMessage.js.map +1 -1
- package/dist/converters/modelContentEnvelope.d.ts +6 -7
- package/dist/converters/modelContentEnvelope.d.ts.map +1 -1
- package/dist/converters/toCreateMessage.d.ts +2 -4
- package/dist/converters/toCreateMessage.d.ts.map +1 -1
- package/dist/converters/toolOutputConversion.d.ts +20 -22
- package/dist/converters/toolOutputConversion.d.ts.map +1 -1
- package/dist/hooks.d.ts +2 -4
- package/dist/hooks.d.ts.map +1 -1
- package/dist/index.d.ts +17 -14
- package/dist/index.d.ts.map +1 -0
- package/dist/index.native.d.ts +15 -12
- package/dist/index.native.d.ts.map +1 -0
- package/dist/model-context/injectInteractableContext.d.ts +2 -4
- package/dist/model-context/injectInteractableContext.d.ts.map +1 -1
- package/dist/model-context/injectQuoteContext.d.ts +1 -3
- package/dist/model-context/injectQuoteContext.d.ts.map +1 -1
- package/dist/runtime/AISDKChat.d.ts +2 -4
- package/dist/runtime/AISDKChat.d.ts.map +1 -1
- package/dist/runtime/AISDKChat.js +1 -1
- package/dist/runtime/AISDKChat.js.map +1 -1
- package/dist/runtime/AISDKThreads.d.ts +29 -31
- package/dist/runtime/AISDKThreads.d.ts.map +1 -1
- package/dist/runtime/AISDKThreads.js +22 -23
- package/dist/runtime/AISDKThreads.js.map +1 -1
- package/dist/runtime/sdkIdentity.d.ts +1 -3
- package/dist/runtime/sdkIdentity.d.ts.map +1 -1
- package/dist/runtime/sdkIdentity.js +1 -1
- package/dist/runtime/useAISDKRuntime.d.ts +76 -70
- package/dist/runtime/useAISDKRuntime.d.ts.map +1 -1
- package/dist/runtime/useAISDKRuntime.js +311 -77
- package/dist/runtime/useAISDKRuntime.js.map +1 -1
- package/dist/runtime/useChatRuntime.d.ts +6 -8
- package/dist/runtime/useChatRuntime.d.ts.map +1 -1
- package/dist/runtime/useChatRuntime.js +14 -10
- package/dist/runtime/useChatRuntime.js.map +1 -1
- package/dist/runtime/useChatThread.d.ts +94 -89
- package/dist/runtime/useChatThread.d.ts.map +1 -1
- package/dist/runtime/useChatThread.js +44 -15
- package/dist/runtime/useChatThread.js.map +1 -1
- package/dist/runtime/useExternalHistory.d.ts +7 -7
- package/dist/runtime/useExternalHistory.d.ts.map +1 -1
- package/dist/runtime/useExternalHistory.js +231 -60
- package/dist/runtime/useExternalHistory.js.map +1 -1
- package/dist/runtime/useResourceCleanup.d.ts +1 -3
- package/dist/runtime/useResourceCleanup.d.ts.map +1 -1
- package/dist/runtime/useResourceCleanup.js +38 -14
- package/dist/runtime/useResourceCleanup.js.map +1 -1
- package/dist/runtime/useStreamingTiming.d.ts +3 -5
- package/dist/runtime/useStreamingTiming.d.ts.map +1 -1
- package/dist/runtime/useStreamingTiming.js +1 -1
- package/dist/tools/frontendTools.d.ts +21 -23
- package/dist/tools/frontendTools.d.ts.map +1 -1
- package/dist/tools/generativeTools.d.ts +24 -26
- package/dist/tools/generativeTools.d.ts.map +1 -1
- package/dist/tools/mcp-stdio.node.d.ts +2 -2
- package/dist/tools/mcp-stdio.node.d.ts.map +1 -0
- package/dist/tools/mcp-stdio.unsupported.d.ts +2 -4
- package/dist/tools/mcp-stdio.unsupported.d.ts.map +1 -1
- package/dist/transport/AssistantChatTransport.d.ts +14 -16
- package/dist/transport/AssistantChatTransport.d.ts.map +1 -1
- package/dist/transport/AssistantChatTransport.js +9 -2
- package/dist/transport/AssistantChatTransport.js.map +1 -1
- package/dist/transport/resumable.d.ts +25 -27
- package/dist/transport/resumable.d.ts.map +1 -1
- package/dist/usage.d.ts +8 -10
- package/dist/usage.d.ts.map +1 -1
- package/dist/usage.js.map +1 -1
- package/dist/utils/getVercelAIMessages.d.ts +2 -4
- package/dist/utils/getVercelAIMessages.d.ts.map +1 -1
- package/dist/utils/sliceMessagesUntil.d.ts +1 -3
- package/dist/utils/sliceMessagesUntil.d.ts.map +1 -1
- package/package.json +13 -9
- package/src/adapters/vercelAttachmentAdapter.test.ts +23 -0
- package/src/adapters/vercelAttachmentAdapter.ts +2 -2
- package/src/converters/convertMessage.test.ts +541 -3
- package/src/converters/convertMessage.ts +123 -15
- package/src/index.native.ts +1 -1
- package/src/index.ts +1 -1
- package/src/runtime/AISDKChat.ts +0 -4
- package/src/runtime/AISDKThreads.cloud.test.ts +60 -2
- package/src/runtime/AISDKThreads.test.ts +105 -46
- package/src/runtime/AISDKThreads.ts +1 -1
- package/src/runtime/useAISDKRuntime.approval.integration.test.tsx +1639 -19
- package/src/runtime/useAISDKRuntime.approval.test.tsx +252 -9
- package/src/runtime/useAISDKRuntime.fast-refresh.test.tsx +182 -0
- package/src/runtime/useAISDKRuntime.test.ts +275 -5
- package/src/runtime/useAISDKRuntime.ts +533 -60
- package/src/runtime/useAISDKRuntime.voice.test.tsx +50 -1
- package/src/runtime/useChatRuntime.fast-refresh.test.tsx +94 -0
- package/src/runtime/useChatRuntime.integration.test.tsx +143 -65
- package/src/runtime/useChatRuntime.test.ts +11 -7
- package/src/runtime/useChatRuntime.ts +6 -2
- package/src/runtime/useChatThread.test.ts +166 -2
- package/src/runtime/useChatThread.transport.test.tsx +152 -0
- package/src/runtime/useChatThread.ts +89 -22
- package/src/runtime/useExternalHistory.test.ts +943 -3
- package/src/runtime/useExternalHistory.ts +378 -15
- package/src/runtime/useResourceCleanup.ts +41 -15
- package/src/transport/AssistantChatTransport.test.ts +164 -0
- package/src/transport/AssistantChatTransport.ts +22 -2
- package/src/usage.ts +1 -1
|
@@ -205,6 +205,170 @@ const wrappedFetchOf = (
|
|
|
205
205
|
).fetch;
|
|
206
206
|
|
|
207
207
|
describe("AssistantChatTransport resumable fetch wrapper", () => {
|
|
208
|
+
it("keeps a replacement checkpoint set while preparing an older reconnect", async () => {
|
|
209
|
+
const storage = createMemoryStorage("stream-old");
|
|
210
|
+
let finishPrepare!: () => void;
|
|
211
|
+
const prepareReconnectToStreamRequest = vi.fn(async () => {
|
|
212
|
+
await new Promise<void>((resolve) => {
|
|
213
|
+
finishPrepare = resolve;
|
|
214
|
+
});
|
|
215
|
+
return { headers: { "x-custom": "retained" } };
|
|
216
|
+
});
|
|
217
|
+
const fetch = vi
|
|
218
|
+
.fn<typeof globalThis.fetch>()
|
|
219
|
+
.mockResolvedValue(new Response(null, { status: 204 }));
|
|
220
|
+
const transport = new AssistantChatTransport({
|
|
221
|
+
fetch,
|
|
222
|
+
prepareReconnectToStreamRequest,
|
|
223
|
+
resumable: { storage, resumeApi: (id) => `/api/resume/${id}` },
|
|
224
|
+
});
|
|
225
|
+
const pending = transport.reconnectToStream({ chatId: "thread" });
|
|
226
|
+
await vi.waitFor(() =>
|
|
227
|
+
expect(prepareReconnectToStreamRequest).toHaveBeenCalledOnce(),
|
|
228
|
+
);
|
|
229
|
+
storage.setStreamId("stream-new");
|
|
230
|
+
finishPrepare();
|
|
231
|
+
await expect(pending).resolves.toBeNull();
|
|
232
|
+
expect(fetch.mock.calls[0]?.[0]).toBe("/api/resume/stream-old");
|
|
233
|
+
expect(
|
|
234
|
+
Array.from(new Headers(fetch.mock.calls[0]?.[1]?.headers).entries()),
|
|
235
|
+
).toEqual([["x-custom", "retained"]]);
|
|
236
|
+
expect(storage.getStreamId()).toBe("stream-new");
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
it.each([
|
|
240
|
+
{ status: 204, replaceCheckpoint: false },
|
|
241
|
+
{ status: 204, replaceCheckpoint: true },
|
|
242
|
+
{ status: 404, replaceCheckpoint: false },
|
|
243
|
+
{ status: 404, replaceCheckpoint: true },
|
|
244
|
+
])(
|
|
245
|
+
"clears only the matching checkpoint after $status (replacement: $replaceCheckpoint)",
|
|
246
|
+
async ({ status, replaceCheckpoint }) => {
|
|
247
|
+
const storage = createMemoryStorage("stream-old");
|
|
248
|
+
let respond!: (response: Response) => void;
|
|
249
|
+
const fetch = vi.fn(
|
|
250
|
+
() =>
|
|
251
|
+
new Promise<Response>((resolve) => {
|
|
252
|
+
respond = resolve;
|
|
253
|
+
}),
|
|
254
|
+
);
|
|
255
|
+
const transport = new AssistantChatTransport({
|
|
256
|
+
fetch,
|
|
257
|
+
resumable: { storage, resumeApi: "/api/resume" },
|
|
258
|
+
});
|
|
259
|
+
const pending = transport.reconnectToStream({ chatId: "thread" });
|
|
260
|
+
await vi.waitFor(() => expect(fetch).toHaveBeenCalledOnce());
|
|
261
|
+
if (replaceCheckpoint) storage.setStreamId("stream-new");
|
|
262
|
+
respond(
|
|
263
|
+
new Response(status === 404 ? "stream expired" : null, { status }),
|
|
264
|
+
);
|
|
265
|
+
if (status === 404) {
|
|
266
|
+
await expect(pending).rejects.toThrow("stream expired");
|
|
267
|
+
} else {
|
|
268
|
+
await expect(pending).resolves.toBeNull();
|
|
269
|
+
}
|
|
270
|
+
expect(storage.getStreamId()).toBe(
|
|
271
|
+
replaceCheckpoint ? "stream-new" : null,
|
|
272
|
+
);
|
|
273
|
+
},
|
|
274
|
+
);
|
|
275
|
+
|
|
276
|
+
it.each([undefined, "stream-old", "response-id"])(
|
|
277
|
+
"preserves a replacement checkpoint on a delayed successful reconnect (%s)",
|
|
278
|
+
async (responseId) => {
|
|
279
|
+
const storage = createMemoryStorage("stream-old");
|
|
280
|
+
let respond!: (response: Response) => void;
|
|
281
|
+
const fetch = vi.fn(
|
|
282
|
+
() =>
|
|
283
|
+
new Promise<Response>((resolve) => {
|
|
284
|
+
respond = resolve;
|
|
285
|
+
}),
|
|
286
|
+
);
|
|
287
|
+
const transport = new AssistantChatTransport({
|
|
288
|
+
fetch,
|
|
289
|
+
resumable: { storage, resumeApi: "/api/resume" },
|
|
290
|
+
});
|
|
291
|
+
const pending = transport.reconnectToStream({ chatId: "thread" });
|
|
292
|
+
await vi.waitFor(() => expect(fetch).toHaveBeenCalledOnce());
|
|
293
|
+
storage.setStreamId("stream-new");
|
|
294
|
+
respond(
|
|
295
|
+
new Response('data: {"type":"finish"}\n\n', {
|
|
296
|
+
headers: {
|
|
297
|
+
"content-type": "text/event-stream",
|
|
298
|
+
...(responseId && { [RESUMABLE_STREAM_ID_HEADER]: responseId }),
|
|
299
|
+
},
|
|
300
|
+
}),
|
|
301
|
+
);
|
|
302
|
+
const stream = await pending;
|
|
303
|
+
expect(storage.getStreamId()).toBe("stream-new");
|
|
304
|
+
const reader = stream!.getReader();
|
|
305
|
+
while (!(await reader.read()).done) {}
|
|
306
|
+
expect(storage.getStreamId()).toBe("stream-new");
|
|
307
|
+
},
|
|
308
|
+
);
|
|
309
|
+
|
|
310
|
+
it.each([undefined, "stream-old", "response-id"])(
|
|
311
|
+
"preserves a checkpoint replaced while consuming a reconnect (%s)",
|
|
312
|
+
async (responseId) => {
|
|
313
|
+
const storage = createMemoryStorage("stream-old");
|
|
314
|
+
let controller!: ReadableStreamDefaultController<Uint8Array>;
|
|
315
|
+
const transport = new AssistantChatTransport({
|
|
316
|
+
fetch: vi.fn(
|
|
317
|
+
async () =>
|
|
318
|
+
new Response(
|
|
319
|
+
new ReadableStream({
|
|
320
|
+
start(value) {
|
|
321
|
+
controller = value;
|
|
322
|
+
},
|
|
323
|
+
}),
|
|
324
|
+
{
|
|
325
|
+
headers: {
|
|
326
|
+
"content-type": "text/event-stream",
|
|
327
|
+
...(responseId && {
|
|
328
|
+
[RESUMABLE_STREAM_ID_HEADER]: responseId,
|
|
329
|
+
}),
|
|
330
|
+
},
|
|
331
|
+
},
|
|
332
|
+
),
|
|
333
|
+
),
|
|
334
|
+
resumable: { storage, resumeApi: "/api/resume" },
|
|
335
|
+
});
|
|
336
|
+
const stream = await transport.reconnectToStream({ chatId: "thread" });
|
|
337
|
+
expect(storage.getStreamId()).toBe(responseId ?? "stream-old");
|
|
338
|
+
storage.setStreamId("stream-new");
|
|
339
|
+
controller.enqueue(
|
|
340
|
+
new TextEncoder().encode('data: {"type":"finish"}\n\n'),
|
|
341
|
+
);
|
|
342
|
+
controller.close();
|
|
343
|
+
const reader = stream!.getReader();
|
|
344
|
+
while (!(await reader.read()).done) {}
|
|
345
|
+
expect(storage.getStreamId()).toBe("stream-new");
|
|
346
|
+
},
|
|
347
|
+
);
|
|
348
|
+
|
|
349
|
+
it.each([undefined, "stream-old", "response-id"])(
|
|
350
|
+
"clears the checkpoint owned by a completed reconnect (%s)",
|
|
351
|
+
async (responseId) => {
|
|
352
|
+
const storage = createMemoryStorage("stream-old");
|
|
353
|
+
const transport = new AssistantChatTransport({
|
|
354
|
+
fetch: vi.fn(
|
|
355
|
+
async () =>
|
|
356
|
+
new Response('data: {"type":"finish"}\n\n', {
|
|
357
|
+
headers: {
|
|
358
|
+
"content-type": "text/event-stream",
|
|
359
|
+
...(responseId && { [RESUMABLE_STREAM_ID_HEADER]: responseId }),
|
|
360
|
+
},
|
|
361
|
+
}),
|
|
362
|
+
),
|
|
363
|
+
resumable: { storage, resumeApi: "/api/resume" },
|
|
364
|
+
});
|
|
365
|
+
const stream = await transport.reconnectToStream({ chatId: "thread" });
|
|
366
|
+
const reader = stream!.getReader();
|
|
367
|
+
while (!(await reader.read()).done) {}
|
|
368
|
+
expect(storage.getStreamId()).toBeNull();
|
|
369
|
+
},
|
|
370
|
+
);
|
|
371
|
+
|
|
208
372
|
it("passes a 204 with a non-null empty body through untouched (WebKit)", async () => {
|
|
209
373
|
const response = nullBodyStatusWithBody(204);
|
|
210
374
|
const fetchMock = vi.fn(async () => response);
|
|
@@ -22,6 +22,7 @@ const FINISH_MARKER = '"type":"finish"';
|
|
|
22
22
|
const FINISH_BUFFER_LIMIT = 4096;
|
|
23
23
|
const FINISH_BUFFER_TAIL = 1024;
|
|
24
24
|
const RESUMABLE_THREAD_ID_HEADER = "x-assistant-ui-resumable-thread-id";
|
|
25
|
+
const RESUMABLE_RECONNECT_ID_HEADER = "x-assistant-ui-resumable-reconnect-id";
|
|
25
26
|
|
|
26
27
|
// 101/204/205/304 are null-body statuses per the fetch spec: `new Response(body, { status })`
|
|
27
28
|
// throws for them, and WebKit returns a non-null empty body, so the body check alone does not guard it.
|
|
@@ -137,10 +138,24 @@ function wrapFetchWithResumable(
|
|
|
137
138
|
return async (input, init) => {
|
|
138
139
|
const headers = new Headers(init?.headers);
|
|
139
140
|
const threadId = headers.get(RESUMABLE_THREAD_ID_HEADER) ?? undefined;
|
|
141
|
+
const reconnectingStreamId = headers.get(RESUMABLE_RECONNECT_ID_HEADER);
|
|
142
|
+
const checkpointAtRequest =
|
|
143
|
+
reconnectingStreamId ?? resumable.storage.getStreamId(threadId);
|
|
140
144
|
headers.delete(RESUMABLE_THREAD_ID_HEADER);
|
|
145
|
+
headers.delete(RESUMABLE_RECONNECT_ID_HEADER);
|
|
141
146
|
const res = await baseFetch(input, { ...init, headers });
|
|
142
147
|
const id = res.headers.get(RESUMABLE_STREAM_ID_HEADER);
|
|
143
|
-
|
|
148
|
+
const ownsCheckpoint =
|
|
149
|
+
!reconnectingStreamId ||
|
|
150
|
+
resumable.storage.getStreamId(threadId) === reconnectingStreamId;
|
|
151
|
+
if (id && ownsCheckpoint) resumable.storage.setStreamId(id, threadId);
|
|
152
|
+
if (
|
|
153
|
+
(res.status === 204 || res.status === 404) &&
|
|
154
|
+
reconnectingStreamId &&
|
|
155
|
+
resumable.storage.getStreamId(threadId) === reconnectingStreamId
|
|
156
|
+
) {
|
|
157
|
+
resumable.storage.clear(threadId);
|
|
158
|
+
}
|
|
144
159
|
if (!res.body || NULL_BODY_STATUSES.has(res.status)) return res;
|
|
145
160
|
|
|
146
161
|
const detectFinish = resumable.isFinishEvent ?? defaultIsFinishEvent;
|
|
@@ -153,7 +168,11 @@ function wrapFetchWithResumable(
|
|
|
153
168
|
controller.enqueue(chunk);
|
|
154
169
|
accumulator += decoder.decode(chunk, { stream: true });
|
|
155
170
|
if (detectFinish(chunk, accumulator)) {
|
|
156
|
-
if (
|
|
171
|
+
if (
|
|
172
|
+
ownsCheckpoint &&
|
|
173
|
+
resumable.storage.getStreamId(threadId) ===
|
|
174
|
+
(id ?? checkpointAtRequest)
|
|
175
|
+
) {
|
|
157
176
|
resumable.storage.clear(threadId);
|
|
158
177
|
}
|
|
159
178
|
accumulator = "";
|
|
@@ -195,6 +214,7 @@ function wrapPrepareReconnect(
|
|
|
195
214
|
const userPrepared = await userPrepareReconnect?.({ ...options, api });
|
|
196
215
|
const headers = new Headers(userPrepared?.headers ?? options.headers);
|
|
197
216
|
headers.set(RESUMABLE_THREAD_ID_HEADER, options.id);
|
|
217
|
+
headers.set(RESUMABLE_RECONNECT_ID_HEADER, streamId);
|
|
198
218
|
return {
|
|
199
219
|
...userPrepared,
|
|
200
220
|
headers,
|
package/src/usage.ts
CHANGED