@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.
Files changed (209) hide show
  1. package/README.md +156 -29
  2. package/dist/audio/audioMeter.d.ts +71 -0
  3. package/dist/audio/audioMeter.d.ts.map +1 -1
  4. package/dist/audio/audioMeter.js +83 -0
  5. package/dist/audio/audioMeter.js.map +1 -1
  6. package/dist/audio/mediaElementAudioRouter.d.ts +41 -0
  7. package/dist/audio/mediaElementAudioRouter.d.ts.map +1 -0
  8. package/dist/audio/mediaElementAudioRouter.js +82 -0
  9. package/dist/audio/mediaElementAudioRouter.js.map +1 -0
  10. package/dist/audio/micCapture.d.ts +12 -3
  11. package/dist/audio/micCapture.d.ts.map +1 -1
  12. package/dist/audio/micCapture.js +15 -1
  13. package/dist/audio/micCapture.js.map +1 -1
  14. package/dist/audio/pcmPlayback.d.ts +72 -4
  15. package/dist/audio/pcmPlayback.d.ts.map +1 -1
  16. package/dist/audio/pcmPlayback.js +101 -1
  17. package/dist/audio/pcmPlayback.js.map +1 -1
  18. package/dist/drivers/assemblyAIRealtimeClient.d.ts +2 -0
  19. package/dist/drivers/assemblyAIRealtimeClient.d.ts.map +1 -1
  20. package/dist/drivers/assemblyAIRealtimeClient.js +14 -0
  21. package/dist/drivers/assemblyAIRealtimeClient.js.map +1 -1
  22. package/dist/drivers/elevenLabsRealtimeClient.d.ts +2 -0
  23. package/dist/drivers/elevenLabsRealtimeClient.d.ts.map +1 -1
  24. package/dist/drivers/elevenLabsRealtimeClient.js +14 -0
  25. package/dist/drivers/elevenLabsRealtimeClient.js.map +1 -1
  26. package/dist/drivers/geminiAvatarOutput.d.ts +132 -0
  27. package/dist/drivers/geminiAvatarOutput.d.ts.map +1 -0
  28. package/dist/drivers/geminiAvatarOutput.js +310 -0
  29. package/dist/drivers/geminiAvatarOutput.js.map +1 -0
  30. package/dist/drivers/geminiEnterpriseRealtimeClient.d.ts +38 -0
  31. package/dist/drivers/geminiEnterpriseRealtimeClient.d.ts.map +1 -0
  32. package/dist/drivers/geminiEnterpriseRealtimeClient.js +99 -0
  33. package/dist/drivers/geminiEnterpriseRealtimeClient.js.map +1 -0
  34. package/dist/drivers/geminiRealtimeClient.d.ts +325 -50
  35. package/dist/drivers/geminiRealtimeClient.d.ts.map +1 -1
  36. package/dist/drivers/geminiRealtimeClient.js +782 -184
  37. package/dist/drivers/geminiRealtimeClient.js.map +1 -1
  38. package/dist/drivers/openAILiveClient.d.ts +5 -8
  39. package/dist/drivers/openAILiveClient.d.ts.map +1 -1
  40. package/dist/drivers/openAILiveClient.js +19 -32
  41. package/dist/drivers/openAILiveClient.js.map +1 -1
  42. package/dist/drivers/openAIRealtimeClient.d.ts +13 -26
  43. package/dist/drivers/openAIRealtimeClient.d.ts.map +1 -1
  44. package/dist/drivers/openAIRealtimeClient.js +28 -55
  45. package/dist/drivers/openAIRealtimeClient.js.map +1 -1
  46. package/dist/drivers/xaiRealtimeClient.d.ts.map +1 -1
  47. package/dist/drivers/xaiRealtimeClient.js +19 -14
  48. package/dist/drivers/xaiRealtimeClient.js.map +1 -1
  49. package/dist/generic/baseRealtimeClient.d.ts +183 -29
  50. package/dist/generic/baseRealtimeClient.d.ts.map +1 -1
  51. package/dist/generic/baseRealtimeClient.js +246 -14
  52. package/dist/generic/baseRealtimeClient.js.map +1 -1
  53. package/dist/generic/openAIProtocolClient.d.ts +2 -0
  54. package/dist/generic/openAIProtocolClient.d.ts.map +1 -1
  55. package/dist/generic/openAIProtocolClient.js +17 -0
  56. package/dist/generic/openAIProtocolClient.js.map +1 -1
  57. package/dist/index.d.ts +2 -3
  58. package/dist/index.d.ts.map +1 -1
  59. package/dist/index.js +3 -3
  60. package/dist/index.js.map +1 -1
  61. package/dist/media/attachVideoSource.d.ts +23 -0
  62. package/dist/media/attachVideoSource.d.ts.map +1 -0
  63. package/dist/media/attachVideoSource.js +49 -0
  64. package/dist/media/attachVideoSource.js.map +1 -0
  65. package/dist/media/channelVideoSource.d.ts +110 -14
  66. package/dist/media/channelVideoSource.d.ts.map +1 -1
  67. package/dist/media/channelVideoSource.js +183 -18
  68. package/dist/media/channelVideoSource.js.map +1 -1
  69. package/dist/media/decoders/frameScheduler.d.ts +119 -0
  70. package/dist/media/decoders/frameScheduler.d.ts.map +1 -0
  71. package/dist/media/decoders/frameScheduler.js +238 -0
  72. package/dist/media/decoders/frameScheduler.js.map +1 -0
  73. package/dist/media/decoders/imageFrameDecoder.d.ts +58 -0
  74. package/dist/media/decoders/imageFrameDecoder.d.ts.map +1 -0
  75. package/dist/media/decoders/imageFrameDecoder.js +110 -0
  76. package/dist/media/decoders/imageFrameDecoder.js.map +1 -0
  77. package/dist/media/decoders/mseFmp4Decoder.d.ts +130 -0
  78. package/dist/media/decoders/mseFmp4Decoder.d.ts.map +1 -0
  79. package/dist/media/decoders/mseFmp4Decoder.js +402 -0
  80. package/dist/media/decoders/mseFmp4Decoder.js.map +1 -0
  81. package/dist/media/decoders/streamCanvas.d.ts +46 -0
  82. package/dist/media/decoders/streamCanvas.d.ts.map +1 -0
  83. package/dist/media/decoders/streamCanvas.js +163 -0
  84. package/dist/media/decoders/streamCanvas.js.map +1 -0
  85. package/dist/media/decoders/videoMimeTypes.d.ts +29 -0
  86. package/dist/media/decoders/videoMimeTypes.d.ts.map +1 -0
  87. package/dist/media/decoders/videoMimeTypes.js +50 -0
  88. package/dist/media/decoders/videoMimeTypes.js.map +1 -0
  89. package/dist/media/decoders/webCodecsChunkDecoder.d.ts +99 -0
  90. package/dist/media/decoders/webCodecsChunkDecoder.d.ts.map +1 -0
  91. package/dist/media/decoders/webCodecsChunkDecoder.js +265 -0
  92. package/dist/media/decoders/webCodecsChunkDecoder.js.map +1 -0
  93. package/dist/media/displayCapture.d.ts +128 -0
  94. package/dist/media/displayCapture.d.ts.map +1 -0
  95. package/dist/media/displayCapture.js +245 -0
  96. package/dist/media/displayCapture.js.map +1 -0
  97. package/dist/media/frameCapture.d.ts +25 -8
  98. package/dist/media/frameCapture.d.ts.map +1 -1
  99. package/dist/media/frameCapture.js +42 -72
  100. package/dist/media/frameCapture.js.map +1 -1
  101. package/dist/media/frameSampler.d.ts +94 -0
  102. package/dist/media/frameSampler.d.ts.map +1 -0
  103. package/dist/media/frameSampler.js +134 -0
  104. package/dist/media/frameSampler.js.map +1 -0
  105. package/dist/media/index.d.ts +32 -0
  106. package/dist/media/index.d.ts.map +1 -0
  107. package/dist/media/index.js +32 -0
  108. package/dist/media/index.js.map +1 -0
  109. package/dist/media/localMediaController.d.ts +116 -0
  110. package/dist/media/localMediaController.d.ts.map +1 -0
  111. package/dist/media/localMediaController.js +281 -0
  112. package/dist/media/localMediaController.js.map +1 -0
  113. package/dist/media/mediaLayout.d.ts +93 -0
  114. package/dist/media/mediaLayout.d.ts.map +1 -0
  115. package/dist/media/mediaLayout.js +187 -0
  116. package/dist/media/mediaLayout.js.map +1 -0
  117. package/dist/media/mediaPreview.d.ts +94 -0
  118. package/dist/media/mediaPreview.d.ts.map +1 -0
  119. package/dist/media/mediaPreview.js +154 -0
  120. package/dist/media/mediaPreview.js.map +1 -0
  121. package/dist/media/mediaStage.d.ts +99 -0
  122. package/dist/media/mediaStage.d.ts.map +1 -0
  123. package/dist/media/mediaStage.js +171 -0
  124. package/dist/media/mediaStage.js.map +1 -0
  125. package/dist/media/model.d.ts +154 -0
  126. package/dist/media/model.d.ts.map +1 -0
  127. package/dist/media/model.js +9 -0
  128. package/dist/media/model.js.map +1 -0
  129. package/dist/media/playbackClock.d.ts +20 -0
  130. package/dist/media/playbackClock.d.ts.map +1 -0
  131. package/dist/media/playbackClock.js +13 -0
  132. package/dist/media/playbackClock.js.map +1 -0
  133. package/dist/media/videoFrameDecoder.d.ts +86 -0
  134. package/dist/media/videoFrameDecoder.d.ts.map +1 -0
  135. package/dist/media/videoFrameDecoder.js +2 -0
  136. package/dist/media/videoFrameDecoder.js.map +1 -0
  137. package/dist/media/videoFrameDecoderRegistry.d.ts +40 -0
  138. package/dist/media/videoFrameDecoderRegistry.d.ts.map +1 -0
  139. package/dist/media/videoFrameDecoderRegistry.js +69 -0
  140. package/dist/media/videoFrameDecoderRegistry.js.map +1 -0
  141. package/dist/media/videoPacing.d.ts +32 -0
  142. package/dist/media/videoPacing.d.ts.map +1 -0
  143. package/dist/media/videoPacing.js +37 -0
  144. package/dist/media/videoPacing.js.map +1 -0
  145. package/dist/media/videoPlayout.d.ts +168 -0
  146. package/dist/media/videoPlayout.d.ts.map +1 -0
  147. package/dist/media/videoPlayout.js +217 -0
  148. package/dist/media/videoPlayout.js.map +1 -0
  149. package/dist/media/videoSourceArbiter.d.ts +250 -0
  150. package/dist/media/videoSourceArbiter.d.ts.map +1 -0
  151. package/dist/media/videoSourceArbiter.js +423 -0
  152. package/dist/media/videoSourceArbiter.js.map +1 -0
  153. package/dist/testing/conformanceAssertions.d.ts +25 -0
  154. package/dist/testing/conformanceAssertions.d.ts.map +1 -0
  155. package/dist/testing/conformanceAssertions.js +64 -0
  156. package/dist/testing/conformanceAssertions.js.map +1 -0
  157. package/dist/testing/conformanceFixtures.d.ts +76 -0
  158. package/dist/testing/conformanceFixtures.d.ts.map +1 -0
  159. package/dist/testing/conformanceFixtures.js +169 -0
  160. package/dist/testing/conformanceFixtures.js.map +1 -0
  161. package/dist/testing/conformanceGates.d.ts +32 -0
  162. package/dist/testing/conformanceGates.d.ts.map +1 -0
  163. package/dist/testing/conformanceGates.js +35 -0
  164. package/dist/testing/conformanceGates.js.map +1 -0
  165. package/dist/testing/conformanceMicrophone.d.ts +10 -0
  166. package/dist/testing/conformanceMicrophone.d.ts.map +1 -0
  167. package/dist/testing/conformanceMicrophone.js +71 -0
  168. package/dist/testing/conformanceMicrophone.js.map +1 -0
  169. package/dist/testing/conformanceTimeline.d.ts +128 -0
  170. package/dist/testing/conformanceTimeline.d.ts.map +1 -0
  171. package/dist/testing/conformanceTimeline.js +32 -0
  172. package/dist/testing/conformanceTimeline.js.map +1 -0
  173. package/dist/testing/conformanceVideoStream.d.ts +29 -0
  174. package/dist/testing/conformanceVideoStream.d.ts.map +1 -0
  175. package/dist/testing/conformanceVideoStream.js +39 -0
  176. package/dist/testing/conformanceVideoStream.js.map +1 -0
  177. package/dist/testing/index.d.ts +29 -0
  178. package/dist/testing/index.d.ts.map +1 -0
  179. package/dist/testing/index.js +28 -0
  180. package/dist/testing/index.js.map +1 -0
  181. package/dist/testing/realtimeVideoConformance.d.ts +23 -0
  182. package/dist/testing/realtimeVideoConformance.d.ts.map +1 -0
  183. package/dist/testing/realtimeVideoConformance.js +139 -0
  184. package/dist/testing/realtimeVideoConformance.js.map +1 -0
  185. package/dist/testing/realtimeVideoConformanceChecks.d.ts +4 -0
  186. package/dist/testing/realtimeVideoConformanceChecks.d.ts.map +1 -0
  187. package/dist/testing/realtimeVideoConformanceChecks.js +164 -0
  188. package/dist/testing/realtimeVideoConformanceChecks.js.map +1 -0
  189. package/dist/testing/realtimeVideoConformanceFrameChecks.d.ts +4 -0
  190. package/dist/testing/realtimeVideoConformanceFrameChecks.d.ts.map +1 -0
  191. package/dist/testing/realtimeVideoConformanceFrameChecks.js +63 -0
  192. package/dist/testing/realtimeVideoConformanceFrameChecks.js.map +1 -0
  193. package/dist/testing/realtimeVideoConformanceSession.d.ts +108 -0
  194. package/dist/testing/realtimeVideoConformanceSession.d.ts.map +1 -0
  195. package/dist/testing/realtimeVideoConformanceSession.js +270 -0
  196. package/dist/testing/realtimeVideoConformanceSession.js.map +1 -0
  197. package/dist/testing/realtimeVideoConformanceTurnChecks.d.ts +4 -0
  198. package/dist/testing/realtimeVideoConformanceTurnChecks.d.ts.map +1 -0
  199. package/dist/testing/realtimeVideoConformanceTurnChecks.js +248 -0
  200. package/dist/testing/realtimeVideoConformanceTurnChecks.js.map +1 -0
  201. package/dist/testing/realtimeVideoConformanceTypes.d.ts +155 -0
  202. package/dist/testing/realtimeVideoConformanceTypes.d.ts.map +1 -0
  203. package/dist/testing/realtimeVideoConformanceTypes.js +2 -0
  204. package/dist/testing/realtimeVideoConformanceTypes.js.map +1 -0
  205. package/dist/testing/recordingPlayout.d.ts +84 -0
  206. package/dist/testing/recordingPlayout.d.ts.map +1 -0
  207. package/dist/testing/recordingPlayout.js +120 -0
  208. package/dist/testing/recordingPlayout.js.map +1 -0
  209. package/package.json +29 -4
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/testing/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,EAAE,6BAA6B,EAAE,MAAM,yBAAyB,CAAC;AACxE,OAAO,EACH,8BAA8B,EAC9B,sBAAsB,EACtB,4BAA4B,EAC5B,0BAA0B,EAC1B,4BAA4B,EAC5B,qBAAqB,EACrB,cAAc,EACd,KAAK,uBAAuB,GAC/B,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,2BAA2B,EAAE,MAAM,yBAAyB,CAAC;AACtE,OAAO,EACH,gCAAgC,EAChC,KAAK,6BAA6B,EAClC,KAAK,iCAAiC,EACtC,KAAK,+BAA+B,GACvC,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AACjF,OAAO,EAAE,kCAAkC,EAAE,iCAAiC,EAAE,2BAA2B,EAAE,MAAM,4BAA4B,CAAC;AAChJ,YAAY,EACR,gCAAgC,EAChC,6BAA6B,EAC7B,gCAAgC,EAChC,qCAAqC,EACrC,6BAA6B,EAC7B,6BAA6B,EAC7B,8BAA8B,EAC9B,8BAA8B,GACjC,MAAM,iCAAiC,CAAC"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * @fileoverview The `@memberjunction/ai-realtime-client/testing` entry point: the video provider conformance kit. A
3
+ * provider's test runs it against the provider's client driver to prove the driver meets the realtime video contract:
4
+ * what it hands the host, where the model's video and voice go, what barge-in, turn ends, resumes and Disconnect do to
5
+ * them, and the generated video seconds it reports.
6
+ *
7
+ * The checks (VC01-VC15, VF01-VF02) take the agent's video as Core's `RealtimeVideoFrame`s, in whichever form the
8
+ * provider's model sends it: fragmented MP4, encoded chunks or images.
9
+ *
10
+ * The kit needs no test framework: {@link RunRealtimeVideoConformance} resolves to results, and
11
+ * {@link ListRealtimeVideoConformanceChecks} gives a runner one test per check. A provider writes a harness
12
+ * ({@link IRealtimeVideoConformanceHarness}): what its server mints, its client driver with a fake transport and its
13
+ * creation seams wired to the kit's recorders, and the model's side of the wire.
14
+ *
15
+ * Everything reachable from here may import only other `/testing` modules, `/media`, `src/audio/`,
16
+ * `generic/baseRealtimeClient`, `@memberjunction/ai`, `@memberjunction/global` and `rxjs`: no driver, no
17
+ * `@google/genai`, no test framework. `testing-entry-boundary.test.ts` fails the build otherwise. The main entry does
18
+ * not export the kit, so apps never bundle it.
19
+ *
20
+ * @module @memberjunction/ai-realtime-client/testing
21
+ */
22
+ export { RealtimeVideoConformanceError } from './conformanceAssertions.js';
23
+ export { CONFORMANCE_FMP4_FRAME_SECONDS, ConformanceChunkFrames, ConformanceFmp4AudioFragment, ConformanceFmp4InitSegment, ConformanceFmp4VideoFragment, ConformanceImageFrame, ConformancePcm, } from './conformanceFixtures.js';
24
+ export { CreateConformanceMicrophone } from './conformanceMicrophone.js';
25
+ export { RealtimeVideoConformanceTimeline, } from './conformanceTimeline.js';
26
+ export { RecordingPcmPlayback, RecordingVideoPlayout } from './recordingPlayout.js';
27
+ export { ListRealtimeVideoConformanceChecks, REALTIME_VIDEO_CONFORMANCE_CHECKS, RunRealtimeVideoConformance } from './realtimeVideoConformance.js';
28
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/testing/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,EAAE,6BAA6B,EAAE,MAAM,yBAAyB,CAAC;AACxE,OAAO,EACH,8BAA8B,EAC9B,sBAAsB,EACtB,4BAA4B,EAC5B,0BAA0B,EAC1B,4BAA4B,EAC5B,qBAAqB,EACrB,cAAc,GAEjB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,2BAA2B,EAAE,MAAM,yBAAyB,CAAC;AACtE,OAAO,EACH,gCAAgC,GAInC,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AACjF,OAAO,EAAE,kCAAkC,EAAE,iCAAiC,EAAE,2BAA2B,EAAE,MAAM,4BAA4B,CAAC"}
@@ -0,0 +1,23 @@
1
+ import type { RealtimeVideoConformanceCheck, RealtimeVideoConformanceCheckRun, RealtimeVideoConformanceDriverFactory, RealtimeVideoConformanceResult } from './realtimeVideoConformanceTypes.js';
2
+ /** Every check of the kit, VC01-VC15 then VF01-VF02, in the order they run. */
3
+ export declare const REALTIME_VIDEO_CONFORMANCE_CHECKS: readonly RealtimeVideoConformanceCheck[];
4
+ /**
5
+ * Runs every check against a provider, one at a time, each on a new harness from `driverFactory`, and resolves to one
6
+ * result per check, in order. Never rejects: a check that can't run is Skipped with the reason, and any error makes its
7
+ * check Failed with the rule it broke. Each harness is disposed after its check.
8
+ *
9
+ * @param driverFactory Makes the provider's harness; called once per check.
10
+ */
11
+ export declare function RunRealtimeVideoConformance(driverFactory: RealtimeVideoConformanceDriverFactory): Promise<RealtimeVideoConformanceResult[]>;
12
+ /**
13
+ * The checks bound to a provider, for a test runner to register one test each. A check whose `SkipReason` is set has a
14
+ * `Run` that does nothing; register it as a conditional skip (vitest: `it.skipIf(check.SkipReason !== null)`), since the
15
+ * harness's traits and methods decide it, not as a disabled test. `Run` makes a new harness, runs the check, disposes
16
+ * the harness, and rejects with a {@link RealtimeVideoConformanceError} when the provider breaks a rule.
17
+ *
18
+ * `driverFactory` is called once here, to read the harness's traits and methods, and that harness is disposed.
19
+ *
20
+ * @param driverFactory Makes the provider's harness.
21
+ */
22
+ export declare function ListRealtimeVideoConformanceChecks(driverFactory: RealtimeVideoConformanceDriverFactory): RealtimeVideoConformanceCheckRun[];
23
+ //# sourceMappingURL=realtimeVideoConformance.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"realtimeVideoConformance.d.ts","sourceRoot":"","sources":["../../src/testing/realtimeVideoConformance.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAER,6BAA6B,EAC7B,gCAAgC,EAChC,qCAAqC,EACrC,8BAA8B,EACjC,MAAM,iCAAiC,CAAC;AAEzC,+EAA+E;AAC/E,eAAO,MAAM,iCAAiC,EAAE,SAAS,6BAA6B,EAIrF,CAAC;AAEF;;;;;;GAMG;AACH,wBAAsB,2BAA2B,CAAC,aAAa,EAAE,qCAAqC,GAAG,OAAO,CAAC,8BAA8B,EAAE,CAAC,CAMjJ;AAED;;;;;;;;;GASG;AACH,wBAAgB,kCAAkC,CAAC,aAAa,EAAE,qCAAqC,GAAG,gCAAgC,EAAE,CAe3I"}
@@ -0,0 +1,139 @@
1
+ /**
2
+ * @fileoverview The video conformance kit's runners. {@link RunRealtimeVideoConformance} runs every check against a
3
+ * provider and reports each outcome; {@link ListRealtimeVideoConformanceChecks} hands a test runner the same checks
4
+ * bound to the provider, one test each. Neither needs a test framework.
5
+ *
6
+ * ```ts
7
+ * for (const check of ListRealtimeVideoConformanceChecks(() => new MyProviderHarness())) {
8
+ * it.skipIf(check.SkipReason !== null)(`${check.Id} ${check.Title}`, () => check.Run());
9
+ * }
10
+ * ```
11
+ *
12
+ * @module @memberjunction/ai-realtime-client/testing
13
+ */
14
+ import { RealtimeVideoConformanceError } from './conformanceAssertions.js';
15
+ import { REALTIME_VIDEO_SESSION_CHECKS } from './realtimeVideoConformanceChecks.js';
16
+ import { REALTIME_VIDEO_FRAME_CHECKS } from './realtimeVideoConformanceFrameChecks.js';
17
+ import { REALTIME_VIDEO_TURN_CHECKS } from './realtimeVideoConformanceTurnChecks.js';
18
+ /** Every check of the kit, VC01-VC15 then VF01-VF02, in the order they run. */
19
+ export const REALTIME_VIDEO_CONFORMANCE_CHECKS = [
20
+ ...REALTIME_VIDEO_SESSION_CHECKS,
21
+ ...REALTIME_VIDEO_TURN_CHECKS,
22
+ ...REALTIME_VIDEO_FRAME_CHECKS,
23
+ ];
24
+ /**
25
+ * Runs every check against a provider, one at a time, each on a new harness from `driverFactory`, and resolves to one
26
+ * result per check, in order. Never rejects: a check that can't run is Skipped with the reason, and any error makes its
27
+ * check Failed with the rule it broke. Each harness is disposed after its check.
28
+ *
29
+ * @param driverFactory Makes the provider's harness; called once per check.
30
+ */
31
+ export async function RunRealtimeVideoConformance(driverFactory) {
32
+ const results = [];
33
+ for (const check of REALTIME_VIDEO_CONFORMANCE_CHECKS) {
34
+ results.push(await runForResult(check, driverFactory));
35
+ }
36
+ return results;
37
+ }
38
+ /**
39
+ * The checks bound to a provider, for a test runner to register one test each. A check whose `SkipReason` is set has a
40
+ * `Run` that does nothing; register it as a conditional skip (vitest: `it.skipIf(check.SkipReason !== null)`), since the
41
+ * harness's traits and methods decide it, not as a disabled test. `Run` makes a new harness, runs the check, disposes
42
+ * the harness, and rejects with a {@link RealtimeVideoConformanceError} when the provider breaks a rule.
43
+ *
44
+ * `driverFactory` is called once here, to read the harness's traits and methods, and that harness is disposed.
45
+ *
46
+ * @param driverFactory Makes the provider's harness.
47
+ */
48
+ export function ListRealtimeVideoConformanceChecks(driverFactory) {
49
+ const probe = driverFactory();
50
+ try {
51
+ return REALTIME_VIDEO_CONFORMANCE_CHECKS.map((check) => {
52
+ const skipReason = skipReasonFor(check, probe);
53
+ return {
54
+ Id: check.Id,
55
+ Title: check.Title,
56
+ SkipReason: skipReason,
57
+ Run: () => (skipReason === null ? runCheck(check, driverFactory) : Promise.resolve()),
58
+ };
59
+ });
60
+ }
61
+ finally {
62
+ void disposeHarness(probe);
63
+ }
64
+ }
65
+ /**
66
+ * Why a check doesn't run against a harness: the environment's skip for it, else its gate's reason; `null` when it runs.
67
+ * A harness whose traits can't be read skips with the error.
68
+ */
69
+ function skipReasonFor(check, harness) {
70
+ try {
71
+ const environment = harness.Traits.EnvironmentSkips?.[check.Id];
72
+ return environment !== undefined ? `environment: ${environment}` : check.Gate(harness);
73
+ }
74
+ catch (error) {
75
+ return `the gate failed: ${describeError(error)}`;
76
+ }
77
+ }
78
+ /** Runs one check on a new harness and disposes the harness, whatever happens. */
79
+ async function runCheck(check, driverFactory) {
80
+ const harness = driverFactory();
81
+ try {
82
+ await runOn(check, harness);
83
+ }
84
+ finally {
85
+ await disposeHarness(harness);
86
+ }
87
+ }
88
+ /** Runs one check on a harness. Rejects with a {@link RealtimeVideoConformanceError} whose message starts with the check's id. */
89
+ async function runOn(check, harness) {
90
+ try {
91
+ await check.Run(harness);
92
+ }
93
+ catch (error) {
94
+ throw new RealtimeVideoConformanceError(`${check.Id} (${harness.Name}): ${describeError(error)}`);
95
+ }
96
+ }
97
+ /** One check's outcome for {@link RunRealtimeVideoConformance}: gated and run on one new harness, then disposed. */
98
+ async function runForResult(check, driverFactory) {
99
+ const base = { Id: check.Id, Title: check.Title };
100
+ let harness;
101
+ try {
102
+ harness = driverFactory();
103
+ }
104
+ catch (error) {
105
+ return { ...base, Status: 'Failed', Detail: `the driver factory failed: ${describeError(error)}`, DurationMs: 0 };
106
+ }
107
+ const startedAt = Date.now();
108
+ try {
109
+ const skipReason = skipReasonFor(check, harness);
110
+ if (skipReason !== null) {
111
+ return { ...base, Status: 'Skipped', Detail: skipReason, DurationMs: 0 };
112
+ }
113
+ await runOn(check, harness);
114
+ return { ...base, Status: 'Passed', Detail: null, DurationMs: Date.now() - startedAt };
115
+ }
116
+ catch (error) {
117
+ return { ...base, Status: 'Failed', Detail: describeError(error), DurationMs: Date.now() - startedAt };
118
+ }
119
+ finally {
120
+ await disposeHarness(harness);
121
+ }
122
+ }
123
+ /** Disposes a harness; an error in its Dispose is logged, never thrown over the check's outcome. */
124
+ async function disposeHarness(harness) {
125
+ try {
126
+ await harness.Dispose?.();
127
+ }
128
+ catch (error) {
129
+ console.warn(`[RealtimeVideoConformance] ${harness.Name}'s Dispose failed: ${describeError(error)}`);
130
+ }
131
+ }
132
+ /** A thrown value as text: a conformance failure's message as it is, anything else with its name. */
133
+ function describeError(error) {
134
+ if (error instanceof RealtimeVideoConformanceError) {
135
+ return error.message;
136
+ }
137
+ return error instanceof Error ? `${error.name}: ${error.message}` : String(error);
138
+ }
139
+ //# sourceMappingURL=realtimeVideoConformance.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"realtimeVideoConformance.js","sourceRoot":"","sources":["../../src/testing/realtimeVideoConformance.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import type { RealtimeVideoConformanceCheck } from './realtimeVideoConformanceTypes.js';
2
+ /** VC01-VC07, in order. */
3
+ export declare const REALTIME_VIDEO_SESSION_CHECKS: readonly RealtimeVideoConformanceCheck[];
4
+ //# sourceMappingURL=realtimeVideoConformanceChecks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"realtimeVideoConformanceChecks.d.ts","sourceRoot":"","sources":["../../src/testing/realtimeVideoConformanceChecks.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,6BAA6B,EAAE,MAAM,iCAAiC,CAAC;AA0HrF,2BAA2B;AAC3B,eAAO,MAAM,6BAA6B,EAAE,SAAS,6BAA6B,EAA+C,CAAC"}
@@ -0,0 +1,164 @@
1
+ /**
2
+ * @fileoverview Checks VC01-VC07: what a session hands the host and where the model's parts go. An audio-only session
3
+ * shows no video and still plays the voice; a granted avatar is handed over once, or not at all when the host doesn't
4
+ * show it or the browser can't play it; video goes to its player in order and never to the voice; a part of an unknown
5
+ * type goes nowhere and is reported once.
6
+ *
7
+ * @module @memberjunction/ai-realtime-client/testing
8
+ */
9
+ import { AssertCount, AssertSameBytes, AssertTrue, AssertValue } from './conformanceAssertions.js';
10
+ import { ConformanceFmp4InitSegment, ConformanceFmp4VideoFragment, ConformancePcm } from './conformanceFixtures.js';
11
+ import { AllOf, NeedsAgentVideo, NeedsGrant, NeedsHarnessMethod, NeedsPlayout, NoGate } from './conformanceGates.js';
12
+ import { WithConformanceSession } from './realtimeVideoConformanceSession.js';
13
+ /** The MIME type of a part no realtime driver plays. */
14
+ const UNKNOWN_PART_TYPE = 'application/x-unknown';
15
+ const VC01 = {
16
+ Id: 'VC01',
17
+ Title: 'An audio-only session hands over no agent video and still works',
18
+ Gate: NoGate,
19
+ Run: (harness) => WithConformanceSession(harness, { Grant: { Avatar: false } }, async (s) => {
20
+ AssertValue(s.LastState(), 'listening', 'an audio-only session listens after Connect');
21
+ AssertCount(s.Count('error'), 0, 'errors reported by an audio-only session');
22
+ assertAgentVideoTrackNotLive(s, 'unsupported');
23
+ await assertVoicePlays(s, ConformancePcm(1));
24
+ await harness.TurnComplete();
25
+ AssertCount(s.RemoteVideos().length, 0, 'agent video handed over in an audio-only session');
26
+ AssertCount(s.Players.length, 0, 'video players created in an audio-only session');
27
+ }),
28
+ };
29
+ const VC02 = {
30
+ Id: 'VC02',
31
+ Title: 'In an audio-only session, a video part never plays as audio',
32
+ Gate: NeedsHarnessMethod('SendPart'),
33
+ Run: (harness) => WithConformanceSession(harness, { Grant: { Avatar: false } }, async (s) => {
34
+ const mark = s.Timeline.Mark();
35
+ await harness.SendPart?.('video/mp4', ConformanceFmp4InitSegment());
36
+ await harness.SendPart?.('video/mp4', ConformanceFmp4VideoFragment());
37
+ AssertCount(s.Count('voice-enqueue', mark), 0, 'video/mp4 parts played as audio');
38
+ AssertCount(s.Players.length, 0, 'video players created in an audio-only session');
39
+ AssertCount(s.RemoteVideos().length, 0, 'agent video handed over in an audio-only session');
40
+ AssertCount(s.Warnings('video/mp4', mark), 1, 'warnings naming the dropped video/mp4 parts (once per type)');
41
+ }),
42
+ };
43
+ const VC03 = {
44
+ Id: 'VC03',
45
+ Title: 'A granted avatar is handed over once, and its track is live',
46
+ Gate: AllOf(NeedsAgentVideo, NeedsGrant),
47
+ Run: (harness) => WithConformanceSession(harness, { Grant: { Avatar: true } }, async (s) => {
48
+ AssertValue(s.OutboundVideoTrack()?.State ?? 'absent', 'live', 'the outbound video track of a granted avatar the host shows');
49
+ if (harness.AskedProviderForVideo) {
50
+ AssertValue(harness.AskedProviderForVideo(), true, 'the provider was asked for the avatar the host shows');
51
+ }
52
+ AssertTrue(s.RemoteVideos().length <= 1, 'the agent video is handed over once', `handed over ${s.RemoteVideos().length} times by Connect`);
53
+ await s.StartVideo();
54
+ await s.SendFrames(2);
55
+ await s.SendVoice(ConformancePcm(1));
56
+ await s.EndTurn();
57
+ AssertCount(s.RemoteVideos().length, 1, 'agent video hand-overs through a full turn');
58
+ assertHandedOverItsVideo(s);
59
+ }),
60
+ };
61
+ const VC04 = {
62
+ Id: 'VC04',
63
+ Title: "A granted avatar the host doesn't show stays audio only",
64
+ Gate: AllOf(NeedsAgentVideo, NeedsGrant),
65
+ Run: (harness) => WithConformanceSession(harness, { Grant: { Avatar: true }, ShowAgentVideo: false }, async (s) => {
66
+ assertAgentVideoTrackNotLive(s, null);
67
+ await assertVoicePlays(s, ConformancePcm(1));
68
+ AssertCount(s.RemoteVideos().length, 0, 'agent video handed over to a host that shows none');
69
+ assertNoPlayerKept(s);
70
+ assertAskedForAudioOnly(s);
71
+ }),
72
+ };
73
+ const VC05 = {
74
+ Id: 'VC05',
75
+ Title: "A granted avatar this browser can't play stays audio only",
76
+ Gate: AllOf(NeedsPlayout, NeedsGrant, NeedsHarnessMethod('RefusePlayback')),
77
+ Run: async (harness) => {
78
+ const undo = harness.RefusePlayback?.() ?? (() => undefined);
79
+ try {
80
+ await WithConformanceSession(harness, { Grant: { Avatar: true } }, async (s) => {
81
+ assertAgentVideoTrackNotLive(s, 'unsupported');
82
+ AssertCount(s.RemoteVideos().length, 0, "agent video handed over when the browser can't play it");
83
+ assertNoPlayerKept(s);
84
+ await assertVoicePlays(s, ConformancePcm(1));
85
+ assertAskedForAudioOnly(s);
86
+ });
87
+ }
88
+ finally {
89
+ undo();
90
+ }
91
+ },
92
+ };
93
+ const VC06 = {
94
+ Id: 'VC06',
95
+ Title: 'The avatar\'s video goes to its player in order, never to the voice',
96
+ Gate: AllOf(NeedsPlayout, NeedsGrant),
97
+ Run: (harness) => WithConformanceSession(harness, { Grant: { Avatar: true } }, async (s) => {
98
+ const mark = s.Timeline.Mark();
99
+ const sent = [await s.StartVideo(), ...(await s.SendFrames(3))];
100
+ AssertCount(s.Players.length, 1, 'video players created for one avatar');
101
+ const appended = s.AppendedFrames(0, mark).map((frame) => frame.Data);
102
+ AssertSameBytes(appended, sent.map((frame) => frame.Data), 'every frame of video reaches the player once, in order, unchanged');
103
+ AssertCount(s.Count('voice-enqueue', mark), 0, 'frames of video played as the voice');
104
+ }),
105
+ };
106
+ const VC07 = {
107
+ Id: 'VC07',
108
+ Title: 'A part of an unknown type is dropped and reported once',
109
+ Gate: AllOf(NeedsPlayout, NeedsGrant, NeedsHarnessMethod('SendPart')),
110
+ Run: (harness) => WithConformanceSession(harness, { Grant: { Avatar: true } }, async (s) => {
111
+ const mark = s.Timeline.Mark();
112
+ // Bytes with no format of their own, as PCM has none: the type the part names is all a driver has to go by.
113
+ await harness.SendPart?.(UNKNOWN_PART_TYPE, ConformancePcm(7));
114
+ await harness.SendPart?.(UNKNOWN_PART_TYPE, ConformancePcm(8));
115
+ AssertCount(s.Count('player-append', mark), 0, `${UNKNOWN_PART_TYPE} parts played as video`);
116
+ AssertCount(s.Count('voice-enqueue', mark), 0, `${UNKNOWN_PART_TYPE} parts played as the voice`);
117
+ AssertCount(s.Warnings(UNKNOWN_PART_TYPE, mark), 1, `warnings naming the dropped ${UNKNOWN_PART_TYPE} parts (once per type)`);
118
+ }),
119
+ };
120
+ /** VC01-VC07, in order. */
121
+ export const REALTIME_VIDEO_SESSION_CHECKS = [VC01, VC02, VC03, VC04, VC05, VC06, VC07];
122
+ /**
123
+ * The agent's video track is not live. A driver that negotiates tracks and was asked for it reports it `expected`
124
+ * (`'unsupported'`), so the host can say why; `null` accepts any state but live, or no track.
125
+ */
126
+ function assertAgentVideoTrackNotLive(s, expected) {
127
+ const state = s.OutboundVideoTrack()?.State ?? 'absent';
128
+ AssertTrue(state !== 'live', 'the outbound video track of a session that shows no avatar is not live', 'it is live');
129
+ if (expected !== null && s.Client.AllTracks.length > 0) {
130
+ AssertValue(state, expected, 'the requested outbound video track a driver negotiated and could not grant');
131
+ }
132
+ }
133
+ /** The model's voice plays: a PCM driver queues these bytes, and the client says the agent is audible. */
134
+ async function assertVoicePlays(s, pcm) {
135
+ const mark = s.Timeline.Mark();
136
+ await s.SendVoice(pcm);
137
+ if (s.Harness.Traits.Voice === 'pcm') {
138
+ AssertSameBytes(s.VoiceEnqueued(mark), [pcm], "the model's voice reaches the voice playback");
139
+ }
140
+ AssertValue(s.Client.IsAudioPlaying, true, "IsAudioPlaying while the model's voice plays");
141
+ }
142
+ /** The handed-over video is the driver's own: its player's source (playout), or a live stream. */
143
+ function assertHandedOverItsVideo(s) {
144
+ const video = s.RemoteVideos()[0];
145
+ if (s.Harness.Traits.AgentVideo === 'playout') {
146
+ AssertCount(s.Players.length, 1, 'video players created for one avatar');
147
+ AssertTrue(video === s.Players[0].Source, "the handed-over video is the player's video", `it is another '${video.Kind}' source`);
148
+ return;
149
+ }
150
+ AssertValue(video.Kind, 'stream', 'the kind of video a stream driver hands over');
151
+ AssertTrue(video.Kind === 'stream' && !!video.Stream, 'a stream driver hands over its stream', 'the stream is missing');
152
+ }
153
+ /** No video player outlives the connect: none was created, or each was released. */
154
+ function assertNoPlayerKept(s) {
155
+ const kept = s.Players.filter((player) => s.PlayerEvents('player-dispose', player.Index).length === 0);
156
+ AssertCount(kept.length, 0, 'video players kept by a session that shows no avatar');
157
+ }
158
+ /** When the harness can tell, the provider was asked for audio only: an avatar nobody sees is still generated and billed. */
159
+ function assertAskedForAudioOnly(s) {
160
+ if (s.Harness.AskedProviderForVideo) {
161
+ AssertValue(s.Harness.AskedProviderForVideo(), false, 'the provider was asked for video that nobody sees');
162
+ }
163
+ }
164
+ //# sourceMappingURL=realtimeVideoConformanceChecks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"realtimeVideoConformanceChecks.js","sourceRoot":"","sources":["../../src/testing/realtimeVideoConformanceChecks.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import type { RealtimeVideoConformanceCheck } from './realtimeVideoConformanceTypes.js';
2
+ /** VF01-VF02, in order. */
3
+ export declare const REALTIME_VIDEO_FRAME_CHECKS: readonly RealtimeVideoConformanceCheck[];
4
+ //# sourceMappingURL=realtimeVideoConformanceFrameChecks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"realtimeVideoConformanceFrameChecks.d.ts","sourceRoot":"","sources":["../../src/testing/realtimeVideoConformanceFrameChecks.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,6BAA6B,EAAE,MAAM,iCAAiC,CAAC;AAsCrF,2BAA2B;AAC3B,eAAO,MAAM,2BAA2B,EAAE,SAAS,6BAA6B,EAAiB,CAAC"}
@@ -0,0 +1,63 @@
1
+ import { AssertCount, AssertSameBytes, AssertTrue, AssertValue, Fail } from './conformanceAssertions.js';
2
+ import { ConformancePcm } from './conformanceFixtures.js';
3
+ import { AllOf, NeedsGrant, NeedsPlayout, NeedsTimedVoice } from './conformanceGates.js';
4
+ import { WithConformanceSession } from './realtimeVideoConformanceSession.js';
5
+ /** A granted avatar whose voice comes separately, as PCM: the case a clock is for. */
6
+ const SEPARATE_VOICE = { Grant: { Avatar: true, VideoCarriesVoice: false } };
7
+ const VF01 = {
8
+ Id: 'VF01',
9
+ Title: 'Frames reach the player typed, as the model sent them',
10
+ Gate: AllOf(NeedsPlayout, NeedsGrant),
11
+ Run: (harness) => WithConformanceSession(harness, { Grant: { Avatar: true } }, async (s) => {
12
+ const mark = s.Timeline.Mark();
13
+ const sent = [await s.StartVideo(), ...(await s.SendFrames(3))];
14
+ const appended = s.AppendedFrames(0, mark);
15
+ AssertSameBytes(appended.map((frame) => frame.Data), sent.map((frame) => frame.Data), "the frames' bytes, in the order the model sent them");
16
+ sent.forEach((frame, index) => assertSameFrame(appended[index], frame, index + 1));
17
+ }),
18
+ };
19
+ const VF02 = {
20
+ Id: 'VF02',
21
+ Title: "A timed voice drives the video's clock",
22
+ Gate: AllOf(NeedsPlayout, NeedsGrant, NeedsTimedVoice),
23
+ Run: (harness) => WithConformanceSession(harness, SEPARATE_VOICE, async (s) => {
24
+ const mark = s.Timeline.Mark();
25
+ const times = [await s.SendVoice(ConformancePcm(1))];
26
+ const frames = [await s.StartVideo()];
27
+ times.push(await s.SendVoice(ConformancePcm(2)));
28
+ frames.push(...(await s.SendFrames(2)));
29
+ AssertCount(s.Players.length, 1, 'video players created for one avatar');
30
+ AssertTrue(s.Players[0].Options.Clock === s.Voice, "the video player's clock is the voice's playback", 'the player was created with another clock, or none');
31
+ const queued = s.Timeline.Of('voice-enqueue', mark).map((event) => event.MediaTimeMs);
32
+ AssertValue(JSON.stringify(queued), JSON.stringify(times), "the media times the voice was queued at (the model's)");
33
+ assertFramesOnTheVoicesTimeline(s, mark, frames);
34
+ }),
35
+ };
36
+ /** VF01-VF02, in order. */
37
+ export const REALTIME_VIDEO_FRAME_CHECKS = [VF01, VF02];
38
+ /** A frame the player got is the frame the model sent: its kind and type, and the fields its kind has. */
39
+ function assertSameFrame(actual, expected, position) {
40
+ if (!actual) {
41
+ Fail(`frame ${position}`, 'the player never got it');
42
+ }
43
+ AssertValue(actual.Kind, expected.Kind, `frame ${position}'s kind`);
44
+ AssertValue(actual.MimeType, expected.MimeType, `frame ${position}'s MIME type`);
45
+ if (actual.Kind === 'fmp4' && expected.Kind === 'fmp4') {
46
+ AssertValue(actual.Piece, expected.Piece, `fMP4 frame ${position}'s piece (init or fragment, by its first box)`);
47
+ }
48
+ if (expected.Kind !== 'fmp4') {
49
+ AssertValue(String(actual.PresentationTimeMs), String(expected.PresentationTimeMs), `frame ${position}'s presentation time (ms)`);
50
+ AssertValue(actual.KeyFrame ?? null, expected.KeyFrame ?? null, `frame ${position}'s key-frame flag`);
51
+ }
52
+ }
53
+ /** The frames the player got keep the model's presentation times: the timeline the voice was queued on. */
54
+ function assertFramesOnTheVoicesTimeline(s, mark, sent) {
55
+ const appended = s.AppendedFrames(0, mark);
56
+ AssertCount(appended.length, sent.length, 'frames given to the player');
57
+ sent.forEach((frame, index) => {
58
+ if (frame.Kind !== 'fmp4') {
59
+ AssertValue(String(appended[index].PresentationTimeMs), String(frame.PresentationTimeMs), `frame ${index + 1}'s presentation time on the voice's timeline (ms)`);
60
+ }
61
+ });
62
+ }
63
+ //# sourceMappingURL=realtimeVideoConformanceFrameChecks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"realtimeVideoConformanceFrameChecks.js","sourceRoot":"","sources":["../../src/testing/realtimeVideoConformanceFrameChecks.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,108 @@
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 { type ClientRealtimeSessionConfig, type RealtimeTrack, type RealtimeVideoFrame } from '@memberjunction/ai';
9
+ import { BaseRealtimeClient, type RealtimeClientState } from '../generic/baseRealtimeClient.js';
10
+ import type { MediaVideoSource } from '../media/model.js';
11
+ import { RealtimeVideoConformanceTimeline, type RealtimeVideoConformanceEventKind, type RealtimeVideoConformanceEventOf } from './conformanceTimeline.js';
12
+ import { ConformanceVideoStream } from './conformanceVideoStream.js';
13
+ import { RecordingPcmPlayback, RecordingVideoPlayout } from './recordingPlayout.js';
14
+ import type { IRealtimeVideoConformanceHarness, RealtimeVideoConformanceGrant } from './realtimeVideoConformanceTypes.js';
15
+ /** What a session starts with. */
16
+ export interface ConformanceSessionSetup {
17
+ /** What the provider's server grants. */
18
+ Grant: RealtimeVideoConformanceGrant;
19
+ /** Whether the host shows the agent's video: its Avatar channel asks for the outbound video track. Default `true`. */
20
+ ShowAgentVideo?: boolean;
21
+ }
22
+ /** One connected session of a check, recorded on one timeline. */
23
+ export declare class ConformanceSession {
24
+ readonly Harness: IRealtimeVideoConformanceHarness;
25
+ readonly Setup: ConformanceSessionSetup;
26
+ readonly Timeline: RealtimeVideoConformanceTimeline;
27
+ readonly Microphone: MediaStream;
28
+ readonly Voice: RecordingPcmPlayback;
29
+ /** The video the model sends, in the provider's form. */
30
+ readonly Video: ConformanceVideoStream;
31
+ private readonly players;
32
+ private client;
33
+ private disconnected;
34
+ constructor(Harness: IRealtimeVideoConformanceHarness, Setup: ConformanceSessionSetup);
35
+ /** The video players the driver created, in order. */
36
+ get Players(): readonly RecordingVideoPlayout[];
37
+ /** The connected client. */
38
+ get Client(): BaseRealtimeClient;
39
+ /** Whether the avatar's video carries the voice: as the grant says, else whenever the provider's video can. */
40
+ get VideoCarriesVoice(): boolean;
41
+ /** Mints, adds the host's track request, creates the client with the recorders, observes it, and connects. */
42
+ Start(): Promise<void>;
43
+ /** Disconnects the client, as the host does when the call ends. */
44
+ Disconnect(): Promise<void>;
45
+ /** Ends the session after the check: disconnects the client if the check didn't. Never throws. */
46
+ Close(): Promise<void>;
47
+ /**
48
+ * The model starts its video: an fMP4 init segment (with the voice's audio track when the video carries it), or the
49
+ * first chunk (a key frame) or image. Returns the frame sent.
50
+ */
51
+ StartVideo(): Promise<RealtimeVideoFrame>;
52
+ /** The model sends the stream's next `count` video frames. Returns them. */
53
+ SendFrames(count: number): Promise<RealtimeVideoFrame[]>;
54
+ /**
55
+ * The model sends a chunk of its voice: timed at the video's next frame when the provider keeps one timeline
56
+ * (`TimedVoice`), else untimed. Returns the media time sent, or `undefined`.
57
+ */
58
+ SendVoice(pcm16: ArrayBuffer): Promise<number | undefined>;
59
+ /** The model ends its turn: generation complete when the harness has it, then turn complete. */
60
+ EndTurn(): Promise<void>;
61
+ /** Models the agent's media playing out: every video player and the voice reach the end of what they were given. */
62
+ FinishPlaying(): void;
63
+ /** The states the client reported, from a mark on. */
64
+ States(since?: number): RealtimeClientState[];
65
+ /** How many events of a kind happened from a mark on. */
66
+ Count(kind: RealtimeVideoConformanceEventKind, since?: number): number;
67
+ /** The events of a kind that one player recorded, from a mark on. */
68
+ PlayerEvents<K extends 'player-append' | 'player-end-of-turn' | 'player-flush' | 'player-dispose'>(kind: K, player: number, since?: number): RealtimeVideoConformanceEventOf<K>[];
69
+ /** The frames one player was given, from a mark on. */
70
+ AppendedFrames(player: number, since?: number): RealtimeVideoFrame[];
71
+ /** The agent's video the client handed over, from a mark on. */
72
+ RemoteVideos(since?: number): MediaVideoSource[];
73
+ /** The PCM the driver queued, from a mark on. */
74
+ VoiceEnqueued(since?: number): ArrayBuffer[];
75
+ /** The last state the client reported, or `null`. */
76
+ LastState(): RealtimeClientState | null;
77
+ /** The agent's video track (outbound video), as the client reports it now; `undefined` when it has none. */
78
+ OutboundVideoTrack(): RealtimeTrack | undefined;
79
+ /** The seconds of generated video the client reported (`OutputTokenDetails.VideoSeconds`), from a mark on. */
80
+ VideoSeconds(since?: number): number;
81
+ /** How many usage updates carried video seconds, from a mark on. */
82
+ VideoSecondsReports(since?: number): number;
83
+ /** How many warnings mention `text`, from a mark on. */
84
+ Warnings(text: string, since?: number): number;
85
+ /** The warning and info lines logged so far, for a failure's message. */
86
+ LoggedLines(): string[];
87
+ /** What the harness wires into the driver: the microphone, the voice recorder, and a recorder per player. */
88
+ private media;
89
+ /** Registers the kit's handlers: every emission goes on the timeline. */
90
+ private observe;
91
+ }
92
+ /**
93
+ * Runs `body` on a started session with the console captured onto its timeline, then disconnects the client (if `body`
94
+ * didn't) and restores the console, failed or not. A failure's message gains the lines logged during the session.
95
+ */
96
+ export declare function WithConformanceSession(harness: IRealtimeVideoConformanceHarness, setup: ConformanceSessionSetup, body: (session: ConformanceSession) => Promise<void>): Promise<void>;
97
+ /**
98
+ * The config with the host's track request added, as the realtime runtime builds it when a channel shows the agent's
99
+ * video: the audio floor, what the mint requested, and the agent's outbound video, one each. Unchanged when the host
100
+ * shows no agent video.
101
+ */
102
+ export declare function WithHostTrackRequest(config: ClientRealtimeSessionConfig, showAgentVideo: boolean): ClientRealtimeSessionConfig;
103
+ /**
104
+ * Sends `console.warn` and `console.info` to the timeline instead of the output, for "reported once" and quiet test
105
+ * output. Returns the function that restores them.
106
+ */
107
+ export declare function CaptureConsole(timeline: RealtimeVideoConformanceTimeline): () => void;
108
+ //# sourceMappingURL=realtimeVideoConformanceSession.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"realtimeVideoConformanceSession.d.ts","sourceRoot":"","sources":["../../src/testing/realtimeVideoConformanceSession.ts"],"names":[],"mappings":"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"}