@livekit/agents 1.2.0 → 1.2.2

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 (205) hide show
  1. package/dist/_exceptions.cjs.map +1 -1
  2. package/dist/_exceptions.d.ts.map +1 -1
  3. package/dist/_exceptions.js.map +1 -1
  4. package/dist/audio.cjs +10 -0
  5. package/dist/audio.cjs.map +1 -1
  6. package/dist/audio.d.cts +1 -1
  7. package/dist/audio.d.ts +1 -1
  8. package/dist/audio.d.ts.map +1 -1
  9. package/dist/audio.js +10 -0
  10. package/dist/audio.js.map +1 -1
  11. package/dist/beta/workflows/task_group.cjs +7 -4
  12. package/dist/beta/workflows/task_group.cjs.map +1 -1
  13. package/dist/beta/workflows/task_group.d.ts.map +1 -1
  14. package/dist/beta/workflows/task_group.js +7 -4
  15. package/dist/beta/workflows/task_group.js.map +1 -1
  16. package/dist/inference/api_protos.d.cts +26 -26
  17. package/dist/inference/api_protos.d.ts +26 -26
  18. package/dist/inference/interruption/http_transport.cjs.map +1 -1
  19. package/dist/inference/interruption/http_transport.d.cts +3 -1
  20. package/dist/inference/interruption/http_transport.d.ts +3 -1
  21. package/dist/inference/interruption/http_transport.d.ts.map +1 -1
  22. package/dist/inference/interruption/http_transport.js.map +1 -1
  23. package/dist/inference/interruption/ws_transport.cjs +37 -32
  24. package/dist/inference/interruption/ws_transport.cjs.map +1 -1
  25. package/dist/inference/interruption/ws_transport.d.ts.map +1 -1
  26. package/dist/inference/interruption/ws_transport.js +37 -32
  27. package/dist/inference/interruption/ws_transport.js.map +1 -1
  28. package/dist/inference/tts.cjs +14 -1
  29. package/dist/inference/tts.cjs.map +1 -1
  30. package/dist/inference/tts.d.cts +42 -4
  31. package/dist/inference/tts.d.ts +42 -4
  32. package/dist/inference/tts.d.ts.map +1 -1
  33. package/dist/inference/tts.js +24 -3
  34. package/dist/inference/tts.js.map +1 -1
  35. package/dist/inference/tts.test.cjs +72 -0
  36. package/dist/inference/tts.test.cjs.map +1 -1
  37. package/dist/inference/tts.test.js +72 -0
  38. package/dist/inference/tts.test.js.map +1 -1
  39. package/dist/ipc/job_proc_lazy_main.cjs +7 -2
  40. package/dist/ipc/job_proc_lazy_main.cjs.map +1 -1
  41. package/dist/ipc/job_proc_lazy_main.js +7 -2
  42. package/dist/ipc/job_proc_lazy_main.js.map +1 -1
  43. package/dist/ipc/supervised_proc.cjs +4 -1
  44. package/dist/ipc/supervised_proc.cjs.map +1 -1
  45. package/dist/ipc/supervised_proc.d.ts.map +1 -1
  46. package/dist/ipc/supervised_proc.js +4 -1
  47. package/dist/ipc/supervised_proc.js.map +1 -1
  48. package/dist/ipc/supervised_proc.test.cjs +82 -0
  49. package/dist/ipc/supervised_proc.test.cjs.map +1 -1
  50. package/dist/ipc/supervised_proc.test.js +82 -0
  51. package/dist/ipc/supervised_proc.test.js.map +1 -1
  52. package/dist/job.cjs +2 -1
  53. package/dist/job.cjs.map +1 -1
  54. package/dist/job.d.ts.map +1 -1
  55. package/dist/job.js +2 -1
  56. package/dist/job.js.map +1 -1
  57. package/dist/llm/chat_context.cjs +102 -31
  58. package/dist/llm/chat_context.cjs.map +1 -1
  59. package/dist/llm/chat_context.d.ts.map +1 -1
  60. package/dist/llm/chat_context.js +102 -31
  61. package/dist/llm/chat_context.js.map +1 -1
  62. package/dist/llm/chat_context.test.cjs +123 -5
  63. package/dist/llm/chat_context.test.cjs.map +1 -1
  64. package/dist/llm/chat_context.test.js +123 -5
  65. package/dist/llm/chat_context.test.js.map +1 -1
  66. package/dist/llm/fallback_adapter.cjs +2 -0
  67. package/dist/llm/fallback_adapter.cjs.map +1 -1
  68. package/dist/llm/fallback_adapter.d.ts.map +1 -1
  69. package/dist/llm/fallback_adapter.js +2 -0
  70. package/dist/llm/fallback_adapter.js.map +1 -1
  71. package/dist/llm/index.cjs +2 -0
  72. package/dist/llm/index.cjs.map +1 -1
  73. package/dist/llm/index.d.cts +1 -1
  74. package/dist/llm/index.d.ts +1 -1
  75. package/dist/llm/index.d.ts.map +1 -1
  76. package/dist/llm/index.js +2 -0
  77. package/dist/llm/index.js.map +1 -1
  78. package/dist/llm/utils.cjs +89 -0
  79. package/dist/llm/utils.cjs.map +1 -1
  80. package/dist/llm/utils.d.cts +8 -0
  81. package/dist/llm/utils.d.ts +8 -0
  82. package/dist/llm/utils.d.ts.map +1 -1
  83. package/dist/llm/utils.js +88 -0
  84. package/dist/llm/utils.js.map +1 -1
  85. package/dist/llm/utils.test.cjs +90 -0
  86. package/dist/llm/utils.test.cjs.map +1 -1
  87. package/dist/llm/utils.test.js +98 -2
  88. package/dist/llm/utils.test.js.map +1 -1
  89. package/dist/stt/stt.cjs +8 -0
  90. package/dist/stt/stt.cjs.map +1 -1
  91. package/dist/stt/stt.d.cts +8 -0
  92. package/dist/stt/stt.d.ts +8 -0
  93. package/dist/stt/stt.d.ts.map +1 -1
  94. package/dist/stt/stt.js +8 -0
  95. package/dist/stt/stt.js.map +1 -1
  96. package/dist/tts/fallback_adapter.cjs +6 -0
  97. package/dist/tts/fallback_adapter.cjs.map +1 -1
  98. package/dist/tts/fallback_adapter.d.ts.map +1 -1
  99. package/dist/tts/fallback_adapter.js +6 -0
  100. package/dist/tts/fallback_adapter.js.map +1 -1
  101. package/dist/typed_promise.cjs +48 -0
  102. package/dist/typed_promise.cjs.map +1 -0
  103. package/dist/typed_promise.d.cts +24 -0
  104. package/dist/typed_promise.d.ts +24 -0
  105. package/dist/typed_promise.d.ts.map +1 -0
  106. package/dist/typed_promise.js +28 -0
  107. package/dist/typed_promise.js.map +1 -0
  108. package/dist/utils.cjs +30 -2
  109. package/dist/utils.cjs.map +1 -1
  110. package/dist/utils.d.cts +18 -0
  111. package/dist/utils.d.ts +18 -0
  112. package/dist/utils.d.ts.map +1 -1
  113. package/dist/utils.js +27 -2
  114. package/dist/utils.js.map +1 -1
  115. package/dist/version.cjs +1 -1
  116. package/dist/version.js +1 -1
  117. package/dist/voice/agent_activity.cjs +10 -0
  118. package/dist/voice/agent_activity.cjs.map +1 -1
  119. package/dist/voice/agent_activity.d.ts.map +1 -1
  120. package/dist/voice/agent_activity.js +11 -0
  121. package/dist/voice/agent_activity.js.map +1 -1
  122. package/dist/voice/agent_session.cjs +1 -1
  123. package/dist/voice/agent_session.cjs.map +1 -1
  124. package/dist/voice/agent_session.d.cts +4 -2
  125. package/dist/voice/agent_session.d.ts +4 -2
  126. package/dist/voice/agent_session.d.ts.map +1 -1
  127. package/dist/voice/agent_session.js +1 -1
  128. package/dist/voice/agent_session.js.map +1 -1
  129. package/dist/voice/events.cjs +11 -0
  130. package/dist/voice/events.cjs.map +1 -1
  131. package/dist/voice/events.d.cts +12 -1
  132. package/dist/voice/events.d.ts +12 -1
  133. package/dist/voice/events.d.ts.map +1 -1
  134. package/dist/voice/events.js +10 -0
  135. package/dist/voice/events.js.map +1 -1
  136. package/dist/voice/generation.cjs +23 -4
  137. package/dist/voice/generation.cjs.map +1 -1
  138. package/dist/voice/generation.d.ts.map +1 -1
  139. package/dist/voice/generation.js +32 -5
  140. package/dist/voice/generation.js.map +1 -1
  141. package/dist/voice/generation_tts_timeout.test.cjs +85 -0
  142. package/dist/voice/generation_tts_timeout.test.cjs.map +1 -0
  143. package/dist/voice/generation_tts_timeout.test.js +84 -0
  144. package/dist/voice/generation_tts_timeout.test.js.map +1 -0
  145. package/dist/voice/index.cjs.map +1 -1
  146. package/dist/voice/index.d.cts +1 -1
  147. package/dist/voice/index.d.ts +1 -1
  148. package/dist/voice/index.d.ts.map +1 -1
  149. package/dist/voice/index.js +3 -1
  150. package/dist/voice/index.js.map +1 -1
  151. package/dist/voice/recorder_io/recorder_io.cjs +1 -2
  152. package/dist/voice/recorder_io/recorder_io.cjs.map +1 -1
  153. package/dist/voice/recorder_io/recorder_io.d.ts.map +1 -1
  154. package/dist/voice/recorder_io/recorder_io.js +2 -3
  155. package/dist/voice/recorder_io/recorder_io.js.map +1 -1
  156. package/dist/voice/report.cjs +1 -1
  157. package/dist/voice/report.cjs.map +1 -1
  158. package/dist/voice/report.js +1 -1
  159. package/dist/voice/report.js.map +1 -1
  160. package/dist/voice/report.test.cjs +70 -0
  161. package/dist/voice/report.test.cjs.map +1 -1
  162. package/dist/voice/report.test.js +70 -0
  163. package/dist/voice/report.test.js.map +1 -1
  164. package/dist/voice/room_io/room_io.cjs +5 -1
  165. package/dist/voice/room_io/room_io.cjs.map +1 -1
  166. package/dist/voice/room_io/room_io.d.ts.map +1 -1
  167. package/dist/voice/room_io/room_io.js +5 -1
  168. package/dist/voice/room_io/room_io.js.map +1 -1
  169. package/dist/voice/room_io/room_io.test.cjs +18 -0
  170. package/dist/voice/room_io/room_io.test.cjs.map +1 -0
  171. package/dist/voice/room_io/room_io.test.js +17 -0
  172. package/dist/voice/room_io/room_io.test.js.map +1 -0
  173. package/package.json +4 -2
  174. package/src/_exceptions.ts +5 -0
  175. package/src/audio.ts +12 -1
  176. package/src/beta/workflows/task_group.ts +14 -5
  177. package/src/inference/interruption/http_transport.ts +2 -1
  178. package/src/inference/interruption/ws_transport.ts +44 -34
  179. package/src/inference/tts.test.ts +87 -0
  180. package/src/inference/tts.ts +71 -9
  181. package/src/ipc/job_proc_lazy_main.ts +7 -2
  182. package/src/ipc/supervised_proc.test.ts +96 -0
  183. package/src/ipc/supervised_proc.ts +8 -1
  184. package/src/job.ts +1 -0
  185. package/src/llm/chat_context.test.ts +137 -5
  186. package/src/llm/chat_context.ts +119 -38
  187. package/src/llm/fallback_adapter.ts +5 -2
  188. package/src/llm/index.ts +2 -0
  189. package/src/llm/utils.test.ts +103 -2
  190. package/src/llm/utils.ts +128 -0
  191. package/src/stt/stt.ts +9 -1
  192. package/src/tts/fallback_adapter.ts +9 -2
  193. package/src/typed_promise.ts +67 -0
  194. package/src/utils.ts +45 -2
  195. package/src/voice/agent_activity.ts +11 -0
  196. package/src/voice/agent_session.ts +13 -7
  197. package/src/voice/events.ts +21 -0
  198. package/src/voice/generation.ts +35 -8
  199. package/src/voice/generation_tts_timeout.test.ts +112 -0
  200. package/src/voice/index.ts +6 -1
  201. package/src/voice/recorder_io/recorder_io.ts +2 -7
  202. package/src/voice/report.test.ts +78 -0
  203. package/src/voice/report.ts +1 -1
  204. package/src/voice/room_io/room_io.test.ts +38 -0
  205. package/src/voice/room_io/room_io.ts +7 -2
@@ -11,27 +11,63 @@ export type ElevenlabsModels = 'elevenlabs/eleven_flash_v2' | 'elevenlabs/eleven
11
11
  export type InworldModels = 'inworld/inworld-tts-1.5-max' | 'inworld/inworld-tts-1.5-mini' | 'inworld/inworld-tts-1-max' | 'inworld/inworld-tts-1';
12
12
  export type RimeModels = 'rime/arcana' | 'rime/mistv2';
13
13
  export interface CartesiaOptions {
14
+ emotion?: string;
14
15
  /** Maximum duration of audio in seconds. */
15
16
  duration?: number;
16
17
  /** Speech speed. Default: not specified. */
17
- speed?: 'slow' | 'normal' | 'fast';
18
+ speed?: 'slow' | 'normal' | 'fast' | number;
19
+ volume?: number;
20
+ max_buffer_delay_ms?: number;
21
+ add_timestamps?: boolean;
22
+ add_phoneme_timestamps?: boolean;
23
+ use_normalized_timestamps?: boolean;
18
24
  }
19
25
  export interface ElevenlabsOptions {
20
26
  /** Inactivity timeout in seconds. Default: 60. */
21
27
  inactivity_timeout?: number;
22
28
  /** Text normalization mode. Default: "auto". */
23
29
  apply_text_normalization?: 'auto' | 'off' | 'on';
30
+ auto_mode?: boolean;
31
+ enable_logging?: boolean;
32
+ enable_ssml_parsing?: boolean;
33
+ sync_alignment?: boolean;
34
+ language_code?: string;
35
+ /** Voice stability tuning, typically in the range [0, 1]. */
36
+ stability?: number;
37
+ /** Voice similarity tuning, typically in the range [0, 1]. */
38
+ similarity_boost?: number;
39
+ /** Style exaggeration tuning, typically in the range [0, 1]. */
40
+ style?: number;
41
+ /** Speech speed multiplier. */
42
+ speed?: number;
43
+ use_speaker_boost?: boolean;
44
+ chunk_length_schedule?: number[];
45
+ preferred_alignment?: string;
24
46
  }
25
47
  export interface DeepgramTTSOptions {
48
+ /** Default: false. */
49
+ mip_opt_out?: boolean;
26
50
  }
27
51
  export interface RimeOptions {
52
+ /** Default 1.0, <1 = faster, >1 = slower. */
53
+ speed_alpha?: number;
54
+ /** Default false. */
55
+ pause_between_brackets?: boolean;
56
+ /** Default false. */
57
+ phonemize_between_brackets?: boolean;
58
+ /** Comma-separated speed factors for [bracketed] words. */
59
+ inline_speed_alpha?: string;
60
+ /** Default false. */
61
+ no_text_normalization?: boolean;
28
62
  }
29
63
  export interface InworldOptions {
30
- /** Controls how fast the voice speaks. 1.0 is normal speed, 0.5 is half, 1.5 is 1.5x. Default: 1.0. */
64
+ /** Range >0.5, <=1.5. */
31
65
  speaking_rate?: number;
32
- /** Controls randomness in the output. Recommended between 0.6 and 1.1. Default: 1.1. */
66
+ /** Range 0-2. */
33
67
  temperature?: number;
34
- /** Controls text normalization. "ON" expands numbers, dates, abbreviations. "OFF" reads text as written. Default: "ON". */
68
+ timestamp_type?: 'TIMESTAMP_TYPE_UNSPECIFIED' | 'WORD' | 'CHARACTER';
69
+ apply_text_normalization?: 'APPLY_TEXT_NORMALIZATION_UNSPECIFIED' | 'ON' | 'OFF';
70
+ /** @deprecated Backward-compatible alias. Use `apply_text_normalization`. */
35
71
  text_normalization?: 'ON' | 'OFF';
36
72
  }
37
73
  type _TTSModels = CartesiaModels | DeepgramTTSModels | ElevenlabsModels | RimeModels | InworldModels;
@@ -62,6 +98,7 @@ export interface InferenceTTSOptions<TModel extends TTSModels> {
62
98
  baseURL: string;
63
99
  apiKey: string;
64
100
  apiSecret: string;
101
+ /** Flat provider-specific inference options forwarded as the `extra` payload field. */
65
102
  modelOptions: TTSOptions<TModel>;
66
103
  fallback?: TTSFallbackModel[];
67
104
  connOptions?: APIConnectOptions;
@@ -83,6 +120,7 @@ export declare class TTS<TModel extends TTSModels> extends BaseTTS {
83
120
  sampleRate?: number;
84
121
  apiKey?: string;
85
122
  apiSecret?: string;
123
+ /** Flat provider-specific inference options forwarded as the `extra` payload field. */
86
124
  modelOptions?: TTSOptions<TModel>;
87
125
  fallback?: TTSFallbackModelType | TTSFallbackModelType[];
88
126
  connOptions?: APIConnectOptions;
@@ -11,27 +11,63 @@ export type ElevenlabsModels = 'elevenlabs/eleven_flash_v2' | 'elevenlabs/eleven
11
11
  export type InworldModels = 'inworld/inworld-tts-1.5-max' | 'inworld/inworld-tts-1.5-mini' | 'inworld/inworld-tts-1-max' | 'inworld/inworld-tts-1';
12
12
  export type RimeModels = 'rime/arcana' | 'rime/mistv2';
13
13
  export interface CartesiaOptions {
14
+ emotion?: string;
14
15
  /** Maximum duration of audio in seconds. */
15
16
  duration?: number;
16
17
  /** Speech speed. Default: not specified. */
17
- speed?: 'slow' | 'normal' | 'fast';
18
+ speed?: 'slow' | 'normal' | 'fast' | number;
19
+ volume?: number;
20
+ max_buffer_delay_ms?: number;
21
+ add_timestamps?: boolean;
22
+ add_phoneme_timestamps?: boolean;
23
+ use_normalized_timestamps?: boolean;
18
24
  }
19
25
  export interface ElevenlabsOptions {
20
26
  /** Inactivity timeout in seconds. Default: 60. */
21
27
  inactivity_timeout?: number;
22
28
  /** Text normalization mode. Default: "auto". */
23
29
  apply_text_normalization?: 'auto' | 'off' | 'on';
30
+ auto_mode?: boolean;
31
+ enable_logging?: boolean;
32
+ enable_ssml_parsing?: boolean;
33
+ sync_alignment?: boolean;
34
+ language_code?: string;
35
+ /** Voice stability tuning, typically in the range [0, 1]. */
36
+ stability?: number;
37
+ /** Voice similarity tuning, typically in the range [0, 1]. */
38
+ similarity_boost?: number;
39
+ /** Style exaggeration tuning, typically in the range [0, 1]. */
40
+ style?: number;
41
+ /** Speech speed multiplier. */
42
+ speed?: number;
43
+ use_speaker_boost?: boolean;
44
+ chunk_length_schedule?: number[];
45
+ preferred_alignment?: string;
24
46
  }
25
47
  export interface DeepgramTTSOptions {
48
+ /** Default: false. */
49
+ mip_opt_out?: boolean;
26
50
  }
27
51
  export interface RimeOptions {
52
+ /** Default 1.0, <1 = faster, >1 = slower. */
53
+ speed_alpha?: number;
54
+ /** Default false. */
55
+ pause_between_brackets?: boolean;
56
+ /** Default false. */
57
+ phonemize_between_brackets?: boolean;
58
+ /** Comma-separated speed factors for [bracketed] words. */
59
+ inline_speed_alpha?: string;
60
+ /** Default false. */
61
+ no_text_normalization?: boolean;
28
62
  }
29
63
  export interface InworldOptions {
30
- /** Controls how fast the voice speaks. 1.0 is normal speed, 0.5 is half, 1.5 is 1.5x. Default: 1.0. */
64
+ /** Range >0.5, <=1.5. */
31
65
  speaking_rate?: number;
32
- /** Controls randomness in the output. Recommended between 0.6 and 1.1. Default: 1.1. */
66
+ /** Range 0-2. */
33
67
  temperature?: number;
34
- /** Controls text normalization. "ON" expands numbers, dates, abbreviations. "OFF" reads text as written. Default: "ON". */
68
+ timestamp_type?: 'TIMESTAMP_TYPE_UNSPECIFIED' | 'WORD' | 'CHARACTER';
69
+ apply_text_normalization?: 'APPLY_TEXT_NORMALIZATION_UNSPECIFIED' | 'ON' | 'OFF';
70
+ /** @deprecated Backward-compatible alias. Use `apply_text_normalization`. */
35
71
  text_normalization?: 'ON' | 'OFF';
36
72
  }
37
73
  type _TTSModels = CartesiaModels | DeepgramTTSModels | ElevenlabsModels | RimeModels | InworldModels;
@@ -62,6 +98,7 @@ export interface InferenceTTSOptions<TModel extends TTSModels> {
62
98
  baseURL: string;
63
99
  apiKey: string;
64
100
  apiSecret: string;
101
+ /** Flat provider-specific inference options forwarded as the `extra` payload field. */
65
102
  modelOptions: TTSOptions<TModel>;
66
103
  fallback?: TTSFallbackModel[];
67
104
  connOptions?: APIConnectOptions;
@@ -83,6 +120,7 @@ export declare class TTS<TModel extends TTSModels> extends BaseTTS {
83
120
  sampleRate?: number;
84
121
  apiKey?: string;
85
122
  apiSecret?: string;
123
+ /** Flat provider-specific inference options forwarded as the `extra` payload field. */
86
124
  modelOptions?: TTSOptions<TModel>;
87
125
  fallback?: TTSFallbackModelType | TTSFallbackModelType[];
88
126
  connOptions?: APIConnectOptions;
@@ -1 +1 @@
1
- {"version":3,"file":"tts.d.ts","sourceRoot":"","sources":["../../src/inference/tts.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,SAAS,EAAE,MAAM,IAAI,CAAC;AAG/B,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,KAAK,YAAY,EAAqB,MAAM,gBAAgB,CAAC;AAItE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AACrD,OAAO,EAAE,gBAAgB,IAAI,oBAAoB,EAAE,GAAG,IAAI,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAC3F,OAAO,EAAE,KAAK,iBAAiB,EAA+B,MAAM,aAAa,CAAC;AAQlF,OAAO,EAAE,KAAK,SAAS,EAAwD,MAAM,YAAY,CAAC;AAElG,MAAM,MAAM,cAAc,GACtB,kBAAkB,GAClB,kBAAkB,GAClB,sBAAsB,GACtB,gBAAgB,CAAC;AAErB,MAAM,MAAM,iBAAiB,GAAG,eAAe,GAAG,iBAAiB,CAAC;AAEpE,MAAM,MAAM,gBAAgB,GACxB,4BAA4B,GAC5B,8BAA8B,GAC9B,4BAA4B,GAC5B,8BAA8B,GAC9B,mCAAmC,CAAC;AAExC,MAAM,MAAM,aAAa,GACrB,6BAA6B,GAC7B,8BAA8B,GAC9B,2BAA2B,GAC3B,uBAAuB,CAAC;AAE5B,MAAM,MAAM,UAAU,GAAG,aAAa,GAAG,aAAa,CAAC;AAEvD,MAAM,WAAW,eAAe;IAC9B,4CAA4C;IAC5C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,4CAA4C;IAC5C,KAAK,CAAC,EAAE,MAAM,GAAG,QAAQ,GAAG,MAAM,CAAC;CACpC;AAED,MAAM,WAAW,iBAAiB;IAChC,kDAAkD;IAClD,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,gDAAgD;IAChD,wBAAwB,CAAC,EAAE,MAAM,GAAG,KAAK,GAAG,IAAI,CAAC;CAClD;AAED,MAAM,WAAW,kBAAkB;CAAG;AAEtC,MAAM,WAAW,WAAW;CAAG;AAE/B,MAAM,WAAW,cAAc;IAC7B,uGAAuG;IACvG,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,wFAAwF;IACxF,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,2HAA2H;IAC3H,kBAAkB,CAAC,EAAE,IAAI,GAAG,KAAK,CAAC;CACnC;AAED,KAAK,UAAU,GACX,cAAc,GACd,iBAAiB,GACjB,gBAAgB,GAChB,UAAU,GACV,aAAa,CAAC;AAElB,MAAM,MAAM,SAAS,GACjB,cAAc,GACd,iBAAiB,GACjB,gBAAgB,GAChB,UAAU,GACV,aAAa,GACb,SAAS,CAAC;AAEd,MAAM,MAAM,cAAc,GAAG,GAAG,UAAU,IAAI,MAAM,EAAE,GAAG,SAAS,CAAC;AAEnE,MAAM,MAAM,UAAU,CAAC,MAAM,SAAS,SAAS,IAAI,MAAM,SAAS,cAAc,GAC5E,eAAe,GACf,MAAM,SAAS,iBAAiB,GAC9B,kBAAkB,GAClB,MAAM,SAAS,gBAAgB,GAC7B,iBAAiB,GACjB,MAAM,SAAS,UAAU,GACvB,WAAW,GACX,MAAM,SAAS,aAAa,GAC1B,cAAc,GACd,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEpC,qFAAqF;AACrF,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAM/E;AAED,2GAA2G;AAC3G,MAAM,WAAW,gBAAgB;IAC/B,uFAAuF;IACvF,KAAK,EAAE,MAAM,CAAC;IACd,kCAAkC;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,yCAAyC;IACzC,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACvC;AAED,MAAM,MAAM,oBAAoB,GAAG,gBAAgB,GAAG,MAAM,CAAC;AAE7D,+EAA+E;AAC/E,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,oBAAoB,GAAG,oBAAoB,EAAE,GACtD,gBAAgB,EAAE,CAapB;AAED,KAAK,WAAW,GAAG,WAAW,CAAC;AAO/B,MAAM,WAAW,mBAAmB,CAAC,MAAM,SAAS,SAAS;IAC3D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,YAAY,CAAC;IACxB,QAAQ,EAAE,WAAW,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,UAAU,CAAC,MAAM,CAAC,CAAC;IACjC,QAAQ,CAAC,EAAE,gBAAgB,EAAE,CAAC;IAC9B,WAAW,CAAC,EAAE,iBAAiB,CAAC;CACjC;AAED;;GAEG;AACH,qBAAa,GAAG,CAAC,MAAM,SAAS,SAAS,CAAE,SAAQ,OAAO;;IACxD,OAAO,CAAC,IAAI,CAA8B;IAC1C,OAAO,CAAC,OAAO,CAA4C;IAC3D,IAAI,EAAE,cAAc,CAAC,SAAS,CAAC,CAAC;gBAIpB,IAAI,EAAE;QAChB,KAAK,EAAE,MAAM,CAAC;QACd,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,QAAQ,CAAC,EAAE,WAAW,CAAC;QACvB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,YAAY,CAAC,EAAE,UAAU,CAAC,MAAM,CAAC,CAAC;QAClC,QAAQ,CAAC,EAAE,oBAAoB,GAAG,oBAAoB,EAAE,CAAC;QACzD,WAAW,CAAC,EAAE,iBAAiB,CAAC;KACjC;IA0ED,IAAI,KAAK,WAER;IAED,IAAI,KAAK,IAAI,MAAM,CAElB;IAED,IAAI,QAAQ,IAAI,MAAM,CAErB;IAED,MAAM,CAAC,eAAe,CAAC,WAAW,EAAE,MAAM,GAAG,GAAG,CAAC,SAAS,CAAC;IAK3D,aAAa,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,mBAAmB,CAAC,MAAM,CAAC,EAAE,OAAO,GAAG,OAAO,GAAG,UAAU,CAAC,CAAC;IAW9F,UAAU,CAAC,CAAC,EAAE,MAAM,GAAG,aAAa;IAIpC,MAAM,CAAC,OAAO,CAAC,EAAE;QAAE,WAAW,CAAC,EAAE,iBAAiB,CAAA;KAAE,GAAG,gBAAgB,CAAC,MAAM,CAAC;IAOzE,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC;IA4C9C,OAAO,CAAC,EAAE,EAAE,SAAS;IAI3B,OAAO,IAAI,IAAI;IAIT,KAAK;CAOZ;AAED,qBAAa,gBAAgB,CAAC,MAAM,SAAS,SAAS,CAAE,SAAQ,oBAAoB;;IAClF,OAAO,CAAC,IAAI,CAA8B;IAC1C,OAAO,CAAC,GAAG,CAAc;gBAIb,GAAG,EAAE,GAAG,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,mBAAmB,CAAC,MAAM,CAAC,EAAE,WAAW,EAAE,iBAAiB;IAM/F,IAAI,KAAK,WAER;IAED,aAAa,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,mBAAmB,CAAC,MAAM,CAAC,EAAE,OAAO,GAAG,OAAO,GAAG,UAAU,CAAC,CAAC;cAQ9E,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;CA2SrC"}
1
+ {"version":3,"file":"tts.d.ts","sourceRoot":"","sources":["../../src/inference/tts.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  import { WebSocket } from "ws";
2
- import { APIError, APIStatusError } from "../_exceptions.js";
2
+ import { APIError, APIStatusError, APITimeoutError } from "../_exceptions.js";
3
3
  import { AudioByteStream } from "../audio.js";
4
4
  import { ConnectionPool } from "../connection_pool.js";
5
5
  import { normalizeLanguage } from "../language.js";
@@ -8,7 +8,15 @@ import { createStreamChannel } from "../stream/stream_channel.js";
8
8
  import { basic as tokenizeBasic } from "../tokenize/index.js";
9
9
  import { SynthesizeStream as BaseSynthesizeStream, TTS as BaseTTS } from "../tts/index.js";
10
10
  import { DEFAULT_API_CONNECT_OPTIONS } from "../types.js";
11
- import { Event, Future, Task, cancelAndWait, combineSignals, shortuuid } from "../utils.js";
11
+ import {
12
+ Event,
13
+ Future,
14
+ Task,
15
+ cancelAndWait,
16
+ combineSignals,
17
+ shortuuid,
18
+ waitUntilTimeout
19
+ } from "../utils.js";
12
20
  import {
13
21
  ttsClientEventSchema,
14
22
  ttsServerEventSchema
@@ -333,13 +341,18 @@ class SynthesizeStream extends BaseSynthesizeStream {
333
341
  };
334
342
  const createRecvTask = async (signal) => {
335
343
  let currentSessionId = null;
344
+ const recvTimeoutMs = this.connOptions.timeoutMs;
336
345
  const bstream = new AudioByteStream(this.opts.sampleRate, NUM_CHANNELS);
337
346
  const serverEventStream = eventChannel.stream();
338
347
  const reader = serverEventStream.getReader();
339
348
  try {
340
349
  await inputSentEvent.wait();
341
350
  while (!this.closed && !signal.aborted) {
342
- const result = await reader.read();
351
+ const result = await waitUntilTimeout(
352
+ reader.read(),
353
+ recvTimeoutMs,
354
+ () => new APITimeoutError({ message: "TTS recv idle timeout" })
355
+ );
343
356
  if (signal.aborted) return;
344
357
  if (result.done) return;
345
358
  const serverEvent = result.value;
@@ -383,6 +396,14 @@ class SynthesizeStream extends BaseSynthesizeStream {
383
396
  break;
384
397
  }
385
398
  }
399
+ } catch (e) {
400
+ if (e instanceof APITimeoutError) {
401
+ this.#logger.warn("TTS recv task timed out waiting for server message");
402
+ await resourceCleanup();
403
+ completionFuture.reject(e);
404
+ return;
405
+ }
406
+ throw e;
386
407
  } finally {
387
408
  reader.releaseLock();
388
409
  try {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/inference/tts.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2025 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport type { AudioFrame } from '@livekit/rtc-node';\nimport { WebSocket } from 'ws';\nimport { APIError, APIStatusError } from '../_exceptions.js';\nimport { AudioByteStream } from '../audio.js';\nimport { ConnectionPool } from '../connection_pool.js';\nimport { type LanguageCode, normalizeLanguage } from '../language.js';\nimport { log } from '../log.js';\nimport { createStreamChannel } from '../stream/stream_channel.js';\nimport { basic as tokenizeBasic } from '../tokenize/index.js';\nimport type { ChunkedStream } from '../tts/index.js';\nimport { SynthesizeStream as BaseSynthesizeStream, TTS as BaseTTS } from '../tts/index.js';\nimport { type APIConnectOptions, DEFAULT_API_CONNECT_OPTIONS } from '../types.js';\nimport { Event, Future, Task, cancelAndWait, combineSignals, shortuuid } from '../utils.js';\nimport {\n type TtsClientEvent,\n type TtsServerEvent,\n ttsClientEventSchema,\n ttsServerEventSchema,\n} from './api_protos.js';\nimport { type AnyString, connectWs, createAccessToken, getDefaultInferenceUrl } from './utils.js';\n\nexport type CartesiaModels =\n | 'cartesia/sonic-3'\n | 'cartesia/sonic-2'\n | 'cartesia/sonic-turbo'\n | 'cartesia/sonic';\n\nexport type DeepgramTTSModels = 'deepgram/aura' | 'deepgram/aura-2';\n\nexport type ElevenlabsModels =\n | 'elevenlabs/eleven_flash_v2'\n | 'elevenlabs/eleven_flash_v2_5'\n | 'elevenlabs/eleven_turbo_v2'\n | 'elevenlabs/eleven_turbo_v2_5'\n | 'elevenlabs/eleven_multilingual_v2';\n\nexport type InworldModels =\n | 'inworld/inworld-tts-1.5-max'\n | 'inworld/inworld-tts-1.5-mini'\n | 'inworld/inworld-tts-1-max'\n | 'inworld/inworld-tts-1';\n\nexport type RimeModels = 'rime/arcana' | 'rime/mistv2';\n\nexport interface CartesiaOptions {\n /** Maximum duration of audio in seconds. */\n duration?: number;\n /** Speech speed. Default: not specified. */\n speed?: 'slow' | 'normal' | 'fast';\n}\n\nexport interface ElevenlabsOptions {\n /** Inactivity timeout in seconds. Default: 60. */\n inactivity_timeout?: number;\n /** Text normalization mode. Default: \"auto\". */\n apply_text_normalization?: 'auto' | 'off' | 'on';\n}\n\nexport interface DeepgramTTSOptions {}\n\nexport interface RimeOptions {}\n\nexport interface InworldOptions {\n /** Controls how fast the voice speaks. 1.0 is normal speed, 0.5 is half, 1.5 is 1.5x. Default: 1.0. */\n speaking_rate?: number;\n /** Controls randomness in the output. Recommended between 0.6 and 1.1. Default: 1.1. */\n temperature?: number;\n /** Controls text normalization. \"ON\" expands numbers, dates, abbreviations. \"OFF\" reads text as written. Default: \"ON\". */\n text_normalization?: 'ON' | 'OFF';\n}\n\ntype _TTSModels =\n | CartesiaModels\n | DeepgramTTSModels\n | ElevenlabsModels\n | RimeModels\n | InworldModels;\n\nexport type TTSModels =\n | CartesiaModels\n | DeepgramTTSModels\n | ElevenlabsModels\n | RimeModels\n | InworldModels\n | AnyString;\n\nexport type ModelWithVoice = `${_TTSModels}:${string}` | TTSModels;\n\nexport type TTSOptions<TModel extends TTSModels> = TModel extends CartesiaModels\n ? CartesiaOptions\n : TModel extends DeepgramTTSModels\n ? DeepgramTTSOptions\n : TModel extends ElevenlabsModels\n ? ElevenlabsOptions\n : TModel extends RimeModels\n ? RimeOptions\n : TModel extends InworldModels\n ? InworldOptions\n : Record<string, unknown>;\n\n/** Parse a model string into [model, voice]. Voice is undefined if not specified. */\nexport function parseTTSModelString(model: string): [string, string | undefined] {\n const idx = model.lastIndexOf(':');\n if (idx !== -1) {\n return [model.slice(0, idx), model.slice(idx + 1)];\n }\n return [model, undefined];\n}\n\n/** A fallback model with optional extra configuration. Extra fields are passed through to the provider. */\nexport interface TTSFallbackModel {\n /** Model name (e.g. \"cartesia/sonic\", \"elevenlabs/eleven_flash_v2\", \"rime/arcana\"). */\n model: string;\n /** Voice to use for the model. */\n voice: string;\n /** Extra configuration for the model. */\n extraKwargs?: Record<string, unknown>;\n}\n\nexport type TTSFallbackModelType = TTSFallbackModel | string;\n\n/** Normalize a single or list of FallbackModelType into TTSFallbackModel[]. */\nexport function normalizeTTSFallback(\n fallback: TTSFallbackModelType | TTSFallbackModelType[],\n): TTSFallbackModel[] {\n const makeFallback = (model: TTSFallbackModelType): TTSFallbackModel => {\n if (typeof model === 'string') {\n const [name, voice] = parseTTSModelString(model);\n return { model: name, voice: voice ?? '' };\n }\n return model;\n };\n\n if (Array.isArray(fallback)) {\n return fallback.map(makeFallback);\n }\n return [makeFallback(fallback)];\n}\n\ntype TTSEncoding = 'pcm_s16le';\n\nconst DEFAULT_ENCODING: TTSEncoding = 'pcm_s16le';\nconst DEFAULT_SAMPLE_RATE = 16000;\nconst NUM_CHANNELS = 1;\nconst DEFAULT_LANGUAGE = 'en';\n\nexport interface InferenceTTSOptions<TModel extends TTSModels> {\n model?: TModel;\n voice?: string;\n language?: LanguageCode;\n encoding: TTSEncoding;\n sampleRate: number;\n baseURL: string;\n apiKey: string;\n apiSecret: string;\n modelOptions: TTSOptions<TModel>;\n fallback?: TTSFallbackModel[];\n connOptions?: APIConnectOptions;\n}\n\n/**\n * Livekit Cloud Inference TTS\n */\nexport class TTS<TModel extends TTSModels> extends BaseTTS {\n private opts: InferenceTTSOptions<TModel>;\n private streams: Set<SynthesizeStream<TModel>> = new Set();\n pool: ConnectionPool<WebSocket>;\n\n #logger = log();\n\n constructor(opts: {\n model: TModel;\n voice?: string;\n language?: string;\n baseURL?: string;\n encoding?: TTSEncoding;\n sampleRate?: number;\n apiKey?: string;\n apiSecret?: string;\n modelOptions?: TTSOptions<TModel>;\n fallback?: TTSFallbackModelType | TTSFallbackModelType[];\n connOptions?: APIConnectOptions;\n }) {\n const sampleRate = opts?.sampleRate ?? DEFAULT_SAMPLE_RATE;\n super(sampleRate, 1, { streaming: true });\n\n const {\n model,\n voice,\n language = DEFAULT_LANGUAGE,\n baseURL,\n encoding = DEFAULT_ENCODING,\n apiKey,\n apiSecret,\n modelOptions = {} as TTSOptions<TModel>,\n fallback,\n connOptions,\n } = opts || {};\n\n const lkBaseURL = baseURL || getDefaultInferenceUrl();\n const lkApiKey = apiKey || process.env.LIVEKIT_INFERENCE_API_KEY || process.env.LIVEKIT_API_KEY;\n if (!lkApiKey) {\n throw new Error('apiKey is required: pass apiKey or set LIVEKIT_API_KEY');\n }\n\n const lkApiSecret =\n apiSecret || process.env.LIVEKIT_INFERENCE_API_SECRET || process.env.LIVEKIT_API_SECRET;\n if (!lkApiSecret) {\n throw new Error('apiSecret is required: pass apiSecret or set LIVEKIT_API_SECRET');\n }\n\n // read voice id from the model if provided: \"provider/model:voice_id\"\n let nextModel = model;\n let nextVoice = voice;\n if (typeof nextModel === 'string') {\n const idx = nextModel.lastIndexOf(':');\n if (idx !== -1) {\n const voiceFromModel = nextModel.slice(idx + 1);\n if (nextVoice && nextVoice !== voiceFromModel) {\n this.#logger.warn(\n '`voice` is provided via both argument and model, using the one from the argument',\n { voice: nextVoice, model: nextModel },\n );\n } else {\n nextVoice = voiceFromModel;\n }\n nextModel = nextModel.slice(0, idx) as TModel;\n }\n }\n\n const normalizedFallback = fallback ? normalizeTTSFallback(fallback) : undefined;\n\n this.opts = {\n model: nextModel,\n voice: nextVoice,\n language: normalizeLanguage(language),\n encoding,\n sampleRate,\n baseURL: lkBaseURL,\n apiKey: lkApiKey,\n apiSecret: lkApiSecret,\n modelOptions,\n fallback: normalizedFallback,\n connOptions: connOptions ?? DEFAULT_API_CONNECT_OPTIONS,\n };\n\n // Initialize connection pool\n this.pool = new ConnectionPool<WebSocket>({\n connectCb: (timeout) => this.connectWs(timeout),\n closeCb: (ws) => this.closeWs(ws),\n maxSessionDuration: 300_000,\n markRefreshedOnGet: true,\n connectTimeout: 10_000, // 10 seconds default\n });\n }\n\n get label() {\n return 'inference.TTS';\n }\n\n get model(): string {\n return this.opts.model ?? 'unknown';\n }\n\n get provider(): string {\n return 'livekit';\n }\n\n static fromModelString(modelString: string): TTS<AnyString> {\n const [model, voice] = parseTTSModelString(modelString);\n return new TTS({ model, voice: voice || undefined });\n }\n\n updateOptions(opts: Partial<Pick<InferenceTTSOptions<TModel>, 'model' | 'voice' | 'language'>>) {\n this.opts = {\n ...this.opts,\n ...opts,\n language: opts.language !== undefined ? normalizeLanguage(opts.language) : this.opts.language,\n };\n for (const stream of this.streams) {\n stream.updateOptions(this.opts);\n }\n }\n\n synthesize(_: string): ChunkedStream {\n throw new Error('ChunkedStream is not implemented');\n }\n\n stream(options?: { connOptions?: APIConnectOptions }): SynthesizeStream<TModel> {\n const { connOptions = this.opts.connOptions ?? DEFAULT_API_CONNECT_OPTIONS } = options || {};\n const stream = new SynthesizeStream(this, { ...this.opts }, connOptions);\n this.streams.add(stream);\n return stream;\n }\n\n async connectWs(timeout: number): Promise<WebSocket> {\n let baseURL = this.opts.baseURL;\n if (baseURL.startsWith('http://') || baseURL.startsWith('https://')) {\n baseURL = baseURL.replace('http', 'ws');\n }\n\n const token = await createAccessToken(this.opts.apiKey, this.opts.apiSecret);\n const url = `${baseURL}/tts`;\n const headers = { Authorization: `Bearer ${token}` } as Record<string, string>;\n\n const params = {\n type: 'session.create',\n sample_rate: String(this.opts.sampleRate),\n encoding: this.opts.encoding,\n extra: this.opts.modelOptions,\n } as Record<string, unknown>;\n\n if (this.opts.voice) (params as Record<string, unknown>).voice = this.opts.voice;\n if (this.opts.model) (params as Record<string, unknown>).model = this.opts.model;\n if (this.opts.language) (params as Record<string, unknown>).language = this.opts.language;\n\n if (this.opts.fallback?.length) {\n params.fallback = {\n models: this.opts.fallback.map((m) => ({\n model: m.model,\n voice: m.voice,\n extra: m.extraKwargs ?? {},\n })),\n };\n }\n\n if (this.opts.connOptions) {\n params.connection = {\n timeout: this.opts.connOptions.timeoutMs / 1000,\n retries: this.opts.connOptions.maxRetry,\n };\n }\n\n this.#logger.debug({ url }, 'inference.TTS creating new websocket connection (pool miss)');\n const socket = await connectWs(url, headers, timeout);\n socket.send(JSON.stringify(params));\n return socket;\n }\n\n async closeWs(ws: WebSocket) {\n await ws.close();\n }\n\n prewarm(): void {\n this.pool.prewarm();\n }\n\n async close() {\n for (const stream of this.streams) {\n await stream.close();\n }\n this.streams.clear();\n await this.pool.close();\n }\n}\n\nexport class SynthesizeStream<TModel extends TTSModels> extends BaseSynthesizeStream {\n private opts: InferenceTTSOptions<TModel>;\n private tts: TTS<TModel>;\n\n #logger = log();\n\n constructor(tts: TTS<TModel>, opts: InferenceTTSOptions<TModel>, connOptions: APIConnectOptions) {\n super(tts, connOptions);\n this.opts = opts;\n this.tts = tts;\n }\n\n get label() {\n return 'inference.SynthesizeStream';\n }\n\n updateOptions(opts: Partial<Pick<InferenceTTSOptions<TModel>, 'model' | 'voice' | 'language'>>) {\n this.opts = {\n ...this.opts,\n ...opts,\n language: opts.language !== undefined ? normalizeLanguage(opts.language) : this.opts.language,\n };\n }\n\n protected async run(): Promise<void> {\n let closing = false;\n let lastFrame: AudioFrame | undefined;\n\n const sendTokenizerStream = new tokenizeBasic.SentenceTokenizer().stream();\n const eventChannel = createStreamChannel<TtsServerEvent>();\n const requestId = shortuuid('tts_request_');\n const inputSentEvent = new Event();\n\n // Signal for protocol-driven completion (when 'done' message is received)\n const completionFuture = new Future<void>();\n\n const resourceCleanup = async () => {\n if (closing) return;\n closing = true;\n sendTokenizerStream.close();\n // close() returns a promise; don't leak it\n await eventChannel.close();\n };\n\n const sendClientEvent = async (event: TtsClientEvent, ws: WebSocket, signal: AbortSignal) => {\n // Don't send events to a closed WebSocket or aborted controller\n if (signal.aborted || closing) return;\n\n const validatedEvent = await ttsClientEventSchema.parseAsync(event);\n if (ws.readyState !== WebSocket.OPEN) {\n this.#logger.warn('Trying to send client TTS event to a closed WebSocket');\n return;\n }\n ws.send(JSON.stringify(validatedEvent));\n };\n\n const sendLastFrame = (segmentId: string, final: boolean) => {\n if (lastFrame) {\n this.queue.put({ requestId, segmentId, frame: lastFrame, final });\n lastFrame = undefined;\n }\n };\n\n const createInputTask = async (signal: AbortSignal) => {\n for await (const data of this.input) {\n if (signal.aborted || closing) break;\n if (data === SynthesizeStream.FLUSH_SENTINEL) {\n sendTokenizerStream.flush();\n continue;\n }\n sendTokenizerStream.pushText(data);\n }\n // Only call endInput if the stream hasn't been closed by cleanup\n if (!closing) {\n sendTokenizerStream.endInput();\n }\n };\n\n const createSentenceStreamTask = async (ws: WebSocket, signal: AbortSignal) => {\n for await (const ev of sendTokenizerStream) {\n if (signal.aborted || closing) break;\n\n await sendClientEvent(\n {\n type: 'input_transcript',\n transcript: ev.token + ' ',\n },\n ws,\n signal,\n );\n inputSentEvent.set();\n }\n\n await sendClientEvent({ type: 'session.flush' }, ws, signal);\n // needed in case empty input is sent\n inputSentEvent.set();\n };\n\n // Handles WebSocket message routing and error handling\n // Completes based on protocol messages, NOT on ws.close()\n const createWsListenerTask = async (ws: WebSocket, signal: AbortSignal) => {\n const onMessage = (data: Buffer) => {\n try {\n const eventJson = JSON.parse(data.toString()) as Record<string, unknown>;\n const validatedEvent = ttsServerEventSchema.parse(eventJson);\n // writer.write returns a promise; avoid unhandled rejections if stream is closed\n void eventChannel.write(validatedEvent).catch((error) => {\n this.#logger.debug(\n { error },\n 'Failed writing TTS event to stream channel (likely closed)',\n );\n });\n } catch (e) {\n this.#logger.error({ error: e }, 'Error parsing WebSocket message');\n }\n };\n\n const onError = (e: Error) => {\n this.#logger.error({ error: e }, 'WebSocket error');\n void resourceCleanup();\n try {\n // If the ws is misbehaving, hard-stop it immediately to avoid buffering.\n ws.terminate?.();\n } catch {\n // ignore\n }\n // Ensure this ws is not reused\n this.tts.pool.remove(ws);\n completionFuture.reject(e);\n };\n\n const onClose = () => {\n // WebSocket closed unexpectedly (not by us)\n if (!closing) {\n this.#logger.error('WebSocket closed unexpectedly');\n void resourceCleanup();\n // Ensure this ws is not reused\n this.tts.pool.remove(ws);\n completionFuture.reject(\n new APIStatusError({\n message: 'Gateway connection closed unexpectedly',\n options: { requestId },\n }),\n );\n }\n };\n\n const onAbort = () => {\n void resourceCleanup();\n try {\n // On interruption/abort, close the websocket immediately so the server stops streaming\n // and the ws library doesn't buffer unread frames in memory.\n ws.terminate?.();\n } catch {\n // ignore\n }\n this.tts.pool.remove(ws);\n inputSentEvent.set();\n completionFuture.resolve();\n };\n\n // Attach listeners\n ws.on('message', onMessage);\n ws.on('error', onError);\n ws.on('close', onClose);\n signal.addEventListener('abort', onAbort);\n\n try {\n // Wait for protocol-driven completion or error\n await completionFuture.await;\n } finally {\n // IMPORTANT: Remove listeners so connection can be reused\n ws.off('message', onMessage);\n ws.off('error', onError);\n ws.off('close', onClose);\n signal.removeEventListener('abort', onAbort);\n }\n };\n\n const createRecvTask = async (signal: AbortSignal) => {\n let currentSessionId: string | null = null;\n\n const bstream = new AudioByteStream(this.opts.sampleRate, NUM_CHANNELS);\n const serverEventStream = eventChannel.stream();\n const reader = serverEventStream.getReader();\n\n try {\n await inputSentEvent.wait();\n\n while (!this.closed && !signal.aborted) {\n const result = await reader.read();\n if (signal.aborted) return;\n if (result.done) return;\n\n const serverEvent = result.value;\n switch (serverEvent.type) {\n case 'session.created':\n currentSessionId = serverEvent.session_id;\n break;\n case 'output_audio':\n const base64Data = new Int8Array(Buffer.from(serverEvent.audio, 'base64'));\n for (const frame of bstream.write(base64Data.buffer)) {\n sendLastFrame(currentSessionId!, false);\n lastFrame = frame;\n }\n break;\n case 'done':\n for (const frame of bstream.flush()) {\n sendLastFrame(currentSessionId!, false);\n lastFrame = frame;\n }\n sendLastFrame(currentSessionId!, true);\n this.queue.put(SynthesizeStream.END_OF_STREAM);\n await resourceCleanup();\n completionFuture.resolve();\n return;\n case 'session.closed':\n await resourceCleanup();\n completionFuture.resolve();\n return;\n case 'error':\n this.#logger.error(\n { serverEvent },\n 'Received error message from LiveKit TTS WebSocket',\n );\n await resourceCleanup();\n completionFuture.reject(\n new APIError(`LiveKit TTS returned error: ${serverEvent.message}`),\n );\n return;\n default:\n this.#logger.warn('Unexpected message %s', serverEvent);\n break;\n }\n }\n } finally {\n reader.releaseLock();\n try {\n await serverEventStream.cancel();\n } catch (e) {\n this.#logger.debug('Error cancelling serverEventStream (may already be cancelled):', e);\n }\n }\n };\n\n try {\n await this.tts.pool.withConnection(\n async (ws: WebSocket) => {\n try {\n // IMPORTANT: don't cancel the stream's controller on normal completion,\n // otherwise the pool will remove+close the ws and every run becomes a pool miss.\n const runController = new AbortController();\n const onStreamAbort = () => runController.abort(this.abortController.signal.reason);\n this.abortController.signal.addEventListener('abort', onStreamAbort, { once: true });\n\n const tasks = [\n Task.from(\n async (controller) => {\n const combined = combineSignals(runController.signal, controller.signal);\n await createInputTask(combined);\n },\n undefined,\n 'inference-tts-input',\n ),\n Task.from(\n async (controller) => {\n const combined = combineSignals(runController.signal, controller.signal);\n await createSentenceStreamTask(ws, combined);\n },\n undefined,\n 'inference-tts-sentence',\n ),\n Task.from(\n async (controller) => {\n const combined = combineSignals(runController.signal, controller.signal);\n await createWsListenerTask(ws, combined);\n },\n undefined,\n 'inference-tts-ws-listener',\n ),\n Task.from(\n async (controller) => {\n const combined = combineSignals(runController.signal, controller.signal);\n await createRecvTask(combined);\n },\n undefined,\n 'inference-tts-recv',\n ),\n ];\n\n try {\n await Promise.all(tasks.map((t) => t.result));\n } finally {\n // Mirror python finally: unblock recv and cancel all tasks.\n inputSentEvent.set();\n await resourceCleanup();\n await cancelAndWait(tasks, 5000);\n this.abortController.signal.removeEventListener('abort', onStreamAbort);\n }\n } catch (e) {\n // If aborted, don't throw - let cleanup handle it\n if (e instanceof Error && e.name === 'AbortError') {\n return;\n }\n throw e;\n }\n },\n {\n timeout: this.connOptions.timeoutMs,\n },\n );\n } catch (e) {\n // Handle connection errors\n if (e instanceof Error && e.name === 'AbortError') {\n // Abort is expected during normal shutdown\n return;\n }\n throw e;\n } finally {\n // Ensure cleanup always runs (and don't leak the promise)\n await resourceCleanup();\n }\n }\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/inference/tts.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2025 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport type { AudioFrame } from '@livekit/rtc-node';\nimport { WebSocket } from 'ws';\nimport { APIError, APIStatusError, APITimeoutError } from '../_exceptions.js';\nimport { AudioByteStream } from '../audio.js';\nimport { ConnectionPool } from '../connection_pool.js';\nimport { type LanguageCode, normalizeLanguage } from '../language.js';\nimport { log } from '../log.js';\nimport { createStreamChannel } from '../stream/stream_channel.js';\nimport { basic as tokenizeBasic } from '../tokenize/index.js';\nimport type { ChunkedStream } from '../tts/index.js';\nimport { SynthesizeStream as BaseSynthesizeStream, TTS as BaseTTS } from '../tts/index.js';\nimport { type APIConnectOptions, DEFAULT_API_CONNECT_OPTIONS } from '../types.js';\nimport {\n Event,\n Future,\n Task,\n cancelAndWait,\n combineSignals,\n shortuuid,\n waitUntilTimeout,\n} from '../utils.js';\nimport {\n type TtsClientEvent,\n type TtsServerEvent,\n ttsClientEventSchema,\n ttsServerEventSchema,\n} from './api_protos.js';\nimport { type AnyString, connectWs, createAccessToken, getDefaultInferenceUrl } from './utils.js';\n\nexport type CartesiaModels =\n | 'cartesia/sonic-3'\n | 'cartesia/sonic-2'\n | 'cartesia/sonic-turbo'\n | 'cartesia/sonic';\n\nexport type DeepgramTTSModels = 'deepgram/aura' | 'deepgram/aura-2';\n\nexport type ElevenlabsModels =\n | 'elevenlabs/eleven_flash_v2'\n | 'elevenlabs/eleven_flash_v2_5'\n | 'elevenlabs/eleven_turbo_v2'\n | 'elevenlabs/eleven_turbo_v2_5'\n | 'elevenlabs/eleven_multilingual_v2';\n\nexport type InworldModels =\n | 'inworld/inworld-tts-1.5-max'\n | 'inworld/inworld-tts-1.5-mini'\n | 'inworld/inworld-tts-1-max'\n | 'inworld/inworld-tts-1';\n\nexport type RimeModels = 'rime/arcana' | 'rime/mistv2';\n\nexport interface CartesiaOptions {\n emotion?: string;\n /** Maximum duration of audio in seconds. */\n duration?: number;\n /** Speech speed. Default: not specified. */\n speed?: 'slow' | 'normal' | 'fast' | number;\n volume?: number;\n max_buffer_delay_ms?: number;\n add_timestamps?: boolean;\n add_phoneme_timestamps?: boolean;\n use_normalized_timestamps?: boolean;\n}\n\nexport interface ElevenlabsOptions {\n /** Inactivity timeout in seconds. Default: 60. */\n inactivity_timeout?: number;\n /** Text normalization mode. Default: \"auto\". */\n apply_text_normalization?: 'auto' | 'off' | 'on';\n auto_mode?: boolean;\n enable_logging?: boolean;\n enable_ssml_parsing?: boolean;\n sync_alignment?: boolean;\n language_code?: string;\n /** Voice stability tuning, typically in the range [0, 1]. */\n stability?: number;\n /** Voice similarity tuning, typically in the range [0, 1]. */\n similarity_boost?: number;\n /** Style exaggeration tuning, typically in the range [0, 1]. */\n style?: number;\n /** Speech speed multiplier. */\n speed?: number;\n use_speaker_boost?: boolean;\n chunk_length_schedule?: number[];\n preferred_alignment?: string;\n}\n\nexport interface DeepgramTTSOptions {\n /** Default: false. */\n mip_opt_out?: boolean;\n}\n\nexport interface RimeOptions {\n /** Default 1.0, <1 = faster, >1 = slower. */\n speed_alpha?: number;\n /** Default false. */\n pause_between_brackets?: boolean;\n /** Default false. */\n phonemize_between_brackets?: boolean;\n /** Comma-separated speed factors for [bracketed] words. */\n inline_speed_alpha?: string;\n /** Default false. */\n no_text_normalization?: boolean;\n}\n\nexport interface InworldOptions {\n /** Range >0.5, <=1.5. */\n speaking_rate?: number;\n /** Range 0-2. */\n temperature?: number;\n timestamp_type?: 'TIMESTAMP_TYPE_UNSPECIFIED' | 'WORD' | 'CHARACTER';\n apply_text_normalization?: 'APPLY_TEXT_NORMALIZATION_UNSPECIFIED' | 'ON' | 'OFF';\n /** @deprecated Backward-compatible alias. Use `apply_text_normalization`. */\n text_normalization?: 'ON' | 'OFF';\n}\n\ntype _TTSModels =\n | CartesiaModels\n | DeepgramTTSModels\n | ElevenlabsModels\n | RimeModels\n | InworldModels;\n\nexport type TTSModels =\n | CartesiaModels\n | DeepgramTTSModels\n | ElevenlabsModels\n | RimeModels\n | InworldModels\n | AnyString;\n\nexport type ModelWithVoice = `${_TTSModels}:${string}` | TTSModels;\n\nexport type TTSOptions<TModel extends TTSModels> = TModel extends CartesiaModels\n ? CartesiaOptions\n : TModel extends DeepgramTTSModels\n ? DeepgramTTSOptions\n : TModel extends ElevenlabsModels\n ? ElevenlabsOptions\n : TModel extends RimeModels\n ? RimeOptions\n : TModel extends InworldModels\n ? InworldOptions\n : Record<string, unknown>;\n\n/** Parse a model string into [model, voice]. Voice is undefined if not specified. */\nexport function parseTTSModelString(model: string): [string, string | undefined] {\n const idx = model.lastIndexOf(':');\n if (idx !== -1) {\n return [model.slice(0, idx), model.slice(idx + 1)];\n }\n return [model, undefined];\n}\n\n/** A fallback model with optional extra configuration. Extra fields are passed through to the provider. */\nexport interface TTSFallbackModel {\n /** Model name (e.g. \"cartesia/sonic\", \"elevenlabs/eleven_flash_v2\", \"rime/arcana\"). */\n model: string;\n /** Voice to use for the model. */\n voice: string;\n /** Extra configuration for the model. */\n extraKwargs?: Record<string, unknown>;\n}\n\nexport type TTSFallbackModelType = TTSFallbackModel | string;\n\n/** Normalize a single or list of FallbackModelType into TTSFallbackModel[]. */\nexport function normalizeTTSFallback(\n fallback: TTSFallbackModelType | TTSFallbackModelType[],\n): TTSFallbackModel[] {\n const makeFallback = (model: TTSFallbackModelType): TTSFallbackModel => {\n if (typeof model === 'string') {\n const [name, voice] = parseTTSModelString(model);\n return { model: name, voice: voice ?? '' };\n }\n return model;\n };\n\n if (Array.isArray(fallback)) {\n return fallback.map(makeFallback);\n }\n return [makeFallback(fallback)];\n}\n\ntype TTSEncoding = 'pcm_s16le';\n\nconst DEFAULT_ENCODING: TTSEncoding = 'pcm_s16le';\nconst DEFAULT_SAMPLE_RATE = 16000;\nconst NUM_CHANNELS = 1;\nconst DEFAULT_LANGUAGE = 'en';\n\nexport interface InferenceTTSOptions<TModel extends TTSModels> {\n model?: TModel;\n voice?: string;\n language?: LanguageCode;\n encoding: TTSEncoding;\n sampleRate: number;\n baseURL: string;\n apiKey: string;\n apiSecret: string;\n /** Flat provider-specific inference options forwarded as the `extra` payload field. */\n modelOptions: TTSOptions<TModel>;\n fallback?: TTSFallbackModel[];\n connOptions?: APIConnectOptions;\n}\n\n/**\n * Livekit Cloud Inference TTS\n */\nexport class TTS<TModel extends TTSModels> extends BaseTTS {\n private opts: InferenceTTSOptions<TModel>;\n private streams: Set<SynthesizeStream<TModel>> = new Set();\n pool: ConnectionPool<WebSocket>;\n\n #logger = log();\n\n constructor(opts: {\n model: TModel;\n voice?: string;\n language?: string;\n baseURL?: string;\n encoding?: TTSEncoding;\n sampleRate?: number;\n apiKey?: string;\n apiSecret?: string;\n /** Flat provider-specific inference options forwarded as the `extra` payload field. */\n modelOptions?: TTSOptions<TModel>;\n fallback?: TTSFallbackModelType | TTSFallbackModelType[];\n connOptions?: APIConnectOptions;\n }) {\n const sampleRate = opts?.sampleRate ?? DEFAULT_SAMPLE_RATE;\n super(sampleRate, 1, { streaming: true });\n\n const {\n model,\n voice,\n language = DEFAULT_LANGUAGE,\n baseURL,\n encoding = DEFAULT_ENCODING,\n apiKey,\n apiSecret,\n modelOptions = {} as TTSOptions<TModel>,\n fallback,\n connOptions,\n } = opts || {};\n\n const lkBaseURL = baseURL || getDefaultInferenceUrl();\n const lkApiKey = apiKey || process.env.LIVEKIT_INFERENCE_API_KEY || process.env.LIVEKIT_API_KEY;\n if (!lkApiKey) {\n throw new Error('apiKey is required: pass apiKey or set LIVEKIT_API_KEY');\n }\n\n const lkApiSecret =\n apiSecret || process.env.LIVEKIT_INFERENCE_API_SECRET || process.env.LIVEKIT_API_SECRET;\n if (!lkApiSecret) {\n throw new Error('apiSecret is required: pass apiSecret or set LIVEKIT_API_SECRET');\n }\n\n // read voice id from the model if provided: \"provider/model:voice_id\"\n let nextModel = model;\n let nextVoice = voice;\n if (typeof nextModel === 'string') {\n const idx = nextModel.lastIndexOf(':');\n if (idx !== -1) {\n const voiceFromModel = nextModel.slice(idx + 1);\n if (nextVoice && nextVoice !== voiceFromModel) {\n this.#logger.warn(\n '`voice` is provided via both argument and model, using the one from the argument',\n { voice: nextVoice, model: nextModel },\n );\n } else {\n nextVoice = voiceFromModel;\n }\n nextModel = nextModel.slice(0, idx) as TModel;\n }\n }\n\n const normalizedFallback = fallback ? normalizeTTSFallback(fallback) : undefined;\n\n this.opts = {\n model: nextModel,\n voice: nextVoice,\n language: normalizeLanguage(language),\n encoding,\n sampleRate,\n baseURL: lkBaseURL,\n apiKey: lkApiKey,\n apiSecret: lkApiSecret,\n modelOptions,\n fallback: normalizedFallback,\n connOptions: connOptions ?? DEFAULT_API_CONNECT_OPTIONS,\n };\n\n // Initialize connection pool\n this.pool = new ConnectionPool<WebSocket>({\n connectCb: (timeout) => this.connectWs(timeout),\n closeCb: (ws) => this.closeWs(ws),\n maxSessionDuration: 300_000,\n markRefreshedOnGet: true,\n connectTimeout: 10_000, // 10 seconds default\n });\n }\n\n get label() {\n return 'inference.TTS';\n }\n\n get model(): string {\n return this.opts.model ?? 'unknown';\n }\n\n get provider(): string {\n return 'livekit';\n }\n\n static fromModelString(modelString: string): TTS<AnyString> {\n const [model, voice] = parseTTSModelString(modelString);\n return new TTS({ model, voice: voice || undefined });\n }\n\n updateOptions(opts: Partial<Pick<InferenceTTSOptions<TModel>, 'model' | 'voice' | 'language'>>) {\n this.opts = {\n ...this.opts,\n ...opts,\n language: opts.language !== undefined ? normalizeLanguage(opts.language) : this.opts.language,\n };\n for (const stream of this.streams) {\n stream.updateOptions(this.opts);\n }\n }\n\n synthesize(_: string): ChunkedStream {\n throw new Error('ChunkedStream is not implemented');\n }\n\n stream(options?: { connOptions?: APIConnectOptions }): SynthesizeStream<TModel> {\n const { connOptions = this.opts.connOptions ?? DEFAULT_API_CONNECT_OPTIONS } = options || {};\n const stream = new SynthesizeStream(this, { ...this.opts }, connOptions);\n this.streams.add(stream);\n return stream;\n }\n\n async connectWs(timeout: number): Promise<WebSocket> {\n let baseURL = this.opts.baseURL;\n if (baseURL.startsWith('http://') || baseURL.startsWith('https://')) {\n baseURL = baseURL.replace('http', 'ws');\n }\n\n const token = await createAccessToken(this.opts.apiKey, this.opts.apiSecret);\n const url = `${baseURL}/tts`;\n const headers = { Authorization: `Bearer ${token}` } as Record<string, string>;\n\n const params = {\n type: 'session.create',\n sample_rate: String(this.opts.sampleRate),\n encoding: this.opts.encoding,\n extra: this.opts.modelOptions,\n } as Record<string, unknown>;\n\n if (this.opts.voice) (params as Record<string, unknown>).voice = this.opts.voice;\n if (this.opts.model) (params as Record<string, unknown>).model = this.opts.model;\n if (this.opts.language) (params as Record<string, unknown>).language = this.opts.language;\n\n if (this.opts.fallback?.length) {\n params.fallback = {\n models: this.opts.fallback.map((m) => ({\n model: m.model,\n voice: m.voice,\n extra: m.extraKwargs ?? {},\n })),\n };\n }\n\n if (this.opts.connOptions) {\n params.connection = {\n timeout: this.opts.connOptions.timeoutMs / 1000,\n retries: this.opts.connOptions.maxRetry,\n };\n }\n\n this.#logger.debug({ url }, 'inference.TTS creating new websocket connection (pool miss)');\n const socket = await connectWs(url, headers, timeout);\n socket.send(JSON.stringify(params));\n return socket;\n }\n\n async closeWs(ws: WebSocket) {\n await ws.close();\n }\n\n prewarm(): void {\n this.pool.prewarm();\n }\n\n async close() {\n for (const stream of this.streams) {\n await stream.close();\n }\n this.streams.clear();\n await this.pool.close();\n }\n}\n\nexport class SynthesizeStream<TModel extends TTSModels> extends BaseSynthesizeStream {\n private opts: InferenceTTSOptions<TModel>;\n private tts: TTS<TModel>;\n\n #logger = log();\n\n constructor(tts: TTS<TModel>, opts: InferenceTTSOptions<TModel>, connOptions: APIConnectOptions) {\n super(tts, connOptions);\n this.opts = opts;\n this.tts = tts;\n }\n\n get label() {\n return 'inference.SynthesizeStream';\n }\n\n updateOptions(opts: Partial<Pick<InferenceTTSOptions<TModel>, 'model' | 'voice' | 'language'>>) {\n this.opts = {\n ...this.opts,\n ...opts,\n language: opts.language !== undefined ? normalizeLanguage(opts.language) : this.opts.language,\n };\n }\n\n protected async run(): Promise<void> {\n let closing = false;\n let lastFrame: AudioFrame | undefined;\n\n const sendTokenizerStream = new tokenizeBasic.SentenceTokenizer().stream();\n const eventChannel = createStreamChannel<TtsServerEvent>();\n const requestId = shortuuid('tts_request_');\n const inputSentEvent = new Event();\n\n // Signal for protocol-driven completion (when 'done' message is received)\n const completionFuture = new Future<void>();\n\n const resourceCleanup = async () => {\n if (closing) return;\n closing = true;\n sendTokenizerStream.close();\n // close() returns a promise; don't leak it\n await eventChannel.close();\n };\n\n const sendClientEvent = async (event: TtsClientEvent, ws: WebSocket, signal: AbortSignal) => {\n // Don't send events to a closed WebSocket or aborted controller\n if (signal.aborted || closing) return;\n\n const validatedEvent = await ttsClientEventSchema.parseAsync(event);\n if (ws.readyState !== WebSocket.OPEN) {\n this.#logger.warn('Trying to send client TTS event to a closed WebSocket');\n return;\n }\n ws.send(JSON.stringify(validatedEvent));\n };\n\n const sendLastFrame = (segmentId: string, final: boolean) => {\n if (lastFrame) {\n this.queue.put({ requestId, segmentId, frame: lastFrame, final });\n lastFrame = undefined;\n }\n };\n\n const createInputTask = async (signal: AbortSignal) => {\n for await (const data of this.input) {\n if (signal.aborted || closing) break;\n if (data === SynthesizeStream.FLUSH_SENTINEL) {\n sendTokenizerStream.flush();\n continue;\n }\n sendTokenizerStream.pushText(data);\n }\n // Only call endInput if the stream hasn't been closed by cleanup\n if (!closing) {\n sendTokenizerStream.endInput();\n }\n };\n\n const createSentenceStreamTask = async (ws: WebSocket, signal: AbortSignal) => {\n for await (const ev of sendTokenizerStream) {\n if (signal.aborted || closing) break;\n\n await sendClientEvent(\n {\n type: 'input_transcript',\n transcript: ev.token + ' ',\n },\n ws,\n signal,\n );\n inputSentEvent.set();\n }\n\n await sendClientEvent({ type: 'session.flush' }, ws, signal);\n // needed in case empty input is sent\n inputSentEvent.set();\n };\n\n // Handles WebSocket message routing and error handling\n // Completes based on protocol messages, NOT on ws.close()\n const createWsListenerTask = async (ws: WebSocket, signal: AbortSignal) => {\n const onMessage = (data: Buffer) => {\n try {\n const eventJson = JSON.parse(data.toString()) as Record<string, unknown>;\n const validatedEvent = ttsServerEventSchema.parse(eventJson);\n // writer.write returns a promise; avoid unhandled rejections if stream is closed\n void eventChannel.write(validatedEvent).catch((error) => {\n this.#logger.debug(\n { error },\n 'Failed writing TTS event to stream channel (likely closed)',\n );\n });\n } catch (e) {\n this.#logger.error({ error: e }, 'Error parsing WebSocket message');\n }\n };\n\n const onError = (e: Error) => {\n this.#logger.error({ error: e }, 'WebSocket error');\n void resourceCleanup();\n try {\n // If the ws is misbehaving, hard-stop it immediately to avoid buffering.\n ws.terminate?.();\n } catch {\n // ignore\n }\n // Ensure this ws is not reused\n this.tts.pool.remove(ws);\n completionFuture.reject(e);\n };\n\n const onClose = () => {\n // WebSocket closed unexpectedly (not by us)\n if (!closing) {\n this.#logger.error('WebSocket closed unexpectedly');\n void resourceCleanup();\n // Ensure this ws is not reused\n this.tts.pool.remove(ws);\n completionFuture.reject(\n new APIStatusError({\n message: 'Gateway connection closed unexpectedly',\n options: { requestId },\n }),\n );\n }\n };\n\n const onAbort = () => {\n void resourceCleanup();\n try {\n // On interruption/abort, close the websocket immediately so the server stops streaming\n // and the ws library doesn't buffer unread frames in memory.\n ws.terminate?.();\n } catch {\n // ignore\n }\n this.tts.pool.remove(ws);\n inputSentEvent.set();\n completionFuture.resolve();\n };\n\n // Attach listeners\n ws.on('message', onMessage);\n ws.on('error', onError);\n ws.on('close', onClose);\n signal.addEventListener('abort', onAbort);\n\n try {\n // Wait for protocol-driven completion or error\n await completionFuture.await;\n } finally {\n // IMPORTANT: Remove listeners so connection can be reused\n ws.off('message', onMessage);\n ws.off('error', onError);\n ws.off('close', onClose);\n signal.removeEventListener('abort', onAbort);\n }\n };\n\n const createRecvTask = async (signal: AbortSignal) => {\n let currentSessionId: string | null = null;\n const recvTimeoutMs = this.connOptions.timeoutMs;\n\n const bstream = new AudioByteStream(this.opts.sampleRate, NUM_CHANNELS);\n const serverEventStream = eventChannel.stream();\n const reader = serverEventStream.getReader();\n\n try {\n await inputSentEvent.wait();\n\n while (!this.closed && !signal.aborted) {\n const result = await waitUntilTimeout(\n reader.read(),\n recvTimeoutMs,\n () => new APITimeoutError({ message: 'TTS recv idle timeout' }),\n );\n\n if (signal.aborted) return;\n if (result.done) return;\n\n const serverEvent = result.value;\n switch (serverEvent.type) {\n case 'session.created':\n currentSessionId = serverEvent.session_id;\n break;\n case 'output_audio':\n const base64Data = new Int8Array(Buffer.from(serverEvent.audio, 'base64'));\n for (const frame of bstream.write(base64Data.buffer)) {\n sendLastFrame(currentSessionId!, false);\n lastFrame = frame;\n }\n break;\n case 'done':\n for (const frame of bstream.flush()) {\n sendLastFrame(currentSessionId!, false);\n lastFrame = frame;\n }\n sendLastFrame(currentSessionId!, true);\n this.queue.put(SynthesizeStream.END_OF_STREAM);\n await resourceCleanup();\n completionFuture.resolve();\n return;\n case 'session.closed':\n await resourceCleanup();\n completionFuture.resolve();\n return;\n case 'error':\n this.#logger.error(\n { serverEvent },\n 'Received error message from LiveKit TTS WebSocket',\n );\n await resourceCleanup();\n completionFuture.reject(\n new APIError(`LiveKit TTS returned error: ${serverEvent.message}`),\n );\n return;\n default:\n this.#logger.warn('Unexpected message %s', serverEvent);\n break;\n }\n }\n } catch (e) {\n if (e instanceof APITimeoutError) {\n this.#logger.warn('TTS recv task timed out waiting for server message');\n await resourceCleanup();\n completionFuture.reject(e);\n return;\n }\n throw e;\n } finally {\n reader.releaseLock();\n try {\n await serverEventStream.cancel();\n } catch (e) {\n this.#logger.debug('Error cancelling serverEventStream (may already be cancelled):', e);\n }\n }\n };\n\n try {\n await this.tts.pool.withConnection(\n async (ws: WebSocket) => {\n try {\n // IMPORTANT: don't cancel the stream's controller on normal completion,\n // otherwise the pool will remove+close the ws and every run becomes a pool miss.\n const runController = new AbortController();\n const onStreamAbort = () => runController.abort(this.abortController.signal.reason);\n this.abortController.signal.addEventListener('abort', onStreamAbort, { once: true });\n\n const tasks = [\n Task.from(\n async (controller) => {\n const combined = combineSignals(runController.signal, controller.signal);\n await createInputTask(combined);\n },\n undefined,\n 'inference-tts-input',\n ),\n Task.from(\n async (controller) => {\n const combined = combineSignals(runController.signal, controller.signal);\n await createSentenceStreamTask(ws, combined);\n },\n undefined,\n 'inference-tts-sentence',\n ),\n Task.from(\n async (controller) => {\n const combined = combineSignals(runController.signal, controller.signal);\n await createWsListenerTask(ws, combined);\n },\n undefined,\n 'inference-tts-ws-listener',\n ),\n Task.from(\n async (controller) => {\n const combined = combineSignals(runController.signal, controller.signal);\n await createRecvTask(combined);\n },\n undefined,\n 'inference-tts-recv',\n ),\n ];\n\n try {\n await Promise.all(tasks.map((t) => t.result));\n } finally {\n // Mirror python finally: unblock recv and cancel all tasks.\n inputSentEvent.set();\n await resourceCleanup();\n await cancelAndWait(tasks, 5000);\n this.abortController.signal.removeEventListener('abort', onStreamAbort);\n }\n } catch (e) {\n // If aborted, don't throw - let cleanup handle it\n if (e instanceof Error && e.name === 'AbortError') {\n return;\n }\n throw e;\n }\n },\n {\n timeout: this.connOptions.timeoutMs,\n },\n );\n } catch (e) {\n // Handle connection errors\n if (e instanceof Error && e.name === 'AbortError') {\n // Abort is expected during normal shutdown\n return;\n }\n throw e;\n } finally {\n // Ensure cleanup always runs (and don't leak the promise)\n await resourceCleanup();\n }\n }\n}\n"],"mappings":"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","names":[]}
@@ -230,4 +230,76 @@ function makeTts(overrides = {}) {
230
230
  (0, import_vitest.expect)(tts["opts"].connOptions.retryIntervalMs).toBe(2e3);
231
231
  });
232
232
  });
233
+ (0, import_vitest.describe)("TTS provider modelOptions parity", () => {
234
+ (0, import_vitest.it)("preserves ElevenLabs inference model options", () => {
235
+ const modelOptions = {
236
+ speed: 1.2,
237
+ stability: 0.5,
238
+ similarity_boost: 0.8,
239
+ enable_logging: false
240
+ };
241
+ const tts = new import_tts.TTS({
242
+ model: "elevenlabs/eleven_flash_v2_5",
243
+ apiKey: "test-key",
244
+ apiSecret: "test-secret",
245
+ baseURL: "https://example.livekit.cloud",
246
+ modelOptions
247
+ });
248
+ (0, import_vitest.expect)(tts["opts"].modelOptions).toEqual(modelOptions);
249
+ });
250
+ (0, import_vitest.it)("accepts expanded Cartesia inference model options", () => {
251
+ const modelOptions = {
252
+ speed: 1.15,
253
+ emotion: "curious",
254
+ add_timestamps: true
255
+ };
256
+ const tts = new import_tts.TTS({
257
+ model: "cartesia/sonic",
258
+ apiKey: "test-key",
259
+ apiSecret: "test-secret",
260
+ baseURL: "https://example.livekit.cloud",
261
+ modelOptions
262
+ });
263
+ (0, import_vitest.expect)(tts["opts"].modelOptions).toEqual(modelOptions);
264
+ });
265
+ (0, import_vitest.it)("accepts Deepgram inference model options", () => {
266
+ const modelOptions = { mip_opt_out: true };
267
+ const tts = new import_tts.TTS({
268
+ model: "deepgram/aura-2",
269
+ apiKey: "test-key",
270
+ apiSecret: "test-secret",
271
+ baseURL: "https://example.livekit.cloud",
272
+ modelOptions
273
+ });
274
+ (0, import_vitest.expect)(tts["opts"].modelOptions).toEqual(modelOptions);
275
+ });
276
+ (0, import_vitest.it)("accepts Rime inference model options", () => {
277
+ const modelOptions = {
278
+ speed_alpha: 0.9,
279
+ pause_between_brackets: true
280
+ };
281
+ const tts = new import_tts.TTS({
282
+ model: "rime/mistv2",
283
+ apiKey: "test-key",
284
+ apiSecret: "test-secret",
285
+ baseURL: "https://example.livekit.cloud",
286
+ modelOptions
287
+ });
288
+ (0, import_vitest.expect)(tts["opts"].modelOptions).toEqual(modelOptions);
289
+ });
290
+ (0, import_vitest.it)("accepts Inworld inference model options", () => {
291
+ const modelOptions = {
292
+ timestamp_type: "WORD",
293
+ apply_text_normalization: "ON"
294
+ };
295
+ const tts = new import_tts.TTS({
296
+ model: "inworld/inworld-tts-1",
297
+ apiKey: "test-key",
298
+ apiSecret: "test-secret",
299
+ baseURL: "https://example.livekit.cloud",
300
+ modelOptions
301
+ });
302
+ (0, import_vitest.expect)(tts["opts"].modelOptions).toEqual(modelOptions);
303
+ });
304
+ });
233
305
  //# sourceMappingURL=tts.test.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/inference/tts.test.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2025 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport { beforeAll, describe, expect, it } from 'vitest';\nimport { normalizeLanguage } from '../language.js';\nimport { initializeLogger } from '../log.js';\nimport { type APIConnectOptions, DEFAULT_API_CONNECT_OPTIONS } from '../types.js';\nimport { TTS, type TTSFallbackModel, normalizeTTSFallback, parseTTSModelString } from './tts.js';\n\nbeforeAll(() => {\n initializeLogger({ level: 'silent', pretty: false });\n});\n\n/** Helper to create TTS with required credentials. */\nfunction makeTts(overrides: Record<string, unknown> = {}) {\n const defaults = {\n model: 'cartesia/sonic' as const,\n apiKey: 'test-key',\n apiSecret: 'test-secret',\n baseURL: 'https://example.livekit.cloud',\n };\n return new TTS({ ...defaults, ...overrides });\n}\n\ndescribe('parseTTSModelString', () => {\n it('simple model without voice', () => {\n const [model, voice] = parseTTSModelString('cartesia');\n expect(model).toBe('cartesia');\n expect(voice).toBeUndefined();\n });\n\n it('model with voice suffix', () => {\n const [model, voice] = parseTTSModelString('cartesia:my-voice-id');\n expect(model).toBe('cartesia');\n expect(voice).toBe('my-voice-id');\n });\n\n it('provider/model format without voice', () => {\n const [model, voice] = parseTTSModelString('cartesia/sonic');\n expect(model).toBe('cartesia/sonic');\n expect(voice).toBeUndefined();\n });\n\n it('provider/model format with voice', () => {\n const [model, voice] = parseTTSModelString('cartesia/sonic:my-voice-id');\n expect(model).toBe('cartesia/sonic');\n expect(voice).toBe('my-voice-id');\n });\n\n it.each([\n ['elevenlabs/eleven_flash_v2:voice123', 'elevenlabs/eleven_flash_v2', 'voice123'],\n ['rime:speaker-a', 'rime', 'speaker-a'],\n ['rime/mist:narrator', 'rime/mist', 'narrator'],\n ['inworld/inworld-tts-1:character', 'inworld/inworld-tts-1', 'character'],\n ['cartesia/sonic-turbo:deep-voice', 'cartesia/sonic-turbo', 'deep-voice'],\n ])('various providers and voices: %s', (modelStr, expectedModel, expectedVoice) => {\n const [model, voice] = parseTTSModelString(modelStr);\n expect(model).toBe(expectedModel);\n expect(voice).toBe(expectedVoice);\n });\n\n it('empty voice after colon', () => {\n const [model, voice] = parseTTSModelString('cartesia/sonic:');\n expect(model).toBe('cartesia/sonic');\n expect(voice).toBe('');\n });\n});\n\ndescribe('normalizeTTSFallback', () => {\n it('single string model', () => {\n const result = normalizeTTSFallback('cartesia/sonic');\n expect(result).toEqual([{ model: 'cartesia/sonic', voice: '' }]);\n });\n\n it('single string model with voice', () => {\n const result = normalizeTTSFallback('cartesia/sonic:my-voice');\n expect(result).toEqual([{ model: 'cartesia/sonic', voice: 'my-voice' }]);\n });\n\n it('single FallbackModel dict', () => {\n const fallback: TTSFallbackModel = { model: 'cartesia/sonic', voice: 'narrator' };\n const result = normalizeTTSFallback(fallback);\n expect(result).toEqual([{ model: 'cartesia/sonic', voice: 'narrator' }]);\n });\n\n it('list of string models', () => {\n const result = normalizeTTSFallback(['cartesia/sonic', 'elevenlabs/eleven_flash_v2']);\n expect(result).toEqual([\n { model: 'cartesia/sonic', voice: '' },\n { model: 'elevenlabs/eleven_flash_v2', voice: '' },\n ]);\n });\n\n it('list of string models with voices', () => {\n const result = normalizeTTSFallback(['cartesia/sonic:voice1', 'elevenlabs:voice2']);\n expect(result).toEqual([\n { model: 'cartesia/sonic', voice: 'voice1' },\n { model: 'elevenlabs', voice: 'voice2' },\n ]);\n });\n\n it('list of FallbackModel dicts', () => {\n const fallbacks: TTSFallbackModel[] = [\n { model: 'cartesia/sonic', voice: 'narrator' },\n { model: 'elevenlabs', voice: '' },\n ];\n const result = normalizeTTSFallback(fallbacks);\n expect(result).toEqual([\n { model: 'cartesia/sonic', voice: 'narrator' },\n { model: 'elevenlabs', voice: '' },\n ]);\n });\n\n it('mixed list of strings and dicts', () => {\n const result = normalizeTTSFallback([\n 'cartesia/sonic:voice1',\n { model: 'elevenlabs/eleven_flash_v2', voice: 'custom' } as TTSFallbackModel,\n 'rime/mist',\n ]);\n expect(result).toEqual([\n { model: 'cartesia/sonic', voice: 'voice1' },\n { model: 'elevenlabs/eleven_flash_v2', voice: 'custom' },\n { model: 'rime/mist', voice: '' },\n ]);\n });\n\n it('FallbackModel with extraKwargs is preserved', () => {\n const fallback: TTSFallbackModel = {\n model: 'cartesia/sonic',\n voice: 'narrator',\n extraKwargs: { duration: 30.0, speed: 'fast' },\n };\n const result = normalizeTTSFallback(fallback);\n expect(result).toEqual([\n {\n model: 'cartesia/sonic',\n voice: 'narrator',\n extraKwargs: { duration: 30.0, speed: 'fast' },\n },\n ]);\n });\n\n it('list with extraKwargs preserved', () => {\n const result = normalizeTTSFallback([\n { model: 'cartesia/sonic', voice: 'v1', extraKwargs: { speed: 'slow' } } as TTSFallbackModel,\n 'elevenlabs:voice2',\n { model: 'rime/mist', voice: '', extraKwargs: { custom: true } } as TTSFallbackModel,\n ]);\n expect(result).toEqual([\n { model: 'cartesia/sonic', voice: 'v1', extraKwargs: { speed: 'slow' } },\n { model: 'elevenlabs', voice: 'voice2' },\n { model: 'rime/mist', voice: '', extraKwargs: { custom: true } },\n ]);\n });\n\n it('empty list returns empty list', () => {\n const result = normalizeTTSFallback([]);\n expect(result).toEqual([]);\n });\n\n it('FallbackModel with empty voice', () => {\n const fallback: TTSFallbackModel = { model: 'cartesia/sonic', voice: '' };\n const result = normalizeTTSFallback(fallback);\n expect(result).toEqual([{ model: 'cartesia/sonic', voice: '' }]);\n });\n});\n\ndescribe('TTS constructor fallback and connOptions', () => {\n it('normalizes language in constructor', () => {\n const tts = makeTts({ language: 'english' });\n expect(tts['opts'].language).toBe('en');\n });\n\n it('normalizes updated language values', () => {\n const tts = makeTts();\n tts.updateOptions({ language: 'en_US' });\n expect(tts['opts'].language).toBe(normalizeLanguage('en_US'));\n });\n\n it('fallback not given defaults to undefined', () => {\n const tts = makeTts();\n expect(tts['opts'].fallback).toBeUndefined();\n });\n\n it('fallback single string is normalized', () => {\n const tts = makeTts({ fallback: 'elevenlabs/eleven_flash_v2' });\n expect(tts['opts'].fallback).toEqual([{ model: 'elevenlabs/eleven_flash_v2', voice: '' }]);\n });\n\n it('fallback single string with voice is normalized', () => {\n const tts = makeTts({ fallback: 'cartesia/sonic:my-voice' });\n expect(tts['opts'].fallback).toEqual([{ model: 'cartesia/sonic', voice: 'my-voice' }]);\n });\n\n it('fallback list of strings is normalized', () => {\n const tts = makeTts({ fallback: ['cartesia/sonic', 'elevenlabs'] });\n expect(tts['opts'].fallback).toEqual([\n { model: 'cartesia/sonic', voice: '' },\n { model: 'elevenlabs', voice: '' },\n ]);\n });\n\n it('fallback single FallbackModel is normalized to list', () => {\n const tts = makeTts({ fallback: { model: 'cartesia/sonic', voice: 'narrator' } });\n expect(tts['opts'].fallback).toEqual([{ model: 'cartesia/sonic', voice: 'narrator' }]);\n });\n\n it('fallback with extraKwargs is preserved', () => {\n const tts = makeTts({\n fallback: {\n model: 'cartesia/sonic',\n voice: 'narrator',\n extraKwargs: { duration: 30.0, speed: 'fast' },\n },\n });\n expect(tts['opts'].fallback).toEqual([\n {\n model: 'cartesia/sonic',\n voice: 'narrator',\n extraKwargs: { duration: 30.0, speed: 'fast' },\n },\n ]);\n });\n\n it('fallback mixed list is normalized', () => {\n const tts = makeTts({\n fallback: [\n 'cartesia/sonic:voice1',\n { model: 'elevenlabs', voice: 'custom', extraKwargs: { speed: 'slow' } },\n 'rime/mist',\n ],\n });\n expect(tts['opts'].fallback).toEqual([\n { model: 'cartesia/sonic', voice: 'voice1' },\n { model: 'elevenlabs', voice: 'custom', extraKwargs: { speed: 'slow' } },\n { model: 'rime/mist', voice: '' },\n ]);\n });\n\n it('connOptions not given uses default', () => {\n const tts = makeTts();\n expect(tts['opts'].connOptions).toEqual(DEFAULT_API_CONNECT_OPTIONS);\n });\n\n it('connOptions custom timeout', () => {\n const custom: APIConnectOptions = { timeoutMs: 30000, maxRetry: 3, retryIntervalMs: 2000 };\n const tts = makeTts({ connOptions: custom });\n expect(tts['opts'].connOptions).toEqual(custom);\n expect(tts['opts'].connOptions!.timeoutMs).toBe(30000);\n });\n\n it('connOptions custom maxRetry', () => {\n const custom: APIConnectOptions = { timeoutMs: 10000, maxRetry: 5, retryIntervalMs: 2000 };\n const tts = makeTts({ connOptions: custom });\n expect(tts['opts'].connOptions).toEqual(custom);\n expect(tts['opts'].connOptions!.maxRetry).toBe(5);\n });\n\n it('connOptions full custom', () => {\n const custom: APIConnectOptions = { timeoutMs: 60000, maxRetry: 10, retryIntervalMs: 2000 };\n const tts = makeTts({ connOptions: custom });\n expect(tts['opts'].connOptions).toEqual(custom);\n expect(tts['opts'].connOptions!.timeoutMs).toBe(60000);\n expect(tts['opts'].connOptions!.maxRetry).toBe(10);\n expect(tts['opts'].connOptions!.retryIntervalMs).toBe(2000);\n });\n});\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/inference/tts.test.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2025 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport { beforeAll, describe, expect, it } from 'vitest';\nimport { normalizeLanguage } from '../language.js';\nimport { initializeLogger } from '../log.js';\nimport { type APIConnectOptions, DEFAULT_API_CONNECT_OPTIONS } from '../types.js';\nimport { TTS, type TTSFallbackModel, normalizeTTSFallback, parseTTSModelString } from './tts.js';\n\nbeforeAll(() => {\n initializeLogger({ level: 'silent', pretty: false });\n});\n\n/** Helper to create TTS with required credentials. */\nfunction makeTts(overrides: Record<string, unknown> = {}) {\n const defaults = {\n model: 'cartesia/sonic' as const,\n apiKey: 'test-key',\n apiSecret: 'test-secret',\n baseURL: 'https://example.livekit.cloud',\n };\n return new TTS({ ...defaults, ...overrides });\n}\n\ndescribe('parseTTSModelString', () => {\n it('simple model without voice', () => {\n const [model, voice] = parseTTSModelString('cartesia');\n expect(model).toBe('cartesia');\n expect(voice).toBeUndefined();\n });\n\n it('model with voice suffix', () => {\n const [model, voice] = parseTTSModelString('cartesia:my-voice-id');\n expect(model).toBe('cartesia');\n expect(voice).toBe('my-voice-id');\n });\n\n it('provider/model format without voice', () => {\n const [model, voice] = parseTTSModelString('cartesia/sonic');\n expect(model).toBe('cartesia/sonic');\n expect(voice).toBeUndefined();\n });\n\n it('provider/model format with voice', () => {\n const [model, voice] = parseTTSModelString('cartesia/sonic:my-voice-id');\n expect(model).toBe('cartesia/sonic');\n expect(voice).toBe('my-voice-id');\n });\n\n it.each([\n ['elevenlabs/eleven_flash_v2:voice123', 'elevenlabs/eleven_flash_v2', 'voice123'],\n ['rime:speaker-a', 'rime', 'speaker-a'],\n ['rime/mist:narrator', 'rime/mist', 'narrator'],\n ['inworld/inworld-tts-1:character', 'inworld/inworld-tts-1', 'character'],\n ['cartesia/sonic-turbo:deep-voice', 'cartesia/sonic-turbo', 'deep-voice'],\n ])('various providers and voices: %s', (modelStr, expectedModel, expectedVoice) => {\n const [model, voice] = parseTTSModelString(modelStr);\n expect(model).toBe(expectedModel);\n expect(voice).toBe(expectedVoice);\n });\n\n it('empty voice after colon', () => {\n const [model, voice] = parseTTSModelString('cartesia/sonic:');\n expect(model).toBe('cartesia/sonic');\n expect(voice).toBe('');\n });\n});\n\ndescribe('normalizeTTSFallback', () => {\n it('single string model', () => {\n const result = normalizeTTSFallback('cartesia/sonic');\n expect(result).toEqual([{ model: 'cartesia/sonic', voice: '' }]);\n });\n\n it('single string model with voice', () => {\n const result = normalizeTTSFallback('cartesia/sonic:my-voice');\n expect(result).toEqual([{ model: 'cartesia/sonic', voice: 'my-voice' }]);\n });\n\n it('single FallbackModel dict', () => {\n const fallback: TTSFallbackModel = { model: 'cartesia/sonic', voice: 'narrator' };\n const result = normalizeTTSFallback(fallback);\n expect(result).toEqual([{ model: 'cartesia/sonic', voice: 'narrator' }]);\n });\n\n it('list of string models', () => {\n const result = normalizeTTSFallback(['cartesia/sonic', 'elevenlabs/eleven_flash_v2']);\n expect(result).toEqual([\n { model: 'cartesia/sonic', voice: '' },\n { model: 'elevenlabs/eleven_flash_v2', voice: '' },\n ]);\n });\n\n it('list of string models with voices', () => {\n const result = normalizeTTSFallback(['cartesia/sonic:voice1', 'elevenlabs:voice2']);\n expect(result).toEqual([\n { model: 'cartesia/sonic', voice: 'voice1' },\n { model: 'elevenlabs', voice: 'voice2' },\n ]);\n });\n\n it('list of FallbackModel dicts', () => {\n const fallbacks: TTSFallbackModel[] = [\n { model: 'cartesia/sonic', voice: 'narrator' },\n { model: 'elevenlabs', voice: '' },\n ];\n const result = normalizeTTSFallback(fallbacks);\n expect(result).toEqual([\n { model: 'cartesia/sonic', voice: 'narrator' },\n { model: 'elevenlabs', voice: '' },\n ]);\n });\n\n it('mixed list of strings and dicts', () => {\n const result = normalizeTTSFallback([\n 'cartesia/sonic:voice1',\n { model: 'elevenlabs/eleven_flash_v2', voice: 'custom' } as TTSFallbackModel,\n 'rime/mist',\n ]);\n expect(result).toEqual([\n { model: 'cartesia/sonic', voice: 'voice1' },\n { model: 'elevenlabs/eleven_flash_v2', voice: 'custom' },\n { model: 'rime/mist', voice: '' },\n ]);\n });\n\n it('FallbackModel with extraKwargs is preserved', () => {\n const fallback: TTSFallbackModel = {\n model: 'cartesia/sonic',\n voice: 'narrator',\n extraKwargs: { duration: 30.0, speed: 'fast' },\n };\n const result = normalizeTTSFallback(fallback);\n expect(result).toEqual([\n {\n model: 'cartesia/sonic',\n voice: 'narrator',\n extraKwargs: { duration: 30.0, speed: 'fast' },\n },\n ]);\n });\n\n it('list with extraKwargs preserved', () => {\n const result = normalizeTTSFallback([\n { model: 'cartesia/sonic', voice: 'v1', extraKwargs: { speed: 'slow' } } as TTSFallbackModel,\n 'elevenlabs:voice2',\n { model: 'rime/mist', voice: '', extraKwargs: { custom: true } } as TTSFallbackModel,\n ]);\n expect(result).toEqual([\n { model: 'cartesia/sonic', voice: 'v1', extraKwargs: { speed: 'slow' } },\n { model: 'elevenlabs', voice: 'voice2' },\n { model: 'rime/mist', voice: '', extraKwargs: { custom: true } },\n ]);\n });\n\n it('empty list returns empty list', () => {\n const result = normalizeTTSFallback([]);\n expect(result).toEqual([]);\n });\n\n it('FallbackModel with empty voice', () => {\n const fallback: TTSFallbackModel = { model: 'cartesia/sonic', voice: '' };\n const result = normalizeTTSFallback(fallback);\n expect(result).toEqual([{ model: 'cartesia/sonic', voice: '' }]);\n });\n});\n\ndescribe('TTS constructor fallback and connOptions', () => {\n it('normalizes language in constructor', () => {\n const tts = makeTts({ language: 'english' });\n expect(tts['opts'].language).toBe('en');\n });\n\n it('normalizes updated language values', () => {\n const tts = makeTts();\n tts.updateOptions({ language: 'en_US' });\n expect(tts['opts'].language).toBe(normalizeLanguage('en_US'));\n });\n\n it('fallback not given defaults to undefined', () => {\n const tts = makeTts();\n expect(tts['opts'].fallback).toBeUndefined();\n });\n\n it('fallback single string is normalized', () => {\n const tts = makeTts({ fallback: 'elevenlabs/eleven_flash_v2' });\n expect(tts['opts'].fallback).toEqual([{ model: 'elevenlabs/eleven_flash_v2', voice: '' }]);\n });\n\n it('fallback single string with voice is normalized', () => {\n const tts = makeTts({ fallback: 'cartesia/sonic:my-voice' });\n expect(tts['opts'].fallback).toEqual([{ model: 'cartesia/sonic', voice: 'my-voice' }]);\n });\n\n it('fallback list of strings is normalized', () => {\n const tts = makeTts({ fallback: ['cartesia/sonic', 'elevenlabs'] });\n expect(tts['opts'].fallback).toEqual([\n { model: 'cartesia/sonic', voice: '' },\n { model: 'elevenlabs', voice: '' },\n ]);\n });\n\n it('fallback single FallbackModel is normalized to list', () => {\n const tts = makeTts({ fallback: { model: 'cartesia/sonic', voice: 'narrator' } });\n expect(tts['opts'].fallback).toEqual([{ model: 'cartesia/sonic', voice: 'narrator' }]);\n });\n\n it('fallback with extraKwargs is preserved', () => {\n const tts = makeTts({\n fallback: {\n model: 'cartesia/sonic',\n voice: 'narrator',\n extraKwargs: { duration: 30.0, speed: 'fast' },\n },\n });\n expect(tts['opts'].fallback).toEqual([\n {\n model: 'cartesia/sonic',\n voice: 'narrator',\n extraKwargs: { duration: 30.0, speed: 'fast' },\n },\n ]);\n });\n\n it('fallback mixed list is normalized', () => {\n const tts = makeTts({\n fallback: [\n 'cartesia/sonic:voice1',\n { model: 'elevenlabs', voice: 'custom', extraKwargs: { speed: 'slow' } },\n 'rime/mist',\n ],\n });\n expect(tts['opts'].fallback).toEqual([\n { model: 'cartesia/sonic', voice: 'voice1' },\n { model: 'elevenlabs', voice: 'custom', extraKwargs: { speed: 'slow' } },\n { model: 'rime/mist', voice: '' },\n ]);\n });\n\n it('connOptions not given uses default', () => {\n const tts = makeTts();\n expect(tts['opts'].connOptions).toEqual(DEFAULT_API_CONNECT_OPTIONS);\n });\n\n it('connOptions custom timeout', () => {\n const custom: APIConnectOptions = { timeoutMs: 30000, maxRetry: 3, retryIntervalMs: 2000 };\n const tts = makeTts({ connOptions: custom });\n expect(tts['opts'].connOptions).toEqual(custom);\n expect(tts['opts'].connOptions!.timeoutMs).toBe(30000);\n });\n\n it('connOptions custom maxRetry', () => {\n const custom: APIConnectOptions = { timeoutMs: 10000, maxRetry: 5, retryIntervalMs: 2000 };\n const tts = makeTts({ connOptions: custom });\n expect(tts['opts'].connOptions).toEqual(custom);\n expect(tts['opts'].connOptions!.maxRetry).toBe(5);\n });\n\n it('connOptions full custom', () => {\n const custom: APIConnectOptions = { timeoutMs: 60000, maxRetry: 10, retryIntervalMs: 2000 };\n const tts = makeTts({ connOptions: custom });\n expect(tts['opts'].connOptions).toEqual(custom);\n expect(tts['opts'].connOptions!.timeoutMs).toBe(60000);\n expect(tts['opts'].connOptions!.maxRetry).toBe(10);\n expect(tts['opts'].connOptions!.retryIntervalMs).toBe(2000);\n });\n});\n\ndescribe('TTS provider modelOptions parity', () => {\n it('preserves ElevenLabs inference model options', () => {\n const modelOptions = {\n speed: 1.2,\n stability: 0.5,\n similarity_boost: 0.8,\n enable_logging: false,\n };\n\n const tts = new TTS({\n model: 'elevenlabs/eleven_flash_v2_5' as const,\n apiKey: 'test-key',\n apiSecret: 'test-secret',\n baseURL: 'https://example.livekit.cloud',\n modelOptions,\n });\n\n expect(tts['opts'].modelOptions).toEqual(modelOptions);\n });\n\n it('accepts expanded Cartesia inference model options', () => {\n const modelOptions = {\n speed: 1.15,\n emotion: 'curious',\n add_timestamps: true,\n };\n\n const tts = new TTS({\n model: 'cartesia/sonic' as const,\n apiKey: 'test-key',\n apiSecret: 'test-secret',\n baseURL: 'https://example.livekit.cloud',\n modelOptions,\n });\n\n expect(tts['opts'].modelOptions).toEqual(modelOptions);\n });\n\n it('accepts Deepgram inference model options', () => {\n const modelOptions = { mip_opt_out: true };\n\n const tts = new TTS({\n model: 'deepgram/aura-2' as const,\n apiKey: 'test-key',\n apiSecret: 'test-secret',\n baseURL: 'https://example.livekit.cloud',\n modelOptions,\n });\n\n expect(tts['opts'].modelOptions).toEqual(modelOptions);\n });\n\n it('accepts Rime inference model options', () => {\n const modelOptions = {\n speed_alpha: 0.9,\n pause_between_brackets: true,\n };\n\n const tts = new TTS({\n model: 'rime/mistv2' as const,\n apiKey: 'test-key',\n apiSecret: 'test-secret',\n baseURL: 'https://example.livekit.cloud',\n modelOptions,\n });\n\n expect(tts['opts'].modelOptions).toEqual(modelOptions);\n });\n\n it('accepts Inworld inference model options', () => {\n const modelOptions = {\n timestamp_type: 'WORD' as const,\n apply_text_normalization: 'ON' as const,\n };\n\n const tts = new TTS({\n model: 'inworld/inworld-tts-1' as const,\n apiKey: 'test-key',\n apiSecret: 'test-secret',\n baseURL: 'https://example.livekit.cloud',\n modelOptions,\n });\n\n expect(tts['opts'].modelOptions).toEqual(modelOptions);\n });\n});\n"],"mappings":"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","names":[]}