@livekit/agents 0.6.4 → 0.7.0

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 (178) hide show
  1. package/dist/index.cjs +6 -1
  2. package/dist/index.cjs.map +1 -1
  3. package/dist/index.d.ts +3 -1
  4. package/dist/index.d.ts.map +1 -1
  5. package/dist/index.js +3 -0
  6. package/dist/index.js.map +1 -1
  7. package/dist/inference_runner.cjs +38 -0
  8. package/dist/inference_runner.cjs.map +1 -0
  9. package/dist/inference_runner.d.ts +11 -0
  10. package/dist/inference_runner.d.ts.map +1 -0
  11. package/dist/inference_runner.js +14 -0
  12. package/dist/inference_runner.js.map +1 -0
  13. package/dist/ipc/index.cjs +23 -0
  14. package/dist/ipc/index.cjs.map +1 -0
  15. package/dist/ipc/index.d.ts +2 -0
  16. package/dist/ipc/index.d.ts.map +1 -0
  17. package/dist/ipc/index.js +2 -0
  18. package/dist/ipc/index.js.map +1 -0
  19. package/dist/ipc/inference_executor.cjs +17 -0
  20. package/dist/ipc/inference_executor.cjs.map +1 -0
  21. package/dist/ipc/inference_executor.d.ts +4 -0
  22. package/dist/ipc/inference_executor.d.ts.map +1 -0
  23. package/dist/ipc/inference_executor.js +1 -0
  24. package/dist/ipc/inference_executor.js.map +1 -0
  25. package/dist/ipc/inference_proc_executor.cjs +97 -0
  26. package/dist/ipc/inference_proc_executor.cjs.map +1 -0
  27. package/dist/ipc/inference_proc_executor.d.ts +23 -0
  28. package/dist/ipc/inference_proc_executor.d.ts.map +1 -0
  29. package/dist/ipc/inference_proc_executor.js +72 -0
  30. package/dist/ipc/inference_proc_executor.js.map +1 -0
  31. package/dist/ipc/inference_proc_lazy_main.cjs +90 -0
  32. package/dist/ipc/inference_proc_lazy_main.cjs.map +1 -0
  33. package/dist/ipc/inference_proc_lazy_main.d.ts +2 -0
  34. package/dist/ipc/inference_proc_lazy_main.d.ts.map +1 -0
  35. package/dist/ipc/inference_proc_lazy_main.js +67 -0
  36. package/dist/ipc/inference_proc_lazy_main.js.map +1 -0
  37. package/dist/ipc/job_executor.cjs +8 -7
  38. package/dist/ipc/job_executor.cjs.map +1 -1
  39. package/dist/ipc/job_executor.d.ts +14 -15
  40. package/dist/ipc/job_executor.d.ts.map +1 -1
  41. package/dist/ipc/job_executor.js +7 -6
  42. package/dist/ipc/job_executor.js.map +1 -1
  43. package/dist/ipc/job_proc_executor.cjs +108 -0
  44. package/dist/ipc/job_proc_executor.cjs.map +1 -0
  45. package/dist/ipc/job_proc_executor.d.ts +19 -0
  46. package/dist/ipc/job_proc_executor.d.ts.map +1 -0
  47. package/dist/ipc/job_proc_executor.js +83 -0
  48. package/dist/ipc/job_proc_executor.js.map +1 -0
  49. package/dist/ipc/{job_main.cjs → job_proc_lazy_main.cjs} +41 -36
  50. package/dist/ipc/job_proc_lazy_main.cjs.map +1 -0
  51. package/dist/ipc/job_proc_lazy_main.d.ts +2 -0
  52. package/dist/ipc/job_proc_lazy_main.d.ts.map +1 -0
  53. package/dist/ipc/{job_main.js → job_proc_lazy_main.js} +41 -11
  54. package/dist/ipc/job_proc_lazy_main.js.map +1 -0
  55. package/dist/ipc/message.cjs.map +1 -1
  56. package/dist/ipc/message.d.ts +17 -0
  57. package/dist/ipc/message.d.ts.map +1 -1
  58. package/dist/ipc/proc_pool.cjs +30 -4
  59. package/dist/ipc/proc_pool.cjs.map +1 -1
  60. package/dist/ipc/proc_pool.d.ts +5 -1
  61. package/dist/ipc/proc_pool.d.ts.map +1 -1
  62. package/dist/ipc/proc_pool.js +30 -4
  63. package/dist/ipc/proc_pool.js.map +1 -1
  64. package/dist/ipc/{proc_job_executor.cjs → supervised_proc.cjs} +57 -45
  65. package/dist/ipc/supervised_proc.cjs.map +1 -0
  66. package/dist/ipc/supervised_proc.d.ts +30 -0
  67. package/dist/ipc/supervised_proc.d.ts.map +1 -0
  68. package/dist/ipc/{proc_job_executor.js → supervised_proc.js} +53 -31
  69. package/dist/ipc/supervised_proc.js.map +1 -0
  70. package/dist/job.cjs +18 -1
  71. package/dist/job.cjs.map +1 -1
  72. package/dist/job.d.ts +9 -1
  73. package/dist/job.d.ts.map +1 -1
  74. package/dist/job.js +17 -1
  75. package/dist/job.js.map +1 -1
  76. package/dist/multimodal/agent_playout.cjs +13 -14
  77. package/dist/multimodal/agent_playout.cjs.map +1 -1
  78. package/dist/multimodal/agent_playout.d.ts +4 -4
  79. package/dist/multimodal/agent_playout.d.ts.map +1 -1
  80. package/dist/multimodal/agent_playout.js +13 -14
  81. package/dist/multimodal/agent_playout.js.map +1 -1
  82. package/dist/multimodal/multimodal_agent.cjs +12 -8
  83. package/dist/multimodal/multimodal_agent.cjs.map +1 -1
  84. package/dist/multimodal/multimodal_agent.d.ts.map +1 -1
  85. package/dist/multimodal/multimodal_agent.js +13 -9
  86. package/dist/multimodal/multimodal_agent.js.map +1 -1
  87. package/dist/pipeline/agent_output.cjs +20 -4
  88. package/dist/pipeline/agent_output.cjs.map +1 -1
  89. package/dist/pipeline/agent_output.d.ts +4 -2
  90. package/dist/pipeline/agent_output.d.ts.map +1 -1
  91. package/dist/pipeline/agent_output.js +20 -4
  92. package/dist/pipeline/agent_output.js.map +1 -1
  93. package/dist/pipeline/agent_playout.cjs +9 -3
  94. package/dist/pipeline/agent_playout.cjs.map +1 -1
  95. package/dist/pipeline/agent_playout.d.ts +4 -2
  96. package/dist/pipeline/agent_playout.d.ts.map +1 -1
  97. package/dist/pipeline/agent_playout.js +9 -3
  98. package/dist/pipeline/agent_playout.js.map +1 -1
  99. package/dist/pipeline/human_input.cjs +6 -0
  100. package/dist/pipeline/human_input.cjs.map +1 -1
  101. package/dist/pipeline/human_input.d.ts +3 -1
  102. package/dist/pipeline/human_input.d.ts.map +1 -1
  103. package/dist/pipeline/human_input.js +6 -0
  104. package/dist/pipeline/human_input.js.map +1 -1
  105. package/dist/pipeline/pipeline_agent.cjs +79 -12
  106. package/dist/pipeline/pipeline_agent.cjs.map +1 -1
  107. package/dist/pipeline/pipeline_agent.d.ts +8 -0
  108. package/dist/pipeline/pipeline_agent.d.ts.map +1 -1
  109. package/dist/pipeline/pipeline_agent.js +79 -12
  110. package/dist/pipeline/pipeline_agent.js.map +1 -1
  111. package/dist/stt/stream_adapter.cjs +16 -4
  112. package/dist/stt/stream_adapter.cjs.map +1 -1
  113. package/dist/stt/stream_adapter.d.ts.map +1 -1
  114. package/dist/stt/stream_adapter.js +16 -4
  115. package/dist/stt/stream_adapter.js.map +1 -1
  116. package/dist/tokenize/basic/basic.cjs +2 -0
  117. package/dist/tokenize/basic/basic.cjs.map +1 -1
  118. package/dist/tokenize/basic/basic.d.ts +2 -0
  119. package/dist/tokenize/basic/basic.d.ts.map +1 -1
  120. package/dist/tokenize/basic/basic.js +1 -0
  121. package/dist/tokenize/basic/basic.js.map +1 -1
  122. package/dist/tokenize/basic/index.cjs +2 -0
  123. package/dist/tokenize/basic/index.cjs.map +1 -1
  124. package/dist/tokenize/basic/index.d.ts +1 -1
  125. package/dist/tokenize/basic/index.d.ts.map +1 -1
  126. package/dist/tokenize/basic/index.js +8 -1
  127. package/dist/tokenize/basic/index.js.map +1 -1
  128. package/dist/tokenize/token_stream.cjs +5 -3
  129. package/dist/tokenize/token_stream.cjs.map +1 -1
  130. package/dist/tokenize/token_stream.d.ts.map +1 -1
  131. package/dist/tokenize/token_stream.js +5 -3
  132. package/dist/tokenize/token_stream.js.map +1 -1
  133. package/dist/transcription.cjs +203 -86
  134. package/dist/transcription.cjs.map +1 -1
  135. package/dist/transcription.d.ts +24 -17
  136. package/dist/transcription.d.ts.map +1 -1
  137. package/dist/transcription.js +201 -85
  138. package/dist/transcription.js.map +1 -1
  139. package/dist/worker.cjs +42 -9
  140. package/dist/worker.cjs.map +1 -1
  141. package/dist/worker.d.ts +5 -1
  142. package/dist/worker.d.ts.map +1 -1
  143. package/dist/worker.js +42 -9
  144. package/dist/worker.js.map +1 -1
  145. package/package.json +3 -3
  146. package/src/index.ts +3 -1
  147. package/src/inference_runner.ts +19 -0
  148. package/src/ipc/index.ts +5 -0
  149. package/src/ipc/inference_executor.ts +7 -0
  150. package/src/ipc/inference_proc_executor.ts +93 -0
  151. package/src/ipc/inference_proc_lazy_main.ts +86 -0
  152. package/src/ipc/job_executor.ts +15 -17
  153. package/src/ipc/job_proc_executor.ts +112 -0
  154. package/src/ipc/{job_main.ts → job_proc_lazy_main.ts} +44 -14
  155. package/src/ipc/message.ts +14 -1
  156. package/src/ipc/proc_pool.ts +33 -3
  157. package/src/ipc/{proc_job_executor.ts → supervised_proc.ts} +77 -29
  158. package/src/job.ts +21 -0
  159. package/src/multimodal/agent_playout.ts +14 -16
  160. package/src/multimodal/multimodal_agent.ts +13 -9
  161. package/src/pipeline/agent_output.ts +34 -5
  162. package/src/pipeline/agent_playout.ts +10 -1
  163. package/src/pipeline/human_input.ts +8 -0
  164. package/src/pipeline/pipeline_agent.ts +96 -11
  165. package/src/stt/stream_adapter.ts +17 -5
  166. package/src/tokenize/basic/basic.ts +2 -0
  167. package/src/tokenize/basic/index.ts +7 -1
  168. package/src/tokenize/token_stream.ts +6 -3
  169. package/src/transcription.ts +270 -96
  170. package/src/worker.ts +42 -5
  171. package/dist/ipc/job_main.cjs.map +0 -1
  172. package/dist/ipc/job_main.d.ts +0 -8
  173. package/dist/ipc/job_main.d.ts.map +0 -1
  174. package/dist/ipc/job_main.js.map +0 -1
  175. package/dist/ipc/proc_job_executor.cjs.map +0 -1
  176. package/dist/ipc/proc_job_executor.d.ts +0 -15
  177. package/dist/ipc/proc_job_executor.d.ts.map +0 -1
  178. package/dist/ipc/proc_job_executor.js.map +0 -1
@@ -1,4 +1,5 @@
1
1
  import * as tokenizer from '../tokenizer.js';
2
+ import { splitWords } from './word.js';
2
3
  export declare class SentenceTokenizer extends tokenizer.SentenceTokenizer {
3
4
  #private;
4
5
  constructor(language?: string, minSentenceLength?: number, streamContextLength?: number);
@@ -12,5 +13,6 @@ export declare class WordTokenizer extends tokenizer.WordTokenizer {
12
13
  stream(language?: string): tokenizer.WordStream;
13
14
  }
14
15
  export declare const hyphenateWord: (word: string) => string[];
16
+ export { splitWords };
15
17
  export declare const tokenizeParagraphs: (text: string) => string[];
16
18
  //# sourceMappingURL=basic.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"basic.d.ts","sourceRoot":"","sources":["../../../src/tokenize/basic/basic.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,SAAS,MAAM,iBAAiB,CAAC;AAY7C,qBAAa,iBAAkB,SAAQ,SAAS,CAAC,iBAAiB;;gBAGpD,QAAQ,SAAU,EAAE,iBAAiB,SAAK,EAAE,mBAAmB,SAAK;IAUhF,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE;IAKnD,MAAM,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC,cAAc;CAOpD;AAED,qBAAa,aAAc,SAAQ,SAAS,CAAC,aAAa;;gBAG5C,iBAAiB,UAAO;IAMpC,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE;IAKnD,MAAM,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC,UAAU;CAOhD;AAED,eAAO,MAAM,aAAa,SAAU,MAAM,KAAG,MAAM,EAElD,CAAC;AAEF,eAAO,MAAM,kBAAkB,SAAU,MAAM,KAAG,MAAM,EAEvD,CAAC"}
1
+ {"version":3,"file":"basic.d.ts","sourceRoot":"","sources":["../../../src/tokenize/basic/basic.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,SAAS,MAAM,iBAAiB,CAAC;AAI7C,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAQvC,qBAAa,iBAAkB,SAAQ,SAAS,CAAC,iBAAiB;;gBAGpD,QAAQ,SAAU,EAAE,iBAAiB,SAAK,EAAE,mBAAmB,SAAK;IAUhF,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE;IAKnD,MAAM,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC,cAAc;CAOpD;AAED,qBAAa,aAAc,SAAQ,SAAS,CAAC,aAAa;;gBAG5C,iBAAiB,UAAO;IAMpC,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE;IAKnD,MAAM,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC,UAAU;CAOhD;AAED,eAAO,MAAM,aAAa,SAAU,MAAM,KAAG,MAAM,EAElD,CAAC;AAEF,OAAO,EAAE,UAAU,EAAE,CAAC;AAEtB,eAAO,MAAM,kBAAkB,SAAU,MAAM,KAAG,MAAM,EAEvD,CAAC"}
@@ -56,6 +56,7 @@ export {
56
56
  SentenceTokenizer,
57
57
  WordTokenizer,
58
58
  hyphenateWord,
59
+ splitWords,
59
60
  tokenizeParagraphs
60
61
  };
61
62
  //# sourceMappingURL=basic.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/tokenize/basic/basic.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport { BufferedSentenceStream, BufferedWordStream } from '../token_stream.js';\nimport * as tokenizer from '../tokenizer.js';\nimport { hyphenator } from './hyphenator.js';\nimport { splitParagraphs } from './paragraph.js';\nimport { splitSentences } from './sentence.js';\nimport { splitWords } from './word.js';\n\ninterface TokenizerOptions {\n language: string;\n minSentenceLength: number;\n streamContextLength: number;\n}\n\nexport class SentenceTokenizer extends tokenizer.SentenceTokenizer {\n #config: TokenizerOptions;\n\n constructor(language = 'en-US', minSentenceLength = 20, streamContextLength = 10) {\n super();\n this.#config = {\n language,\n minSentenceLength,\n streamContextLength,\n };\n }\n\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n tokenize(text: string, language?: string): string[] {\n return splitSentences(text, this.#config.minSentenceLength).map((tok) => tok[0]);\n }\n\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n stream(language?: string): tokenizer.SentenceStream {\n return new BufferedSentenceStream(\n (text: string) => splitSentences(text, this.#config.minSentenceLength),\n this.#config.minSentenceLength,\n this.#config.streamContextLength,\n );\n }\n}\n\nexport class WordTokenizer extends tokenizer.WordTokenizer {\n #ignorePunctuation: boolean;\n\n constructor(ignorePunctuation = true) {\n super();\n this.#ignorePunctuation = ignorePunctuation;\n }\n\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n tokenize(text: string, language?: string): string[] {\n return splitWords(text, this.#ignorePunctuation).map((tok) => tok[0]);\n }\n\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n stream(language?: string): tokenizer.WordStream {\n return new BufferedWordStream(\n (text: string) => splitWords(text, this.#ignorePunctuation),\n 1,\n 1,\n );\n }\n}\n\nexport const hyphenateWord = (word: string): string[] => {\n return hyphenator.hyphenateWord(word);\n};\n\nexport const tokenizeParagraphs = (text: string): string[] => {\n return splitParagraphs(text).map((tok) => tok[0]);\n};\n"],"mappings":"AAGA,SAAS,wBAAwB,0BAA0B;AAC3D,YAAY,eAAe;AAC3B,SAAS,kBAAkB;AAC3B,SAAS,uBAAuB;AAChC,SAAS,sBAAsB;AAC/B,SAAS,kBAAkB;AAQpB,MAAM,0BAA0B,UAAU,kBAAkB;AAAA,EACjE;AAAA,EAEA,YAAY,WAAW,SAAS,oBAAoB,IAAI,sBAAsB,IAAI;AAChF,UAAM;AACN,SAAK,UAAU;AAAA,MACb;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA;AAAA,EAGA,SAAS,MAAc,UAA6B;AAClD,WAAO,eAAe,MAAM,KAAK,QAAQ,iBAAiB,EAAE,IAAI,CAAC,QAAQ,IAAI,CAAC,CAAC;AAAA,EACjF;AAAA;AAAA,EAGA,OAAO,UAA6C;AAClD,WAAO,IAAI;AAAA,MACT,CAAC,SAAiB,eAAe,MAAM,KAAK,QAAQ,iBAAiB;AAAA,MACrE,KAAK,QAAQ;AAAA,MACb,KAAK,QAAQ;AAAA,IACf;AAAA,EACF;AACF;AAEO,MAAM,sBAAsB,UAAU,cAAc;AAAA,EACzD;AAAA,EAEA,YAAY,oBAAoB,MAAM;AACpC,UAAM;AACN,SAAK,qBAAqB;AAAA,EAC5B;AAAA;AAAA,EAGA,SAAS,MAAc,UAA6B;AAClD,WAAO,WAAW,MAAM,KAAK,kBAAkB,EAAE,IAAI,CAAC,QAAQ,IAAI,CAAC,CAAC;AAAA,EACtE;AAAA;AAAA,EAGA,OAAO,UAAyC;AAC9C,WAAO,IAAI;AAAA,MACT,CAAC,SAAiB,WAAW,MAAM,KAAK,kBAAkB;AAAA,MAC1D;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACF;AAEO,MAAM,gBAAgB,CAAC,SAA2B;AACvD,SAAO,WAAW,cAAc,IAAI;AACtC;AAEO,MAAM,qBAAqB,CAAC,SAA2B;AAC5D,SAAO,gBAAgB,IAAI,EAAE,IAAI,CAAC,QAAQ,IAAI,CAAC,CAAC;AAClD;","names":[]}
1
+ {"version":3,"sources":["../../../src/tokenize/basic/basic.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport { BufferedSentenceStream, BufferedWordStream } from '../token_stream.js';\nimport * as tokenizer from '../tokenizer.js';\nimport { hyphenator } from './hyphenator.js';\nimport { splitParagraphs } from './paragraph.js';\nimport { splitSentences } from './sentence.js';\nimport { splitWords } from './word.js';\n\ninterface TokenizerOptions {\n language: string;\n minSentenceLength: number;\n streamContextLength: number;\n}\n\nexport class SentenceTokenizer extends tokenizer.SentenceTokenizer {\n #config: TokenizerOptions;\n\n constructor(language = 'en-US', minSentenceLength = 20, streamContextLength = 10) {\n super();\n this.#config = {\n language,\n minSentenceLength,\n streamContextLength,\n };\n }\n\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n tokenize(text: string, language?: string): string[] {\n return splitSentences(text, this.#config.minSentenceLength).map((tok) => tok[0]);\n }\n\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n stream(language?: string): tokenizer.SentenceStream {\n return new BufferedSentenceStream(\n (text: string) => splitSentences(text, this.#config.minSentenceLength),\n this.#config.minSentenceLength,\n this.#config.streamContextLength,\n );\n }\n}\n\nexport class WordTokenizer extends tokenizer.WordTokenizer {\n #ignorePunctuation: boolean;\n\n constructor(ignorePunctuation = true) {\n super();\n this.#ignorePunctuation = ignorePunctuation;\n }\n\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n tokenize(text: string, language?: string): string[] {\n return splitWords(text, this.#ignorePunctuation).map((tok) => tok[0]);\n }\n\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n stream(language?: string): tokenizer.WordStream {\n return new BufferedWordStream(\n (text: string) => splitWords(text, this.#ignorePunctuation),\n 1,\n 1,\n );\n }\n}\n\nexport const hyphenateWord = (word: string): string[] => {\n return hyphenator.hyphenateWord(word);\n};\n\nexport { splitWords };\n\nexport const tokenizeParagraphs = (text: string): string[] => {\n return splitParagraphs(text).map((tok) => tok[0]);\n};\n"],"mappings":"AAGA,SAAS,wBAAwB,0BAA0B;AAC3D,YAAY,eAAe;AAC3B,SAAS,kBAAkB;AAC3B,SAAS,uBAAuB;AAChC,SAAS,sBAAsB;AAC/B,SAAS,kBAAkB;AAQpB,MAAM,0BAA0B,UAAU,kBAAkB;AAAA,EACjE;AAAA,EAEA,YAAY,WAAW,SAAS,oBAAoB,IAAI,sBAAsB,IAAI;AAChF,UAAM;AACN,SAAK,UAAU;AAAA,MACb;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA;AAAA,EAGA,SAAS,MAAc,UAA6B;AAClD,WAAO,eAAe,MAAM,KAAK,QAAQ,iBAAiB,EAAE,IAAI,CAAC,QAAQ,IAAI,CAAC,CAAC;AAAA,EACjF;AAAA;AAAA,EAGA,OAAO,UAA6C;AAClD,WAAO,IAAI;AAAA,MACT,CAAC,SAAiB,eAAe,MAAM,KAAK,QAAQ,iBAAiB;AAAA,MACrE,KAAK,QAAQ;AAAA,MACb,KAAK,QAAQ;AAAA,IACf;AAAA,EACF;AACF;AAEO,MAAM,sBAAsB,UAAU,cAAc;AAAA,EACzD;AAAA,EAEA,YAAY,oBAAoB,MAAM;AACpC,UAAM;AACN,SAAK,qBAAqB;AAAA,EAC5B;AAAA;AAAA,EAGA,SAAS,MAAc,UAA6B;AAClD,WAAO,WAAW,MAAM,KAAK,kBAAkB,EAAE,IAAI,CAAC,QAAQ,IAAI,CAAC,CAAC;AAAA,EACtE;AAAA;AAAA,EAGA,OAAO,UAAyC;AAC9C,WAAO,IAAI;AAAA,MACT,CAAC,SAAiB,WAAW,MAAM,KAAK,kBAAkB;AAAA,MAC1D;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACF;AAEO,MAAM,gBAAgB,CAAC,SAA2B;AACvD,SAAO,WAAW,cAAc,IAAI;AACtC;AAIO,MAAM,qBAAqB,CAAC,SAA2B;AAC5D,SAAO,gBAAgB,IAAI,EAAE,IAAI,CAAC,QAAQ,IAAI,CAAC,CAAC;AAClD;","names":[]}
@@ -21,6 +21,7 @@ __export(basic_exports, {
21
21
  SentenceTokenizer: () => import_basic.SentenceTokenizer,
22
22
  WordTokenizer: () => import_basic.WordTokenizer,
23
23
  hyphenateWord: () => import_basic.hyphenateWord,
24
+ splitWords: () => import_basic.splitWords,
24
25
  tokenizeParagraphs: () => import_basic.tokenizeParagraphs
25
26
  });
26
27
  module.exports = __toCommonJS(basic_exports);
@@ -30,6 +31,7 @@ var import_basic = require("./basic.cjs");
30
31
  SentenceTokenizer,
31
32
  WordTokenizer,
32
33
  hyphenateWord,
34
+ splitWords,
33
35
  tokenizeParagraphs
34
36
  });
35
37
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/tokenize/basic/index.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\n\nexport { SentenceTokenizer, WordTokenizer, tokenizeParagraphs, hyphenateWord } from './basic.js';\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAIA,mBAAoF;","names":[]}
1
+ {"version":3,"sources":["../../../src/tokenize/basic/index.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\n\nexport {\n SentenceTokenizer,\n WordTokenizer,\n tokenizeParagraphs,\n hyphenateWord,\n splitWords,\n} from './basic.js';\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAIA,mBAMO;","names":[]}
@@ -1,2 +1,2 @@
1
- export { SentenceTokenizer, WordTokenizer, tokenizeParagraphs, hyphenateWord } from './basic.js';
1
+ export { SentenceTokenizer, WordTokenizer, tokenizeParagraphs, hyphenateWord, splitWords, } from './basic.js';
2
2
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/tokenize/basic/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,iBAAiB,EAAE,aAAa,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/tokenize/basic/index.ts"],"names":[],"mappings":"AAIA,OAAO,EACL,iBAAiB,EACjB,aAAa,EACb,kBAAkB,EAClB,aAAa,EACb,UAAU,GACX,MAAM,YAAY,CAAC"}
@@ -1,8 +1,15 @@
1
- import { SentenceTokenizer, WordTokenizer, tokenizeParagraphs, hyphenateWord } from "./basic.js";
1
+ import {
2
+ SentenceTokenizer,
3
+ WordTokenizer,
4
+ tokenizeParagraphs,
5
+ hyphenateWord,
6
+ splitWords
7
+ } from "./basic.js";
2
8
  export {
3
9
  SentenceTokenizer,
4
10
  WordTokenizer,
5
11
  hyphenateWord,
12
+ splitWords,
6
13
  tokenizeParagraphs
7
14
  };
8
15
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/tokenize/basic/index.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\n\nexport { SentenceTokenizer, WordTokenizer, tokenizeParagraphs, hyphenateWord } from './basic.js';\n"],"mappings":"AAIA,SAAS,mBAAmB,eAAe,oBAAoB,qBAAqB;","names":[]}
1
+ {"version":3,"sources":["../../../src/tokenize/basic/index.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\n\nexport {\n SentenceTokenizer,\n WordTokenizer,\n tokenizeParagraphs,\n hyphenateWord,\n splitWords,\n} from './basic.js';\n"],"mappings":"AAIA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;","names":[]}
@@ -54,9 +54,11 @@ class BufferedTokenStream {
54
54
  if (tokens.length <= 1) break;
55
55
  if (this.#outBuf) this.#outBuf += " ";
56
56
  const tok = tokens.shift();
57
- let tokText = tok;
58
- if (tok.length > 1 && typeof tok[1] === "number") {
57
+ let tokText;
58
+ if (Array.isArray(tok)) {
59
59
  tokText = tok[0];
60
+ } else {
61
+ tokText = tok;
60
62
  }
61
63
  this.#outBuf += tokText;
62
64
  if (this.#outBuf.length >= this.#minTokenLength) {
@@ -79,7 +81,7 @@ class BufferedTokenStream {
79
81
  const tokens = this.#func(this.#inBuf);
80
82
  if (tokens) {
81
83
  if (this.#outBuf) this.#outBuf += " ";
82
- if (typeof tokens[0] !== "string") {
84
+ if (Array.isArray(tokens[0])) {
83
85
  this.#outBuf += tokens.map((tok) => tok[0]).join(" ");
84
86
  } else {
85
87
  this.#outBuf += tokens.join(" ");
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/tokenize/token_stream.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport { randomUUID } from 'node:crypto';\nimport { AsyncIterableQueue } from '../utils.js';\nimport type { TokenData } from './tokenizer.js';\nimport { SentenceStream, WordStream } from './tokenizer.js';\n\ntype TokenizeFunc = (x: string) => string[] | [string, number, number][];\n\nexport class BufferedTokenStream implements AsyncIterableIterator<TokenData> {\n protected queue = new AsyncIterableQueue<TokenData>();\n protected closed = false;\n\n #func: TokenizeFunc;\n #minTokenLength: number;\n #minContextLength: number;\n #bufTokens: string[] = [];\n #inBuf = '';\n #outBuf = '';\n #currentSegmentId: string;\n\n constructor(func: TokenizeFunc, minTokenLength: number, minContextLength: number) {\n this.#func = func;\n this.#minTokenLength = minTokenLength;\n this.#minContextLength = minContextLength;\n\n this.#currentSegmentId = randomUUID();\n }\n\n /** Push a string of text into the token stream */\n pushText(text: string) {\n if (this.closed) {\n throw new Error('Stream is closed');\n }\n\n this.#inBuf += text;\n if (this.#inBuf.length < this.#minContextLength) return;\n\n while (true) {\n const tokens = this.#func(this.#inBuf);\n if (tokens.length <= 1) break;\n\n if (this.#outBuf) this.#outBuf += ' ';\n\n const tok = tokens.shift()!;\n let tokText = tok as string;\n if (tok.length > 1 && typeof tok[1] === 'number') {\n tokText = tok[0];\n }\n\n this.#outBuf += tokText;\n if (this.#outBuf.length >= this.#minTokenLength) {\n this.queue.put({ token: this.#outBuf, segmentId: this.#currentSegmentId });\n this.#outBuf = '';\n }\n\n if (typeof tok! !== 'string') {\n this.#inBuf = this.#inBuf.slice(tok![2]);\n } else {\n this.#inBuf = this.#inBuf\n .slice(Math.max(0, this.#inBuf.indexOf(tok)) + tok.length)\n .trimStart();\n }\n }\n }\n\n /** Flush the stream, causing it to process all pending text */\n flush() {\n if (this.closed) {\n throw new Error('Stream is closed');\n }\n\n if (this.#inBuf || this.#outBuf) {\n const tokens = this.#func(this.#inBuf);\n if (tokens) {\n if (this.#outBuf) this.#outBuf += ' ';\n\n if (typeof tokens[0] !== 'string') {\n this.#outBuf += tokens.map((tok) => tok[0]).join(' ');\n } else {\n this.#outBuf += tokens.join(' ');\n }\n }\n\n if (this.#outBuf) {\n this.queue.put({ token: this.#outBuf, segmentId: this.#currentSegmentId });\n }\n\n this.#currentSegmentId = randomUUID();\n }\n\n this.#inBuf = '';\n this.#outBuf = '';\n }\n\n /** Mark the input as ended and forbid additional pushes */\n endInput() {\n if (this.closed) {\n throw new Error('Stream is closed');\n }\n this.flush();\n this.close();\n }\n\n next(): Promise<IteratorResult<TokenData>> {\n return this.queue.next();\n }\n\n /** Close both the input and output of the token stream */\n close() {\n this.queue.close();\n this.closed = true;\n }\n\n [Symbol.asyncIterator](): BufferedTokenStream {\n return this;\n }\n}\n\nexport class BufferedSentenceStream extends SentenceStream {\n #stream: BufferedTokenStream;\n\n constructor(func: TokenizeFunc, minTokenLength: number, minContextLength: number) {\n super();\n this.#stream = new BufferedTokenStream(func, minTokenLength, minContextLength);\n }\n\n pushText(text: string) {\n this.#stream.pushText(text);\n }\n\n flush() {\n this.#stream.flush();\n }\n\n close() {\n super.close();\n this.#stream.close();\n }\n\n next(): Promise<IteratorResult<TokenData>> {\n return this.#stream.next();\n }\n}\n\nexport class BufferedWordStream extends WordStream {\n #stream: BufferedTokenStream;\n\n constructor(func: TokenizeFunc, minTokenLength: number, minContextLength: number) {\n super();\n this.#stream = new BufferedTokenStream(func, minTokenLength, minContextLength);\n }\n\n pushText(text: string) {\n this.#stream.pushText(text);\n }\n\n flush() {\n this.#stream.flush();\n }\n\n endInput() {\n this.#stream.endInput();\n }\n\n close() {\n this.#stream.close();\n }\n\n next(): Promise<IteratorResult<TokenData>> {\n return this.#stream.next();\n }\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/tokenize/token_stream.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport { randomUUID } from 'node:crypto';\nimport { AsyncIterableQueue } from '../utils.js';\nimport type { TokenData } from './tokenizer.js';\nimport { SentenceStream, WordStream } from './tokenizer.js';\n\ntype TokenizeFunc = (x: string) => string[] | [string, number, number][];\n\nexport class BufferedTokenStream implements AsyncIterableIterator<TokenData> {\n protected queue = new AsyncIterableQueue<TokenData>();\n protected closed = false;\n\n #func: TokenizeFunc;\n #minTokenLength: number;\n #minContextLength: number;\n #bufTokens: string[] = [];\n #inBuf = '';\n #outBuf = '';\n #currentSegmentId: string;\n\n constructor(func: TokenizeFunc, minTokenLength: number, minContextLength: number) {\n this.#func = func;\n this.#minTokenLength = minTokenLength;\n this.#minContextLength = minContextLength;\n\n this.#currentSegmentId = randomUUID();\n }\n\n /** Push a string of text into the token stream */\n pushText(text: string) {\n if (this.closed) {\n throw new Error('Stream is closed');\n }\n\n this.#inBuf += text;\n if (this.#inBuf.length < this.#minContextLength) return;\n\n while (true) {\n const tokens = this.#func(this.#inBuf);\n if (tokens.length <= 1) break;\n\n if (this.#outBuf) this.#outBuf += ' ';\n\n const tok = tokens.shift()!;\n let tokText: string;\n if (Array.isArray(tok)) {\n tokText = tok[0];\n } else {\n tokText = tok;\n }\n\n this.#outBuf += tokText;\n\n if (this.#outBuf.length >= this.#minTokenLength) {\n this.queue.put({ token: this.#outBuf, segmentId: this.#currentSegmentId });\n this.#outBuf = '';\n }\n\n if (typeof tok! !== 'string') {\n this.#inBuf = this.#inBuf.slice(tok![2]);\n } else {\n this.#inBuf = this.#inBuf\n .slice(Math.max(0, this.#inBuf.indexOf(tok)) + tok.length)\n .trimStart();\n }\n }\n }\n\n /** Flush the stream, causing it to process all pending text */\n flush() {\n if (this.closed) {\n throw new Error('Stream is closed');\n }\n\n if (this.#inBuf || this.#outBuf) {\n const tokens = this.#func(this.#inBuf);\n if (tokens) {\n if (this.#outBuf) this.#outBuf += ' ';\n\n if (Array.isArray(tokens[0])) {\n this.#outBuf += tokens.map((tok) => tok[0]).join(' ');\n } else {\n this.#outBuf += tokens.join(' ');\n }\n }\n\n if (this.#outBuf) {\n this.queue.put({ token: this.#outBuf, segmentId: this.#currentSegmentId });\n }\n\n this.#currentSegmentId = randomUUID();\n }\n\n this.#inBuf = '';\n this.#outBuf = '';\n }\n\n /** Mark the input as ended and forbid additional pushes */\n endInput() {\n if (this.closed) {\n throw new Error('Stream is closed');\n }\n this.flush();\n this.close();\n }\n\n next(): Promise<IteratorResult<TokenData>> {\n return this.queue.next();\n }\n\n /** Close both the input and output of the token stream */\n close() {\n this.queue.close();\n this.closed = true;\n }\n\n [Symbol.asyncIterator](): BufferedTokenStream {\n return this;\n }\n}\n\nexport class BufferedSentenceStream extends SentenceStream {\n #stream: BufferedTokenStream;\n\n constructor(func: TokenizeFunc, minTokenLength: number, minContextLength: number) {\n super();\n this.#stream = new BufferedTokenStream(func, minTokenLength, minContextLength);\n }\n\n pushText(text: string) {\n this.#stream.pushText(text);\n }\n\n flush() {\n this.#stream.flush();\n }\n\n close() {\n super.close();\n this.#stream.close();\n }\n\n next(): Promise<IteratorResult<TokenData>> {\n return this.#stream.next();\n }\n}\n\nexport class BufferedWordStream extends WordStream {\n #stream: BufferedTokenStream;\n\n constructor(func: TokenizeFunc, minTokenLength: number, minContextLength: number) {\n super();\n this.#stream = new BufferedTokenStream(func, minTokenLength, minContextLength);\n }\n\n pushText(text: string) {\n this.#stream.pushText(text);\n }\n\n flush() {\n this.#stream.flush();\n }\n\n endInput() {\n this.#stream.endInput();\n }\n\n close() {\n this.#stream.close();\n }\n\n next(): Promise<IteratorResult<TokenData>> {\n return this.#stream.next();\n }\n}\n"],"mappings":"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","names":[]}
@@ -1 +1 @@
1
- {"version":3,"file":"token_stream.d.ts","sourceRoot":"","sources":["../../src/tokenize/token_stream.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAE5D,KAAK,YAAY,GAAG,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC;AAEzE,qBAAa,mBAAoB,YAAW,qBAAqB,CAAC,SAAS,CAAC;;IAC1E,SAAS,CAAC,KAAK,gCAAuC;IACtD,SAAS,CAAC,MAAM,UAAS;gBAUb,IAAI,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM;IAQhF,kDAAkD;IAClD,QAAQ,CAAC,IAAI,EAAE,MAAM;IAoCrB,+DAA+D;IAC/D,KAAK;IA4BL,2DAA2D;IAC3D,QAAQ;IAQR,IAAI,IAAI,OAAO,CAAC,cAAc,CAAC,SAAS,CAAC,CAAC;IAI1C,0DAA0D;IAC1D,KAAK;IAKL,CAAC,MAAM,CAAC,aAAa,CAAC,IAAI,mBAAmB;CAG9C;AAED,qBAAa,sBAAuB,SAAQ,cAAc;;gBAG5C,IAAI,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM;IAKhF,QAAQ,CAAC,IAAI,EAAE,MAAM;IAIrB,KAAK;IAIL,KAAK;IAKL,IAAI,IAAI,OAAO,CAAC,cAAc,CAAC,SAAS,CAAC,CAAC;CAG3C;AAED,qBAAa,kBAAmB,SAAQ,UAAU;;gBAGpC,IAAI,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM;IAKhF,QAAQ,CAAC,IAAI,EAAE,MAAM;IAIrB,KAAK;IAIL,QAAQ;IAIR,KAAK;IAIL,IAAI,IAAI,OAAO,CAAC,cAAc,CAAC,SAAS,CAAC,CAAC;CAG3C"}
1
+ {"version":3,"file":"token_stream.d.ts","sourceRoot":"","sources":["../../src/tokenize/token_stream.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAE5D,KAAK,YAAY,GAAG,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC;AAEzE,qBAAa,mBAAoB,YAAW,qBAAqB,CAAC,SAAS,CAAC;;IAC1E,SAAS,CAAC,KAAK,gCAAuC;IACtD,SAAS,CAAC,MAAM,UAAS;gBAUb,IAAI,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM;IAQhF,kDAAkD;IAClD,QAAQ,CAAC,IAAI,EAAE,MAAM;IAuCrB,+DAA+D;IAC/D,KAAK;IA4BL,2DAA2D;IAC3D,QAAQ;IAQR,IAAI,IAAI,OAAO,CAAC,cAAc,CAAC,SAAS,CAAC,CAAC;IAI1C,0DAA0D;IAC1D,KAAK;IAKL,CAAC,MAAM,CAAC,aAAa,CAAC,IAAI,mBAAmB;CAG9C;AAED,qBAAa,sBAAuB,SAAQ,cAAc;;gBAG5C,IAAI,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM;IAKhF,QAAQ,CAAC,IAAI,EAAE,MAAM;IAIrB,KAAK;IAIL,KAAK;IAKL,IAAI,IAAI,OAAO,CAAC,cAAc,CAAC,SAAS,CAAC,CAAC;CAG3C;AAED,qBAAa,kBAAmB,SAAQ,UAAU;;gBAGpC,IAAI,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM;IAKhF,QAAQ,CAAC,IAAI,EAAE,MAAM;IAIrB,KAAK;IAIL,QAAQ;IAIR,KAAK;IAIL,IAAI,IAAI,OAAO,CAAC,cAAc,CAAC,SAAS,CAAC,CAAC;CAG3C"}
@@ -29,9 +29,11 @@ class BufferedTokenStream {
29
29
  if (tokens.length <= 1) break;
30
30
  if (this.#outBuf) this.#outBuf += " ";
31
31
  const tok = tokens.shift();
32
- let tokText = tok;
33
- if (tok.length > 1 && typeof tok[1] === "number") {
32
+ let tokText;
33
+ if (Array.isArray(tok)) {
34
34
  tokText = tok[0];
35
+ } else {
36
+ tokText = tok;
35
37
  }
36
38
  this.#outBuf += tokText;
37
39
  if (this.#outBuf.length >= this.#minTokenLength) {
@@ -54,7 +56,7 @@ class BufferedTokenStream {
54
56
  const tokens = this.#func(this.#inBuf);
55
57
  if (tokens) {
56
58
  if (this.#outBuf) this.#outBuf += " ";
57
- if (typeof tokens[0] !== "string") {
59
+ if (Array.isArray(tokens[0])) {
58
60
  this.#outBuf += tokens.map((tok) => tok[0]).join(" ");
59
61
  } else {
60
62
  this.#outBuf += tokens.join(" ");
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/tokenize/token_stream.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport { randomUUID } from 'node:crypto';\nimport { AsyncIterableQueue } from '../utils.js';\nimport type { TokenData } from './tokenizer.js';\nimport { SentenceStream, WordStream } from './tokenizer.js';\n\ntype TokenizeFunc = (x: string) => string[] | [string, number, number][];\n\nexport class BufferedTokenStream implements AsyncIterableIterator<TokenData> {\n protected queue = new AsyncIterableQueue<TokenData>();\n protected closed = false;\n\n #func: TokenizeFunc;\n #minTokenLength: number;\n #minContextLength: number;\n #bufTokens: string[] = [];\n #inBuf = '';\n #outBuf = '';\n #currentSegmentId: string;\n\n constructor(func: TokenizeFunc, minTokenLength: number, minContextLength: number) {\n this.#func = func;\n this.#minTokenLength = minTokenLength;\n this.#minContextLength = minContextLength;\n\n this.#currentSegmentId = randomUUID();\n }\n\n /** Push a string of text into the token stream */\n pushText(text: string) {\n if (this.closed) {\n throw new Error('Stream is closed');\n }\n\n this.#inBuf += text;\n if (this.#inBuf.length < this.#minContextLength) return;\n\n while (true) {\n const tokens = this.#func(this.#inBuf);\n if (tokens.length <= 1) break;\n\n if (this.#outBuf) this.#outBuf += ' ';\n\n const tok = tokens.shift()!;\n let tokText = tok as string;\n if (tok.length > 1 && typeof tok[1] === 'number') {\n tokText = tok[0];\n }\n\n this.#outBuf += tokText;\n if (this.#outBuf.length >= this.#minTokenLength) {\n this.queue.put({ token: this.#outBuf, segmentId: this.#currentSegmentId });\n this.#outBuf = '';\n }\n\n if (typeof tok! !== 'string') {\n this.#inBuf = this.#inBuf.slice(tok![2]);\n } else {\n this.#inBuf = this.#inBuf\n .slice(Math.max(0, this.#inBuf.indexOf(tok)) + tok.length)\n .trimStart();\n }\n }\n }\n\n /** Flush the stream, causing it to process all pending text */\n flush() {\n if (this.closed) {\n throw new Error('Stream is closed');\n }\n\n if (this.#inBuf || this.#outBuf) {\n const tokens = this.#func(this.#inBuf);\n if (tokens) {\n if (this.#outBuf) this.#outBuf += ' ';\n\n if (typeof tokens[0] !== 'string') {\n this.#outBuf += tokens.map((tok) => tok[0]).join(' ');\n } else {\n this.#outBuf += tokens.join(' ');\n }\n }\n\n if (this.#outBuf) {\n this.queue.put({ token: this.#outBuf, segmentId: this.#currentSegmentId });\n }\n\n this.#currentSegmentId = randomUUID();\n }\n\n this.#inBuf = '';\n this.#outBuf = '';\n }\n\n /** Mark the input as ended and forbid additional pushes */\n endInput() {\n if (this.closed) {\n throw new Error('Stream is closed');\n }\n this.flush();\n this.close();\n }\n\n next(): Promise<IteratorResult<TokenData>> {\n return this.queue.next();\n }\n\n /** Close both the input and output of the token stream */\n close() {\n this.queue.close();\n this.closed = true;\n }\n\n [Symbol.asyncIterator](): BufferedTokenStream {\n return this;\n }\n}\n\nexport class BufferedSentenceStream extends SentenceStream {\n #stream: BufferedTokenStream;\n\n constructor(func: TokenizeFunc, minTokenLength: number, minContextLength: number) {\n super();\n this.#stream = new BufferedTokenStream(func, minTokenLength, minContextLength);\n }\n\n pushText(text: string) {\n this.#stream.pushText(text);\n }\n\n flush() {\n this.#stream.flush();\n }\n\n close() {\n super.close();\n this.#stream.close();\n }\n\n next(): Promise<IteratorResult<TokenData>> {\n return this.#stream.next();\n }\n}\n\nexport class BufferedWordStream extends WordStream {\n #stream: BufferedTokenStream;\n\n constructor(func: TokenizeFunc, minTokenLength: number, minContextLength: number) {\n super();\n this.#stream = new BufferedTokenStream(func, minTokenLength, minContextLength);\n }\n\n pushText(text: string) {\n this.#stream.pushText(text);\n }\n\n flush() {\n this.#stream.flush();\n }\n\n endInput() {\n this.#stream.endInput();\n }\n\n close() {\n this.#stream.close();\n }\n\n next(): Promise<IteratorResult<TokenData>> {\n return this.#stream.next();\n }\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/tokenize/token_stream.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport { randomUUID } from 'node:crypto';\nimport { AsyncIterableQueue } from '../utils.js';\nimport type { TokenData } from './tokenizer.js';\nimport { SentenceStream, WordStream } from './tokenizer.js';\n\ntype TokenizeFunc = (x: string) => string[] | [string, number, number][];\n\nexport class BufferedTokenStream implements AsyncIterableIterator<TokenData> {\n protected queue = new AsyncIterableQueue<TokenData>();\n protected closed = false;\n\n #func: TokenizeFunc;\n #minTokenLength: number;\n #minContextLength: number;\n #bufTokens: string[] = [];\n #inBuf = '';\n #outBuf = '';\n #currentSegmentId: string;\n\n constructor(func: TokenizeFunc, minTokenLength: number, minContextLength: number) {\n this.#func = func;\n this.#minTokenLength = minTokenLength;\n this.#minContextLength = minContextLength;\n\n this.#currentSegmentId = randomUUID();\n }\n\n /** Push a string of text into the token stream */\n pushText(text: string) {\n if (this.closed) {\n throw new Error('Stream is closed');\n }\n\n this.#inBuf += text;\n if (this.#inBuf.length < this.#minContextLength) return;\n\n while (true) {\n const tokens = this.#func(this.#inBuf);\n if (tokens.length <= 1) break;\n\n if (this.#outBuf) this.#outBuf += ' ';\n\n const tok = tokens.shift()!;\n let tokText: string;\n if (Array.isArray(tok)) {\n tokText = tok[0];\n } else {\n tokText = tok;\n }\n\n this.#outBuf += tokText;\n\n if (this.#outBuf.length >= this.#minTokenLength) {\n this.queue.put({ token: this.#outBuf, segmentId: this.#currentSegmentId });\n this.#outBuf = '';\n }\n\n if (typeof tok! !== 'string') {\n this.#inBuf = this.#inBuf.slice(tok![2]);\n } else {\n this.#inBuf = this.#inBuf\n .slice(Math.max(0, this.#inBuf.indexOf(tok)) + tok.length)\n .trimStart();\n }\n }\n }\n\n /** Flush the stream, causing it to process all pending text */\n flush() {\n if (this.closed) {\n throw new Error('Stream is closed');\n }\n\n if (this.#inBuf || this.#outBuf) {\n const tokens = this.#func(this.#inBuf);\n if (tokens) {\n if (this.#outBuf) this.#outBuf += ' ';\n\n if (Array.isArray(tokens[0])) {\n this.#outBuf += tokens.map((tok) => tok[0]).join(' ');\n } else {\n this.#outBuf += tokens.join(' ');\n }\n }\n\n if (this.#outBuf) {\n this.queue.put({ token: this.#outBuf, segmentId: this.#currentSegmentId });\n }\n\n this.#currentSegmentId = randomUUID();\n }\n\n this.#inBuf = '';\n this.#outBuf = '';\n }\n\n /** Mark the input as ended and forbid additional pushes */\n endInput() {\n if (this.closed) {\n throw new Error('Stream is closed');\n }\n this.flush();\n this.close();\n }\n\n next(): Promise<IteratorResult<TokenData>> {\n return this.queue.next();\n }\n\n /** Close both the input and output of the token stream */\n close() {\n this.queue.close();\n this.closed = true;\n }\n\n [Symbol.asyncIterator](): BufferedTokenStream {\n return this;\n }\n}\n\nexport class BufferedSentenceStream extends SentenceStream {\n #stream: BufferedTokenStream;\n\n constructor(func: TokenizeFunc, minTokenLength: number, minContextLength: number) {\n super();\n this.#stream = new BufferedTokenStream(func, minTokenLength, minContextLength);\n }\n\n pushText(text: string) {\n this.#stream.pushText(text);\n }\n\n flush() {\n this.#stream.flush();\n }\n\n close() {\n super.close();\n this.#stream.close();\n }\n\n next(): Promise<IteratorResult<TokenData>> {\n return this.#stream.next();\n }\n}\n\nexport class BufferedWordStream extends WordStream {\n #stream: BufferedTokenStream;\n\n constructor(func: TokenizeFunc, minTokenLength: number, minContextLength: number) {\n super();\n this.#stream = new BufferedTokenStream(func, minTokenLength, minContextLength);\n }\n\n pushText(text: string) {\n this.#stream.pushText(text);\n }\n\n flush() {\n this.#stream.flush();\n }\n\n endInput() {\n this.#stream.endInput();\n }\n\n close() {\n this.#stream.close();\n }\n\n next(): Promise<IteratorResult<TokenData>> {\n return this.#stream.next();\n }\n}\n"],"mappings":"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","names":[]}
@@ -18,114 +18,231 @@ var __copyProps = (to, from, except, desc) => {
18
18
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
19
  var transcription_exports = {};
20
20
  __export(transcription_exports, {
21
- BasicTranscriptionForwarder: () => BasicTranscriptionForwarder
21
+ TextAudioSynchronizer: () => TextAudioSynchronizer,
22
+ defaultTextSyncOptions: () => defaultTextSyncOptions
22
23
  });
23
24
  module.exports = __toCommonJS(transcription_exports);
24
- var import_log = require("./log.cjs");
25
- class BasicTranscriptionForwarder {
26
- #room;
27
- #participantIdentity;
28
- #trackSid;
29
- #currentText = "";
30
- #totalAudioDuration = 0;
31
- #currentPlayoutTime = 0;
32
- #DEFAULT_CHARS_PER_SECOND = 16;
33
- #charsPerSecond = this.#DEFAULT_CHARS_PER_SECOND;
34
- #messageId;
35
- #isRunning = false;
36
- #logger = (0, import_log.log)();
37
- currentCharacterIndex = 0;
38
- constructor(room, participantIdentity, trackSid, messageId) {
39
- this.#room = room;
40
- this.#participantIdentity = participantIdentity;
41
- this.#trackSid = trackSid;
42
- this.#messageId = messageId;
43
- }
44
- get text() {
45
- return this.#currentText;
46
- }
47
- start() {
48
- if (!this.#isRunning) {
49
- this.#isRunning = true;
50
- this.#startPublishingLoop().catch((error) => {
51
- this.#logger.error("Error in publishing loop:", error);
52
- this.#isRunning = false;
53
- });
54
- }
25
+ var import_protocol = require("@livekit/protocol");
26
+ var import_rtc_node = require("@livekit/rtc-node");
27
+ var import_node_crypto = require("node:crypto");
28
+ var import_node_events = require("node:events");
29
+ var import_tokenize = require("./tokenize/index.cjs");
30
+ var import_utils = require("./utils.cjs");
31
+ const STANDARD_SPEECH_RATE = 3830;
32
+ const defaultTextSyncOptions = {
33
+ language: "",
34
+ speed: 1,
35
+ newSentenceDelay: 400,
36
+ sentenceTokenizer: new import_tokenize.basic.SentenceTokenizer(),
37
+ hyphenateWord: import_tokenize.basic.hyphenateWord,
38
+ splitWords: import_tokenize.basic.splitWords
39
+ };
40
+ class TextAudioSynchronizer extends import_node_events.EventEmitter {
41
+ #opts;
42
+ #speed;
43
+ #closed = false;
44
+ #interrupted = false;
45
+ #closeFut = new import_utils.Future();
46
+ #playingSegIndex = -1;
47
+ #finishedSegIndex = -1;
48
+ #textQChanged = new import_utils.AsyncIterableQueue();
49
+ #textQ = [];
50
+ #audioQChanged = new import_utils.AsyncIterableQueue();
51
+ #audioQ = [];
52
+ #playedText = "";
53
+ #task;
54
+ #audioData;
55
+ #textData;
56
+ constructor(opts) {
57
+ super();
58
+ this.#opts = opts;
59
+ this.#speed = opts.speed * STANDARD_SPEECH_RATE;
55
60
  }
56
61
  pushAudio(frame) {
57
- this.#totalAudioDuration += frame.samplesPerChannel / frame.sampleRate;
62
+ this.#checkNotClosed();
63
+ if (!this.#audioData) {
64
+ this.#audioData = { pushedDuration: 0, done: false };
65
+ this.#audioQ.push(this.#audioData);
66
+ this.#audioQChanged.put(1);
67
+ }
68
+ this.#audioData.pushedDuration += frame.samplesPerChannel / frame.sampleRate;
58
69
  }
59
70
  pushText(text) {
60
- this.#currentText += text;
71
+ this.#checkNotClosed();
72
+ if (!this.#textData) {
73
+ this.#textData = {
74
+ sentenceStream: this.#opts.sentenceTokenizer.stream(),
75
+ pushedText: "",
76
+ done: false,
77
+ forwardedHyphens: 0,
78
+ forwardedSentences: 0
79
+ };
80
+ this.#textQ.push(this.#textData);
81
+ this.#textQChanged.put(1);
82
+ }
83
+ this.#textData.pushedText += text;
84
+ this.#textData.sentenceStream.pushText(text);
61
85
  }
62
- #textIsComplete = false;
63
- #audioIsComplete = false;
64
- markTextComplete() {
65
- this.#textIsComplete = true;
66
- this.#adjustTimingIfBothFinished();
86
+ markAudioSegmentEnd() {
87
+ this.#checkNotClosed();
88
+ if (!this.#audioData) {
89
+ this.pushAudio(new import_rtc_node.AudioFrame(new Int16Array(), 24e3, 1, 0));
90
+ }
91
+ this.#audioData.done = true;
92
+ this.#audioData = void 0;
67
93
  }
68
- markAudioComplete() {
69
- this.#audioIsComplete = true;
70
- this.#adjustTimingIfBothFinished();
94
+ markTextSegmentEnd() {
95
+ var _a, _b;
96
+ this.#checkNotClosed();
97
+ if (!this.#textData) {
98
+ this.pushText("");
99
+ }
100
+ this.#textData.done = true;
101
+ (_a = this.#textData) == null ? void 0 : _a.sentenceStream.flush();
102
+ (_b = this.#textData) == null ? void 0 : _b.sentenceStream.close();
103
+ this.#textData = void 0;
71
104
  }
72
- #adjustTimingIfBothFinished() {
73
- if (this.#textIsComplete && this.#audioIsComplete) {
74
- const actualDuration = this.#totalAudioDuration;
75
- if (actualDuration > 0 && this.#currentText.length > 0) {
76
- this.#charsPerSecond = this.#currentText.length / actualDuration;
77
- }
105
+ segmentPlayoutStarted() {
106
+ this.#checkNotClosed();
107
+ this.#playingSegIndex++;
108
+ if (!this.#task) {
109
+ this.#task = this.#mainLoop();
78
110
  }
79
111
  }
80
- #computeSleepInterval() {
81
- return Math.min(Math.max(1 / this.#charsPerSecond, 0.0625), 0.5);
112
+ segmentPlayoutFinished() {
113
+ this.#checkNotClosed();
114
+ this.#finishedSegIndex++;
82
115
  }
83
- async #startPublishingLoop() {
84
- this.#isRunning = true;
85
- let sleepInterval = this.#computeSleepInterval();
86
- let isComplete = false;
87
- while (this.#isRunning && !isComplete) {
88
- this.#currentPlayoutTime += sleepInterval;
89
- this.currentCharacterIndex = Math.floor(this.#currentPlayoutTime * this.#charsPerSecond);
90
- isComplete = this.#textIsComplete && this.currentCharacterIndex >= this.#currentText.length;
91
- await this.#publishTranscription(false);
92
- if (this.#isRunning && !isComplete) {
93
- sleepInterval = this.#computeSleepInterval();
94
- await new Promise((resolve) => setTimeout(resolve, sleepInterval * 1e3));
95
- }
116
+ get playedText() {
117
+ return this.#playedText;
118
+ }
119
+ async close(interrupt) {
120
+ if (this.#closed) {
121
+ return;
122
+ }
123
+ this.#closed = true;
124
+ this.#interrupted = interrupt;
125
+ this.#closeFut.resolve();
126
+ for (const textData of this.#textQ) {
127
+ textData == null ? void 0 : textData.sentenceStream.close();
96
128
  }
97
- if (this.#isRunning) {
98
- this.close(false);
129
+ this.#textQ.push(void 0);
130
+ this.#audioQ.push(void 0);
131
+ this.#textQChanged.put(1);
132
+ this.#audioQChanged.put(1);
133
+ await this.#task;
134
+ }
135
+ async #mainLoop() {
136
+ let segIndex = 0;
137
+ let qDone = false;
138
+ while (!qDone) {
139
+ await this.#textQChanged.next();
140
+ await this.#audioQChanged.next();
141
+ while (this.#textQ.length && this.#audioQ.length) {
142
+ const textData = this.#textQ.pop();
143
+ const audioData = this.#audioQ.pop();
144
+ if (!(textData && audioData)) {
145
+ qDone = true;
146
+ break;
147
+ }
148
+ while (!this.#closed) {
149
+ if (this.#playingSegIndex >= segIndex) break;
150
+ await this.#sleepIfNotClosed(125);
151
+ }
152
+ const sentenceStream = textData.sentenceStream;
153
+ const forwardStartTime = Date.now();
154
+ for await (const ev of sentenceStream) {
155
+ await this.#syncSentence(segIndex, forwardStartTime, textData, audioData, ev.token);
156
+ }
157
+ segIndex++;
158
+ }
99
159
  }
100
160
  }
101
- async #publishTranscription(final) {
102
- var _a;
103
- const textToPublish = this.#currentText.slice(0, this.currentCharacterIndex);
104
- await ((_a = this.#room.localParticipant) == null ? void 0 : _a.publishTranscription({
105
- participantIdentity: this.#participantIdentity,
106
- trackSid: this.#trackSid,
107
- segments: [
108
- {
109
- text: textToPublish,
110
- final,
111
- id: this.#messageId,
161
+ async #syncSentence(segIndex, segStartTime, textData, audioData, sentence) {
162
+ let realSpeed;
163
+ if (audioData.pushedDuration > 0 && audioData.done) {
164
+ realSpeed = this.#calcHyphens(textData.pushedText).length / audioData.pushedDuration;
165
+ }
166
+ const segId = "SG_" + (0, import_node_crypto.randomUUID)();
167
+ const words = this.#opts.splitWords(sentence);
168
+ const processedWords = [];
169
+ const ogText = this.#playedText;
170
+ for (const [word, _, end] of words) {
171
+ if (segIndex <= this.#finishedSegIndex) break;
172
+ if (this.#interrupted) return;
173
+ const wordHyphens = this.#opts.hyphenateWord(word).length;
174
+ processedWords.push(word);
175
+ const elapsed = Date.now() - segStartTime;
176
+ const text = sentence.slice(0, end);
177
+ let speed = this.#speed;
178
+ let delay;
179
+ if (realSpeed) {
180
+ speed = realSpeed;
181
+ const estimatedPausesMs = textData.forwardedSentences * this.#opts.newSentenceDelay;
182
+ const hyphPauses = estimatedPausesMs * speed;
183
+ const targetHyphens = Math.round(speed * elapsed);
184
+ const dt = targetHyphens - textData.forwardedHyphens - hyphPauses;
185
+ const toWaitHyphens = Math.max(0, wordHyphens - dt);
186
+ delay = toWaitHyphens / speed;
187
+ } else {
188
+ delay = wordHyphens / speed;
189
+ }
190
+ const firstDelay = Math.min(delay / 2, 2 / speed);
191
+ await this.#sleepIfNotClosed(firstDelay * 1e6);
192
+ this.emit(
193
+ "textUpdated",
194
+ new import_protocol.TranscriptionSegment({
195
+ id: segId,
196
+ text,
112
197
  startTime: BigInt(0),
113
198
  endTime: BigInt(0),
114
- language: ""
115
- }
116
- ]
117
- }));
199
+ final: false,
200
+ language: this.#opts.language
201
+ })
202
+ );
203
+ this.#playedText = `${ogText} ${text}`;
204
+ await this.#sleepIfNotClosed((delay - firstDelay) * 1e6);
205
+ textData.forwardedHyphens += wordHyphens;
206
+ }
207
+ this.emit(
208
+ "textUpdated",
209
+ new import_protocol.TranscriptionSegment({
210
+ id: segId,
211
+ text: sentence,
212
+ startTime: BigInt(0),
213
+ endTime: BigInt(0),
214
+ final: true,
215
+ language: this.#opts.language
216
+ })
217
+ );
218
+ this.#playedText = `${ogText} ${sentence}`;
219
+ await this.#sleepIfNotClosed(this.#opts.newSentenceDelay);
220
+ textData.forwardedSentences++;
118
221
  }
119
- async close(interrupt) {
120
- this.#isRunning = false;
121
- if (!interrupt) {
122
- this.currentCharacterIndex = this.#currentText.length;
222
+ async #sleepIfNotClosed(delay) {
223
+ await Promise.race([
224
+ this.#closeFut.await,
225
+ new Promise((resolve) => setTimeout(resolve, delay))
226
+ ]);
227
+ }
228
+ #calcHyphens(text) {
229
+ const hyphens = [];
230
+ const words = this.#opts.splitWords(text);
231
+ for (const word of words) {
232
+ const n = this.#opts.hyphenateWord(word[0]);
233
+ hyphens.push(...n);
234
+ }
235
+ return hyphens;
236
+ }
237
+ #checkNotClosed() {
238
+ if (this.#closed) {
239
+ throw new Error("TextAudioSynchronizer is closed");
123
240
  }
124
- await this.#publishTranscription(true);
125
241
  }
126
242
  }
127
243
  // Annotate the CommonJS export names for ESM import in node:
128
244
  0 && (module.exports = {
129
- BasicTranscriptionForwarder
245
+ TextAudioSynchronizer,
246
+ defaultTextSyncOptions
130
247
  });
131
248
  //# sourceMappingURL=transcription.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/transcription.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport type { AudioFrame, Room } from '@livekit/rtc-node';\nimport { log } from './log.js';\n\nexport interface TranscriptionForwarder {\n start(): void;\n pushAudio(frame: AudioFrame): void;\n pushText(text: string): void;\n markTextComplete(): void;\n markAudioComplete(): void;\n close(interrupt: boolean): Promise<void>;\n currentCharacterIndex: number;\n text: string;\n}\n\nexport class BasicTranscriptionForwarder implements TranscriptionForwarder {\n #room: Room;\n #participantIdentity: string;\n #trackSid: string;\n #currentText: string = '';\n #totalAudioDuration: number = 0;\n #currentPlayoutTime: number = 0;\n #DEFAULT_CHARS_PER_SECOND = 16;\n #charsPerSecond: number = this.#DEFAULT_CHARS_PER_SECOND;\n #messageId: string;\n #isRunning: boolean = false;\n #logger = log();\n currentCharacterIndex: number = 0;\n\n constructor(room: Room, participantIdentity: string, trackSid: string, messageId: string) {\n this.#room = room;\n this.#participantIdentity = participantIdentity;\n this.#trackSid = trackSid;\n this.#messageId = messageId;\n }\n\n get text(): string {\n return this.#currentText;\n }\n\n start(): void {\n if (!this.#isRunning) {\n this.#isRunning = true;\n this.#startPublishingLoop().catch((error) => {\n this.#logger.error('Error in publishing loop:', error);\n this.#isRunning = false;\n });\n }\n }\n\n pushAudio(frame: AudioFrame): void {\n this.#totalAudioDuration += frame.samplesPerChannel / frame.sampleRate;\n }\n\n pushText(text: string): void {\n this.#currentText += text;\n }\n\n #textIsComplete: boolean = false;\n #audioIsComplete: boolean = false;\n\n markTextComplete(): void {\n this.#textIsComplete = true;\n this.#adjustTimingIfBothFinished();\n }\n\n markAudioComplete(): void {\n this.#audioIsComplete = true;\n this.#adjustTimingIfBothFinished();\n }\n\n #adjustTimingIfBothFinished(): void {\n if (this.#textIsComplete && this.#audioIsComplete) {\n const actualDuration = this.#totalAudioDuration;\n if (actualDuration > 0 && this.#currentText.length > 0) {\n this.#charsPerSecond = this.#currentText.length / actualDuration;\n }\n }\n }\n\n #computeSleepInterval(): number {\n return Math.min(Math.max(1 / this.#charsPerSecond, 0.0625), 0.5);\n }\n\n async #startPublishingLoop(): Promise<void> {\n this.#isRunning = true;\n let sleepInterval = this.#computeSleepInterval();\n let isComplete = false;\n while (this.#isRunning && !isComplete) {\n this.#currentPlayoutTime += sleepInterval;\n this.currentCharacterIndex = Math.floor(this.#currentPlayoutTime * this.#charsPerSecond);\n isComplete = this.#textIsComplete && this.currentCharacterIndex >= this.#currentText.length;\n await this.#publishTranscription(false);\n if (this.#isRunning && !isComplete) {\n sleepInterval = this.#computeSleepInterval();\n await new Promise((resolve) => setTimeout(resolve, sleepInterval * 1000));\n }\n }\n\n if (this.#isRunning) {\n this.close(false);\n }\n }\n\n async #publishTranscription(final: boolean): Promise<void> {\n const textToPublish = this.#currentText.slice(0, this.currentCharacterIndex);\n await this.#room.localParticipant?.publishTranscription({\n participantIdentity: this.#participantIdentity,\n trackSid: this.#trackSid,\n segments: [\n {\n text: textToPublish,\n final: final,\n id: this.#messageId,\n startTime: BigInt(0),\n endTime: BigInt(0),\n language: '',\n },\n ],\n });\n }\n\n async close(interrupt: boolean): Promise<void> {\n this.#isRunning = false;\n\n // Publish whatever we had as final\n if (!interrupt) {\n this.currentCharacterIndex = this.#currentText.length;\n }\n await this.#publishTranscription(true);\n }\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/transcription.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport { TranscriptionSegment } from '@livekit/protocol';\nimport { AudioFrame } from '@livekit/rtc-node';\nimport type { TypedEventEmitter as TypedEmitter } from '@livekit/typed-emitter';\nimport { randomUUID } from 'node:crypto';\nimport { EventEmitter } from 'node:events';\nimport { basic } from './tokenize/index.js';\nimport type { SentenceStream, SentenceTokenizer } from './tokenize/tokenizer.js';\nimport { AsyncIterableQueue, Future } from './utils.js';\n\n// standard speech rate in hyphens/ms\nconst STANDARD_SPEECH_RATE = 3830;\n\nexport interface TextSyncOptions {\n language: string;\n speed: number;\n newSentenceDelay: number;\n sentenceTokenizer: SentenceTokenizer;\n hyphenateWord: (word: string) => string[];\n splitWords: (words: string) => [string, number, number][];\n}\n\nexport const defaultTextSyncOptions: TextSyncOptions = {\n language: '',\n speed: 1,\n newSentenceDelay: 400,\n sentenceTokenizer: new basic.SentenceTokenizer(),\n hyphenateWord: basic.hyphenateWord,\n splitWords: basic.splitWords,\n};\n\ninterface AudioData {\n pushedDuration: number;\n done: boolean;\n}\n\ninterface TextData {\n sentenceStream: SentenceStream;\n pushedText: string;\n done: boolean;\n forwardedHyphens: number;\n forwardedSentences: number;\n}\n\ntype SyncCallbacks = {\n textUpdated: (text: TranscriptionSegment) => void;\n};\n\nexport class TextAudioSynchronizer extends (EventEmitter as new () => TypedEmitter<SyncCallbacks>) {\n #opts: TextSyncOptions;\n #speed: number;\n\n #closed = false;\n #interrupted = false;\n #closeFut = new Future();\n\n #playingSegIndex = -1;\n #finishedSegIndex = -1;\n\n #textQChanged = new AsyncIterableQueue<number>();\n #textQ: (TextData | undefined)[] = [];\n #audioQChanged = new AsyncIterableQueue<number>();\n #audioQ: (AudioData | undefined)[] = [];\n\n #playedText = '';\n #task?: Promise<void>;\n\n #audioData?: AudioData;\n #textData?: TextData;\n\n constructor(opts: TextSyncOptions) {\n super();\n\n this.#opts = opts;\n this.#speed = opts.speed * STANDARD_SPEECH_RATE;\n }\n\n pushAudio(frame: AudioFrame) {\n this.#checkNotClosed();\n if (!this.#audioData) {\n this.#audioData = { pushedDuration: 0, done: false };\n this.#audioQ.push(this.#audioData);\n this.#audioQChanged.put(1);\n }\n this.#audioData.pushedDuration += frame.samplesPerChannel / frame.sampleRate;\n }\n\n pushText(text: string) {\n this.#checkNotClosed();\n if (!this.#textData) {\n this.#textData = {\n sentenceStream: this.#opts.sentenceTokenizer.stream(),\n pushedText: '',\n done: false,\n forwardedHyphens: 0,\n forwardedSentences: 0,\n };\n this.#textQ.push(this.#textData);\n this.#textQChanged.put(1);\n }\n\n this.#textData.pushedText += text;\n this.#textData.sentenceStream.pushText(text);\n }\n\n markAudioSegmentEnd() {\n this.#checkNotClosed();\n\n if (!this.#audioData) {\n // create empty audio data if none exists\n this.pushAudio(new AudioFrame(new Int16Array(), 24000, 1, 0));\n }\n\n this.#audioData!.done = true;\n this.#audioData = undefined;\n }\n\n markTextSegmentEnd() {\n this.#checkNotClosed();\n\n if (!this.#textData) {\n this.pushText('');\n }\n\n this.#textData!.done = true;\n this.#textData?.sentenceStream.flush();\n this.#textData?.sentenceStream.close();\n this.#textData = undefined;\n }\n\n segmentPlayoutStarted() {\n this.#checkNotClosed();\n this.#playingSegIndex++;\n\n if (!this.#task) {\n this.#task = this.#mainLoop();\n }\n }\n\n segmentPlayoutFinished() {\n this.#checkNotClosed();\n this.#finishedSegIndex++;\n }\n\n get playedText(): string {\n return this.#playedText;\n }\n\n async close(interrupt: boolean) {\n if (this.#closed) {\n return;\n }\n this.#closed = true;\n this.#interrupted = interrupt;\n this.#closeFut.resolve();\n\n for (const textData of this.#textQ) {\n textData?.sentenceStream.close();\n }\n\n this.#textQ.push(undefined);\n this.#audioQ.push(undefined);\n this.#textQChanged.put(1);\n this.#audioQChanged.put(1);\n\n await this.#task;\n }\n\n async #mainLoop() {\n let segIndex = 0;\n let qDone = false;\n\n while (!qDone) {\n await this.#textQChanged.next();\n await this.#audioQChanged.next();\n\n while (this.#textQ.length && this.#audioQ.length) {\n const textData = this.#textQ.pop();\n const audioData = this.#audioQ.pop();\n\n if (!(textData && audioData)) {\n qDone = true;\n break;\n }\n\n // wait for segment to start playing\n while (!this.#closed) {\n if (this.#playingSegIndex >= segIndex) break;\n await this.#sleepIfNotClosed(125);\n }\n\n const sentenceStream = textData.sentenceStream;\n const forwardStartTime = Date.now();\n\n for await (const ev of sentenceStream) {\n await this.#syncSentence(segIndex, forwardStartTime, textData, audioData, ev.token);\n }\n\n segIndex++;\n }\n }\n }\n\n async #syncSentence(\n segIndex: number,\n segStartTime: number,\n textData: TextData,\n audioData: AudioData,\n sentence: string,\n ) {\n let realSpeed: number | undefined;\n if (audioData.pushedDuration > 0 && audioData.done) {\n realSpeed = this.#calcHyphens(textData.pushedText).length / audioData.pushedDuration;\n }\n\n const segId = 'SG_' + randomUUID();\n const words = this.#opts.splitWords(sentence);\n const processedWords: string[] = [];\n\n const ogText = this.#playedText;\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n for (const [word, _, end] of words) {\n if (segIndex <= this.#finishedSegIndex) break;\n if (this.#interrupted) return;\n\n const wordHyphens = this.#opts.hyphenateWord(word).length;\n processedWords.push(word);\n\n const elapsed = Date.now() - segStartTime;\n const text = sentence.slice(0, end); // TODO: rstrip punctuations\n\n let speed = this.#speed;\n let delay: number;\n if (realSpeed) {\n speed = realSpeed;\n const estimatedPausesMs = textData.forwardedSentences * this.#opts.newSentenceDelay;\n const hyphPauses = estimatedPausesMs * speed;\n const targetHyphens = Math.round(speed * elapsed);\n const dt = targetHyphens - textData.forwardedHyphens - hyphPauses;\n const toWaitHyphens = Math.max(0, wordHyphens - dt);\n delay = toWaitHyphens / speed;\n } else {\n delay = wordHyphens / speed;\n }\n\n const firstDelay = Math.min(delay / 2, 2 / speed);\n await this.#sleepIfNotClosed(firstDelay * 1000000);\n\n this.emit(\n 'textUpdated',\n new TranscriptionSegment({\n id: segId,\n text: text,\n startTime: BigInt(0),\n endTime: BigInt(0),\n final: false,\n language: this.#opts.language,\n }),\n );\n\n this.#playedText = `${ogText} ${text}`;\n await this.#sleepIfNotClosed((delay - firstDelay) * 1000000);\n textData.forwardedHyphens += wordHyphens;\n }\n\n this.emit(\n 'textUpdated',\n new TranscriptionSegment({\n id: segId,\n text: sentence,\n startTime: BigInt(0),\n endTime: BigInt(0),\n final: true,\n language: this.#opts.language,\n }),\n );\n\n this.#playedText = `${ogText} ${sentence}`;\n\n await this.#sleepIfNotClosed(this.#opts.newSentenceDelay);\n textData.forwardedSentences++;\n }\n\n async #sleepIfNotClosed(delay: number) {\n await Promise.race([\n this.#closeFut.await,\n new Promise((resolve) => setTimeout(resolve, delay)),\n ]);\n }\n\n #calcHyphens(text: string): string[] {\n const hyphens: string[] = [];\n const words = this.#opts.splitWords(text);\n for (const word of words) {\n const n = this.#opts.hyphenateWord(word[0]);\n hyphens.push(...n);\n }\n return hyphens;\n }\n\n #checkNotClosed() {\n if (this.#closed) {\n throw new Error('TextAudioSynchronizer is closed');\n }\n }\n}\n"],"mappings":"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","names":[]}