echogarden 1.4.4 → 1.6.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 (240) hide show
  1. package/data/schemas/options.json +310 -25
  2. package/dist/alignment/DTWMfccSequenceAlignment.d.ts +1 -1
  3. package/dist/alignment/DTWMfccSequenceAlignment.js +5 -5
  4. package/dist/alignment/DTWSequenceAlignmentWindowed.js +1 -3
  5. package/dist/alignment/DTWSequenceAlignmentWindowed.js.map +1 -1
  6. package/dist/alignment/{TextAlignment.d.ts → SemanticTextAlignment.d.ts} +4 -2
  7. package/dist/alignment/SemanticTextAlignment.js +336 -0
  8. package/dist/alignment/SemanticTextAlignment.js.map +1 -0
  9. package/dist/alignment/SpeechAlignment.d.ts +4 -3
  10. package/dist/alignment/SpeechAlignment.js +130 -39
  11. package/dist/alignment/SpeechAlignment.js.map +1 -1
  12. package/dist/api/API.d.ts +7 -3
  13. package/dist/api/API.js +7 -2
  14. package/dist/api/API.js.map +1 -1
  15. package/dist/api/APIOptions.d.ts +4 -1
  16. package/dist/api/Alignment.d.ts +1 -1
  17. package/dist/api/Alignment.js +13 -5
  18. package/dist/api/Alignment.js.map +1 -1
  19. package/dist/api/LanguageDetectionCommon.d.ts +6 -0
  20. package/dist/api/LanguageDetectionCommon.js +2 -0
  21. package/dist/api/LanguageDetectionCommon.js.map +1 -0
  22. package/dist/api/Recognition.js.map +1 -1
  23. package/dist/api/{LanguageDetection.d.ts → SpeechLanguageDetection.d.ts} +1 -25
  24. package/dist/api/{LanguageDetection.js → SpeechLanguageDetection.js} +1 -68
  25. package/dist/api/SpeechLanguageDetection.js.map +1 -0
  26. package/dist/api/{Translation.js → SpeechTranslation.js} +3 -3
  27. package/dist/api/SpeechTranslation.js.map +1 -0
  28. package/dist/api/Synthesis.d.ts +0 -1
  29. package/dist/api/Synthesis.js +4 -4
  30. package/dist/api/TextLanguageDetection.d.ts +21 -0
  31. package/dist/api/TextLanguageDetection.js +67 -0
  32. package/dist/api/TextLanguageDetection.js.map +1 -0
  33. package/dist/api/TextTranslation.d.ts +25 -0
  34. package/dist/api/TextTranslation.js +101 -0
  35. package/dist/api/TextTranslation.js.map +1 -0
  36. package/dist/api/TimelineTranslationAlignment.d.ts +23 -0
  37. package/dist/api/TimelineTranslationAlignment.js +92 -0
  38. package/dist/api/TimelineTranslationAlignment.js.map +1 -0
  39. package/dist/api/TranscriptAndTranslationAlignment.d.ts +35 -0
  40. package/dist/api/TranscriptAndTranslationAlignment.js +78 -0
  41. package/dist/api/TranscriptAndTranslationAlignment.js.map +1 -0
  42. package/dist/api/TranslationAlignment.d.ts +4 -3
  43. package/dist/api/TranslationAlignment.js +9 -8
  44. package/dist/api/TranslationAlignment.js.map +1 -1
  45. package/dist/api/VoiceActivityDetection.js +16 -1
  46. package/dist/api/VoiceActivityDetection.js.map +1 -1
  47. package/dist/audio/AudioBufferConversion.d.ts +0 -1
  48. package/dist/audio/AudioPlayer.d.ts +0 -1
  49. package/dist/audio/AudioPlayer.js +62 -41
  50. package/dist/audio/AudioPlayer.js.map +1 -1
  51. package/dist/audio/AudioUtilities.d.ts +0 -1
  52. package/dist/cli/CLI.d.ts +28 -7
  53. package/dist/cli/CLI.js +265 -37
  54. package/dist/cli/CLI.js.map +1 -1
  55. package/dist/codecs/FFMpegTranscoder.d.ts +0 -1
  56. package/dist/codecs/FFMpegTranscoder.js +7 -0
  57. package/dist/codecs/FFMpegTranscoder.js.map +1 -1
  58. package/dist/codecs/TIMITCodec.d.ts +0 -1
  59. package/dist/codecs/WaveCodec.d.ts +0 -1
  60. package/dist/dsp/FFT.d.ts +1 -1
  61. package/dist/dsp/FFT.js +6 -0
  62. package/dist/dsp/FFT.js.map +1 -1
  63. package/dist/dsp/KWeightingFilter.js +1 -1
  64. package/dist/dsp/KWeightingFilter.js.map +1 -1
  65. package/dist/dsp/MelSpectogram.d.ts +3 -2
  66. package/dist/dsp/MelSpectogram.js +14 -8
  67. package/dist/dsp/MelSpectogram.js.map +1 -1
  68. package/dist/math/VectorMath.d.ts +9 -9
  69. package/dist/math/VectorMath.js +10 -10
  70. package/dist/math/VectorMath.js.map +1 -1
  71. package/dist/nlp/ChineseSegmentation.js +4 -4
  72. package/dist/nlp/ChineseSegmentation.js.map +1 -1
  73. package/dist/nlp/Segmentation.d.ts +2 -2
  74. package/dist/nlp/Segmentation.js +20 -13
  75. package/dist/nlp/Segmentation.js.map +1 -1
  76. package/dist/recognition/OpenAICloudSTT.d.ts +2 -1
  77. package/dist/recognition/OpenAICloudSTT.js +30 -19
  78. package/dist/recognition/OpenAICloudSTT.js.map +1 -1
  79. package/dist/recognition/SileroSTT.d.ts +0 -1
  80. package/dist/recognition/WhisperCppSTT.d.ts +3 -3
  81. package/dist/recognition/WhisperCppSTT.js +21 -9
  82. package/dist/recognition/WhisperCppSTT.js.map +1 -1
  83. package/dist/recognition/WhisperSTT.d.ts +9 -6
  84. package/dist/recognition/WhisperSTT.js +227 -46
  85. package/dist/recognition/WhisperSTT.js.map +1 -1
  86. package/dist/server/Client.d.ts +3 -4
  87. package/dist/server/Client.js.map +1 -1
  88. package/dist/server/Worker.d.ts +3 -3
  89. package/dist/server/Worker.js +3 -2
  90. package/dist/server/Worker.js.map +1 -1
  91. package/dist/source-separation/MDXNetSourceSeparation.d.ts +0 -1
  92. package/dist/source-separation/MDXNetSourceSeparation.js +1 -1
  93. package/dist/source-separation/MDXNetSourceSeparation.js.map +1 -1
  94. package/dist/speech-embeddings/WavToVec2BertFeatureEmbeddings.d.ts +12 -0
  95. package/dist/speech-embeddings/WavToVec2BertFeatureEmbeddings.js +68 -0
  96. package/dist/speech-embeddings/WavToVec2BertFeatureEmbeddings.js.map +1 -0
  97. package/dist/speech-language-detection/SileroLanguageDetection.d.ts +1 -2
  98. package/dist/speech-language-detection/SileroLanguageDetection.js +1 -1
  99. package/dist/speech-language-detection/SileroLanguageDetection.js.map +1 -1
  100. package/dist/subtitles/Subtitles.js +2 -2
  101. package/dist/subtitles/Subtitles.js.map +1 -1
  102. package/dist/synthesis/GoogleCloudTTS.d.ts +0 -1
  103. package/dist/synthesis/GoogleTranslateTTS.d.ts +0 -1
  104. package/dist/synthesis/GoogleTranslateTTS.js +6 -21
  105. package/dist/synthesis/GoogleTranslateTTS.js.map +1 -1
  106. package/dist/synthesis/StreamlabsPollyTTS.d.ts +0 -1
  107. package/dist/synthesis/VitsTTS.d.ts +0 -1
  108. package/dist/synthesis/VitsTTS.js +30 -0
  109. package/dist/synthesis/VitsTTS.js.map +1 -1
  110. package/dist/tests/Test.js +0 -31
  111. package/dist/tests/Test.js.map +1 -1
  112. package/dist/text-language-detection/FastTextLanguageDetection.d.ts +1 -1
  113. package/dist/text-language-detection/FastTextLanguageDetection.js.map +1 -1
  114. package/dist/text-language-detection/TinyLDLanguageDetection.d.ts +1 -1
  115. package/dist/text-language-detection/TinyLDLanguageDetection.js.map +1 -1
  116. package/dist/text-translation/DeepLTextTranslation.d.ts +2 -0
  117. package/dist/text-translation/DeepLTextTranslation.js +67 -0
  118. package/dist/text-translation/DeepLTextTranslation.js.map +1 -0
  119. package/dist/text-translation/GoogleTranslateTextTranslation.d.ts +10 -0
  120. package/dist/text-translation/GoogleTranslateTextTranslation.js +554 -0
  121. package/dist/text-translation/GoogleTranslateTextTranslation.js.map +1 -0
  122. package/dist/text-translation/NLLBTextTranslation.d.ts +2 -1
  123. package/dist/text-translation/NLLBTextTranslation.js +249 -19
  124. package/dist/text-translation/NLLBTextTranslation.js.map +1 -1
  125. package/dist/utilities/BinaryArrayConversion.d.ts +0 -1
  126. package/dist/utilities/BrowserRequestHeaders.d.ts +6 -0
  127. package/dist/utilities/BrowserRequestHeaders.js +52 -0
  128. package/dist/utilities/BrowserRequestHeaders.js.map +1 -0
  129. package/dist/utilities/BufferFileReadStream.d.ts +20 -0
  130. package/dist/utilities/BufferFileReadStream.js +81 -0
  131. package/dist/utilities/BufferFileReadStream.js.map +1 -0
  132. package/dist/utilities/DynamicUint8Array.d.ts +9 -0
  133. package/dist/utilities/DynamicUint8Array.js +31 -0
  134. package/dist/utilities/DynamicUint8Array.js.map +1 -0
  135. package/dist/utilities/FileSystem.d.ts +0 -2
  136. package/dist/utilities/Hashing.d.ts +3 -10
  137. package/dist/utilities/Hashing.js +10 -127
  138. package/dist/utilities/Hashing.js.map +1 -1
  139. package/dist/utilities/LEB128.d.ts +15 -5
  140. package/dist/utilities/LEB128.js +199 -119
  141. package/dist/utilities/LEB128.js.map +1 -1
  142. package/dist/utilities/LPVarInt.d.ts +11 -0
  143. package/dist/utilities/LPVarInt.js +187 -0
  144. package/dist/utilities/LPVarInt.js.map +1 -0
  145. package/dist/utilities/Locale.d.ts +1 -1
  146. package/dist/utilities/Locale.js +1 -1
  147. package/dist/utilities/OnnxUtilities.d.ts +1 -2
  148. package/dist/utilities/PVarInt.d.ts +4 -0
  149. package/dist/utilities/PVarInt.js +166 -0
  150. package/dist/utilities/PVarInt.js.map +1 -0
  151. package/dist/utilities/PackageManager.js +48 -25
  152. package/dist/utilities/PackageManager.js.map +1 -1
  153. package/dist/utilities/RandomGenerator.d.ts +3 -17
  154. package/dist/utilities/RandomGenerator.js +12 -81
  155. package/dist/utilities/RandomGenerator.js.map +1 -1
  156. package/dist/utilities/Timeline.d.ts +2 -0
  157. package/dist/utilities/Timeline.js +129 -20
  158. package/dist/utilities/Timeline.js.map +1 -1
  159. package/dist/utilities/Utilities.d.ts +1 -3
  160. package/dist/utilities/Utilities.js +30 -3
  161. package/dist/utilities/Utilities.js.map +1 -1
  162. package/dist/utilities/VarInt.d.ts +4 -0
  163. package/dist/utilities/VarInt.js +166 -0
  164. package/dist/utilities/VarInt.js.map +1 -0
  165. package/dist/utilities/VirtualFileReadStream.d.ts +20 -0
  166. package/dist/utilities/VirtualFileReadStream.js +79 -0
  167. package/dist/utilities/VirtualFileReadStream.js.map +1 -0
  168. package/dist/utilities/WebReader.js +7 -23
  169. package/dist/utilities/WebReader.js.map +1 -1
  170. package/dist/voice-activity-detection/SileroVAD.d.ts +0 -1
  171. package/docs/API.md +105 -3
  172. package/docs/CLI.md +51 -1
  173. package/docs/Engines.md +32 -3
  174. package/docs/Options.md +53 -12
  175. package/docs/Tasklist.md +1 -13
  176. package/package.json +20 -24
  177. package/src/alignment/DTWMfccSequenceAlignment.ts +5 -5
  178. package/src/alignment/DTWSequenceAlignmentWindowed.ts +1 -3
  179. package/src/alignment/SemanticTextAlignment.ts +467 -0
  180. package/src/alignment/SpeechAlignment.ts +214 -56
  181. package/src/api/API.ts +18 -2
  182. package/src/api/APIOptions.ts +14 -1
  183. package/src/api/Alignment.ts +31 -9
  184. package/src/api/LanguageDetectionCommon.ts +7 -0
  185. package/src/api/Recognition.ts +2 -0
  186. package/src/api/{LanguageDetection.ts → SpeechLanguageDetection.ts} +1 -119
  187. package/src/api/{Translation.ts → SpeechTranslation.ts} +2 -2
  188. package/src/api/Synthesis.ts +4 -4
  189. package/src/api/TextLanguageDetection.ts +116 -0
  190. package/src/api/TextTranslation.ts +177 -0
  191. package/src/api/TimelineTranslationAlignment.ts +162 -0
  192. package/src/api/TranscriptAndTranslationAlignment.ts +164 -0
  193. package/src/api/TranslationAlignment.ts +12 -10
  194. package/src/api/VoiceActivityDetection.ts +24 -3
  195. package/src/audio/AudioPlayer.ts +2 -0
  196. package/src/cli/CLI.ts +376 -40
  197. package/src/codecs/FFMpegTranscoder.ts +6 -0
  198. package/src/dsp/FFT.ts +8 -2
  199. package/src/dsp/KWeightingFilter.ts +1 -1
  200. package/src/dsp/MelSpectogram.ts +17 -8
  201. package/src/math/VectorMath.ts +15 -15
  202. package/src/nlp/ChineseSegmentation.ts +6 -4
  203. package/src/nlp/Segmentation.ts +18 -13
  204. package/src/recognition/OpenAICloudSTT.ts +47 -29
  205. package/src/recognition/WhisperCppSTT.ts +26 -11
  206. package/src/recognition/WhisperSTT.ts +364 -49
  207. package/src/server/Client.ts +3 -2
  208. package/src/server/Worker.ts +3 -2
  209. package/src/source-separation/MDXNetSourceSeparation.ts +1 -1
  210. package/src/speech-embeddings/WavToVec2BertFeatureEmbeddings.ts +107 -0
  211. package/src/speech-language-detection/SileroLanguageDetection.ts +2 -1
  212. package/src/subtitles/Subtitles.ts +2 -2
  213. package/src/synthesis/GoogleTranslateTTS.ts +7 -21
  214. package/src/synthesis/VitsTTS.ts +31 -3
  215. package/src/tests/Test.ts +1 -38
  216. package/src/text-language-detection/FastTextLanguageDetection.ts +1 -1
  217. package/src/text-language-detection/TinyLDLanguageDetection.ts +1 -1
  218. package/src/text-translation/DeepLTextTranslation.ts +88 -0
  219. package/src/text-translation/GoogleTranslateTextTranslation.ts +667 -0
  220. package/src/text-translation/NLLBTextTranslation.ts +261 -21
  221. package/src/typings/Fillers.d.ts +25 -2
  222. package/src/utilities/BrowserRequestHeaders.ts +59 -0
  223. package/src/utilities/DynamicUint8Array.ts +39 -0
  224. package/src/utilities/Hashing.ts +14 -167
  225. package/src/utilities/LEB128.ts +273 -148
  226. package/src/utilities/LPVarInt.ts +292 -0
  227. package/src/utilities/Locale.ts +1 -1
  228. package/src/utilities/OnnxUtilities.ts +1 -1
  229. package/src/utilities/PackageManager.ts +51 -30
  230. package/src/utilities/RandomGenerator.ts +12 -113
  231. package/src/utilities/Timeline.ts +162 -23
  232. package/src/utilities/Utilities.ts +40 -3
  233. package/src/utilities/VirtualFileReadStream.ts +109 -0
  234. package/src/utilities/WebReader.ts +9 -23
  235. package/dist/alignment/TextAlignment.js +0 -156
  236. package/dist/alignment/TextAlignment.js.map +0 -1
  237. package/dist/api/LanguageDetection.js.map +0 -1
  238. package/dist/api/Translation.js.map +0 -1
  239. package/src/alignment/TextAlignment.ts +0 -234
  240. /package/dist/api/{Translation.d.ts → SpeechTranslation.d.ts} +0 -0
@@ -0,0 +1,467 @@
1
+ import { type PreTrainedModel, type PreTrainedTokenizer } from '@echogarden/transformers-nodejs-lite'
2
+ import { Logger } from '../utilities/Logger.js'
3
+ import { loadPackage } from '../utilities/PackageManager.js'
4
+ import { alignDTWWindowed } from './DTWSequenceAlignmentWindowed.js'
5
+ import { cosineDistance } from '../math/VectorMath.js'
6
+ import { isPunctuation, isWord, splitToSentences, splitToWords } from '../nlp/Segmentation.js'
7
+ import { Timeline, extractEntries } from '../utilities/Timeline.js'
8
+
9
+ export async function alignTimelineToTextSemantically(timeline: Timeline, text: string, textLangCode: string) {
10
+ const logger = new Logger()
11
+
12
+ logger.start(`Prepare text for semantic alignment`)
13
+
14
+ const timelineSentenceEntries = extractEntries(timeline, entry => entry.type === 'sentence')
15
+
16
+ const timelineWordEntryGroups: Timeline[] = []
17
+ const timelineWordGroups: string[][] = []
18
+
19
+ for (const sentenceEntry of timelineSentenceEntries) {
20
+ const wordEntryGroup = sentenceEntry.timeline!
21
+ .filter(wordEntry => isWord(wordEntry.text))
22
+
23
+ timelineWordEntryGroups.push(wordEntryGroup)
24
+ timelineWordGroups.push(wordEntryGroup.map(wordEntry => wordEntry.text))
25
+ }
26
+
27
+ const timelineWordEntriesFiltered = timelineWordEntryGroups.flat()
28
+
29
+ const textSentences = splitToSentences(text, textLangCode)
30
+
31
+ const textWordGroups: string[][] = []
32
+
33
+ for (const sentenceText of textSentences) {
34
+ let wordGroup = await splitToWords(sentenceText, textLangCode)
35
+ wordGroup = wordGroup.filter(word => isWord(word))
36
+
37
+ textWordGroups.push(wordGroup)
38
+ }
39
+
40
+ const textWords = textWordGroups.flat()
41
+
42
+ logger.end()
43
+
44
+ const wordMappingEntries = await alignWordsToWordsSemantically(timelineWordGroups, textWordGroups)
45
+
46
+ logger.start(`Build timeline for translation`)
47
+
48
+ const mappingGroups = new Map<number, number[]>()
49
+
50
+ for (const wordMappingEntry of wordMappingEntries) {
51
+ const wordIndex1 = wordMappingEntry.wordIndex1
52
+ const wordIndex2 = wordMappingEntry.wordIndex2
53
+
54
+ let group = mappingGroups.get(wordIndex1)
55
+
56
+ if (!group) {
57
+ group = []
58
+ mappingGroups.set(wordIndex1, group)
59
+ }
60
+
61
+ if (!group.includes(wordIndex2)) {
62
+ group.push(wordIndex2)
63
+ }
64
+ }
65
+
66
+ type TimeSlice = { startTime: number, endTime: number }
67
+
68
+ const timeSlicesLookup = new Map<number, TimeSlice[]>()
69
+
70
+ for (const [wordIndex1, mappedWordIndexes] of mappingGroups) {
71
+ if (mappedWordIndexes.length === 0) {
72
+ continue
73
+ }
74
+
75
+ const startTime = timelineWordEntriesFiltered[wordIndex1].startTime
76
+ const endTime = timelineWordEntriesFiltered[wordIndex1].endTime
77
+
78
+ const splitCount = mappedWordIndexes.length
79
+
80
+ const sliceDuration = (endTime - startTime) / splitCount
81
+
82
+ let timeOffset = 0
83
+
84
+ for (let i = 0; i < splitCount; i++) {
85
+ const timeSlice: TimeSlice = {
86
+ startTime: startTime + timeOffset,
87
+ endTime: startTime + timeOffset + sliceDuration
88
+ }
89
+
90
+ const wordIndex2 = mappedWordIndexes[i]
91
+
92
+ let timeSlicesForTargetWord = timeSlicesLookup.get(wordIndex2)
93
+
94
+ if (!timeSlicesForTargetWord) {
95
+ timeSlicesForTargetWord = []
96
+ timeSlicesLookup.set(wordIndex2, timeSlicesForTargetWord)
97
+ }
98
+
99
+ timeSlicesForTargetWord.push(timeSlice)
100
+
101
+ timeOffset += sliceDuration
102
+ }
103
+ }
104
+
105
+ const resultTimeline: Timeline = []
106
+
107
+ for (const [key, value] of timeSlicesLookup) {
108
+ resultTimeline.push({
109
+ type: 'word',
110
+ text: textWords[key],
111
+ startTime: value[0].startTime,
112
+ endTime: value[value.length - 1].endTime
113
+ })
114
+ }
115
+
116
+ logger.end()
117
+
118
+ return resultTimeline
119
+ }
120
+
121
+ export async function alignWordsToWordsSemantically(wordsGroups1: string[][], wordsGroups2: string[][], windowTokenCount = 20000) {
122
+ const logger = new Logger()
123
+
124
+ // Load embedding model
125
+ const modelPath = await loadPackage(`xenova-multilingual-e5-small-fp16`)
126
+
127
+ const embeddingModel = new E5TextEmbedding(modelPath)
128
+
129
+ logger.start(`Initialize E5 embedding model`)
130
+ await embeddingModel.initializeIfNeeded()
131
+
132
+ async function extractEmbeddingsFromWordGroups(wordGroups: string[][]) {
133
+ const logger = new Logger()
134
+
135
+ const maxTokensPerFragment = 512
136
+ const { Tensor } = await import('@echogarden/transformers-nodejs-lite')
137
+
138
+ const words: string[] = []
139
+
140
+ const embeddings: TokenEmbeddingData[] = []
141
+ const tokenToWordIndexMapping: number[] = []
142
+
143
+ for (const wordGroup of wordGroups) {
144
+ const { joinedText: joinedTextForGroup, offsets: offsetsForGroup } = joinAndGetOffsets(wordGroup)
145
+
146
+ logger.start(`Tokenize text`)
147
+ const inputsForGroup = await embeddingModel.tokenizeToModelInputs(joinedTextForGroup)
148
+
149
+ logger.start(`Infer embeddings for text`)
150
+
151
+ const allTokenIds = inputsForGroup['input_ids'].data
152
+ const allAttentionMask = inputsForGroup['attention_mask'].data
153
+
154
+ let embeddingsForGroup: TokenEmbeddingData[] = []
155
+
156
+ for (let tokenStart = 0; tokenStart < allTokenIds.length; tokenStart += maxTokensPerFragment) {
157
+ const tokenEnd = Math.min(tokenStart + maxTokensPerFragment, allTokenIds.length)
158
+ const fragmentTokenCount = tokenEnd - tokenStart
159
+
160
+ const fragmentInputIdsTensor = new Tensor('int64', allTokenIds.slice(tokenStart, tokenEnd), [1, fragmentTokenCount])
161
+ const fragmentAttentionMaskTensor = new Tensor('int64', allAttentionMask.slice(tokenStart, tokenEnd), [1, fragmentTokenCount])
162
+
163
+ const inputsForFragment = { input_ids: fragmentInputIdsTensor, attention_mask: fragmentAttentionMaskTensor }
164
+
165
+ const embeddingsForFragment = await embeddingModel.inferTokenEmbeddings(inputsForFragment)
166
+
167
+ embeddingsForGroup.push(...embeddingsForFragment)
168
+ }
169
+
170
+ logger.start(`Compute token to word mapping for text`)
171
+ const filteredEmbeddingsForGroup = embeddingsForGroup.filter((embedding) => embedding.text !== '▁' && embedding.text !== '<s>' && embedding.text !== '</s>')
172
+ const tokenToWordIndexMappingForGroup = mapTokenEmbeddingsToWordIndexes(filteredEmbeddingsForGroup, joinedTextForGroup, offsetsForGroup)
173
+ const tokenToWordIndexMappingForGroupWithOffset = tokenToWordIndexMappingForGroup.map(value => words.length + value)
174
+
175
+ embeddings.push(...filteredEmbeddingsForGroup)
176
+ tokenToWordIndexMapping.push(...tokenToWordIndexMappingForGroupWithOffset)
177
+
178
+ words.push(...wordGroup)
179
+ }
180
+
181
+ return { words, embeddings, tokenToWordIndexMapping }
182
+ }
183
+
184
+ logger.start(`Extract embeddings from source 1`)
185
+ const {
186
+ words: words1,
187
+ embeddings: embeddings1,
188
+ tokenToWordIndexMapping: tokenToWordIndexMapping1
189
+ } = await extractEmbeddingsFromWordGroups(wordsGroups1)
190
+
191
+ logger.start(`Extract embeddings from source 2`)
192
+ const {
193
+ words: words2,
194
+ embeddings: embeddings2,
195
+ tokenToWordIndexMapping: tokenToWordIndexMapping2
196
+ } = await extractEmbeddingsFromWordGroups(wordsGroups2)
197
+
198
+ // Align
199
+ function costFunction(a: TokenEmbeddingData, b: TokenEmbeddingData) {
200
+ const aIsPunctuation = isPunctuation(a.text)
201
+ const bIsPunctuation = isPunctuation(b.text)
202
+
203
+ if (aIsPunctuation === bIsPunctuation) {
204
+ return cosineDistance(a.embeddingVector, b.embeddingVector)
205
+ } else {
206
+ return 1.0
207
+ }
208
+ }
209
+
210
+ logger.start(`Align token embedding vectors using DTW`)
211
+
212
+ const { path } = alignDTWWindowed(embeddings1, embeddings2, costFunction, windowTokenCount)
213
+
214
+ // Use alignment path to words to words
215
+ logger.start(`Map tokens to words`)
216
+
217
+ const wordMapping: WordMapping[] = []
218
+
219
+ for (let i = 0; i < path.length; i++) {
220
+ const pathEntry = path[i]
221
+
222
+ const sourceTokenIndex = pathEntry.source
223
+ const destTokenIndex = pathEntry.dest
224
+
225
+ const mappedWordIndex1 = tokenToWordIndexMapping1[sourceTokenIndex]
226
+ const mappedWordIndex2 = tokenToWordIndexMapping2[destTokenIndex]
227
+
228
+ wordMapping.push({
229
+ wordIndex1: mappedWordIndex1,
230
+ word1: words1[mappedWordIndex1],
231
+ wordIndex2: mappedWordIndex2,
232
+ word2: words2[mappedWordIndex2],
233
+ })
234
+ }
235
+
236
+ logger.end()
237
+
238
+ return wordMapping
239
+ }
240
+
241
+ function mapTokenEmbeddingsToWordIndexes(embeddings: TokenEmbeddingData[], text: string, textWordOffsets: number[]) {
242
+ const tokenToWordIndex: number[] = []
243
+
244
+ let currentTextOffset = 0
245
+
246
+ for (let i = 0; i < embeddings.length; i++) {
247
+ const embedding = embeddings[i]
248
+ let tokenText = embedding.text
249
+
250
+ if (tokenText === '<s>' || tokenText === '</s>') {
251
+ tokenToWordIndex.push(-1)
252
+
253
+ continue
254
+ }
255
+
256
+ if (tokenText.startsWith('▁')) {
257
+ tokenText = tokenText.substring(1)
258
+ }
259
+
260
+ const matchPosition = text.indexOf(tokenText, currentTextOffset)
261
+
262
+ if (matchPosition === -1) {
263
+ throw new Error(`Token '${tokenText}' not found in text`)
264
+ }
265
+
266
+ currentTextOffset = matchPosition + tokenText.length
267
+
268
+ let tokenMatchingWordIndex = textWordOffsets.findIndex((index) => index > matchPosition)
269
+
270
+ if (tokenMatchingWordIndex === -1) {
271
+ throw new Error(`Token '${tokenText}' not found in text`)
272
+ } else {
273
+ tokenMatchingWordIndex = Math.max(tokenMatchingWordIndex - 1, 0)
274
+ }
275
+
276
+ tokenToWordIndex.push(tokenMatchingWordIndex)
277
+ }
278
+
279
+ return tokenToWordIndex
280
+ }
281
+
282
+ function joinAndGetOffsets(words: string[]) {
283
+ let joinedText = ''
284
+ const offsets: number[] = []
285
+
286
+ let offset = 0
287
+
288
+ for (const word of words) {
289
+ const extendedWord = `${word} `
290
+ joinedText += extendedWord
291
+
292
+ offsets.push(offset)
293
+
294
+ offset += extendedWord.length
295
+ }
296
+
297
+ offsets.push(joinedText.length)
298
+
299
+ return { joinedText, offsets }
300
+ }
301
+
302
+ export class E5TextEmbedding {
303
+ tokenizer?: PreTrainedTokenizer
304
+ model?: PreTrainedModel
305
+
306
+ constructor(public readonly modelPath: string) {
307
+ }
308
+
309
+ async tokenizeToModelInputs(text: string) {
310
+ await this.initializeIfNeeded()
311
+
312
+ const inputs = await this.tokenizer!(text)
313
+
314
+ return inputs
315
+ }
316
+
317
+ async inferTokenEmbeddings(inputs: any) {
318
+ await this.initializeIfNeeded()
319
+
320
+ const tokensText = this.tokenizer!.model.convert_ids_to_tokens(Array.from(inputs.input_ids.data))
321
+
322
+ const result = await this.model!(inputs)
323
+
324
+ const lastHiddenState = result.last_hidden_state
325
+
326
+ const tokenCount = lastHiddenState.dims[1]
327
+ const embeddingSize = lastHiddenState.dims[2]
328
+
329
+ const tokenEmbeddings: TokenEmbeddingData[] = []
330
+
331
+ for (let i = 0; i < tokenCount; i++) {
332
+ const tokenEmbeddingVector = lastHiddenState.data.slice(i * embeddingSize, (i + 1) * embeddingSize)
333
+
334
+ const tokenId = Number(inputs.input_ids.data[i])
335
+ const tokenText = tokensText[i]
336
+
337
+ tokenEmbeddings.push({
338
+ id: tokenId,
339
+ text: tokenText,
340
+ embeddingVector: tokenEmbeddingVector
341
+ })
342
+ }
343
+
344
+ return tokenEmbeddings
345
+ }
346
+
347
+ async initializeIfNeeded() {
348
+ if (this.tokenizer && this.model) {
349
+ return
350
+ }
351
+
352
+ const { AutoTokenizer, AutoModel } = await import('@echogarden/transformers-nodejs-lite')
353
+
354
+ this.tokenizer = await AutoTokenizer.from_pretrained(this.modelPath)
355
+ this.model = await AutoModel.from_pretrained(this.modelPath)
356
+ }
357
+ }
358
+
359
+ export interface TokenEmbeddingData {
360
+ id: number
361
+ text: string
362
+ embeddingVector: Float32Array
363
+ }
364
+
365
+ export interface WordMapping {
366
+ wordIndex1: number
367
+ word1: string
368
+
369
+ wordIndex2: number
370
+ word2: string
371
+ }
372
+
373
+ export const e5SupportedLanguages: string[] = [
374
+ 'af', // Afrikaans
375
+ 'am', // Amharic
376
+ 'ar', // Arabic
377
+ 'as', // Assamese
378
+ 'az', // Azerbaijani
379
+ 'be', // Belarusian
380
+ 'bg', // Bulgarian
381
+ 'bn', // Bengali
382
+ 'br', // Breton
383
+ 'bs', // Bosnian
384
+ 'ca', // Catalan
385
+ 'cs', // Czech
386
+ 'cy', // Welsh
387
+ 'da', // Danish
388
+ 'de', // German
389
+ 'el', // Greek
390
+ 'en', // English
391
+ 'eo', // Esperanto
392
+ 'es', // Spanish
393
+ 'et', // Estonian
394
+ 'eu', // Basque
395
+ 'fa', // Persian
396
+ 'fi', // Finnish
397
+ 'fr', // French
398
+ 'fy', // Western Frisian
399
+ 'ga', // Irish
400
+ 'gd', // Scottish Gaelic
401
+ 'gl', // Galician
402
+ 'gu', // Gujarati
403
+ 'ha', // Hausa
404
+ 'he', // Hebrew
405
+ 'hi', // Hindi
406
+ 'hr', // Croatian
407
+ 'hu', // Hungarian
408
+ 'hy', // Armenian
409
+ 'id', // Indonesian
410
+ 'is', // Icelandic
411
+ 'it', // Italian
412
+ 'ja', // Japanese
413
+ 'jv', // Javanese
414
+ 'ka', // Georgian
415
+ 'kk', // Kazakh
416
+ 'km', // Khmer
417
+ 'kn', // Kannada
418
+ 'ko', // Korean
419
+ 'ku', // Kurdish
420
+ 'ky', // Kyrgyz
421
+ 'la', // Latin
422
+ 'lo', // Lao
423
+ 'lt', // Lithuanian
424
+ 'lv', // Latvian
425
+ 'mg', // Malagasy
426
+ 'mk', // Macedonian
427
+ 'ml', // Malayalam
428
+ 'mn', // Mongolian
429
+ 'mr', // Marathi
430
+ 'ms', // Malay
431
+ 'my', // Burmese
432
+ 'ne', // Nepali
433
+ 'nl', // Dutch
434
+ 'no', // Norwegian
435
+ 'om', // Oromo
436
+ 'or', // Oriya
437
+ 'pa', // Panjabi
438
+ 'pl', // Polish
439
+ 'ps', // Pashto
440
+ 'pt', // Portuguese
441
+ 'ro', // Romanian
442
+ 'ru', // Russian
443
+ 'sa', // Sanskrit
444
+ 'sd', // Sindhi
445
+ 'si', // Sinhala
446
+ 'sk', // Slovak
447
+ 'sl', // Slovenian
448
+ 'so', // Somali
449
+ 'sq', // Albanian
450
+ 'sr', // Serbian
451
+ 'su', // Sundanese
452
+ 'sv', // Swedish
453
+ 'sw', // Swahili
454
+ 'ta', // Tamil
455
+ 'te', // Telugu
456
+ 'th', // Thai
457
+ 'tl', // Tagalog
458
+ 'tr', // Turkish
459
+ 'ug', // Uyghur
460
+ 'uk', // Ukrainian
461
+ 'ur', // Urdu
462
+ 'uz', // Uzbek
463
+ 'vi', // Vietnamese
464
+ 'xh', // Xhosa
465
+ 'yi', // Yiddish
466
+ 'zh', // Chinese
467
+ ]