@memberjunction/ai-realtime-client 6.2.0-edge.3 → 6.2.0-edge.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/README.md +156 -29
- package/dist/audio/audioMeter.d.ts +71 -0
- package/dist/audio/audioMeter.d.ts.map +1 -1
- package/dist/audio/audioMeter.js +83 -0
- package/dist/audio/audioMeter.js.map +1 -1
- package/dist/audio/mediaElementAudioRouter.d.ts +41 -0
- package/dist/audio/mediaElementAudioRouter.d.ts.map +1 -0
- package/dist/audio/mediaElementAudioRouter.js +82 -0
- package/dist/audio/mediaElementAudioRouter.js.map +1 -0
- package/dist/audio/micCapture.d.ts +12 -3
- package/dist/audio/micCapture.d.ts.map +1 -1
- package/dist/audio/micCapture.js +15 -1
- package/dist/audio/micCapture.js.map +1 -1
- package/dist/audio/pcmPlayback.d.ts +72 -4
- package/dist/audio/pcmPlayback.d.ts.map +1 -1
- package/dist/audio/pcmPlayback.js +101 -1
- package/dist/audio/pcmPlayback.js.map +1 -1
- package/dist/drivers/assemblyAIRealtimeClient.d.ts +2 -0
- package/dist/drivers/assemblyAIRealtimeClient.d.ts.map +1 -1
- package/dist/drivers/assemblyAIRealtimeClient.js +14 -0
- package/dist/drivers/assemblyAIRealtimeClient.js.map +1 -1
- package/dist/drivers/elevenLabsRealtimeClient.d.ts +2 -0
- package/dist/drivers/elevenLabsRealtimeClient.d.ts.map +1 -1
- package/dist/drivers/elevenLabsRealtimeClient.js +14 -0
- package/dist/drivers/elevenLabsRealtimeClient.js.map +1 -1
- package/dist/drivers/geminiAvatarOutput.d.ts +132 -0
- package/dist/drivers/geminiAvatarOutput.d.ts.map +1 -0
- package/dist/drivers/geminiAvatarOutput.js +310 -0
- package/dist/drivers/geminiAvatarOutput.js.map +1 -0
- package/dist/drivers/geminiEnterpriseRealtimeClient.d.ts +38 -0
- package/dist/drivers/geminiEnterpriseRealtimeClient.d.ts.map +1 -0
- package/dist/drivers/geminiEnterpriseRealtimeClient.js +99 -0
- package/dist/drivers/geminiEnterpriseRealtimeClient.js.map +1 -0
- package/dist/drivers/geminiRealtimeClient.d.ts +325 -50
- package/dist/drivers/geminiRealtimeClient.d.ts.map +1 -1
- package/dist/drivers/geminiRealtimeClient.js +782 -184
- package/dist/drivers/geminiRealtimeClient.js.map +1 -1
- package/dist/drivers/openAILiveClient.d.ts +5 -8
- package/dist/drivers/openAILiveClient.d.ts.map +1 -1
- package/dist/drivers/openAILiveClient.js +19 -32
- package/dist/drivers/openAILiveClient.js.map +1 -1
- package/dist/drivers/openAIRealtimeClient.d.ts +13 -26
- package/dist/drivers/openAIRealtimeClient.d.ts.map +1 -1
- package/dist/drivers/openAIRealtimeClient.js +28 -55
- package/dist/drivers/openAIRealtimeClient.js.map +1 -1
- package/dist/drivers/xaiRealtimeClient.d.ts.map +1 -1
- package/dist/drivers/xaiRealtimeClient.js +19 -14
- package/dist/drivers/xaiRealtimeClient.js.map +1 -1
- package/dist/generic/baseRealtimeClient.d.ts +183 -29
- package/dist/generic/baseRealtimeClient.d.ts.map +1 -1
- package/dist/generic/baseRealtimeClient.js +246 -14
- package/dist/generic/baseRealtimeClient.js.map +1 -1
- package/dist/generic/openAIProtocolClient.d.ts +2 -0
- package/dist/generic/openAIProtocolClient.d.ts.map +1 -1
- package/dist/generic/openAIProtocolClient.js +17 -0
- package/dist/generic/openAIProtocolClient.js.map +1 -1
- package/dist/index.d.ts +2 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -3
- package/dist/index.js.map +1 -1
- package/dist/media/attachVideoSource.d.ts +23 -0
- package/dist/media/attachVideoSource.d.ts.map +1 -0
- package/dist/media/attachVideoSource.js +49 -0
- package/dist/media/attachVideoSource.js.map +1 -0
- package/dist/media/channelVideoSource.d.ts +110 -14
- package/dist/media/channelVideoSource.d.ts.map +1 -1
- package/dist/media/channelVideoSource.js +183 -18
- package/dist/media/channelVideoSource.js.map +1 -1
- package/dist/media/decoders/frameScheduler.d.ts +119 -0
- package/dist/media/decoders/frameScheduler.d.ts.map +1 -0
- package/dist/media/decoders/frameScheduler.js +238 -0
- package/dist/media/decoders/frameScheduler.js.map +1 -0
- package/dist/media/decoders/imageFrameDecoder.d.ts +58 -0
- package/dist/media/decoders/imageFrameDecoder.d.ts.map +1 -0
- package/dist/media/decoders/imageFrameDecoder.js +110 -0
- package/dist/media/decoders/imageFrameDecoder.js.map +1 -0
- package/dist/media/decoders/mseFmp4Decoder.d.ts +130 -0
- package/dist/media/decoders/mseFmp4Decoder.d.ts.map +1 -0
- package/dist/media/decoders/mseFmp4Decoder.js +402 -0
- package/dist/media/decoders/mseFmp4Decoder.js.map +1 -0
- package/dist/media/decoders/streamCanvas.d.ts +46 -0
- package/dist/media/decoders/streamCanvas.d.ts.map +1 -0
- package/dist/media/decoders/streamCanvas.js +163 -0
- package/dist/media/decoders/streamCanvas.js.map +1 -0
- package/dist/media/decoders/videoMimeTypes.d.ts +29 -0
- package/dist/media/decoders/videoMimeTypes.d.ts.map +1 -0
- package/dist/media/decoders/videoMimeTypes.js +50 -0
- package/dist/media/decoders/videoMimeTypes.js.map +1 -0
- package/dist/media/decoders/webCodecsChunkDecoder.d.ts +99 -0
- package/dist/media/decoders/webCodecsChunkDecoder.d.ts.map +1 -0
- package/dist/media/decoders/webCodecsChunkDecoder.js +265 -0
- package/dist/media/decoders/webCodecsChunkDecoder.js.map +1 -0
- package/dist/media/displayCapture.d.ts +128 -0
- package/dist/media/displayCapture.d.ts.map +1 -0
- package/dist/media/displayCapture.js +245 -0
- package/dist/media/displayCapture.js.map +1 -0
- package/dist/media/frameCapture.d.ts +25 -8
- package/dist/media/frameCapture.d.ts.map +1 -1
- package/dist/media/frameCapture.js +42 -72
- package/dist/media/frameCapture.js.map +1 -1
- package/dist/media/frameSampler.d.ts +94 -0
- package/dist/media/frameSampler.d.ts.map +1 -0
- package/dist/media/frameSampler.js +134 -0
- package/dist/media/frameSampler.js.map +1 -0
- package/dist/media/index.d.ts +32 -0
- package/dist/media/index.d.ts.map +1 -0
- package/dist/media/index.js +32 -0
- package/dist/media/index.js.map +1 -0
- package/dist/media/localMediaController.d.ts +116 -0
- package/dist/media/localMediaController.d.ts.map +1 -0
- package/dist/media/localMediaController.js +281 -0
- package/dist/media/localMediaController.js.map +1 -0
- package/dist/media/mediaLayout.d.ts +93 -0
- package/dist/media/mediaLayout.d.ts.map +1 -0
- package/dist/media/mediaLayout.js +187 -0
- package/dist/media/mediaLayout.js.map +1 -0
- package/dist/media/mediaPreview.d.ts +94 -0
- package/dist/media/mediaPreview.d.ts.map +1 -0
- package/dist/media/mediaPreview.js +154 -0
- package/dist/media/mediaPreview.js.map +1 -0
- package/dist/media/mediaStage.d.ts +99 -0
- package/dist/media/mediaStage.d.ts.map +1 -0
- package/dist/media/mediaStage.js +171 -0
- package/dist/media/mediaStage.js.map +1 -0
- package/dist/media/model.d.ts +154 -0
- package/dist/media/model.d.ts.map +1 -0
- package/dist/media/model.js +9 -0
- package/dist/media/model.js.map +1 -0
- package/dist/media/playbackClock.d.ts +20 -0
- package/dist/media/playbackClock.d.ts.map +1 -0
- package/dist/media/playbackClock.js +13 -0
- package/dist/media/playbackClock.js.map +1 -0
- package/dist/media/videoFrameDecoder.d.ts +86 -0
- package/dist/media/videoFrameDecoder.d.ts.map +1 -0
- package/dist/media/videoFrameDecoder.js +2 -0
- package/dist/media/videoFrameDecoder.js.map +1 -0
- package/dist/media/videoFrameDecoderRegistry.d.ts +40 -0
- package/dist/media/videoFrameDecoderRegistry.d.ts.map +1 -0
- package/dist/media/videoFrameDecoderRegistry.js +69 -0
- package/dist/media/videoFrameDecoderRegistry.js.map +1 -0
- package/dist/media/videoPacing.d.ts +32 -0
- package/dist/media/videoPacing.d.ts.map +1 -0
- package/dist/media/videoPacing.js +37 -0
- package/dist/media/videoPacing.js.map +1 -0
- package/dist/media/videoPlayout.d.ts +168 -0
- package/dist/media/videoPlayout.d.ts.map +1 -0
- package/dist/media/videoPlayout.js +217 -0
- package/dist/media/videoPlayout.js.map +1 -0
- package/dist/media/videoSourceArbiter.d.ts +250 -0
- package/dist/media/videoSourceArbiter.d.ts.map +1 -0
- package/dist/media/videoSourceArbiter.js +423 -0
- package/dist/media/videoSourceArbiter.js.map +1 -0
- package/dist/testing/conformanceAssertions.d.ts +25 -0
- package/dist/testing/conformanceAssertions.d.ts.map +1 -0
- package/dist/testing/conformanceAssertions.js +64 -0
- package/dist/testing/conformanceAssertions.js.map +1 -0
- package/dist/testing/conformanceFixtures.d.ts +76 -0
- package/dist/testing/conformanceFixtures.d.ts.map +1 -0
- package/dist/testing/conformanceFixtures.js +169 -0
- package/dist/testing/conformanceFixtures.js.map +1 -0
- package/dist/testing/conformanceGates.d.ts +32 -0
- package/dist/testing/conformanceGates.d.ts.map +1 -0
- package/dist/testing/conformanceGates.js +35 -0
- package/dist/testing/conformanceGates.js.map +1 -0
- package/dist/testing/conformanceMicrophone.d.ts +10 -0
- package/dist/testing/conformanceMicrophone.d.ts.map +1 -0
- package/dist/testing/conformanceMicrophone.js +71 -0
- package/dist/testing/conformanceMicrophone.js.map +1 -0
- package/dist/testing/conformanceTimeline.d.ts +128 -0
- package/dist/testing/conformanceTimeline.d.ts.map +1 -0
- package/dist/testing/conformanceTimeline.js +32 -0
- package/dist/testing/conformanceTimeline.js.map +1 -0
- package/dist/testing/conformanceVideoStream.d.ts +29 -0
- package/dist/testing/conformanceVideoStream.d.ts.map +1 -0
- package/dist/testing/conformanceVideoStream.js +39 -0
- package/dist/testing/conformanceVideoStream.js.map +1 -0
- package/dist/testing/index.d.ts +29 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/index.js +28 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/testing/realtimeVideoConformance.d.ts +23 -0
- package/dist/testing/realtimeVideoConformance.d.ts.map +1 -0
- package/dist/testing/realtimeVideoConformance.js +139 -0
- package/dist/testing/realtimeVideoConformance.js.map +1 -0
- package/dist/testing/realtimeVideoConformanceChecks.d.ts +4 -0
- package/dist/testing/realtimeVideoConformanceChecks.d.ts.map +1 -0
- package/dist/testing/realtimeVideoConformanceChecks.js +164 -0
- package/dist/testing/realtimeVideoConformanceChecks.js.map +1 -0
- package/dist/testing/realtimeVideoConformanceFrameChecks.d.ts +4 -0
- package/dist/testing/realtimeVideoConformanceFrameChecks.d.ts.map +1 -0
- package/dist/testing/realtimeVideoConformanceFrameChecks.js +63 -0
- package/dist/testing/realtimeVideoConformanceFrameChecks.js.map +1 -0
- package/dist/testing/realtimeVideoConformanceSession.d.ts +108 -0
- package/dist/testing/realtimeVideoConformanceSession.d.ts.map +1 -0
- package/dist/testing/realtimeVideoConformanceSession.js +270 -0
- package/dist/testing/realtimeVideoConformanceSession.js.map +1 -0
- package/dist/testing/realtimeVideoConformanceTurnChecks.d.ts +4 -0
- package/dist/testing/realtimeVideoConformanceTurnChecks.d.ts.map +1 -0
- package/dist/testing/realtimeVideoConformanceTurnChecks.js +248 -0
- package/dist/testing/realtimeVideoConformanceTurnChecks.js.map +1 -0
- package/dist/testing/realtimeVideoConformanceTypes.d.ts +155 -0
- package/dist/testing/realtimeVideoConformanceTypes.d.ts.map +1 -0
- package/dist/testing/realtimeVideoConformanceTypes.js +2 -0
- package/dist/testing/realtimeVideoConformanceTypes.js.map +1 -0
- package/dist/testing/recordingPlayout.d.ts +84 -0
- package/dist/testing/recordingPlayout.d.ts.map +1 -0
- package/dist/testing/recordingPlayout.js +120 -0
- package/dist/testing/recordingPlayout.js.map +1 -0
- package/package.json +29 -4
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview One conformance session: the harness's client connected with the kit's recorders, the host's track
|
|
3
|
+
* request, every event the client emits, and the warnings and info lines logged meanwhile, all on one timeline. A check
|
|
4
|
+
* drives the model's side through it and reads the timeline back.
|
|
5
|
+
*
|
|
6
|
+
* @module @memberjunction/ai-realtime-client/testing
|
|
7
|
+
*/
|
|
8
|
+
import { DEFAULT_REALTIME_AUDIO_TRACKS, } from '@memberjunction/ai';
|
|
9
|
+
import { BaseRealtimeClient, REQUESTED_TRACKS_SESSION_KEY } from '../generic/baseRealtimeClient.js';
|
|
10
|
+
import { Fail, RealtimeVideoConformanceError } from './conformanceAssertions.js';
|
|
11
|
+
import { CreateConformanceMicrophone } from './conformanceMicrophone.js';
|
|
12
|
+
import { RealtimeVideoConformanceTimeline, } from './conformanceTimeline.js';
|
|
13
|
+
import { ConformanceVideoStream } from './conformanceVideoStream.js';
|
|
14
|
+
import { RecordingPcmPlayback, RecordingVideoPlayout } from './recordingPlayout.js';
|
|
15
|
+
/** How many logged lines a failure's message carries. */
|
|
16
|
+
const MAX_LOGGED_LINES_IN_FAILURE = 8;
|
|
17
|
+
/** One connected session of a check, recorded on one timeline. */
|
|
18
|
+
export class ConformanceSession {
|
|
19
|
+
constructor(Harness, Setup) {
|
|
20
|
+
this.Harness = Harness;
|
|
21
|
+
this.Setup = Setup;
|
|
22
|
+
this.Timeline = new RealtimeVideoConformanceTimeline();
|
|
23
|
+
this.Microphone = CreateConformanceMicrophone();
|
|
24
|
+
this.players = [];
|
|
25
|
+
this.client = null;
|
|
26
|
+
this.disconnected = false;
|
|
27
|
+
this.Voice = new RecordingPcmPlayback(this.Timeline);
|
|
28
|
+
this.Video = new ConformanceVideoStream(Harness.Traits.VideoFrameKind ?? 'fmp4', this.VideoCarriesVoice);
|
|
29
|
+
}
|
|
30
|
+
/** The video players the driver created, in order. */
|
|
31
|
+
get Players() {
|
|
32
|
+
return this.players;
|
|
33
|
+
}
|
|
34
|
+
/** The connected client. */
|
|
35
|
+
get Client() {
|
|
36
|
+
if (!this.client) {
|
|
37
|
+
throw new RealtimeVideoConformanceError('the session has no client: Start() did not run');
|
|
38
|
+
}
|
|
39
|
+
return this.client;
|
|
40
|
+
}
|
|
41
|
+
/** Whether the avatar's video carries the voice: as the grant says, else whenever the provider's video can. */
|
|
42
|
+
get VideoCarriesVoice() {
|
|
43
|
+
return this.Setup.Grant.VideoCarriesVoice ?? this.Harness.Traits.VideoCanCarryVoice;
|
|
44
|
+
}
|
|
45
|
+
/** Mints, adds the host's track request, creates the client with the recorders, observes it, and connects. */
|
|
46
|
+
async Start() {
|
|
47
|
+
const grant = { ...this.Setup.Grant, VideoCarriesVoice: this.VideoCarriesVoice };
|
|
48
|
+
const config = WithHostTrackRequest(this.Harness.Mint(grant), this.Setup.ShowAgentVideo !== false);
|
|
49
|
+
const client = this.Harness.CreateClient(this.media());
|
|
50
|
+
if (!(client instanceof BaseRealtimeClient)) {
|
|
51
|
+
Fail('the harness', `${this.Harness.Name}'s CreateClient returned something that is not a BaseRealtimeClient`);
|
|
52
|
+
}
|
|
53
|
+
this.client = client;
|
|
54
|
+
this.observe(client);
|
|
55
|
+
await this.Harness.Connect(client, config, this.Microphone);
|
|
56
|
+
}
|
|
57
|
+
/** Disconnects the client, as the host does when the call ends. */
|
|
58
|
+
async Disconnect() {
|
|
59
|
+
this.disconnected = true;
|
|
60
|
+
await this.client?.Disconnect();
|
|
61
|
+
}
|
|
62
|
+
/** Ends the session after the check: disconnects the client if the check didn't. Never throws. */
|
|
63
|
+
async Close() {
|
|
64
|
+
if (this.disconnected || !this.client) {
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
try {
|
|
68
|
+
await this.Disconnect();
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
this.Timeline.Record({ Kind: 'warn', Message: `Disconnect after the check failed: ${describe(error)}` });
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
// ── The model's side ─────────────────────────────────────────────────────────────────────────────────────────
|
|
75
|
+
/**
|
|
76
|
+
* The model starts its video: an fMP4 init segment (with the voice's audio track when the video carries it), or the
|
|
77
|
+
* first chunk (a key frame) or image. Returns the frame sent.
|
|
78
|
+
*/
|
|
79
|
+
async StartVideo() {
|
|
80
|
+
const frame = this.Video.Start();
|
|
81
|
+
await this.Harness.SendVideo(frame);
|
|
82
|
+
return frame;
|
|
83
|
+
}
|
|
84
|
+
/** The model sends the stream's next `count` video frames. Returns them. */
|
|
85
|
+
async SendFrames(count) {
|
|
86
|
+
const frames = this.Video.Frames(count);
|
|
87
|
+
for (const frame of frames) {
|
|
88
|
+
await this.Harness.SendVideo(frame);
|
|
89
|
+
}
|
|
90
|
+
return frames;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* The model sends a chunk of its voice: timed at the video's next frame when the provider keeps one timeline
|
|
94
|
+
* (`TimedVoice`), else untimed. Returns the media time sent, or `undefined`.
|
|
95
|
+
*/
|
|
96
|
+
async SendVoice(pcm16) {
|
|
97
|
+
const mediaTimeMs = this.Harness.Traits.TimedVoice ? this.Video.NextTimeMs : undefined;
|
|
98
|
+
await this.Harness.SendVoice(pcm16, mediaTimeMs);
|
|
99
|
+
return mediaTimeMs;
|
|
100
|
+
}
|
|
101
|
+
/** The model ends its turn: generation complete when the harness has it, then turn complete. */
|
|
102
|
+
async EndTurn() {
|
|
103
|
+
await this.Harness.GenerationComplete?.();
|
|
104
|
+
await this.Harness.TurnComplete();
|
|
105
|
+
}
|
|
106
|
+
/** Models the agent's media playing out: every video player and the voice reach the end of what they were given. */
|
|
107
|
+
FinishPlaying() {
|
|
108
|
+
for (const player of this.players) {
|
|
109
|
+
player.FinishPlaying();
|
|
110
|
+
}
|
|
111
|
+
this.Voice.FinishPlaying();
|
|
112
|
+
}
|
|
113
|
+
// ── Reading the timeline ─────────────────────────────────────────────────────────────────────────────────────
|
|
114
|
+
/** The states the client reported, from a mark on. */
|
|
115
|
+
States(since = 0) {
|
|
116
|
+
return this.Timeline.Of('state', since).map((event) => event.State);
|
|
117
|
+
}
|
|
118
|
+
/** How many events of a kind happened from a mark on. */
|
|
119
|
+
Count(kind, since = 0) {
|
|
120
|
+
return this.Timeline.Of(kind, since).length;
|
|
121
|
+
}
|
|
122
|
+
/** The events of a kind that one player recorded, from a mark on. */
|
|
123
|
+
PlayerEvents(kind, player, since = 0) {
|
|
124
|
+
return this.Timeline.Of(kind, since).filter((event) => playerOf(event) === player);
|
|
125
|
+
}
|
|
126
|
+
/** The frames one player was given, from a mark on. */
|
|
127
|
+
AppendedFrames(player, since = 0) {
|
|
128
|
+
return this.PlayerEvents('player-append', player, since).map((event) => event.Frame);
|
|
129
|
+
}
|
|
130
|
+
/** The agent's video the client handed over, from a mark on. */
|
|
131
|
+
RemoteVideos(since = 0) {
|
|
132
|
+
return this.Timeline.Of('remote-video', since).map((event) => event.Video);
|
|
133
|
+
}
|
|
134
|
+
/** The PCM the driver queued, from a mark on. */
|
|
135
|
+
VoiceEnqueued(since = 0) {
|
|
136
|
+
return this.Timeline.Of('voice-enqueue', since).map((event) => event.Data);
|
|
137
|
+
}
|
|
138
|
+
/** The last state the client reported, or `null`. */
|
|
139
|
+
LastState() {
|
|
140
|
+
return this.Timeline.Of('state').at(-1)?.State ?? null;
|
|
141
|
+
}
|
|
142
|
+
/** The agent's video track (outbound video), as the client reports it now; `undefined` when it has none. */
|
|
143
|
+
OutboundVideoTrack() {
|
|
144
|
+
return this.Client.AllTracks.find((track) => track.Descriptor.Direction === 'outbound' && String(track.Descriptor.Modality).trim().toLowerCase() === 'video');
|
|
145
|
+
}
|
|
146
|
+
/** The seconds of generated video the client reported (`OutputTokenDetails.VideoSeconds`), from a mark on. */
|
|
147
|
+
VideoSeconds(since = 0) {
|
|
148
|
+
return this.Timeline.Of('usage', since).reduce((sum, event) => sum + (event.Usage.OutputTokenDetails?.VideoSeconds ?? 0), 0);
|
|
149
|
+
}
|
|
150
|
+
/** How many usage updates carried video seconds, from a mark on. */
|
|
151
|
+
VideoSecondsReports(since = 0) {
|
|
152
|
+
return this.Timeline.Of('usage', since).filter((event) => (event.Usage.OutputTokenDetails?.VideoSeconds ?? 0) > 0).length;
|
|
153
|
+
}
|
|
154
|
+
/** How many warnings mention `text`, from a mark on. */
|
|
155
|
+
Warnings(text, since = 0) {
|
|
156
|
+
return this.Timeline.Of('warn', since).filter((event) => event.Message.includes(text)).length;
|
|
157
|
+
}
|
|
158
|
+
/** The warning and info lines logged so far, for a failure's message. */
|
|
159
|
+
LoggedLines() {
|
|
160
|
+
return this.Timeline.Entries.flatMap((event) => (event.Kind === 'warn' || event.Kind === 'info' ? [`${event.Kind}: ${event.Message}`] : []));
|
|
161
|
+
}
|
|
162
|
+
// ── Internals ────────────────────────────────────────────────────────────────────────────────────────────────
|
|
163
|
+
/** What the harness wires into the driver: the microphone, the voice recorder, and a recorder per player. */
|
|
164
|
+
media() {
|
|
165
|
+
return {
|
|
166
|
+
Microphone: this.Microphone,
|
|
167
|
+
Voice: this.Voice,
|
|
168
|
+
CreateVideoPlayer: (options) => {
|
|
169
|
+
const player = new RecordingVideoPlayout(options, this.Timeline, this.players.length);
|
|
170
|
+
this.players.push(player);
|
|
171
|
+
return player;
|
|
172
|
+
},
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
/** Registers the kit's handlers: every emission goes on the timeline. */
|
|
176
|
+
observe(client) {
|
|
177
|
+
client.OnStateChange((state) => this.Timeline.Record({ Kind: 'state', State: state }));
|
|
178
|
+
client.OnRemoteVideo((video) => this.Timeline.Record({ Kind: 'remote-video', Video: video }));
|
|
179
|
+
client.OnInterruption(() => this.Timeline.Record({ Kind: 'interruption' }));
|
|
180
|
+
client.OnUsage((usage) => this.Timeline.Record({ Kind: 'usage', Usage: usage }));
|
|
181
|
+
client.OnTrackStateChange((track) => this.Timeline.Record({ Kind: 'track', Track: track }));
|
|
182
|
+
client.OnError((error) => this.Timeline.Record({ Kind: 'error', Error: error }));
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Runs `body` on a started session with the console captured onto its timeline, then disconnects the client (if `body`
|
|
187
|
+
* didn't) and restores the console, failed or not. A failure's message gains the lines logged during the session.
|
|
188
|
+
*/
|
|
189
|
+
export async function WithConformanceSession(harness, setup, body) {
|
|
190
|
+
const session = new ConformanceSession(harness, setup);
|
|
191
|
+
const restoreConsole = CaptureConsole(session.Timeline);
|
|
192
|
+
try {
|
|
193
|
+
await session.Start();
|
|
194
|
+
await body(session);
|
|
195
|
+
}
|
|
196
|
+
catch (error) {
|
|
197
|
+
throw withLoggedLines(error, session.LoggedLines());
|
|
198
|
+
}
|
|
199
|
+
finally {
|
|
200
|
+
await session.Close();
|
|
201
|
+
restoreConsole();
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* The config with the host's track request added, as the realtime runtime builds it when a channel shows the agent's
|
|
206
|
+
* video: the audio floor, what the mint requested, and the agent's outbound video, one each. Unchanged when the host
|
|
207
|
+
* shows no agent video.
|
|
208
|
+
*/
|
|
209
|
+
export function WithHostTrackRequest(config, showAgentVideo) {
|
|
210
|
+
if (!showAgentVideo) {
|
|
211
|
+
return config;
|
|
212
|
+
}
|
|
213
|
+
const minted = config.SessionConfig[REQUESTED_TRACKS_SESSION_KEY];
|
|
214
|
+
const requested = new Map();
|
|
215
|
+
for (const track of DEFAULT_REALTIME_AUDIO_TRACKS) {
|
|
216
|
+
requested.set(`${track.Direction}:audio`, { Modality: 'audio', Direction: track.Direction });
|
|
217
|
+
}
|
|
218
|
+
for (const raw of Array.isArray(minted) ? minted : []) {
|
|
219
|
+
requested.set(trackKeyOf(raw) ?? `minted:${requested.size}`, raw);
|
|
220
|
+
}
|
|
221
|
+
requested.set('outbound:video', { Modality: 'video', Direction: 'outbound' });
|
|
222
|
+
return { ...config, SessionConfig: { ...config.SessionConfig, [REQUESTED_TRACKS_SESSION_KEY]: Array.from(requested.values()) } };
|
|
223
|
+
}
|
|
224
|
+
/** The number of the player an event is about, or `null` for an event about no player. */
|
|
225
|
+
function playerOf(event) {
|
|
226
|
+
return 'Player' in event ? event.Player : null;
|
|
227
|
+
}
|
|
228
|
+
/** `direction:modality` of a requested track in the config, or `null` when it names neither. */
|
|
229
|
+
function trackKeyOf(raw) {
|
|
230
|
+
if (raw === null || typeof raw !== 'object' || Array.isArray(raw)) {
|
|
231
|
+
return null;
|
|
232
|
+
}
|
|
233
|
+
const track = raw;
|
|
234
|
+
const modality = track['Modality'];
|
|
235
|
+
const direction = track['Direction'];
|
|
236
|
+
return typeof modality === 'string' && typeof direction === 'string' ? `${direction}:${modality.trim().toLowerCase()}` : null;
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Sends `console.warn` and `console.info` to the timeline instead of the output, for "reported once" and quiet test
|
|
240
|
+
* output. Returns the function that restores them.
|
|
241
|
+
*/
|
|
242
|
+
export function CaptureConsole(timeline) {
|
|
243
|
+
const warn = console.warn;
|
|
244
|
+
const info = console.info;
|
|
245
|
+
// `unknown`: the console takes values of any type, and each is only turned into text here.
|
|
246
|
+
console.warn = (...values) => timeline.Record({ Kind: 'warn', Message: values.map(describe).join(' ') });
|
|
247
|
+
console.info = (...values) => timeline.Record({ Kind: 'info', Message: values.map(describe).join(' ') });
|
|
248
|
+
return () => {
|
|
249
|
+
console.warn = warn;
|
|
250
|
+
console.info = info;
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
/** A logged value, or a thrown one, as text. */
|
|
254
|
+
function describe(value) {
|
|
255
|
+
return value instanceof Error ? `${value.name}: ${value.message}` : String(value);
|
|
256
|
+
}
|
|
257
|
+
/** The error with the session's logged lines appended to its message; its kind and name kept. */
|
|
258
|
+
function withLoggedLines(error, lines) {
|
|
259
|
+
if (lines.length === 0) {
|
|
260
|
+
return error;
|
|
261
|
+
}
|
|
262
|
+
const logged = ` (logged: ${lines.slice(-MAX_LOGGED_LINES_IN_FAILURE).join(' | ')})`;
|
|
263
|
+
if (error instanceof RealtimeVideoConformanceError) {
|
|
264
|
+
return new RealtimeVideoConformanceError(`${error.message}${logged}`);
|
|
265
|
+
}
|
|
266
|
+
const wrapped = new Error(`${error instanceof Error ? error.message : String(error)}${logged}`);
|
|
267
|
+
wrapped.name = error instanceof Error ? error.name : 'Error';
|
|
268
|
+
return wrapped;
|
|
269
|
+
}
|
|
270
|
+
//# sourceMappingURL=realtimeVideoConformanceSession.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"realtimeVideoConformanceSession.js","sourceRoot":"","sources":["../../src/testing/realtimeVideoConformanceSession.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { RealtimeVideoConformanceCheck } from './realtimeVideoConformanceTypes.js';
|
|
2
|
+
/** VC08-VC15, in order. */
|
|
3
|
+
export declare const REALTIME_VIDEO_TURN_CHECKS: readonly RealtimeVideoConformanceCheck[];
|
|
4
|
+
//# sourceMappingURL=realtimeVideoConformanceTurnChecks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"realtimeVideoConformanceTurnChecks.d.ts","sourceRoot":"","sources":["../../src/testing/realtimeVideoConformanceTurnChecks.ts"],"names":[],"mappings":"AAqBA,OAAO,KAAK,EAAE,6BAA6B,EAAE,MAAM,iCAAiC,CAAC;AA0LrF,2BAA2B;AAC3B,eAAO,MAAM,0BAA0B,EAAE,SAAS,6BAA6B,EAAqD,CAAC"}
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Checks VC08-VC15: what each turn boundary does to the avatar's video and voice. Barge-in stops both at
|
|
3
|
+
* once and drops the cut turn's late media; a turn's end lets its video play out; a video that carries the voice never
|
|
4
|
+
* plays it twice; generated video seconds are reported once, an interrupted turn's included and idle video excluded;
|
|
5
|
+
* the idle video after a turn plays without the agent speaking; a resume keeps the avatar; Disconnect releases it.
|
|
6
|
+
*
|
|
7
|
+
* @module @memberjunction/ai-realtime-client/testing
|
|
8
|
+
*/
|
|
9
|
+
import { AssertCount, AssertSameBytes, AssertSeconds, AssertTrue, AssertValue } from './conformanceAssertions.js';
|
|
10
|
+
import { ConformancePcm } from './conformanceFixtures.js';
|
|
11
|
+
import { AllOf, NeedsGrant, NeedsHarnessMethod, NeedsPcmVoice, NeedsPlayout, NeedsVideoThatCanCarryVoice, NeedsVideoUsage, } from './conformanceGates.js';
|
|
12
|
+
import { SecondsOfVideo } from './conformanceVideoStream.js';
|
|
13
|
+
import { WithConformanceSession } from './realtimeVideoConformanceSession.js';
|
|
14
|
+
/** A granted avatar whose voice comes separately, as PCM, so a turn queues voice beside its video. */
|
|
15
|
+
const SEPARATE_VOICE = { Grant: { Avatar: true, VideoCarriesVoice: false } };
|
|
16
|
+
/** A granted avatar whose video carries the voice. */
|
|
17
|
+
const MUXED_VOICE = { Grant: { Avatar: true, VideoCarriesVoice: true } };
|
|
18
|
+
const VC08 = {
|
|
19
|
+
Id: 'VC08',
|
|
20
|
+
Title: 'Barge-in stops the video and the voice at once',
|
|
21
|
+
Gate: AllOf(NeedsPlayout, NeedsGrant),
|
|
22
|
+
Run: (harness) => WithConformanceSession(harness, SEPARATE_VOICE, async (s) => {
|
|
23
|
+
await s.StartVideo();
|
|
24
|
+
await s.SendFrames(1);
|
|
25
|
+
await s.SendVoice(ConformancePcm(1));
|
|
26
|
+
const players = s.Players.length;
|
|
27
|
+
const mark = s.Timeline.Mark();
|
|
28
|
+
await harness.Interrupted();
|
|
29
|
+
AssertCount(s.Count('interruption', mark), 1, 'barge-ins reported (OnInterruption) for one barge-in');
|
|
30
|
+
AssertCount(s.PlayerEvents('player-flush', 0, mark).length, 1, 'flushes of the video player at a barge-in');
|
|
31
|
+
AssertCount(s.PlayerEvents('player-dispose', 0, mark).length, 0, 'disposes of the video player at a barge-in (its last frame stays)');
|
|
32
|
+
if (harness.Traits.Voice === 'pcm') {
|
|
33
|
+
AssertTrue(s.Count('voice-flush', mark) > 0, 'a barge-in flushes the voice', 'the voice playback was not flushed');
|
|
34
|
+
}
|
|
35
|
+
AssertValue(s.Client.IsAudioPlaying, false, 'IsAudioPlaying after a barge-in');
|
|
36
|
+
AssertCount(s.RemoteVideos(mark).length, 0, 'agent video handed over again at a barge-in');
|
|
37
|
+
AssertCount(s.Players.length, players, 'video players after a barge-in');
|
|
38
|
+
}),
|
|
39
|
+
};
|
|
40
|
+
const VC09 = {
|
|
41
|
+
Id: 'VC09',
|
|
42
|
+
Title: 'Media for an interrupted turn never plays',
|
|
43
|
+
Gate: AllOf(NeedsPlayout, NeedsGrant),
|
|
44
|
+
Run: (harness) => WithConformanceSession(harness, SEPARATE_VOICE, async (s) => {
|
|
45
|
+
await s.StartVideo();
|
|
46
|
+
await s.SendFrames(1);
|
|
47
|
+
await s.SendVoice(ConformancePcm(1));
|
|
48
|
+
await harness.Interrupted();
|
|
49
|
+
const late = s.Timeline.Mark();
|
|
50
|
+
await s.SendFrames(1);
|
|
51
|
+
await s.SendVoice(ConformancePcm(2));
|
|
52
|
+
AssertCount(s.Count('player-append', late), 0, "frames of an interrupted turn's late video played");
|
|
53
|
+
AssertCount(s.Count('voice-enqueue', late), 0, "chunks of an interrupted turn's late voice played");
|
|
54
|
+
await harness.TurnComplete();
|
|
55
|
+
const next = s.Timeline.Mark();
|
|
56
|
+
const frames = await s.SendFrames(1);
|
|
57
|
+
const pcm = ConformancePcm(3);
|
|
58
|
+
await s.SendVoice(pcm);
|
|
59
|
+
AssertSameBytes(s.AppendedFrames(0, next).map((frame) => frame.Data), frames.map((frame) => frame.Data), "the next turn's video plays");
|
|
60
|
+
if (harness.Traits.Voice === 'pcm') {
|
|
61
|
+
AssertSameBytes(s.VoiceEnqueued(next), [pcm], "the next turn's voice plays");
|
|
62
|
+
}
|
|
63
|
+
}),
|
|
64
|
+
};
|
|
65
|
+
const VC10 = {
|
|
66
|
+
Id: 'VC10',
|
|
67
|
+
Title: "A turn's end lets its video play out",
|
|
68
|
+
Gate: AllOf(NeedsPlayout, NeedsGrant),
|
|
69
|
+
Run: (harness) => WithConformanceSession(harness, { Grant: { Avatar: true } }, async (s) => {
|
|
70
|
+
const ends = () => s.PlayerEvents('player-end-of-turn', 0).length;
|
|
71
|
+
await s.StartVideo();
|
|
72
|
+
await s.SendFrames(2);
|
|
73
|
+
if (harness.GenerationComplete) {
|
|
74
|
+
await harness.GenerationComplete();
|
|
75
|
+
AssertCount(ends(), 1, "ends of the turn's video at generation complete");
|
|
76
|
+
}
|
|
77
|
+
await harness.TurnComplete();
|
|
78
|
+
AssertCount(ends(), 1, "ends of the turn's video once the turn is complete (one per turn)");
|
|
79
|
+
await s.SendFrames(1);
|
|
80
|
+
AssertCount(s.Players.length, 1, 'video players after a second turn (the next turn uses the same player)');
|
|
81
|
+
AssertCount(s.PlayerEvents('player-append', 0).length, 4, "frames on the player after the next turn's video");
|
|
82
|
+
await s.EndTurn();
|
|
83
|
+
AssertCount(ends(), 2, "ends of the video after the next turn's end");
|
|
84
|
+
AssertCount(s.Count('player-flush'), 0, "flushes of the video at a turn's end (it must play out)");
|
|
85
|
+
AssertCount(s.Count('player-dispose'), 0, "disposes of the video player at a turn's end");
|
|
86
|
+
}),
|
|
87
|
+
};
|
|
88
|
+
const VC11 = {
|
|
89
|
+
Id: 'VC11',
|
|
90
|
+
Title: 'A video that carries the voice never plays it twice',
|
|
91
|
+
Gate: AllOf(NeedsPlayout, NeedsGrant, NeedsVideoThatCanCarryVoice, NeedsPcmVoice),
|
|
92
|
+
Run: async (harness) => {
|
|
93
|
+
await WithConformanceSession(harness, MUXED_VOICE, assertMuxedVoicePlaysOnce);
|
|
94
|
+
await WithConformanceSession(harness, SEPARATE_VOICE, assertSeparateVoicePlays);
|
|
95
|
+
},
|
|
96
|
+
};
|
|
97
|
+
const VC12 = {
|
|
98
|
+
Id: 'VC12',
|
|
99
|
+
Title: 'Generated video seconds are reported, each once',
|
|
100
|
+
Gate: AllOf(NeedsPlayout, NeedsGrant, NeedsVideoUsage),
|
|
101
|
+
Run: (harness) => WithConformanceSession(harness, { Grant: { Avatar: true } }, async (s) => {
|
|
102
|
+
const first = s.Timeline.Mark();
|
|
103
|
+
const turn = [await s.StartVideo(), ...(await s.SendFrames(3))];
|
|
104
|
+
await s.EndTurn();
|
|
105
|
+
AssertSeconds(s.VideoSeconds(first), SecondsOfVideo(turn), 'video seconds reported for a turn, once');
|
|
106
|
+
// These frames come with no answer start: they count as a turn's here, though VC13 takes such video as idle
|
|
107
|
+
// for whether the agent is speaking. Whether video outside an answer counts in usage is open (#5312).
|
|
108
|
+
const second = s.Timeline.Mark();
|
|
109
|
+
const next = await s.SendFrames(2);
|
|
110
|
+
await s.EndTurn();
|
|
111
|
+
AssertSeconds(s.VideoSeconds(second), SecondsOfVideo(next), 'video seconds reported for the next turn, of 2 frames');
|
|
112
|
+
const last = s.Timeline.Mark();
|
|
113
|
+
const cut = await s.SendFrames(1);
|
|
114
|
+
await s.Disconnect();
|
|
115
|
+
AssertSeconds(s.VideoSeconds(last), SecondsOfVideo(cut), 'video seconds reported at Disconnect for the turn it cut off');
|
|
116
|
+
}),
|
|
117
|
+
};
|
|
118
|
+
const VC13 = {
|
|
119
|
+
Id: 'VC13',
|
|
120
|
+
Title: "An interrupted turn counts what arrived; idle video doesn't count, and is not the agent speaking",
|
|
121
|
+
Gate: AllOf(NeedsPlayout, NeedsGrant, NeedsVideoUsage),
|
|
122
|
+
Run: (harness) => WithConformanceSession(harness, { Grant: { Avatar: true } }, async (s) => {
|
|
123
|
+
const interrupted = s.Timeline.Mark();
|
|
124
|
+
const arrived = [await s.StartVideo(), ...(await s.SendFrames(2))];
|
|
125
|
+
await harness.Interrupted();
|
|
126
|
+
await s.SendFrames(1);
|
|
127
|
+
await harness.TurnComplete();
|
|
128
|
+
AssertSeconds(s.VideoSeconds(interrupted), SecondsOfVideo(arrived), 'video seconds reported for an interrupted turn: what arrived before it (1 more frame came late)');
|
|
129
|
+
// Without generation complete, idle video can't be told from the turn's own.
|
|
130
|
+
if (harness.GenerationComplete) {
|
|
131
|
+
const idle = s.Timeline.Mark();
|
|
132
|
+
const generated = await s.SendFrames(1);
|
|
133
|
+
await harness.GenerationComplete();
|
|
134
|
+
await s.SendFrames(2);
|
|
135
|
+
await harness.TurnComplete();
|
|
136
|
+
AssertSeconds(s.VideoSeconds(idle), SecondsOfVideo(generated), 'video seconds reported for a turn of 1 frame followed by 2 idle frames');
|
|
137
|
+
}
|
|
138
|
+
// Without the answer's start, idle video can't be told from an answer's.
|
|
139
|
+
if (harness.AnswerStarted) {
|
|
140
|
+
await assertIdleVideoIsNotSpeech(s);
|
|
141
|
+
}
|
|
142
|
+
}),
|
|
143
|
+
};
|
|
144
|
+
const VC14 = {
|
|
145
|
+
Id: 'VC14',
|
|
146
|
+
Title: 'A resumed session keeps its avatar',
|
|
147
|
+
Gate: AllOf(NeedsPlayout, NeedsGrant, NeedsHarnessMethod('Resume')),
|
|
148
|
+
Run: (harness) => WithConformanceSession(harness, { Grant: { Avatar: true } }, async (s) => {
|
|
149
|
+
const turn = s.Timeline.Mark();
|
|
150
|
+
const cut = [await s.StartVideo(), ...(await s.SendFrames(1))];
|
|
151
|
+
const mark = s.Timeline.Mark();
|
|
152
|
+
await harness.Resume?.();
|
|
153
|
+
assertAvatarKeptAcrossResume(s, mark);
|
|
154
|
+
const after = s.Timeline.Mark();
|
|
155
|
+
const frames = await s.SendFrames(1);
|
|
156
|
+
AssertSameBytes(s.AppendedFrames(0, after).map((frame) => frame.Data), frames.map((frame) => frame.Data), 'video after a resume reaches the same player');
|
|
157
|
+
if (harness.Traits.VideoUsage) {
|
|
158
|
+
AssertSeconds(s.VideoSeconds(turn), SecondsOfVideo(cut), 'video seconds reported by the resume for the turn it cut off');
|
|
159
|
+
await s.EndTurn();
|
|
160
|
+
AssertSeconds(s.VideoSeconds(turn), SecondsOfVideo([...cut, ...frames]), 'video seconds reported for the cut turn and the next, each once');
|
|
161
|
+
}
|
|
162
|
+
}),
|
|
163
|
+
};
|
|
164
|
+
const VC15 = {
|
|
165
|
+
Id: 'VC15',
|
|
166
|
+
Title: 'Disconnect releases the video',
|
|
167
|
+
Gate: AllOf(NeedsPlayout, NeedsGrant),
|
|
168
|
+
Run: (harness) => WithConformanceSession(harness, { Grant: { Avatar: true } }, async (s) => {
|
|
169
|
+
await s.StartVideo();
|
|
170
|
+
await s.SendFrames(1);
|
|
171
|
+
const mark = s.Timeline.Mark();
|
|
172
|
+
await s.Disconnect();
|
|
173
|
+
await s.Client.Disconnect();
|
|
174
|
+
AssertCount(s.PlayerEvents('player-dispose', 0, mark).length, 1, 'disposes of the video player by Disconnect, called twice');
|
|
175
|
+
AssertValue(s.LastState(), 'closed', 'the state after Disconnect');
|
|
176
|
+
await s.SendFrames(1);
|
|
177
|
+
AssertCount(s.Count('player-append', mark), 0, 'frames of video played after Disconnect');
|
|
178
|
+
AssertCount(s.RemoteVideos(mark).length, 0, 'agent video handed over after Disconnect');
|
|
179
|
+
}),
|
|
180
|
+
};
|
|
181
|
+
/** VC08-VC15, in order. */
|
|
182
|
+
export const REALTIME_VIDEO_TURN_CHECKS = [VC08, VC09, VC10, VC11, VC12, VC13, VC14, VC15];
|
|
183
|
+
/**
|
|
184
|
+
* Muxed voice: PCM plays while the turn has no video; the turn's first video flushes it, and later PCM in that turn is
|
|
185
|
+
* dropped. The next turn without video plays its PCM again.
|
|
186
|
+
*/
|
|
187
|
+
async function assertMuxedVoicePlaysOnce(s) {
|
|
188
|
+
const before = s.Timeline.Mark();
|
|
189
|
+
const early = ConformancePcm(1);
|
|
190
|
+
await s.SendVoice(early);
|
|
191
|
+
AssertSameBytes(s.VoiceEnqueued(before), [early], 'PCM that arrives before the turn has video plays');
|
|
192
|
+
const videoStarts = s.Timeline.Mark();
|
|
193
|
+
await s.StartVideo();
|
|
194
|
+
AssertTrue(s.Count('voice-flush', videoStarts) > 0, "the turn's first video flushes the PCM queued before it", 'the PCM was not flushed');
|
|
195
|
+
AssertValue(s.Players[0]?.CarriesVoice, true, "whether the player plays the video's audio (it is the voice)");
|
|
196
|
+
const later = s.Timeline.Mark();
|
|
197
|
+
await s.SendVoice(ConformancePcm(2));
|
|
198
|
+
await s.SendFrames(1);
|
|
199
|
+
AssertCount(s.Count('voice-enqueue', later), 0, 'PCM chunks played later in a turn whose video carries the voice');
|
|
200
|
+
await s.EndTurn();
|
|
201
|
+
const next = s.Timeline.Mark();
|
|
202
|
+
const pcm = ConformancePcm(3);
|
|
203
|
+
await s.SendVoice(pcm);
|
|
204
|
+
AssertSameBytes(s.VoiceEnqueued(next), [pcm], 'PCM of the next turn, which has no video yet, plays');
|
|
205
|
+
}
|
|
206
|
+
/** Separate voice: every PCM chunk plays beside the video, nothing is flushed, and the player is muted. */
|
|
207
|
+
async function assertSeparateVoicePlays(s) {
|
|
208
|
+
const mark = s.Timeline.Mark();
|
|
209
|
+
const pcm = [ConformancePcm(1), ConformancePcm(2), ConformancePcm(3)];
|
|
210
|
+
await s.SendVoice(pcm[0]);
|
|
211
|
+
await s.StartVideo();
|
|
212
|
+
await s.SendVoice(pcm[1]);
|
|
213
|
+
await s.SendFrames(1);
|
|
214
|
+
await s.SendVoice(pcm[2]);
|
|
215
|
+
AssertSameBytes(s.VoiceEnqueued(mark), pcm, 'a separate voice plays every PCM chunk beside the video');
|
|
216
|
+
AssertCount(s.Count('voice-flush', mark), 0, 'flushes of a separate voice by the video');
|
|
217
|
+
AssertValue(s.Players[0]?.CarriesVoice, false, "whether the player plays the video's audio (the voice plays as PCM)");
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* The idle video after a turn, which a provider may stream between answers (Vertex AI does): the answer's video is the
|
|
221
|
+
* agent speaking; the video after its turn plays, but reports no `'speaking'`, leaves the client idle, and, once the
|
|
222
|
+
* answer has played out, is not audible.
|
|
223
|
+
*/
|
|
224
|
+
async function assertIdleVideoIsNotSpeech(s) {
|
|
225
|
+
const answer = s.Timeline.Mark();
|
|
226
|
+
await s.Harness.AnswerStarted?.();
|
|
227
|
+
await s.SendFrames(2);
|
|
228
|
+
AssertTrue(s.States(answer).includes('speaking'), "the client reports 'speaking' for an answer's video", `it reported ${s.States(answer).join(', ') || 'no state'}`);
|
|
229
|
+
await s.EndTurn();
|
|
230
|
+
s.FinishPlaying();
|
|
231
|
+
const idle = s.Timeline.Mark();
|
|
232
|
+
const frames = await s.SendFrames(2);
|
|
233
|
+
AssertSameBytes(s.AppendedFrames(0, idle).map((frame) => frame.Data), frames.map((frame) => frame.Data), 'idle video after a turn plays');
|
|
234
|
+
AssertCount(s.States(idle).filter((state) => state === 'speaking').length, 0, "'speaking' reported for idle video after a turn");
|
|
235
|
+
AssertValue(s.Client.IsBusy, false, 'IsBusy while idle video plays after a turn');
|
|
236
|
+
AssertValue(s.Client.IsAudioPlaying, false, 'IsAudioPlaying once the answer has played out and only idle video plays');
|
|
237
|
+
}
|
|
238
|
+
/** A resume left the avatar as it was: no new hand-over or player, no flush or dispose, the cut turn ended, the track live. */
|
|
239
|
+
function assertAvatarKeptAcrossResume(s, mark) {
|
|
240
|
+
AssertCount(s.RemoteVideos(mark).length, 0, 'agent video handed over again by a resume');
|
|
241
|
+
AssertCount(s.Players.length, 1, 'video players after a resume');
|
|
242
|
+
AssertCount(s.PlayerEvents('player-flush', 0, mark).length, 0, 'flushes of the video by a resume');
|
|
243
|
+
AssertCount(s.PlayerEvents('player-dispose', 0, mark).length, 0, 'disposes of the video player by a resume');
|
|
244
|
+
AssertCount(s.PlayerEvents('player-end-of-turn', 0, mark).length, 1, "ends of the cut turn's video at a resume (it plays out and holds its last frame)");
|
|
245
|
+
AssertValue(s.OutboundVideoTrack()?.State ?? 'absent', 'live', 'the outbound video track after a resume');
|
|
246
|
+
AssertValue(s.LastState(), 'listening', 'the state after a resume');
|
|
247
|
+
}
|
|
248
|
+
//# sourceMappingURL=realtimeVideoConformanceTurnChecks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"realtimeVideoConformanceTurnChecks.js","sourceRoot":"","sources":["../../src/testing/realtimeVideoConformanceTurnChecks.ts"],"names":[],"mappings":"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"}
|