@cloudflare/ai-chat 0.0.4 → 0.0.5
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/CHANGELOG.md +17 -0
- package/dist/ai-chat-v5-migration.d.ts +0 -1
- package/dist/ai-chat-v5-migration.js.map +1 -1
- package/dist/index.d.ts +15 -8
- package/dist/index.js +386 -353
- package/dist/index.js.map +1 -1
- package/dist/react.d.ts +27 -48
- package/dist/react.js +18 -18
- package/dist/react.js.map +1 -1
- package/dist/types.d.ts +19 -47
- package/dist/types.js +11 -11
- package/dist/types.js.map +1 -1
- package/package.json +6 -6
- package/src/index.ts +720 -628
- package/src/react-tests/setup.ts +3 -0
- package/src/react-tests/use-agent-chat.test.tsx +35 -23
- package/src/react-tests/vitest.config.ts +2 -1
- package/src/tests/chat-context.test.ts +2 -1
- package/src/tests/chat-persistence.test.ts +4 -3
- package/src/tests/client-tool-duplicate-message.test.ts +9 -8
- package/src/tests/resumable-streaming.test.ts +90 -39
- package/src/tests/worker.ts +24 -0
|
@@ -10,6 +10,10 @@ import {
|
|
|
10
10
|
} from "../react";
|
|
11
11
|
import type { useAgent } from "agents/react";
|
|
12
12
|
|
|
13
|
+
function sleep(ms: number) {
|
|
14
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
15
|
+
}
|
|
16
|
+
|
|
13
17
|
function createAgent({ name, url }: { name: string; url: string }) {
|
|
14
18
|
const target = new EventTarget();
|
|
15
19
|
const baseAgent = {
|
|
@@ -63,15 +67,17 @@ describe("useAgentChat", () => {
|
|
|
63
67
|
return "Suspended";
|
|
64
68
|
};
|
|
65
69
|
|
|
66
|
-
const screen = await act(() =>
|
|
67
|
-
render(<TestComponent />, {
|
|
70
|
+
const screen = await act(async () => {
|
|
71
|
+
const screen = render(<TestComponent />, {
|
|
68
72
|
wrapper: ({ children }) => (
|
|
69
73
|
<StrictMode>
|
|
70
74
|
<Suspense fallback={<SuspenseObserver />}>{children}</Suspense>
|
|
71
75
|
</StrictMode>
|
|
72
76
|
)
|
|
73
|
-
})
|
|
74
|
-
|
|
77
|
+
});
|
|
78
|
+
await sleep(10);
|
|
79
|
+
return screen;
|
|
80
|
+
});
|
|
75
81
|
|
|
76
82
|
await expect
|
|
77
83
|
.element(screen.getByTestId("messages"))
|
|
@@ -123,15 +129,18 @@ describe("useAgentChat", () => {
|
|
|
123
129
|
return "Suspended";
|
|
124
130
|
};
|
|
125
131
|
|
|
126
|
-
const screen = await act(() =>
|
|
127
|
-
render(<TestComponent agent={agentA} />, {
|
|
132
|
+
const screen = await act(async () => {
|
|
133
|
+
const screen = render(<TestComponent agent={agentA} />, {
|
|
128
134
|
wrapper: ({ children }) => (
|
|
129
135
|
<StrictMode>
|
|
130
136
|
<Suspense fallback={<SuspenseObserver />}>{children}</Suspense>
|
|
131
137
|
</StrictMode>
|
|
132
138
|
)
|
|
133
|
-
})
|
|
134
|
-
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
await sleep(10);
|
|
142
|
+
return screen;
|
|
143
|
+
});
|
|
135
144
|
|
|
136
145
|
await expect
|
|
137
146
|
.element(screen.getByTestId("messages"))
|
|
@@ -145,7 +154,10 @@ describe("useAgentChat", () => {
|
|
|
145
154
|
|
|
146
155
|
suspenseRendered.mockClear();
|
|
147
156
|
|
|
148
|
-
await act(() =>
|
|
157
|
+
await act(async () => {
|
|
158
|
+
screen.rerender(<TestComponent agent={agentB} />);
|
|
159
|
+
await sleep(10);
|
|
160
|
+
});
|
|
149
161
|
|
|
150
162
|
await expect
|
|
151
163
|
.element(screen.getByTestId("messages"))
|
|
@@ -219,7 +231,7 @@ describe("useAgentChat", () => {
|
|
|
219
231
|
_options: PrepareSendMessagesRequestOptions<UIMessage>
|
|
220
232
|
): Promise<PrepareSendMessagesRequestResult> => {
|
|
221
233
|
// Simulate async operation like fetching tool definitions
|
|
222
|
-
await
|
|
234
|
+
await sleep(10);
|
|
223
235
|
return {
|
|
224
236
|
body: {
|
|
225
237
|
clientTools: [
|
|
@@ -475,26 +487,24 @@ describe("useAgentChat client-side tool execution (issue #728)", () => {
|
|
|
475
487
|
);
|
|
476
488
|
};
|
|
477
489
|
|
|
478
|
-
const screen = await act(() =>
|
|
479
|
-
render(<TestComponent />, {
|
|
490
|
+
const screen = await act(async () => {
|
|
491
|
+
const screen = render(<TestComponent />, {
|
|
480
492
|
wrapper: ({ children }) => (
|
|
481
493
|
<StrictMode>
|
|
482
494
|
<Suspense fallback="Loading...">{children}</Suspense>
|
|
483
495
|
</StrictMode>
|
|
484
496
|
)
|
|
485
|
-
})
|
|
486
|
-
|
|
497
|
+
});
|
|
498
|
+
// The tool should have been automatically executed
|
|
499
|
+
await sleep(10);
|
|
500
|
+
return screen;
|
|
501
|
+
});
|
|
487
502
|
|
|
488
503
|
// Wait for initial messages to load
|
|
489
504
|
await expect
|
|
490
505
|
.element(screen.getByTestId("messages-count"))
|
|
491
506
|
.toHaveTextContent("2");
|
|
492
507
|
|
|
493
|
-
// The tool should have been automatically executed
|
|
494
|
-
await act(async () => {
|
|
495
|
-
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
496
|
-
});
|
|
497
|
-
|
|
498
508
|
// Verify the tool execute was called
|
|
499
509
|
expect(mockExecute).toHaveBeenCalled();
|
|
500
510
|
|
|
@@ -574,15 +584,17 @@ describe("useAgentChat client-side tool execution (issue #728)", () => {
|
|
|
574
584
|
);
|
|
575
585
|
};
|
|
576
586
|
|
|
577
|
-
const screen = await act(() =>
|
|
578
|
-
render(<TestComponent />, {
|
|
587
|
+
const screen = await act(async () => {
|
|
588
|
+
const screen = render(<TestComponent />, {
|
|
579
589
|
wrapper: ({ children }) => (
|
|
580
590
|
<StrictMode>
|
|
581
591
|
<Suspense fallback="Loading...">{children}</Suspense>
|
|
582
592
|
</StrictMode>
|
|
583
593
|
)
|
|
584
|
-
})
|
|
585
|
-
|
|
594
|
+
});
|
|
595
|
+
await sleep(10);
|
|
596
|
+
return screen;
|
|
597
|
+
});
|
|
586
598
|
|
|
587
599
|
await expect
|
|
588
600
|
.element(screen.getByTestId("messages-count"))
|
|
@@ -3,6 +3,7 @@ import { describe, it, expect } from "vitest";
|
|
|
3
3
|
import { MessageType } from "../types";
|
|
4
4
|
import type { UIMessage as ChatMessage } from "ai";
|
|
5
5
|
import { connectChatWS } from "./test-utils";
|
|
6
|
+
import { getAgentByName } from "agents";
|
|
6
7
|
|
|
7
8
|
describe("AIChatAgent Connection Context - Issue #711", () => {
|
|
8
9
|
it("getCurrentAgent() should return connection in onChatMessage and nested async functions (tool execute)", async () => {
|
|
@@ -10,7 +11,7 @@ describe("AIChatAgent Connection Context - Issue #711", () => {
|
|
|
10
11
|
const { ws } = await connectChatWS(`/agents/test-chat-agent/${room}`);
|
|
11
12
|
|
|
12
13
|
// Get the agent stub to access captured context
|
|
13
|
-
const agentStub =
|
|
14
|
+
const agentStub = await getAgentByName(env.TestChatAgent, room);
|
|
14
15
|
|
|
15
16
|
// Clear any previous captured context
|
|
16
17
|
await agentStub.clearCapturedContext();
|
|
@@ -4,6 +4,7 @@ import worker from "./worker";
|
|
|
4
4
|
import { MessageType } from "../types";
|
|
5
5
|
import type { UIMessage as ChatMessage } from "ai";
|
|
6
6
|
import { connectChatWS } from "./test-utils";
|
|
7
|
+
import { getAgentByName } from "agents";
|
|
7
8
|
|
|
8
9
|
// Type helper for tool call parts - extracts ToolUIPart from ChatMessage parts
|
|
9
10
|
type TestToolCallPart = Extract<
|
|
@@ -198,7 +199,7 @@ describe("Chat Agent Persistence", () => {
|
|
|
198
199
|
|
|
199
200
|
await ctx.waitUntil(Promise.resolve());
|
|
200
201
|
|
|
201
|
-
const agentStub =
|
|
202
|
+
const agentStub = await getAgentByName(env.TestChatAgent, room);
|
|
202
203
|
|
|
203
204
|
await agentStub.testPersistToolCall("msg-tool-1", "getLocalTime");
|
|
204
205
|
|
|
@@ -256,7 +257,7 @@ describe("Chat Agent Persistence", () => {
|
|
|
256
257
|
|
|
257
258
|
await ctx.waitUntil(Promise.resolve());
|
|
258
259
|
|
|
259
|
-
const agentStub =
|
|
260
|
+
const agentStub = await getAgentByName(env.TestChatAgent, room);
|
|
260
261
|
|
|
261
262
|
const userMessage: ChatMessage = {
|
|
262
263
|
id: "user-1",
|
|
@@ -362,7 +363,7 @@ describe("Chat Agent Persistence", () => {
|
|
|
362
363
|
|
|
363
364
|
await ctx.waitUntil(Promise.resolve());
|
|
364
365
|
|
|
365
|
-
const agentStub =
|
|
366
|
+
const agentStub = await getAgentByName(env.TestChatAgent, room);
|
|
366
367
|
|
|
367
368
|
const userMessage: ChatMessage = {
|
|
368
369
|
id: "user-1",
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { createExecutionContext, env } from "cloudflare:test";
|
|
2
|
+
import { getAgentByName } from "agents";
|
|
2
3
|
import { describe, it, expect } from "vitest";
|
|
3
4
|
import worker from "./worker";
|
|
4
5
|
import type { UIMessage as ChatMessage } from "ai";
|
|
@@ -17,7 +18,7 @@ describe("Client-side tool duplicate message prevention", () => {
|
|
|
17
18
|
ws.accept();
|
|
18
19
|
await ctx.waitUntil(Promise.resolve());
|
|
19
20
|
|
|
20
|
-
const agentStub =
|
|
21
|
+
const agentStub = await getAgentByName(env.TestChatAgent, room);
|
|
21
22
|
const toolCallId = "call_merge_test";
|
|
22
23
|
|
|
23
24
|
// Persist assistant message with tool in input-available state
|
|
@@ -91,7 +92,7 @@ describe("Client-side tool duplicate message prevention", () => {
|
|
|
91
92
|
ws.accept();
|
|
92
93
|
await ctx.waitUntil(Promise.resolve());
|
|
93
94
|
|
|
94
|
-
const agentStub =
|
|
95
|
+
const agentStub = await getAgentByName(env.TestChatAgent, room);
|
|
95
96
|
const toolCallId = "call_tool_result_test";
|
|
96
97
|
|
|
97
98
|
// Persist assistant message with tool in input-available state
|
|
@@ -164,7 +165,7 @@ describe("Client-side tool duplicate message prevention", () => {
|
|
|
164
165
|
ws.accept();
|
|
165
166
|
await ctx.waitUntil(Promise.resolve());
|
|
166
167
|
|
|
167
|
-
const agentStub =
|
|
168
|
+
const agentStub = await getAgentByName(env.TestChatAgent, room);
|
|
168
169
|
const toolCallId = "call_tool_result_auto_continue";
|
|
169
170
|
|
|
170
171
|
// Persist assistant message with tool in input-available state
|
|
@@ -239,7 +240,7 @@ describe("Client-side tool duplicate message prevention", () => {
|
|
|
239
240
|
ws.accept();
|
|
240
241
|
await ctx.waitUntil(Promise.resolve());
|
|
241
242
|
|
|
242
|
-
const agentStub =
|
|
243
|
+
const agentStub = await getAgentByName(env.TestChatAgent, room);
|
|
243
244
|
|
|
244
245
|
// Persist message with OpenAI itemId in providerMetadata (simulates OpenAI Responses API)
|
|
245
246
|
await agentStub.persistMessages([
|
|
@@ -299,7 +300,7 @@ describe("Client-side tool duplicate message prevention", () => {
|
|
|
299
300
|
ws.accept();
|
|
300
301
|
await ctx.waitUntil(Promise.resolve());
|
|
301
302
|
|
|
302
|
-
const agentStub =
|
|
303
|
+
const agentStub = await getAgentByName(env.TestChatAgent, room);
|
|
303
304
|
const toolCallId = "call_openai_strip_test";
|
|
304
305
|
|
|
305
306
|
// Persist message with tool that has OpenAI itemId in callProviderMetadata
|
|
@@ -362,7 +363,7 @@ describe("Client-side tool duplicate message prevention", () => {
|
|
|
362
363
|
ws.accept();
|
|
363
364
|
await ctx.waitUntil(Promise.resolve());
|
|
364
365
|
|
|
365
|
-
const agentStub =
|
|
366
|
+
const agentStub = await getAgentByName(env.TestChatAgent, room);
|
|
366
367
|
|
|
367
368
|
// Persist message with other metadata alongside itemId
|
|
368
369
|
await agentStub.persistMessages([
|
|
@@ -433,7 +434,7 @@ describe("Client-side tool duplicate message prevention", () => {
|
|
|
433
434
|
ws.accept();
|
|
434
435
|
await ctx.waitUntil(Promise.resolve());
|
|
435
436
|
|
|
436
|
-
const agentStub =
|
|
437
|
+
const agentStub = await getAgentByName(env.TestChatAgent, room);
|
|
437
438
|
|
|
438
439
|
// Persist message with empty reasoning part (simulates OpenAI Responses API)
|
|
439
440
|
await agentStub.persistMessages([
|
|
@@ -487,7 +488,7 @@ describe("Client-side tool duplicate message prevention", () => {
|
|
|
487
488
|
ws.accept();
|
|
488
489
|
await ctx.waitUntil(Promise.resolve());
|
|
489
490
|
|
|
490
|
-
const agentStub =
|
|
491
|
+
const agentStub = await getAgentByName(env.TestChatAgent, room);
|
|
491
492
|
|
|
492
493
|
// Persist message with non-empty reasoning part
|
|
493
494
|
await agentStub.persistMessages([
|
|
@@ -2,6 +2,7 @@ import { env } from "cloudflare:test";
|
|
|
2
2
|
import { describe, it, expect } from "vitest";
|
|
3
3
|
import { MessageType, type OutgoingMessage } from "../types";
|
|
4
4
|
import { connectChatWS, isUseChatResponseMessage } from "./test-utils";
|
|
5
|
+
import { getAgentByName } from "agents";
|
|
5
6
|
|
|
6
7
|
function isStreamResumingMessage(
|
|
7
8
|
m: unknown
|
|
@@ -37,9 +38,7 @@ describe("Resumable Streaming", () => {
|
|
|
37
38
|
|
|
38
39
|
await new Promise((r) => setTimeout(r, 50));
|
|
39
40
|
|
|
40
|
-
const agentStub = env.TestChatAgent
|
|
41
|
-
env.TestChatAgent.idFromName(room)
|
|
42
|
-
);
|
|
41
|
+
const agentStub = await getAgentByName(env.TestChatAgent, room);
|
|
43
42
|
|
|
44
43
|
const streamId = await agentStub.testStartStream("req-123");
|
|
45
44
|
expect(streamId).toBeDefined();
|
|
@@ -59,9 +58,7 @@ describe("Resumable Streaming", () => {
|
|
|
59
58
|
|
|
60
59
|
await new Promise((r) => setTimeout(r, 50));
|
|
61
60
|
|
|
62
|
-
const agentStub = env.TestChatAgent
|
|
63
|
-
env.TestChatAgent.idFromName(room)
|
|
64
|
-
);
|
|
61
|
+
const agentStub = await getAgentByName(env.TestChatAgent, room);
|
|
65
62
|
|
|
66
63
|
const streamId = await agentStub.testStartStream("req-456");
|
|
67
64
|
|
|
@@ -98,9 +95,7 @@ describe("Resumable Streaming", () => {
|
|
|
98
95
|
|
|
99
96
|
await new Promise((r) => setTimeout(r, 50));
|
|
100
97
|
|
|
101
|
-
const agentStub = env.TestChatAgent
|
|
102
|
-
env.TestChatAgent.idFromName(room)
|
|
103
|
-
);
|
|
98
|
+
const agentStub = await getAgentByName(env.TestChatAgent, room);
|
|
104
99
|
|
|
105
100
|
const streamId = await agentStub.testStartStream("req-789");
|
|
106
101
|
|
|
@@ -127,9 +122,7 @@ describe("Resumable Streaming", () => {
|
|
|
127
122
|
|
|
128
123
|
await new Promise((r) => setTimeout(r, 50));
|
|
129
124
|
|
|
130
|
-
const agentStub = env.TestChatAgent
|
|
131
|
-
env.TestChatAgent.idFromName(room)
|
|
132
|
-
);
|
|
125
|
+
const agentStub = await getAgentByName(env.TestChatAgent, room);
|
|
133
126
|
|
|
134
127
|
const streamId = await agentStub.testStartStream("req-error");
|
|
135
128
|
|
|
@@ -156,9 +149,7 @@ describe("Resumable Streaming", () => {
|
|
|
156
149
|
);
|
|
157
150
|
await new Promise((r) => setTimeout(r, 50));
|
|
158
151
|
|
|
159
|
-
const agentStub = env.TestChatAgent
|
|
160
|
-
env.TestChatAgent.idFromName(room)
|
|
161
|
-
);
|
|
152
|
+
const agentStub = await getAgentByName(env.TestChatAgent, room);
|
|
162
153
|
const streamId = await agentStub.testStartStream("req-resume");
|
|
163
154
|
await agentStub.testStoreStreamChunk(
|
|
164
155
|
streamId,
|
|
@@ -193,9 +184,7 @@ describe("Resumable Streaming", () => {
|
|
|
193
184
|
);
|
|
194
185
|
await new Promise((r) => setTimeout(r, 50));
|
|
195
186
|
|
|
196
|
-
const agentStub = env.TestChatAgent
|
|
197
|
-
env.TestChatAgent.idFromName(room)
|
|
198
|
-
);
|
|
187
|
+
const agentStub = await getAgentByName(env.TestChatAgent, room);
|
|
199
188
|
const streamId = await agentStub.testStartStream("req-ack");
|
|
200
189
|
await agentStub.testStoreStreamChunk(
|
|
201
190
|
streamId,
|
|
@@ -237,6 +226,82 @@ describe("Resumable Streaming", () => {
|
|
|
237
226
|
ws2.close();
|
|
238
227
|
});
|
|
239
228
|
|
|
229
|
+
it("does not deliver live chunks before ACK to resuming connections", async () => {
|
|
230
|
+
const room = crypto.randomUUID();
|
|
231
|
+
|
|
232
|
+
// First connection - start a stream
|
|
233
|
+
const { ws: ws1 } = await connectChatWS(
|
|
234
|
+
`/agents/test-chat-agent/${room}`
|
|
235
|
+
);
|
|
236
|
+
const messages1 = collectMessages(ws1);
|
|
237
|
+
await new Promise((r) => setTimeout(r, 50));
|
|
238
|
+
|
|
239
|
+
const agentStub = await getAgentByName(env.TestChatAgent, room);
|
|
240
|
+
const streamId = await agentStub.testStartStream("req-live");
|
|
241
|
+
|
|
242
|
+
// Second connection - will be notified to resume
|
|
243
|
+
const { ws: ws2 } = await connectChatWS(
|
|
244
|
+
`/agents/test-chat-agent/${room}`
|
|
245
|
+
);
|
|
246
|
+
const messages2 = collectMessages(ws2);
|
|
247
|
+
|
|
248
|
+
await new Promise((r) => setTimeout(r, 100));
|
|
249
|
+
|
|
250
|
+
// Broadcast a live chunk while ws2 is pending resume (no ACK yet)
|
|
251
|
+
await agentStub.testBroadcastLiveChunk(
|
|
252
|
+
"req-live",
|
|
253
|
+
streamId,
|
|
254
|
+
'{"type":"text-delta","id":"0","delta":"A"}'
|
|
255
|
+
);
|
|
256
|
+
|
|
257
|
+
await new Promise((r) => setTimeout(r, 100));
|
|
258
|
+
|
|
259
|
+
// ws2 should NOT receive live chunks before ACK
|
|
260
|
+
const preAckChunks = messages2.filter(isUseChatResponseMessage);
|
|
261
|
+
expect(preAckChunks.length).toBe(0);
|
|
262
|
+
|
|
263
|
+
// ws1 should receive the live chunk
|
|
264
|
+
const ws1Chunks = messages1.filter(isUseChatResponseMessage);
|
|
265
|
+
expect(ws1Chunks.length).toBe(1);
|
|
266
|
+
expect(ws1Chunks[0].body).toBe(
|
|
267
|
+
'{"type":"text-delta","id":"0","delta":"A"}'
|
|
268
|
+
);
|
|
269
|
+
|
|
270
|
+
// Send ACK to resume
|
|
271
|
+
ws2.send(
|
|
272
|
+
JSON.stringify({
|
|
273
|
+
type: MessageType.CF_AGENT_STREAM_RESUME_ACK,
|
|
274
|
+
id: "req-live"
|
|
275
|
+
})
|
|
276
|
+
);
|
|
277
|
+
|
|
278
|
+
await new Promise((r) => setTimeout(r, 100));
|
|
279
|
+
|
|
280
|
+
// After ACK, ws2 should receive the replayed chunk
|
|
281
|
+
const postAckChunks = messages2.filter(isUseChatResponseMessage);
|
|
282
|
+
expect(postAckChunks.length).toBeGreaterThanOrEqual(1);
|
|
283
|
+
expect(postAckChunks[0].body).toBe(
|
|
284
|
+
'{"type":"text-delta","id":"0","delta":"A"}'
|
|
285
|
+
);
|
|
286
|
+
|
|
287
|
+
// Live chunks after ACK should be delivered
|
|
288
|
+
await agentStub.testBroadcastLiveChunk(
|
|
289
|
+
"req-live",
|
|
290
|
+
streamId,
|
|
291
|
+
'{"type":"text-delta","id":"0","delta":"B"}'
|
|
292
|
+
);
|
|
293
|
+
|
|
294
|
+
await new Promise((r) => setTimeout(r, 100));
|
|
295
|
+
|
|
296
|
+
const finalChunks = messages2.filter(isUseChatResponseMessage);
|
|
297
|
+
expect(finalChunks.some((m) => m.body?.includes('"delta":"B"'))).toBe(
|
|
298
|
+
true
|
|
299
|
+
);
|
|
300
|
+
|
|
301
|
+
ws1.close();
|
|
302
|
+
ws2.close();
|
|
303
|
+
});
|
|
304
|
+
|
|
240
305
|
it("ignores ACK with wrong request ID", async () => {
|
|
241
306
|
const room = crypto.randomUUID();
|
|
242
307
|
|
|
@@ -246,9 +311,7 @@ describe("Resumable Streaming", () => {
|
|
|
246
311
|
);
|
|
247
312
|
await new Promise((r) => setTimeout(r, 50));
|
|
248
313
|
|
|
249
|
-
const agentStub = env.TestChatAgent
|
|
250
|
-
env.TestChatAgent.idFromName(room)
|
|
251
|
-
);
|
|
314
|
+
const agentStub = await getAgentByName(env.TestChatAgent, room);
|
|
252
315
|
const streamId = await agentStub.testStartStream("req-correct");
|
|
253
316
|
await agentStub.testStoreStreamChunk(
|
|
254
317
|
streamId,
|
|
@@ -292,9 +355,7 @@ describe("Resumable Streaming", () => {
|
|
|
292
355
|
|
|
293
356
|
await new Promise((r) => setTimeout(r, 50));
|
|
294
357
|
|
|
295
|
-
const agentStub = env.TestChatAgent
|
|
296
|
-
env.TestChatAgent.idFromName(room)
|
|
297
|
-
);
|
|
358
|
+
const agentStub = await getAgentByName(env.TestChatAgent, room);
|
|
298
359
|
|
|
299
360
|
// Insert a stale stream (6 minutes old)
|
|
300
361
|
const staleStreamId = "stale-stream-123";
|
|
@@ -327,9 +388,7 @@ describe("Resumable Streaming", () => {
|
|
|
327
388
|
|
|
328
389
|
await new Promise((r) => setTimeout(r, 50));
|
|
329
390
|
|
|
330
|
-
const agentStub = env.TestChatAgent
|
|
331
|
-
env.TestChatAgent.idFromName(room)
|
|
332
|
-
);
|
|
391
|
+
const agentStub = await getAgentByName(env.TestChatAgent, room);
|
|
333
392
|
|
|
334
393
|
// Insert a fresh stream (1 minute old)
|
|
335
394
|
const freshStreamId = "fresh-stream-456";
|
|
@@ -363,9 +422,7 @@ describe("Resumable Streaming", () => {
|
|
|
363
422
|
|
|
364
423
|
await new Promise((r) => setTimeout(r, 50));
|
|
365
424
|
|
|
366
|
-
const agentStub = env.TestChatAgent
|
|
367
|
-
env.TestChatAgent.idFromName(room)
|
|
368
|
-
);
|
|
425
|
+
const agentStub = await getAgentByName(env.TestChatAgent, room);
|
|
369
426
|
|
|
370
427
|
// Create a stream with chunks
|
|
371
428
|
const streamId = await agentStub.testStartStream("req-clear");
|
|
@@ -405,9 +462,7 @@ describe("Resumable Streaming", () => {
|
|
|
405
462
|
|
|
406
463
|
await new Promise((r) => setTimeout(r, 50));
|
|
407
464
|
|
|
408
|
-
const agentStub = env.TestChatAgent
|
|
409
|
-
env.TestChatAgent.idFromName(room)
|
|
410
|
-
);
|
|
465
|
+
const agentStub = await getAgentByName(env.TestChatAgent, room);
|
|
411
466
|
|
|
412
467
|
// Start first stream and add chunks without explicit flush
|
|
413
468
|
const stream1 = await agentStub.testStartStream("req-1");
|
|
@@ -439,9 +494,7 @@ describe("Resumable Streaming", () => {
|
|
|
439
494
|
|
|
440
495
|
await new Promise((r) => setTimeout(r, 50));
|
|
441
496
|
|
|
442
|
-
const agentStub = env.TestChatAgent
|
|
443
|
-
env.TestChatAgent.idFromName(room)
|
|
444
|
-
);
|
|
497
|
+
const agentStub = await getAgentByName(env.TestChatAgent, room);
|
|
445
498
|
|
|
446
499
|
const streamId = await agentStub.testStartStream("req-flush");
|
|
447
500
|
await agentStub.testStoreStreamChunk(
|
|
@@ -470,9 +523,7 @@ describe("Resumable Streaming", () => {
|
|
|
470
523
|
);
|
|
471
524
|
await new Promise((r) => setTimeout(r, 50));
|
|
472
525
|
|
|
473
|
-
const agentStub = env.TestChatAgent
|
|
474
|
-
env.TestChatAgent.idFromName(room)
|
|
475
|
-
);
|
|
526
|
+
const agentStub = await getAgentByName(env.TestChatAgent, room);
|
|
476
527
|
const streamId = await agentStub.testStartStream("req-done");
|
|
477
528
|
await agentStub.testStoreStreamChunk(
|
|
478
529
|
streamId,
|
package/src/tests/worker.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { AIChatAgent } from "../";
|
|
2
2
|
import type { UIMessage as ChatMessage } from "ai";
|
|
3
3
|
import { callable, getCurrentAgent, routeAgentRequest } from "agents";
|
|
4
|
+
import { MessageType, type OutgoingMessage } from "../types";
|
|
4
5
|
|
|
5
6
|
// Type helper for tool call parts - extracts from ChatMessage parts
|
|
6
7
|
type TestToolCallPart = Extract<
|
|
@@ -148,6 +149,29 @@ export class TestChatAgent extends AIChatAgent<Env> {
|
|
|
148
149
|
this._storeStreamChunk(streamId, body);
|
|
149
150
|
}
|
|
150
151
|
|
|
152
|
+
@callable()
|
|
153
|
+
testBroadcastLiveChunk(
|
|
154
|
+
requestId: string,
|
|
155
|
+
streamId: string,
|
|
156
|
+
body: string
|
|
157
|
+
): void {
|
|
158
|
+
this._storeStreamChunk(streamId, body);
|
|
159
|
+
const message: OutgoingMessage = {
|
|
160
|
+
body,
|
|
161
|
+
done: false,
|
|
162
|
+
id: requestId,
|
|
163
|
+
type: MessageType.CF_AGENT_USE_CHAT_RESPONSE
|
|
164
|
+
};
|
|
165
|
+
(
|
|
166
|
+
this as unknown as {
|
|
167
|
+
_broadcastChatMessage: (
|
|
168
|
+
msg: OutgoingMessage,
|
|
169
|
+
exclude?: string[]
|
|
170
|
+
) => void;
|
|
171
|
+
}
|
|
172
|
+
)._broadcastChatMessage(message);
|
|
173
|
+
}
|
|
174
|
+
|
|
151
175
|
@callable()
|
|
152
176
|
testFlushChunkBuffer(): void {
|
|
153
177
|
this._flushChunkBuffer();
|