@alexkroman1/aai 1.4.5 → 1.5.1
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/.turbo/turbo-build.log +10 -10
- package/CHANGELOG.md +19 -0
- package/dist/{_internal-types-3p3OJZPb.js → _internal-types-DFL07G3f.js} +2 -0
- package/dist/assemblyai-C969QGi4.js +35 -0
- package/dist/cartesia-BfQPOQ7Y.js +37 -0
- package/dist/host/_pipeline-test-fakes.d.ts +3 -1
- package/dist/host/providers/stt/deepgram.d.ts +28 -0
- package/dist/host/providers/tts/cartesia.d.ts +1 -1
- package/dist/host/providers/tts/rime.d.ts +44 -0
- package/dist/host/runtime-barrel.d.ts +4 -2
- package/dist/host/runtime-barrel.js +1434 -1209
- package/dist/host/runtime.d.ts +2 -2
- package/dist/host/s2s.d.ts +16 -16
- package/dist/host/session-core.d.ts +37 -0
- package/dist/host/transports/pipeline-transport.d.ts +48 -0
- package/dist/host/transports/s2s-transport.d.ts +19 -0
- package/dist/host/transports/types.d.ts +45 -0
- package/dist/host/ws-handler.d.ts +14 -10
- package/dist/sdk/_internal-types.d.ts +2 -0
- package/dist/sdk/manifest-barrel.js +1 -1
- package/dist/sdk/protocol.d.ts +6 -5
- package/dist/sdk/providers/llm-barrel.js +1 -1
- package/dist/sdk/providers/stt/deepgram.d.ts +35 -0
- package/dist/sdk/providers/stt-barrel.d.ts +1 -0
- package/dist/sdk/providers/stt-barrel.js +2 -2
- package/dist/sdk/providers/tts/cartesia.d.ts +12 -4
- package/dist/sdk/providers/tts/rime.d.ts +42 -0
- package/dist/sdk/providers/tts-barrel.d.ts +1 -0
- package/dist/sdk/providers/tts-barrel.js +2 -2
- package/host/_pipeline-test-fakes.ts +6 -3
- package/host/_test-utils.ts +209 -128
- package/host/builtin-tools.ts +1 -0
- package/host/cleanup.test.ts +25 -298
- package/host/integration/pipeline-reference.integration.test.ts +30 -35
- package/host/providers/resolve.ts +10 -2
- package/host/providers/stt/deepgram.test.ts +229 -0
- package/host/providers/stt/deepgram.ts +172 -0
- package/host/providers/tts/cartesia.ts +7 -3
- package/host/providers/tts/rime.test.ts +251 -0
- package/host/providers/tts/rime.ts +322 -0
- package/host/runtime-barrel.ts +4 -2
- package/host/runtime.test.ts +16 -47
- package/host/runtime.ts +131 -23
- package/host/s2s.test.ts +122 -131
- package/host/s2s.ts +44 -52
- package/host/session-core.test.ts +257 -0
- package/host/session-core.ts +262 -0
- package/host/to-vercel-tools.test.ts +9 -1
- package/host/transports/pipeline-transport.test.ts +653 -0
- package/host/transports/pipeline-transport.ts +532 -0
- package/host/{fixture-replay.test.ts → transports/s2s-transport-fixtures.test.ts} +76 -106
- package/host/transports/s2s-transport.test.ts +56 -0
- package/host/transports/s2s-transport.ts +116 -0
- package/host/transports/types.test.ts +22 -0
- package/host/transports/types.ts +51 -0
- package/host/ws-handler.test.ts +324 -242
- package/host/ws-handler.ts +56 -59
- package/package.json +2 -1
- package/sdk/__snapshots__/exports.test.ts.snap +3 -3
- package/sdk/__snapshots__/schema-shapes.test.ts.snap +1 -0
- package/sdk/_internal-types.ts +3 -0
- package/sdk/protocol-compat.test.ts +8 -0
- package/sdk/protocol.ts +6 -5
- package/sdk/providers/stt/deepgram.ts +43 -0
- package/sdk/providers/stt-barrel.ts +2 -0
- package/sdk/providers/tts/cartesia.ts +15 -5
- package/sdk/providers/tts/rime.ts +52 -0
- package/sdk/providers/tts-barrel.ts +2 -0
- package/sdk/schema-alignment.test.ts +18 -6
- package/dist/assemblyai-Cxg9eobY.js +0 -18
- package/dist/cartesia-DwDk2tEu.js +0 -10
- package/dist/host/pipeline-session-ctx.d.ts +0 -24
- package/dist/host/pipeline-session.d.ts +0 -52
- package/dist/host/session-ctx.d.ts +0 -73
- package/dist/host/session.d.ts +0 -62
- package/host/pipeline-session-ctx.test.ts +0 -31
- package/host/pipeline-session-ctx.ts +0 -36
- package/host/pipeline-session.test.ts +0 -672
- package/host/pipeline-session.ts +0 -533
- package/host/s2s-fixtures.test.ts +0 -237
- package/host/session-ctx.test.ts +0 -387
- package/host/session-ctx.ts +0 -134
- package/host/session-fixture-replay.test.ts +0 -128
- package/host/session.test.ts +0 -634
- package/host/session.ts +0 -412
- /package/dist/{anthropic-BrUCPKUc.js → anthropic-CcLZygAr.js} +0 -0
package/host/s2s.test.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { describe, expect, test, vi } from "vitest";
|
|
2
2
|
import { silentLogger } from "./_test-utils.ts";
|
|
3
|
-
import type { S2sWebSocket } from "./s2s.ts";
|
|
3
|
+
import type { S2sCallbacks, S2sWebSocket } from "./s2s.ts";
|
|
4
4
|
import { connectS2s } from "./s2s.ts";
|
|
5
5
|
|
|
6
6
|
/** EventTarget-based WebSocket stub (standard API, no `.on()` adapter needed). */
|
|
@@ -37,6 +37,24 @@ function createWebSocketStub() {
|
|
|
37
37
|
|
|
38
38
|
const s2sConfig = { wssUrl: "wss://fake", inputSampleRate: 16_000, outputSampleRate: 16_000 };
|
|
39
39
|
|
|
40
|
+
function makeMockCallbacks(): S2sCallbacks {
|
|
41
|
+
return {
|
|
42
|
+
onSessionReady: vi.fn(),
|
|
43
|
+
onReplyStarted: vi.fn(),
|
|
44
|
+
onReplyDone: vi.fn(),
|
|
45
|
+
onCancelled: vi.fn(),
|
|
46
|
+
onAudio: vi.fn(),
|
|
47
|
+
onUserTranscript: vi.fn(),
|
|
48
|
+
onAgentTranscript: vi.fn(),
|
|
49
|
+
onToolCall: vi.fn(),
|
|
50
|
+
onSpeechStarted: vi.fn(),
|
|
51
|
+
onSpeechStopped: vi.fn(),
|
|
52
|
+
onSessionExpired: vi.fn(),
|
|
53
|
+
onError: vi.fn(),
|
|
54
|
+
onClose: vi.fn(),
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
40
58
|
function createTestS2s() {
|
|
41
59
|
const raw = createWebSocketStub();
|
|
42
60
|
const createWebSocket = () => {
|
|
@@ -49,12 +67,13 @@ function createTestS2s() {
|
|
|
49
67
|
return { raw, createWebSocket, logger: { ...silentLogger } };
|
|
50
68
|
}
|
|
51
69
|
|
|
52
|
-
async function setupHandle() {
|
|
70
|
+
async function setupHandle(callbacks?: S2sCallbacks) {
|
|
53
71
|
const { raw, createWebSocket, logger } = createTestS2s();
|
|
54
72
|
const handle = await connectS2s({
|
|
55
73
|
apiKey: "test-key",
|
|
56
74
|
config: s2sConfig,
|
|
57
75
|
createWebSocket,
|
|
76
|
+
callbacks: callbacks ?? makeMockCallbacks(),
|
|
58
77
|
logger,
|
|
59
78
|
});
|
|
60
79
|
return { raw, handle, logger };
|
|
@@ -89,6 +108,7 @@ describe("connectS2s", () => {
|
|
|
89
108
|
apiKey: "test-key",
|
|
90
109
|
config: s2sConfig,
|
|
91
110
|
createWebSocket,
|
|
111
|
+
callbacks: makeMockCallbacks(),
|
|
92
112
|
logger: silentLogger,
|
|
93
113
|
}),
|
|
94
114
|
).rejects.toThrow("connection refused");
|
|
@@ -184,10 +204,9 @@ describe("connectS2s", () => {
|
|
|
184
204
|
|
|
185
205
|
// ─── Message dispatch ──────────────────────────────────────────────────
|
|
186
206
|
|
|
187
|
-
test("session.ready dispatches '
|
|
188
|
-
const
|
|
189
|
-
const
|
|
190
|
-
handle.on("ready", onReady);
|
|
207
|
+
test("session.ready dispatches 'onSessionReady' callback", async () => {
|
|
208
|
+
const callbacks = makeMockCallbacks();
|
|
209
|
+
const { raw } = await setupHandle(callbacks);
|
|
191
210
|
|
|
192
211
|
raw.emit(
|
|
193
212
|
"message",
|
|
@@ -199,51 +218,45 @@ describe("connectS2s", () => {
|
|
|
199
218
|
),
|
|
200
219
|
);
|
|
201
220
|
|
|
202
|
-
expect(
|
|
203
|
-
expect(
|
|
221
|
+
expect(callbacks.onSessionReady).toHaveBeenCalledOnce();
|
|
222
|
+
expect(callbacks.onSessionReady).toHaveBeenCalledWith("s123");
|
|
204
223
|
});
|
|
205
224
|
|
|
206
|
-
test("input.speech.started dispatches '
|
|
207
|
-
const
|
|
208
|
-
const
|
|
209
|
-
handle.on("event", handler);
|
|
225
|
+
test("input.speech.started dispatches 'onSpeechStarted' callback", async () => {
|
|
226
|
+
const callbacks = makeMockCallbacks();
|
|
227
|
+
const { raw } = await setupHandle(callbacks);
|
|
210
228
|
|
|
211
229
|
raw.emit("message", Buffer.from(JSON.stringify({ type: "input.speech.started" })));
|
|
212
230
|
|
|
213
|
-
expect(
|
|
214
|
-
expect(handler.mock.calls[0]?.[0]).toEqual({ type: "speech_started" });
|
|
231
|
+
expect(callbacks.onSpeechStarted).toHaveBeenCalledOnce();
|
|
215
232
|
});
|
|
216
233
|
|
|
217
|
-
test("input.speech.stopped dispatches '
|
|
218
|
-
const
|
|
219
|
-
const
|
|
220
|
-
handle.on("event", handler);
|
|
234
|
+
test("input.speech.stopped dispatches 'onSpeechStopped' callback", async () => {
|
|
235
|
+
const callbacks = makeMockCallbacks();
|
|
236
|
+
const { raw } = await setupHandle(callbacks);
|
|
221
237
|
|
|
222
238
|
// Prime VAD state — speech_stopped is only forwarded after a speech_started.
|
|
223
239
|
raw.emit("message", Buffer.from(JSON.stringify({ type: "input.speech.started" })));
|
|
224
240
|
raw.emit("message", Buffer.from(JSON.stringify({ type: "input.speech.stopped" })));
|
|
225
241
|
|
|
226
|
-
expect(
|
|
227
|
-
expect(
|
|
228
|
-
expect(handler.mock.calls[1]?.[0]).toEqual({ type: "speech_stopped" });
|
|
242
|
+
expect(callbacks.onSpeechStarted).toHaveBeenCalledOnce();
|
|
243
|
+
expect(callbacks.onSpeechStopped).toHaveBeenCalledOnce();
|
|
229
244
|
});
|
|
230
245
|
|
|
231
246
|
test("duplicate input.speech.stopped is suppressed", async () => {
|
|
232
|
-
const
|
|
233
|
-
const
|
|
234
|
-
handle.on("event", handler);
|
|
247
|
+
const callbacks = makeMockCallbacks();
|
|
248
|
+
const { raw } = await setupHandle(callbacks);
|
|
235
249
|
|
|
236
250
|
raw.emit("message", Buffer.from(JSON.stringify({ type: "input.speech.started" })));
|
|
237
251
|
raw.emit("message", Buffer.from(JSON.stringify({ type: "input.speech.stopped" })));
|
|
238
252
|
raw.emit("message", Buffer.from(JSON.stringify({ type: "input.speech.stopped" })));
|
|
239
253
|
|
|
240
|
-
expect(
|
|
254
|
+
expect(callbacks.onSpeechStopped).toHaveBeenCalledOnce();
|
|
241
255
|
});
|
|
242
256
|
|
|
243
|
-
test("transcript.user dispatches '
|
|
244
|
-
const
|
|
245
|
-
const
|
|
246
|
-
handle.on("event", handler);
|
|
257
|
+
test("transcript.user dispatches 'onUserTranscript' callback", async () => {
|
|
258
|
+
const callbacks = makeMockCallbacks();
|
|
259
|
+
const { raw } = await setupHandle(callbacks);
|
|
247
260
|
|
|
248
261
|
raw.emit(
|
|
249
262
|
"message",
|
|
@@ -256,17 +269,13 @@ describe("connectS2s", () => {
|
|
|
256
269
|
),
|
|
257
270
|
);
|
|
258
271
|
|
|
259
|
-
expect(
|
|
260
|
-
expect(
|
|
261
|
-
type: "user_transcript",
|
|
262
|
-
text: "Hello world",
|
|
263
|
-
});
|
|
272
|
+
expect(callbacks.onUserTranscript).toHaveBeenCalledOnce();
|
|
273
|
+
expect(callbacks.onUserTranscript).toHaveBeenCalledWith("Hello world");
|
|
264
274
|
});
|
|
265
275
|
|
|
266
|
-
test("reply.started dispatches '
|
|
267
|
-
const
|
|
268
|
-
const
|
|
269
|
-
handle.on("replyStarted", handler);
|
|
276
|
+
test("reply.started dispatches 'onReplyStarted' callback", async () => {
|
|
277
|
+
const callbacks = makeMockCallbacks();
|
|
278
|
+
const { raw } = await setupHandle(callbacks);
|
|
270
279
|
|
|
271
280
|
raw.emit(
|
|
272
281
|
"message",
|
|
@@ -278,14 +287,13 @@ describe("connectS2s", () => {
|
|
|
278
287
|
),
|
|
279
288
|
);
|
|
280
289
|
|
|
281
|
-
expect(
|
|
282
|
-
expect(
|
|
290
|
+
expect(callbacks.onReplyStarted).toHaveBeenCalledOnce();
|
|
291
|
+
expect(callbacks.onReplyStarted).toHaveBeenCalledWith("r1");
|
|
283
292
|
});
|
|
284
293
|
|
|
285
|
-
test("transcript.agent dispatches '
|
|
286
|
-
const
|
|
287
|
-
const
|
|
288
|
-
handle.on("event", handler);
|
|
294
|
+
test("transcript.agent dispatches 'onAgentTranscript' callback", async () => {
|
|
295
|
+
const callbacks = makeMockCallbacks();
|
|
296
|
+
const { raw } = await setupHandle(callbacks);
|
|
289
297
|
|
|
290
298
|
raw.emit(
|
|
291
299
|
"message",
|
|
@@ -300,30 +308,25 @@ describe("connectS2s", () => {
|
|
|
300
308
|
),
|
|
301
309
|
);
|
|
302
310
|
|
|
303
|
-
expect(
|
|
304
|
-
|
|
305
|
-
expect(payload.type).toBe("agent_transcript");
|
|
306
|
-
expect(payload.text).toBe("Full response");
|
|
307
|
-
expect(payload._interrupted).toBe(false);
|
|
311
|
+
expect(callbacks.onAgentTranscript).toHaveBeenCalledOnce();
|
|
312
|
+
expect(callbacks.onAgentTranscript).toHaveBeenCalledWith("Full response", false);
|
|
308
313
|
});
|
|
309
314
|
|
|
310
|
-
test("transcript.agent defaults
|
|
311
|
-
const
|
|
312
|
-
const
|
|
313
|
-
handle.on("event", handler);
|
|
315
|
+
test("transcript.agent defaults interrupted to false when missing", async () => {
|
|
316
|
+
const callbacks = makeMockCallbacks();
|
|
317
|
+
const { raw } = await setupHandle(callbacks);
|
|
314
318
|
|
|
315
319
|
raw.emit(
|
|
316
320
|
"message",
|
|
317
321
|
Buffer.from(JSON.stringify({ type: "transcript.agent", text: "response" })),
|
|
318
322
|
);
|
|
319
323
|
|
|
320
|
-
expect(
|
|
324
|
+
expect(callbacks.onAgentTranscript).toHaveBeenCalledWith("response", false);
|
|
321
325
|
});
|
|
322
326
|
|
|
323
|
-
test("transcript.agent with interrupted:true
|
|
324
|
-
const
|
|
325
|
-
const
|
|
326
|
-
handle.on("event", handler);
|
|
327
|
+
test("transcript.agent with interrupted:true passes interrupted:true", async () => {
|
|
328
|
+
const callbacks = makeMockCallbacks();
|
|
329
|
+
const { raw } = await setupHandle(callbacks);
|
|
327
330
|
|
|
328
331
|
raw.emit(
|
|
329
332
|
"message",
|
|
@@ -336,13 +339,12 @@ describe("connectS2s", () => {
|
|
|
336
339
|
),
|
|
337
340
|
);
|
|
338
341
|
|
|
339
|
-
expect(
|
|
342
|
+
expect(callbacks.onAgentTranscript).toHaveBeenCalledWith("Interrupted response", true);
|
|
340
343
|
});
|
|
341
344
|
|
|
342
|
-
test("tool.call dispatches '
|
|
343
|
-
const
|
|
344
|
-
const
|
|
345
|
-
handle.on("event", handler);
|
|
345
|
+
test("tool.call dispatches 'onToolCall' callback", async () => {
|
|
346
|
+
const callbacks = makeMockCallbacks();
|
|
347
|
+
const { raw } = await setupHandle(callbacks);
|
|
346
348
|
|
|
347
349
|
raw.emit(
|
|
348
350
|
"message",
|
|
@@ -356,18 +358,13 @@ describe("connectS2s", () => {
|
|
|
356
358
|
),
|
|
357
359
|
);
|
|
358
360
|
|
|
359
|
-
expect(
|
|
360
|
-
|
|
361
|
-
expect(payload.type).toBe("tool_call");
|
|
362
|
-
expect(payload.toolCallId).toBe("c1");
|
|
363
|
-
expect(payload.toolName).toBe("web_search");
|
|
364
|
-
expect(payload.args).toEqual({ query: "test" });
|
|
361
|
+
expect(callbacks.onToolCall).toHaveBeenCalledOnce();
|
|
362
|
+
expect(callbacks.onToolCall).toHaveBeenCalledWith("c1", "web_search", { query: "test" });
|
|
365
363
|
});
|
|
366
364
|
|
|
367
|
-
test("reply.done (non-interrupted) dispatches '
|
|
368
|
-
const
|
|
369
|
-
const
|
|
370
|
-
handle.on("event", handler);
|
|
365
|
+
test("reply.done (non-interrupted) dispatches 'onReplyDone' callback", async () => {
|
|
366
|
+
const callbacks = makeMockCallbacks();
|
|
367
|
+
const { raw } = await setupHandle(callbacks);
|
|
371
368
|
|
|
372
369
|
raw.emit(
|
|
373
370
|
"message",
|
|
@@ -379,14 +376,13 @@ describe("connectS2s", () => {
|
|
|
379
376
|
),
|
|
380
377
|
);
|
|
381
378
|
|
|
382
|
-
expect(
|
|
383
|
-
expect(
|
|
379
|
+
expect(callbacks.onReplyDone).toHaveBeenCalledOnce();
|
|
380
|
+
expect(callbacks.onCancelled).not.toHaveBeenCalled();
|
|
384
381
|
});
|
|
385
382
|
|
|
386
|
-
test("reply.done with status 'interrupted' dispatches '
|
|
387
|
-
const
|
|
388
|
-
const
|
|
389
|
-
handle.on("event", handler);
|
|
383
|
+
test("reply.done with status 'interrupted' dispatches 'onCancelled' callback", async () => {
|
|
384
|
+
const callbacks = makeMockCallbacks();
|
|
385
|
+
const { raw } = await setupHandle(callbacks);
|
|
390
386
|
|
|
391
387
|
raw.emit(
|
|
392
388
|
"message",
|
|
@@ -398,22 +394,22 @@ describe("connectS2s", () => {
|
|
|
398
394
|
),
|
|
399
395
|
);
|
|
400
396
|
|
|
401
|
-
expect(
|
|
402
|
-
expect(
|
|
397
|
+
expect(callbacks.onCancelled).toHaveBeenCalledOnce();
|
|
398
|
+
expect(callbacks.onReplyDone).not.toHaveBeenCalled();
|
|
403
399
|
});
|
|
404
400
|
|
|
405
401
|
test("reply.done arrival is logged with sid and status", async () => {
|
|
406
402
|
const { raw, createWebSocket, logger } = createTestS2s();
|
|
407
403
|
const infoSpy = vi.fn();
|
|
408
404
|
logger.info = infoSpy;
|
|
409
|
-
|
|
405
|
+
await connectS2s({
|
|
410
406
|
apiKey: "test-key",
|
|
411
407
|
config: s2sConfig,
|
|
412
408
|
createWebSocket,
|
|
409
|
+
callbacks: makeMockCallbacks(),
|
|
413
410
|
logger,
|
|
414
411
|
sid: "sess-abc",
|
|
415
412
|
});
|
|
416
|
-
handle.on("event", vi.fn());
|
|
417
413
|
|
|
418
414
|
raw.emit("message", Buffer.from(JSON.stringify({ type: "reply.done", status: "completed" })));
|
|
419
415
|
|
|
@@ -422,10 +418,9 @@ describe("connectS2s", () => {
|
|
|
422
418
|
expect(arrivalCall?.[1]).toEqual({ sid: "sess-abc", status: "completed" });
|
|
423
419
|
});
|
|
424
420
|
|
|
425
|
-
test("session.error with session_not_found dispatches '
|
|
426
|
-
const
|
|
427
|
-
const
|
|
428
|
-
handle.on("sessionExpired", handler);
|
|
421
|
+
test("session.error with session_not_found dispatches 'onSessionExpired' callback", async () => {
|
|
422
|
+
const callbacks = makeMockCallbacks();
|
|
423
|
+
const { raw } = await setupHandle(callbacks);
|
|
429
424
|
|
|
430
425
|
raw.emit(
|
|
431
426
|
"message",
|
|
@@ -438,13 +433,12 @@ describe("connectS2s", () => {
|
|
|
438
433
|
),
|
|
439
434
|
);
|
|
440
435
|
|
|
441
|
-
expect(
|
|
436
|
+
expect(callbacks.onSessionExpired).toHaveBeenCalledOnce();
|
|
442
437
|
});
|
|
443
438
|
|
|
444
|
-
test("session.error with session_forbidden dispatches '
|
|
445
|
-
const
|
|
446
|
-
const
|
|
447
|
-
handle.on("sessionExpired", handler);
|
|
439
|
+
test("session.error with session_forbidden dispatches 'onSessionExpired' callback", async () => {
|
|
440
|
+
const callbacks = makeMockCallbacks();
|
|
441
|
+
const { raw } = await setupHandle(callbacks);
|
|
448
442
|
|
|
449
443
|
raw.emit(
|
|
450
444
|
"message",
|
|
@@ -457,13 +451,12 @@ describe("connectS2s", () => {
|
|
|
457
451
|
),
|
|
458
452
|
);
|
|
459
453
|
|
|
460
|
-
expect(
|
|
454
|
+
expect(callbacks.onSessionExpired).toHaveBeenCalledOnce();
|
|
461
455
|
});
|
|
462
456
|
|
|
463
|
-
test("session.error with other code dispatches '
|
|
464
|
-
const
|
|
465
|
-
const
|
|
466
|
-
handle.on("error", handler);
|
|
457
|
+
test("session.error with other code dispatches 'onError' callback with Error object", async () => {
|
|
458
|
+
const callbacks = makeMockCallbacks();
|
|
459
|
+
const { raw } = await setupHandle(callbacks);
|
|
467
460
|
|
|
468
461
|
raw.emit(
|
|
469
462
|
"message",
|
|
@@ -476,16 +469,15 @@ describe("connectS2s", () => {
|
|
|
476
469
|
),
|
|
477
470
|
);
|
|
478
471
|
|
|
479
|
-
expect(
|
|
480
|
-
const err =
|
|
472
|
+
expect(callbacks.onError).toHaveBeenCalledOnce();
|
|
473
|
+
const err = (callbacks.onError as ReturnType<typeof vi.fn>).mock.calls[0]?.[0];
|
|
481
474
|
expect(err).toBeInstanceOf(Error);
|
|
482
475
|
expect(err.message).toBe("Too many requests");
|
|
483
476
|
});
|
|
484
477
|
|
|
485
|
-
test("bare error dispatches '
|
|
486
|
-
const
|
|
487
|
-
const
|
|
488
|
-
handle.on("error", handler);
|
|
478
|
+
test("bare error dispatches 'onError' callback with Error object", async () => {
|
|
479
|
+
const callbacks = makeMockCallbacks();
|
|
480
|
+
const { raw } = await setupHandle(callbacks);
|
|
489
481
|
|
|
490
482
|
raw.emit(
|
|
491
483
|
"message",
|
|
@@ -497,18 +489,17 @@ describe("connectS2s", () => {
|
|
|
497
489
|
),
|
|
498
490
|
);
|
|
499
491
|
|
|
500
|
-
expect(
|
|
501
|
-
const err =
|
|
492
|
+
expect(callbacks.onError).toHaveBeenCalledOnce();
|
|
493
|
+
const err = (callbacks.onError as ReturnType<typeof vi.fn>).mock.calls[0]?.[0];
|
|
502
494
|
expect(err).toBeInstanceOf(Error);
|
|
503
495
|
expect(err.message).toBe("Bad gateway");
|
|
504
496
|
});
|
|
505
497
|
|
|
506
498
|
// ─── Audio fast path ───────────────────────────────────────────────────
|
|
507
499
|
|
|
508
|
-
test("reply.audio dispatches '
|
|
509
|
-
const
|
|
510
|
-
const
|
|
511
|
-
handle.on("audio", handler);
|
|
500
|
+
test("reply.audio dispatches 'onAudio' callback with decoded Uint8Array", async () => {
|
|
501
|
+
const callbacks = makeMockCallbacks();
|
|
502
|
+
const { raw } = await setupHandle(callbacks);
|
|
512
503
|
|
|
513
504
|
const audioBytes = new Uint8Array([10, 20, 30, 40]);
|
|
514
505
|
const base64 = Buffer.from(audioBytes).toString("base64");
|
|
@@ -523,10 +514,10 @@ describe("connectS2s", () => {
|
|
|
523
514
|
),
|
|
524
515
|
);
|
|
525
516
|
|
|
526
|
-
expect(
|
|
527
|
-
const payload =
|
|
528
|
-
expect(payload
|
|
529
|
-
expect(Array.from(payload
|
|
517
|
+
expect(callbacks.onAudio).toHaveBeenCalledOnce();
|
|
518
|
+
const payload = (callbacks.onAudio as ReturnType<typeof vi.fn>).mock.calls[0]?.[0];
|
|
519
|
+
expect(payload).toBeInstanceOf(Uint8Array);
|
|
520
|
+
expect(Array.from(payload)).toEqual([10, 20, 30, 40]);
|
|
530
521
|
});
|
|
531
522
|
|
|
532
523
|
// ─── Edge cases ────────────────────────────────────────────────────────
|
|
@@ -577,39 +568,39 @@ describe("connectS2s", () => {
|
|
|
577
568
|
});
|
|
578
569
|
|
|
579
570
|
test("session.updated is silently ignored (no dispatch)", async () => {
|
|
580
|
-
const
|
|
581
|
-
const
|
|
582
|
-
handle.on("event", eventHandler);
|
|
571
|
+
const callbacks = makeMockCallbacks();
|
|
572
|
+
const { raw } = await setupHandle(callbacks);
|
|
583
573
|
|
|
584
574
|
raw.emit("message", Buffer.from(JSON.stringify({ type: "session.updated" })));
|
|
585
575
|
|
|
586
|
-
// session.updated is dropped — no
|
|
587
|
-
expect(
|
|
576
|
+
// session.updated is dropped — no callbacks fired
|
|
577
|
+
expect(callbacks.onSessionReady).not.toHaveBeenCalled();
|
|
578
|
+
expect(callbacks.onReplyStarted).not.toHaveBeenCalled();
|
|
579
|
+
expect(callbacks.onReplyDone).not.toHaveBeenCalled();
|
|
580
|
+
expect(callbacks.onSpeechStarted).not.toHaveBeenCalled();
|
|
581
|
+
expect(callbacks.onSpeechStopped).not.toHaveBeenCalled();
|
|
588
582
|
});
|
|
589
583
|
|
|
590
584
|
// ─── Close and error events ────────────────────────────────────────────
|
|
591
585
|
|
|
592
|
-
test("close event dispatches '
|
|
593
|
-
const
|
|
594
|
-
const
|
|
595
|
-
handle.on("close", handler);
|
|
586
|
+
test("close event dispatches 'onClose' callback with code and reason", async () => {
|
|
587
|
+
const callbacks = makeMockCallbacks();
|
|
588
|
+
const { raw } = await setupHandle(callbacks);
|
|
596
589
|
|
|
597
590
|
raw.emit("close", 1000, "normal");
|
|
598
591
|
|
|
599
|
-
expect(
|
|
600
|
-
expect(
|
|
601
|
-
expect(handler.mock.calls[0]?.[1]).toBe("normal");
|
|
592
|
+
expect(callbacks.onClose).toHaveBeenCalledOnce();
|
|
593
|
+
expect(callbacks.onClose).toHaveBeenCalledWith(1000, "normal");
|
|
602
594
|
});
|
|
603
595
|
|
|
604
|
-
test("error after open dispatches '
|
|
605
|
-
const
|
|
606
|
-
const
|
|
607
|
-
handle.on("error", handler);
|
|
596
|
+
test("error after open dispatches 'onError' callback with Error object", async () => {
|
|
597
|
+
const callbacks = makeMockCallbacks();
|
|
598
|
+
const { raw } = await setupHandle(callbacks);
|
|
608
599
|
|
|
609
600
|
raw.emit("error", new Error("ws transport error"));
|
|
610
601
|
|
|
611
|
-
expect(
|
|
612
|
-
const err =
|
|
602
|
+
expect(callbacks.onError).toHaveBeenCalledOnce();
|
|
603
|
+
const err = (callbacks.onError as ReturnType<typeof vi.fn>).mock.calls[0]?.[0];
|
|
613
604
|
expect(err).toBeInstanceOf(Error);
|
|
614
605
|
expect(err.message).toBe("ws transport error");
|
|
615
606
|
});
|