cohere-transcribe 0.1.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 (480) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +21 -0
  3. data/LICENSE.txt +201 -0
  4. data/NOTICE +5 -0
  5. data/README.md +265 -0
  6. data/THIRD_PARTY_NOTICES.md +67 -0
  7. data/exe/cohere-transcribe +8 -0
  8. data/exe/cohere-transcribe-doctor +8 -0
  9. data/ext/cohere_transcribe_native/CMakeLists.txt +262 -0
  10. data/ext/cohere_transcribe_native/README.md +26 -0
  11. data/ext/cohere_transcribe_native/audio_abi.cpp +1416 -0
  12. data/ext/cohere_transcribe_native/audio_exports.macos +5 -0
  13. data/ext/cohere_transcribe_native/audio_exports.map +10 -0
  14. data/ext/cohere_transcribe_native/cohere_abi.cpp +1209 -0
  15. data/ext/cohere_transcribe_native/exports.macos +40 -0
  16. data/ext/cohere_transcribe_native/exports.map +45 -0
  17. data/ext/cohere_transcribe_native/extconf.rb +89 -0
  18. data/ext/cohere_transcribe_native/test/abi_smoke.rb +232 -0
  19. data/ext/cohere_transcribe_native/test/audio_matrix_smoke.cpp +121 -0
  20. data/ext/cohere_transcribe_native/test/audio_reliability_smoke.cpp +198 -0
  21. data/ext/cohere_transcribe_native/test/native_abi_reliability_smoke.cpp +186 -0
  22. data/ext/cohere_transcribe_native/test/native_batch_projection_probe.rb +81 -0
  23. data/ext/cohere_transcribe_native/test/native_cancellation_smoke.cpp +194 -0
  24. data/lib/cohere/transcribe/alignment/ATTRIBUTION.md +23 -0
  25. data/lib/cohere/transcribe/alignment/LICENSE.ctc-forced-aligner +407 -0
  26. data/lib/cohere/transcribe/alignment/LICENSE.torchaudio +25 -0
  27. data/lib/cohere/transcribe/alignment/LICENSE.uroman +26 -0
  28. data/lib/cohere/transcribe/alignment/aligner.rb +476 -0
  29. data/lib/cohere/transcribe/alignment/ctc.rb +224 -0
  30. data/lib/cohere/transcribe/alignment/text.rb +237 -0
  31. data/lib/cohere/transcribe/alignment/uroman_data.rb +4950 -0
  32. data/lib/cohere/transcribe/api.rb +173 -0
  33. data/lib/cohere/transcribe/asr/batching.rb +472 -0
  34. data/lib/cohere/transcribe/asr/failure_policy.rb +112 -0
  35. data/lib/cohere/transcribe/asr/native.rb +676 -0
  36. data/lib/cohere/transcribe/audio/ATTRIBUTION.md +8 -0
  37. data/lib/cohere/transcribe/audio/LICENSE.auditok +21 -0
  38. data/lib/cohere/transcribe/audio/decoder.rb +315 -0
  39. data/lib/cohere/transcribe/audio/ffmpeg_native.rb +248 -0
  40. data/lib/cohere/transcribe/audio/segmentation.rb +260 -0
  41. data/lib/cohere/transcribe/cli.rb +727 -0
  42. data/lib/cohere/transcribe/configuration.rb +282 -0
  43. data/lib/cohere/transcribe/constants.rb +14 -0
  44. data/lib/cohere/transcribe/dense_converter.rb +548 -0
  45. data/lib/cohere/transcribe/doctor.rb +576 -0
  46. data/lib/cohere/transcribe/errors.rb +57 -0
  47. data/lib/cohere/transcribe/gguf_writer.rb +268 -0
  48. data/lib/cohere/transcribe/hub.rb +436 -0
  49. data/lib/cohere/transcribe/input.rb +110 -0
  50. data/lib/cohere/transcribe/licenses/crispasr.txt +21 -0
  51. data/lib/cohere/transcribe/loader.rb +128 -0
  52. data/lib/cohere/transcribe/model_identity.rb +440 -0
  53. data/lib/cohere/transcribe/output/publication.rb +1118 -0
  54. data/lib/cohere/transcribe/output/rendering.rb +105 -0
  55. data/lib/cohere/transcribe/output/timing.rb +86 -0
  56. data/lib/cohere/transcribe/python_text.rb +70 -0
  57. data/lib/cohere/transcribe/pytorch_checkpoint.rb +1180 -0
  58. data/lib/cohere/transcribe/runtime/engine.rb +1676 -0
  59. data/lib/cohere/transcribe/runtime/model_provider.rb +390 -0
  60. data/lib/cohere/transcribe/runtime/precision.rb +57 -0
  61. data/lib/cohere/transcribe/runtime/preparation.rb +215 -0
  62. data/lib/cohere/transcribe/runtime/resources.rb +165 -0
  63. data/lib/cohere/transcribe/runtime/word_pipeline.rb +364 -0
  64. data/lib/cohere/transcribe/safetensors.rb +579 -0
  65. data/lib/cohere/transcribe/state/checkpoint.rb +224 -0
  66. data/lib/cohere/transcribe/state/contracts.rb +141 -0
  67. data/lib/cohere/transcribe/state/io.rb +727 -0
  68. data/lib/cohere/transcribe/state/locking.rb +211 -0
  69. data/lib/cohere/transcribe/state/manifest.rb +155 -0
  70. data/lib/cohere/transcribe/state.rb +7 -0
  71. data/lib/cohere/transcribe/types.rb +535 -0
  72. data/lib/cohere/transcribe/vad/ATTRIBUTION.md +14 -0
  73. data/lib/cohere/transcribe/vad/LICENSE.faster-whisper +21 -0
  74. data/lib/cohere/transcribe/vad/LICENSE.silero-vad +21 -0
  75. data/lib/cohere/transcribe/vad/silero.rb +344 -0
  76. data/lib/cohere/transcribe/vad/silero_vad_v6.onnx +0 -0
  77. data/lib/cohere/transcribe/vad/timestamps.rb +219 -0
  78. data/lib/cohere/transcribe/version.rb +7 -0
  79. data/lib/cohere/transcribe.rb +26 -0
  80. data/sig/cohere/transcribe.rbs +250 -0
  81. data/vendor/crispasr/AUTHORS +510 -0
  82. data/vendor/crispasr/LICENSE +21 -0
  83. data/vendor/crispasr/UPSTREAM.md +9 -0
  84. data/vendor/crispasr/VERSION +1 -0
  85. data/vendor/crispasr/ggml/AUTHORS +335 -0
  86. data/vendor/crispasr/ggml/CMakeLists.txt +512 -0
  87. data/vendor/crispasr/ggml/LICENSE +21 -0
  88. data/vendor/crispasr/ggml/README.md +49 -0
  89. data/vendor/crispasr/ggml/cmake/FindNCCL.cmake +36 -0
  90. data/vendor/crispasr/ggml/cmake/GitVars.cmake +22 -0
  91. data/vendor/crispasr/ggml/cmake/common.cmake +50 -0
  92. data/vendor/crispasr/ggml/cmake/ggml-config.cmake.in +191 -0
  93. data/vendor/crispasr/ggml/ggml.pc.in +10 -0
  94. data/vendor/crispasr/ggml/include/ggml-alloc.h +85 -0
  95. data/vendor/crispasr/ggml/include/ggml-backend.h +431 -0
  96. data/vendor/crispasr/ggml/include/ggml-blas.h +25 -0
  97. data/vendor/crispasr/ggml/include/ggml-cann.h +123 -0
  98. data/vendor/crispasr/ggml/include/ggml-cpp.h +39 -0
  99. data/vendor/crispasr/ggml/include/ggml-cpu.h +151 -0
  100. data/vendor/crispasr/ggml/include/ggml-cuda.h +50 -0
  101. data/vendor/crispasr/ggml/include/ggml-hexagon.h +19 -0
  102. data/vendor/crispasr/ggml/include/ggml-metal.h +61 -0
  103. data/vendor/crispasr/ggml/include/ggml-opencl.h +26 -0
  104. data/vendor/crispasr/ggml/include/ggml-openvino.h +37 -0
  105. data/vendor/crispasr/ggml/include/ggml-opt.h +256 -0
  106. data/vendor/crispasr/ggml/include/ggml-rpc.h +35 -0
  107. data/vendor/crispasr/ggml/include/ggml-sycl.h +49 -0
  108. data/vendor/crispasr/ggml/include/ggml-virtgpu.h +14 -0
  109. data/vendor/crispasr/ggml/include/ggml-vulkan.h +29 -0
  110. data/vendor/crispasr/ggml/include/ggml-webgpu.h +19 -0
  111. data/vendor/crispasr/ggml/include/ggml-zdnn.h +17 -0
  112. data/vendor/crispasr/ggml/include/ggml-zendnn.h +22 -0
  113. data/vendor/crispasr/ggml/include/ggml.h +2887 -0
  114. data/vendor/crispasr/ggml/include/gguf.h +204 -0
  115. data/vendor/crispasr/ggml/src/CMakeLists.txt +493 -0
  116. data/vendor/crispasr/ggml/src/ggml-alloc.c +1323 -0
  117. data/vendor/crispasr/ggml/src/ggml-backend-dl.cpp +48 -0
  118. data/vendor/crispasr/ggml/src/ggml-backend-dl.h +44 -0
  119. data/vendor/crispasr/ggml/src/ggml-backend-impl.h +275 -0
  120. data/vendor/crispasr/ggml/src/ggml-backend-meta.cpp +2145 -0
  121. data/vendor/crispasr/ggml/src/ggml-backend-reg.cpp +586 -0
  122. data/vendor/crispasr/ggml/src/ggml-backend.cpp +2437 -0
  123. data/vendor/crispasr/ggml/src/ggml-common.h +1900 -0
  124. data/vendor/crispasr/ggml/src/ggml-cpu/CMakeLists.txt +718 -0
  125. data/vendor/crispasr/ggml/src/ggml-cpu/amx/amx.cpp +249 -0
  126. data/vendor/crispasr/ggml/src/ggml-cpu/amx/amx.h +8 -0
  127. data/vendor/crispasr/ggml/src/ggml-cpu/amx/common.h +115 -0
  128. data/vendor/crispasr/ggml/src/ggml-cpu/amx/mmq.cpp +2512 -0
  129. data/vendor/crispasr/ggml/src/ggml-cpu/amx/mmq.h +10 -0
  130. data/vendor/crispasr/ggml/src/ggml-cpu/arch/arm/cpu-feats.cpp +98 -0
  131. data/vendor/crispasr/ggml/src/ggml-cpu/arch/arm/quants.c +4244 -0
  132. data/vendor/crispasr/ggml/src/ggml-cpu/arch/arm/repack.cpp +5156 -0
  133. data/vendor/crispasr/ggml/src/ggml-cpu/arch/loongarch/quants.c +2158 -0
  134. data/vendor/crispasr/ggml/src/ggml-cpu/arch/powerpc/cpu-feats.cpp +82 -0
  135. data/vendor/crispasr/ggml/src/ggml-cpu/arch/powerpc/quants.c +2304 -0
  136. data/vendor/crispasr/ggml/src/ggml-cpu/arch/riscv/cpu-feats.cpp +38 -0
  137. data/vendor/crispasr/ggml/src/ggml-cpu/arch/riscv/quants.c +4455 -0
  138. data/vendor/crispasr/ggml/src/ggml-cpu/arch/riscv/repack.cpp +1703 -0
  139. data/vendor/crispasr/ggml/src/ggml-cpu/arch/s390/cpu-feats.cpp +50 -0
  140. data/vendor/crispasr/ggml/src/ggml-cpu/arch/s390/quants.c +1465 -0
  141. data/vendor/crispasr/ggml/src/ggml-cpu/arch/wasm/quants.c +1220 -0
  142. data/vendor/crispasr/ggml/src/ggml-cpu/arch/x86/cpu-feats.cpp +327 -0
  143. data/vendor/crispasr/ggml/src/ggml-cpu/arch/x86/quants.c +3970 -0
  144. data/vendor/crispasr/ggml/src/ggml-cpu/arch/x86/repack.cpp +6407 -0
  145. data/vendor/crispasr/ggml/src/ggml-cpu/arch-fallback.h +349 -0
  146. data/vendor/crispasr/ggml/src/ggml-cpu/binary-ops.cpp +154 -0
  147. data/vendor/crispasr/ggml/src/ggml-cpu/binary-ops.h +16 -0
  148. data/vendor/crispasr/ggml/src/ggml-cpu/cmake/FindSIMD.cmake +100 -0
  149. data/vendor/crispasr/ggml/src/ggml-cpu/common.h +95 -0
  150. data/vendor/crispasr/ggml/src/ggml-cpu/ggml-cpu-impl.h +539 -0
  151. data/vendor/crispasr/ggml/src/ggml-cpu/ggml-cpu.c +3791 -0
  152. data/vendor/crispasr/ggml/src/ggml-cpu/ggml-cpu.cpp +703 -0
  153. data/vendor/crispasr/ggml/src/ggml-cpu/hbm.cpp +55 -0
  154. data/vendor/crispasr/ggml/src/ggml-cpu/hbm.h +8 -0
  155. data/vendor/crispasr/ggml/src/ggml-cpu/kleidiai/kernels.cpp +939 -0
  156. data/vendor/crispasr/ggml/src/ggml-cpu/kleidiai/kernels.h +90 -0
  157. data/vendor/crispasr/ggml/src/ggml-cpu/kleidiai/kleidiai.cpp +1513 -0
  158. data/vendor/crispasr/ggml/src/ggml-cpu/kleidiai/kleidiai.h +17 -0
  159. data/vendor/crispasr/ggml/src/ggml-cpu/llamafile/sgemm.cpp +4051 -0
  160. data/vendor/crispasr/ggml/src/ggml-cpu/llamafile/sgemm.h +25 -0
  161. data/vendor/crispasr/ggml/src/ggml-cpu/ops.cpp +11662 -0
  162. data/vendor/crispasr/ggml/src/ggml-cpu/ops.h +121 -0
  163. data/vendor/crispasr/ggml/src/ggml-cpu/quants.c +1288 -0
  164. data/vendor/crispasr/ggml/src/ggml-cpu/quants.h +103 -0
  165. data/vendor/crispasr/ggml/src/ggml-cpu/repack.cpp +4836 -0
  166. data/vendor/crispasr/ggml/src/ggml-cpu/repack.h +245 -0
  167. data/vendor/crispasr/ggml/src/ggml-cpu/simd-gemm.h +226 -0
  168. data/vendor/crispasr/ggml/src/ggml-cpu/simd-mappings.h +1329 -0
  169. data/vendor/crispasr/ggml/src/ggml-cpu/spacemit/ime.cpp +1025 -0
  170. data/vendor/crispasr/ggml/src/ggml-cpu/spacemit/ime.h +13 -0
  171. data/vendor/crispasr/ggml/src/ggml-cpu/spacemit/ime1_kernels.cpp +3196 -0
  172. data/vendor/crispasr/ggml/src/ggml-cpu/spacemit/ime_kernels.h +26 -0
  173. data/vendor/crispasr/ggml/src/ggml-cpu/traits.cpp +36 -0
  174. data/vendor/crispasr/ggml/src/ggml-cpu/traits.h +38 -0
  175. data/vendor/crispasr/ggml/src/ggml-cpu/unary-ops.cpp +336 -0
  176. data/vendor/crispasr/ggml/src/ggml-cpu/unary-ops.h +35 -0
  177. data/vendor/crispasr/ggml/src/ggml-cpu/vec.cpp +681 -0
  178. data/vendor/crispasr/ggml/src/ggml-cpu/vec.h +1606 -0
  179. data/vendor/crispasr/ggml/src/ggml-cuda/CMakeLists.txt +272 -0
  180. data/vendor/crispasr/ggml/src/ggml-cuda/acc.cu +61 -0
  181. data/vendor/crispasr/ggml/src/ggml-cuda/acc.cuh +5 -0
  182. data/vendor/crispasr/ggml/src/ggml-cuda/add-id.cu +58 -0
  183. data/vendor/crispasr/ggml/src/ggml-cuda/add-id.cuh +3 -0
  184. data/vendor/crispasr/ggml/src/ggml-cuda/arange.cu +34 -0
  185. data/vendor/crispasr/ggml/src/ggml-cuda/arange.cuh +5 -0
  186. data/vendor/crispasr/ggml/src/ggml-cuda/argmax.cu +91 -0
  187. data/vendor/crispasr/ggml/src/ggml-cuda/argmax.cuh +3 -0
  188. data/vendor/crispasr/ggml/src/ggml-cuda/argsort.cu +265 -0
  189. data/vendor/crispasr/ggml/src/ggml-cuda/argsort.cuh +19 -0
  190. data/vendor/crispasr/ggml/src/ggml-cuda/binbcast.cu +534 -0
  191. data/vendor/crispasr/ggml/src/ggml-cuda/binbcast.cuh +12 -0
  192. data/vendor/crispasr/ggml/src/ggml-cuda/clamp.cu +45 -0
  193. data/vendor/crispasr/ggml/src/ggml-cuda/clamp.cuh +5 -0
  194. data/vendor/crispasr/ggml/src/ggml-cuda/col2im-1d.cu +81 -0
  195. data/vendor/crispasr/ggml/src/ggml-cuda/col2im-1d.cuh +3 -0
  196. data/vendor/crispasr/ggml/src/ggml-cuda/common.cuh +1489 -0
  197. data/vendor/crispasr/ggml/src/ggml-cuda/concat.cu +204 -0
  198. data/vendor/crispasr/ggml/src/ggml-cuda/concat.cuh +5 -0
  199. data/vendor/crispasr/ggml/src/ggml-cuda/conv-transpose-1d.cu +97 -0
  200. data/vendor/crispasr/ggml/src/ggml-cuda/conv-transpose-1d.cuh +5 -0
  201. data/vendor/crispasr/ggml/src/ggml-cuda/conv2d-dw.cu +161 -0
  202. data/vendor/crispasr/ggml/src/ggml-cuda/conv2d-dw.cuh +5 -0
  203. data/vendor/crispasr/ggml/src/ggml-cuda/conv2d-transpose.cu +115 -0
  204. data/vendor/crispasr/ggml/src/ggml-cuda/conv2d-transpose.cuh +5 -0
  205. data/vendor/crispasr/ggml/src/ggml-cuda/conv2d.cu +166 -0
  206. data/vendor/crispasr/ggml/src/ggml-cuda/conv2d.cuh +5 -0
  207. data/vendor/crispasr/ggml/src/ggml-cuda/convert.cu +892 -0
  208. data/vendor/crispasr/ggml/src/ggml-cuda/convert.cuh +66 -0
  209. data/vendor/crispasr/ggml/src/ggml-cuda/count-equal.cu +64 -0
  210. data/vendor/crispasr/ggml/src/ggml-cuda/count-equal.cuh +5 -0
  211. data/vendor/crispasr/ggml/src/ggml-cuda/cp-async.cuh +57 -0
  212. data/vendor/crispasr/ggml/src/ggml-cuda/cpy-utils.cuh +217 -0
  213. data/vendor/crispasr/ggml/src/ggml-cuda/cpy.cu +581 -0
  214. data/vendor/crispasr/ggml/src/ggml-cuda/cpy.cuh +7 -0
  215. data/vendor/crispasr/ggml/src/ggml-cuda/cross-entropy-loss.cu +177 -0
  216. data/vendor/crispasr/ggml/src/ggml-cuda/cross-entropy-loss.cuh +7 -0
  217. data/vendor/crispasr/ggml/src/ggml-cuda/cumsum.cu +307 -0
  218. data/vendor/crispasr/ggml/src/ggml-cuda/cumsum.cuh +5 -0
  219. data/vendor/crispasr/ggml/src/ggml-cuda/dequantize.cuh +99 -0
  220. data/vendor/crispasr/ggml/src/ggml-cuda/diag.cu +77 -0
  221. data/vendor/crispasr/ggml/src/ggml-cuda/diag.cuh +5 -0
  222. data/vendor/crispasr/ggml/src/ggml-cuda/diagmask.cu +40 -0
  223. data/vendor/crispasr/ggml/src/ggml-cuda/diagmask.cuh +5 -0
  224. data/vendor/crispasr/ggml/src/ggml-cuda/fattn-common.cuh +1212 -0
  225. data/vendor/crispasr/ggml/src/ggml-cuda/fattn-mma-f16.cuh +1860 -0
  226. data/vendor/crispasr/ggml/src/ggml-cuda/fattn-tile.cu +57 -0
  227. data/vendor/crispasr/ggml/src/ggml-cuda/fattn-tile.cuh +1309 -0
  228. data/vendor/crispasr/ggml/src/ggml-cuda/fattn-vec.cuh +600 -0
  229. data/vendor/crispasr/ggml/src/ggml-cuda/fattn-wmma-f16.cu +696 -0
  230. data/vendor/crispasr/ggml/src/ggml-cuda/fattn-wmma-f16.cuh +51 -0
  231. data/vendor/crispasr/ggml/src/ggml-cuda/fattn.cu +620 -0
  232. data/vendor/crispasr/ggml/src/ggml-cuda/fattn.cuh +5 -0
  233. data/vendor/crispasr/ggml/src/ggml-cuda/fill.cu +37 -0
  234. data/vendor/crispasr/ggml/src/ggml-cuda/fill.cuh +3 -0
  235. data/vendor/crispasr/ggml/src/ggml-cuda/gated_delta_net.cu +273 -0
  236. data/vendor/crispasr/ggml/src/ggml-cuda/gated_delta_net.cuh +4 -0
  237. data/vendor/crispasr/ggml/src/ggml-cuda/getrows.cu +332 -0
  238. data/vendor/crispasr/ggml/src/ggml-cuda/getrows.cuh +15 -0
  239. data/vendor/crispasr/ggml/src/ggml-cuda/ggml-cuda.cu +5580 -0
  240. data/vendor/crispasr/ggml/src/ggml-cuda/gla.cu +93 -0
  241. data/vendor/crispasr/ggml/src/ggml-cuda/gla.cuh +3 -0
  242. data/vendor/crispasr/ggml/src/ggml-cuda/im2col.cu +274 -0
  243. data/vendor/crispasr/ggml/src/ggml-cuda/im2col.cuh +6 -0
  244. data/vendor/crispasr/ggml/src/ggml-cuda/mean.cu +75 -0
  245. data/vendor/crispasr/ggml/src/ggml-cuda/mean.cuh +3 -0
  246. data/vendor/crispasr/ggml/src/ggml-cuda/mma.cuh +1333 -0
  247. data/vendor/crispasr/ggml/src/ggml-cuda/mmf.cu +191 -0
  248. data/vendor/crispasr/ggml/src/ggml-cuda/mmf.cuh +908 -0
  249. data/vendor/crispasr/ggml/src/ggml-cuda/mmid.cu +164 -0
  250. data/vendor/crispasr/ggml/src/ggml-cuda/mmid.cuh +5 -0
  251. data/vendor/crispasr/ggml/src/ggml-cuda/mmq.cu +372 -0
  252. data/vendor/crispasr/ggml/src/ggml-cuda/mmq.cuh +4175 -0
  253. data/vendor/crispasr/ggml/src/ggml-cuda/mmvf.cu +862 -0
  254. data/vendor/crispasr/ggml/src/ggml-cuda/mmvf.cuh +14 -0
  255. data/vendor/crispasr/ggml/src/ggml-cuda/mmvq.cu +1161 -0
  256. data/vendor/crispasr/ggml/src/ggml-cuda/mmvq.cuh +16 -0
  257. data/vendor/crispasr/ggml/src/ggml-cuda/norm.cu +756 -0
  258. data/vendor/crispasr/ggml/src/ggml-cuda/norm.cuh +20 -0
  259. data/vendor/crispasr/ggml/src/ggml-cuda/opt-step-adamw.cu +78 -0
  260. data/vendor/crispasr/ggml/src/ggml-cuda/opt-step-adamw.cuh +5 -0
  261. data/vendor/crispasr/ggml/src/ggml-cuda/opt-step-sgd.cu +49 -0
  262. data/vendor/crispasr/ggml/src/ggml-cuda/opt-step-sgd.cuh +5 -0
  263. data/vendor/crispasr/ggml/src/ggml-cuda/out-prod.cu +68 -0
  264. data/vendor/crispasr/ggml/src/ggml-cuda/out-prod.cuh +3 -0
  265. data/vendor/crispasr/ggml/src/ggml-cuda/pad.cu +106 -0
  266. data/vendor/crispasr/ggml/src/ggml-cuda/pad.cuh +5 -0
  267. data/vendor/crispasr/ggml/src/ggml-cuda/pad_reflect_1d.cu +91 -0
  268. data/vendor/crispasr/ggml/src/ggml-cuda/pad_reflect_1d.cuh +5 -0
  269. data/vendor/crispasr/ggml/src/ggml-cuda/pool2d.cu +94 -0
  270. data/vendor/crispasr/ggml/src/ggml-cuda/pool2d.cuh +5 -0
  271. data/vendor/crispasr/ggml/src/ggml-cuda/quantize.cu +443 -0
  272. data/vendor/crispasr/ggml/src/ggml-cuda/quantize.cuh +41 -0
  273. data/vendor/crispasr/ggml/src/ggml-cuda/reduce_rows.cuh +39 -0
  274. data/vendor/crispasr/ggml/src/ggml-cuda/roll.cu +67 -0
  275. data/vendor/crispasr/ggml/src/ggml-cuda/roll.cuh +5 -0
  276. data/vendor/crispasr/ggml/src/ggml-cuda/rope.cu +665 -0
  277. data/vendor/crispasr/ggml/src/ggml-cuda/rope.cuh +9 -0
  278. data/vendor/crispasr/ggml/src/ggml-cuda/scale.cu +34 -0
  279. data/vendor/crispasr/ggml/src/ggml-cuda/scale.cuh +5 -0
  280. data/vendor/crispasr/ggml/src/ggml-cuda/set-rows.cu +330 -0
  281. data/vendor/crispasr/ggml/src/ggml-cuda/set-rows.cuh +7 -0
  282. data/vendor/crispasr/ggml/src/ggml-cuda/set.cu +39 -0
  283. data/vendor/crispasr/ggml/src/ggml-cuda/set.cuh +7 -0
  284. data/vendor/crispasr/ggml/src/ggml-cuda/softcap.cu +34 -0
  285. data/vendor/crispasr/ggml/src/ggml-cuda/softcap.cuh +5 -0
  286. data/vendor/crispasr/ggml/src/ggml-cuda/softmax.cu +472 -0
  287. data/vendor/crispasr/ggml/src/ggml-cuda/softmax.cuh +7 -0
  288. data/vendor/crispasr/ggml/src/ggml-cuda/solve_tri.cu +275 -0
  289. data/vendor/crispasr/ggml/src/ggml-cuda/solve_tri.cuh +3 -0
  290. data/vendor/crispasr/ggml/src/ggml-cuda/ssm-conv.cu +197 -0
  291. data/vendor/crispasr/ggml/src/ggml-cuda/ssm-conv.cuh +3 -0
  292. data/vendor/crispasr/ggml/src/ggml-cuda/ssm-scan.cu +342 -0
  293. data/vendor/crispasr/ggml/src/ggml-cuda/ssm-scan.cuh +3 -0
  294. data/vendor/crispasr/ggml/src/ggml-cuda/sum.cu +41 -0
  295. data/vendor/crispasr/ggml/src/ggml-cuda/sum.cuh +5 -0
  296. data/vendor/crispasr/ggml/src/ggml-cuda/sumrows.cu +43 -0
  297. data/vendor/crispasr/ggml/src/ggml-cuda/sumrows.cuh +4 -0
  298. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-mma-f16-instance-ncols1_1-ncols2_16.cu +5 -0
  299. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-mma-f16-instance-ncols1_1-ncols2_32.cu +6 -0
  300. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-mma-f16-instance-ncols1_1-ncols2_8.cu +11 -0
  301. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-mma-f16-instance-ncols1_16-ncols2_1.cu +10 -0
  302. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-mma-f16-instance-ncols1_16-ncols2_2.cu +10 -0
  303. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-mma-f16-instance-ncols1_16-ncols2_4.cu +12 -0
  304. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-mma-f16-instance-ncols1_2-ncols2_16.cu +5 -0
  305. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-mma-f16-instance-ncols1_2-ncols2_32.cu +6 -0
  306. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-mma-f16-instance-ncols1_2-ncols2_4.cu +12 -0
  307. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-mma-f16-instance-ncols1_2-ncols2_8.cu +11 -0
  308. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-mma-f16-instance-ncols1_32-ncols2_1.cu +10 -0
  309. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-mma-f16-instance-ncols1_32-ncols2_2.cu +10 -0
  310. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-mma-f16-instance-ncols1_4-ncols2_16.cu +5 -0
  311. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-mma-f16-instance-ncols1_4-ncols2_2.cu +10 -0
  312. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-mma-f16-instance-ncols1_4-ncols2_4.cu +12 -0
  313. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-mma-f16-instance-ncols1_4-ncols2_8.cu +11 -0
  314. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-mma-f16-instance-ncols1_64-ncols2_1.cu +10 -0
  315. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-mma-f16-instance-ncols1_8-ncols2_1.cu +10 -0
  316. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-mma-f16-instance-ncols1_8-ncols2_2.cu +10 -0
  317. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-mma-f16-instance-ncols1_8-ncols2_4.cu +12 -0
  318. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-mma-f16-instance-ncols1_8-ncols2_8.cu +11 -0
  319. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-tile-instance-dkq112-dv112.cu +5 -0
  320. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-tile-instance-dkq128-dv128.cu +5 -0
  321. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-tile-instance-dkq256-dv256.cu +5 -0
  322. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-tile-instance-dkq320-dv256.cu +5 -0
  323. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-tile-instance-dkq40-dv40.cu +5 -0
  324. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-tile-instance-dkq512-dv512.cu +5 -0
  325. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-tile-instance-dkq576-dv512.cu +5 -0
  326. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-tile-instance-dkq64-dv64.cu +5 -0
  327. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-tile-instance-dkq72-dv72.cu +5 -0
  328. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-tile-instance-dkq80-dv80.cu +5 -0
  329. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-tile-instance-dkq96-dv96.cu +5 -0
  330. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-bf16-bf16.cu +7 -0
  331. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-bf16-f16.cu +7 -0
  332. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-bf16-q4_0.cu +7 -0
  333. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-bf16-q4_1.cu +7 -0
  334. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-bf16-q5_0.cu +7 -0
  335. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-bf16-q5_1.cu +7 -0
  336. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-bf16-q8_0.cu +7 -0
  337. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-f16-bf16.cu +7 -0
  338. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-f16-f16.cu +7 -0
  339. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-f16-q4_0.cu +7 -0
  340. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-f16-q4_1.cu +7 -0
  341. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-f16-q5_0.cu +7 -0
  342. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-f16-q5_1.cu +7 -0
  343. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-f16-q8_0.cu +7 -0
  344. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-q4_0-bf16.cu +7 -0
  345. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-q4_0-f16.cu +7 -0
  346. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-q4_0-q4_0.cu +7 -0
  347. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-q4_0-q4_1.cu +7 -0
  348. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-q4_0-q5_0.cu +7 -0
  349. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-q4_0-q5_1.cu +7 -0
  350. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-q4_0-q8_0.cu +7 -0
  351. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-q4_1-bf16.cu +7 -0
  352. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-q4_1-f16.cu +7 -0
  353. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-q4_1-q4_0.cu +7 -0
  354. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-q4_1-q4_1.cu +7 -0
  355. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-q4_1-q5_0.cu +7 -0
  356. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-q4_1-q5_1.cu +7 -0
  357. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-q4_1-q8_0.cu +7 -0
  358. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-q5_0-bf16.cu +7 -0
  359. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-q5_0-f16.cu +7 -0
  360. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-q5_0-q4_0.cu +7 -0
  361. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-q5_0-q4_1.cu +7 -0
  362. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-q5_0-q5_0.cu +7 -0
  363. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-q5_0-q5_1.cu +7 -0
  364. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-q5_0-q8_0.cu +7 -0
  365. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-q5_1-bf16.cu +7 -0
  366. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-q5_1-f16.cu +7 -0
  367. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-q5_1-q4_0.cu +7 -0
  368. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-q5_1-q4_1.cu +7 -0
  369. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-q5_1-q5_0.cu +7 -0
  370. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-q5_1-q5_1.cu +7 -0
  371. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-q5_1-q8_0.cu +7 -0
  372. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-q8_0-bf16.cu +7 -0
  373. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-q8_0-f16.cu +7 -0
  374. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-q8_0-q4_0.cu +7 -0
  375. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-q8_0-q4_1.cu +7 -0
  376. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-q8_0-q5_0.cu +7 -0
  377. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-q8_0-q5_1.cu +7 -0
  378. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-q8_0-q8_0.cu +7 -0
  379. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/mmf-instance-ncols_1.cu +5 -0
  380. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/mmf-instance-ncols_10.cu +5 -0
  381. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/mmf-instance-ncols_11.cu +5 -0
  382. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/mmf-instance-ncols_12.cu +5 -0
  383. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/mmf-instance-ncols_13.cu +5 -0
  384. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/mmf-instance-ncols_14.cu +5 -0
  385. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/mmf-instance-ncols_15.cu +5 -0
  386. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/mmf-instance-ncols_16.cu +5 -0
  387. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/mmf-instance-ncols_2.cu +5 -0
  388. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/mmf-instance-ncols_3.cu +5 -0
  389. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/mmf-instance-ncols_4.cu +5 -0
  390. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/mmf-instance-ncols_5.cu +5 -0
  391. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/mmf-instance-ncols_6.cu +5 -0
  392. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/mmf-instance-ncols_7.cu +5 -0
  393. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/mmf-instance-ncols_8.cu +5 -0
  394. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/mmf-instance-ncols_9.cu +5 -0
  395. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/mmq-instance-iq1_s.cu +5 -0
  396. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/mmq-instance-iq2_s.cu +5 -0
  397. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/mmq-instance-iq2_xs.cu +5 -0
  398. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/mmq-instance-iq2_xxs.cu +5 -0
  399. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/mmq-instance-iq3_s.cu +5 -0
  400. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/mmq-instance-iq3_xxs.cu +5 -0
  401. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/mmq-instance-iq4_nl.cu +5 -0
  402. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/mmq-instance-iq4_xs.cu +5 -0
  403. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/mmq-instance-mxfp4.cu +5 -0
  404. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/mmq-instance-nvfp4.cu +5 -0
  405. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/mmq-instance-q1_0.cu +5 -0
  406. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/mmq-instance-q2_k.cu +5 -0
  407. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/mmq-instance-q3_k.cu +5 -0
  408. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/mmq-instance-q4_0.cu +5 -0
  409. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/mmq-instance-q4_1.cu +5 -0
  410. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/mmq-instance-q4_k.cu +5 -0
  411. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/mmq-instance-q5_0.cu +5 -0
  412. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/mmq-instance-q5_1.cu +5 -0
  413. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/mmq-instance-q5_k.cu +5 -0
  414. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/mmq-instance-q6_k.cu +5 -0
  415. data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/mmq-instance-q8_0.cu +5 -0
  416. data/vendor/crispasr/ggml/src/ggml-cuda/top-k.cu +95 -0
  417. data/vendor/crispasr/ggml/src/ggml-cuda/top-k.cuh +3 -0
  418. data/vendor/crispasr/ggml/src/ggml-cuda/topk-moe.cu +415 -0
  419. data/vendor/crispasr/ggml/src/ggml-cuda/topk-moe.cuh +27 -0
  420. data/vendor/crispasr/ggml/src/ggml-cuda/tri.cu +136 -0
  421. data/vendor/crispasr/ggml/src/ggml-cuda/tri.cuh +5 -0
  422. data/vendor/crispasr/ggml/src/ggml-cuda/tsembd.cu +47 -0
  423. data/vendor/crispasr/ggml/src/ggml-cuda/tsembd.cuh +5 -0
  424. data/vendor/crispasr/ggml/src/ggml-cuda/unary.cu +644 -0
  425. data/vendor/crispasr/ggml/src/ggml-cuda/unary.cuh +116 -0
  426. data/vendor/crispasr/ggml/src/ggml-cuda/upscale.cu +293 -0
  427. data/vendor/crispasr/ggml/src/ggml-cuda/upscale.cuh +5 -0
  428. data/vendor/crispasr/ggml/src/ggml-cuda/vecdotq.cuh +1317 -0
  429. data/vendor/crispasr/ggml/src/ggml-cuda/vendors/cuda.h +28 -0
  430. data/vendor/crispasr/ggml/src/ggml-cuda/vendors/hip.h +306 -0
  431. data/vendor/crispasr/ggml/src/ggml-cuda/vendors/musa.h +148 -0
  432. data/vendor/crispasr/ggml/src/ggml-cuda/wkv.cu +199 -0
  433. data/vendor/crispasr/ggml/src/ggml-cuda/wkv.cuh +7 -0
  434. data/vendor/crispasr/ggml/src/ggml-impl.h +783 -0
  435. data/vendor/crispasr/ggml/src/ggml-metal/CMakeLists.txt +124 -0
  436. data/vendor/crispasr/ggml/src/ggml-metal/ggml-metal-common.cpp +457 -0
  437. data/vendor/crispasr/ggml/src/ggml-metal/ggml-metal-common.h +52 -0
  438. data/vendor/crispasr/ggml/src/ggml-metal/ggml-metal-context.h +41 -0
  439. data/vendor/crispasr/ggml/src/ggml-metal/ggml-metal-context.m +778 -0
  440. data/vendor/crispasr/ggml/src/ggml-metal/ggml-metal-device.cpp +2117 -0
  441. data/vendor/crispasr/ggml/src/ggml-metal/ggml-metal-device.h +299 -0
  442. data/vendor/crispasr/ggml/src/ggml-metal/ggml-metal-device.m +2093 -0
  443. data/vendor/crispasr/ggml/src/ggml-metal/ggml-metal-impl.h +1267 -0
  444. data/vendor/crispasr/ggml/src/ggml-metal/ggml-metal-ops.cpp +5023 -0
  445. data/vendor/crispasr/ggml/src/ggml-metal/ggml-metal-ops.h +111 -0
  446. data/vendor/crispasr/ggml/src/ggml-metal/ggml-metal.cpp +954 -0
  447. data/vendor/crispasr/ggml/src/ggml-metal/ggml-metal.metal +11756 -0
  448. data/vendor/crispasr/ggml/src/ggml-opt.cpp +1094 -0
  449. data/vendor/crispasr/ggml/src/ggml-quants.c +5491 -0
  450. data/vendor/crispasr/ggml/src/ggml-quants.h +112 -0
  451. data/vendor/crispasr/ggml/src/ggml-threading.cpp +12 -0
  452. data/vendor/crispasr/ggml/src/ggml-threading.h +14 -0
  453. data/vendor/crispasr/ggml/src/ggml.c +7925 -0
  454. data/vendor/crispasr/ggml/src/ggml.cpp +26 -0
  455. data/vendor/crispasr/ggml/src/gguf.cpp +1556 -0
  456. data/vendor/crispasr/src/cohere-arch.h +137 -0
  457. data/vendor/crispasr/src/cohere.cpp +5642 -0
  458. data/vendor/crispasr/src/cohere.h +327 -0
  459. data/vendor/crispasr/src/cohere_batch_planner.h +82 -0
  460. data/vendor/crispasr/src/cohere_chunking.h +64 -0
  461. data/vendor/crispasr/src/cohere_decoder_batch_layout.h +60 -0
  462. data/vendor/crispasr/src/cohere_encoder_padded_layout.h +27 -0
  463. data/vendor/crispasr/src/cohere_frontend.cpp +189 -0
  464. data/vendor/crispasr/src/cohere_frontend.h +31 -0
  465. data/vendor/crispasr/src/cohere_ragged_controller.h +149 -0
  466. data/vendor/crispasr/src/cohere_token_renderer.h +181 -0
  467. data/vendor/crispasr/src/core/attention.h +924 -0
  468. data/vendor/crispasr/src/core/audio_chunking.h +97 -0
  469. data/vendor/crispasr/src/core/beam_decode.h +486 -0
  470. data/vendor/crispasr/src/core/cpu_ops.h +135 -0
  471. data/vendor/crispasr/src/core/gguf_loader.cpp +1021 -0
  472. data/vendor/crispasr/src/core/gguf_loader.h +216 -0
  473. data/vendor/crispasr/src/core/gpu_backend_pref.h +119 -0
  474. data/vendor/crispasr/src/core/mel.cpp +519 -0
  475. data/vendor/crispasr/src/core/mel.h +265 -0
  476. data/vendor/crispasr/src/core/ngram_loop_fix.h +173 -0
  477. data/vendor/crispasr/src/core/repetition_loop_guard.h +54 -0
  478. data/vendor/crispasr/src/crispasr_imatrix.cpp +255 -0
  479. data/vendor/crispasr/src/crispasr_imatrix.h +38 -0
  480. metadata +596 -0
@@ -0,0 +1,1676 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../python_text"
4
+
5
+ module Cohere
6
+ module Transcribe
7
+ module Runtime
8
+ PROCESS_GATE = Mutex.new
9
+ THREAD_ACTIVE_KEY = :__cohere_transcribe_active
10
+ THREAD_PROGRESS_KEY = :__cohere_transcribe_progress
11
+
12
+ class FatalRuntimeError < TranscriptionRuntimeError; end
13
+ private_constant :FatalRuntimeError
14
+
15
+ Measurements = Struct.new(
16
+ :runtime_import_seconds,
17
+ :serialization_wait_seconds,
18
+ :input_validation_seconds,
19
+ :decode_seconds,
20
+ :vad_seconds,
21
+ :vad_model_load_seconds,
22
+ :vad_inference_seconds,
23
+ :vad_postprocess_seconds,
24
+ :preparation_wait_seconds,
25
+ :asr_load_seconds,
26
+ :asr_seconds,
27
+ :asr_generation_call_seconds,
28
+ :asr_feature_worker_seconds,
29
+ :asr_h2d_seconds,
30
+ :asr_generate_device_seconds,
31
+ :asr_generation_analysis_seconds,
32
+ :aligner_load_seconds,
33
+ :emissions_seconds,
34
+ :viterbi_seconds,
35
+ :post_asr_seconds,
36
+ :checkpoint_seconds,
37
+ :progressive_output_seconds,
38
+ :cuda_total_gib,
39
+ :cuda_free_start_gib,
40
+ :cuda_free_end_gib,
41
+ :asr_batches,
42
+ :asr_processor_rows,
43
+ :generated_tokens,
44
+ :oom_retries,
45
+ :truncation_retries,
46
+ :effective_batch_min,
47
+ :effective_batch_max,
48
+ :final_batch_size,
49
+ :final_batch_cap,
50
+ :batch_history,
51
+ :checkpoint_written_files,
52
+ :vad_prepared_groups,
53
+ :vad_model_calls,
54
+ :vad_valid_frames,
55
+ :vad_padded_frames,
56
+ :vad_max_files_per_call,
57
+ :vad_effective_block_frames,
58
+ :vad_intraop_threads,
59
+ :file_segmentation,
60
+ keyword_init: true
61
+ ) do
62
+ def initialize(**values)
63
+ super
64
+ members.each do |member|
65
+ self[member] ||= if %i[cuda_total_gib cuda_free_start_gib cuda_free_end_gib].include?(member)
66
+ nil
67
+ elsif member == :batch_history
68
+ []
69
+ elsif member == :file_segmentation
70
+ {}
71
+ elsif member.to_s.end_with?("seconds")
72
+ 0.0
73
+ else
74
+ 0
75
+ end
76
+ end
77
+ end
78
+ end
79
+ private_constant :Measurements
80
+
81
+ GenerationOutcome = Data.define(:native, :retried, :hit_token_limit)
82
+ private_constant :GenerationOutcome
83
+
84
+ PreparationItem = Data.define(:index, :entry, :plan)
85
+ private_constant :PreparationItem
86
+
87
+ PreparationSilero = Data.define(:thread, :session)
88
+ private_constant :PreparationSilero
89
+
90
+ # Reusable, serialized Ruby execution engine. Heavy model conversion and
91
+ # native session creation remain lazy until at least one speech segment
92
+ # actually needs inference.
93
+ class Engine
94
+ attr_reader :options
95
+
96
+ def initialize(options, progress: nil, model_provider: nil, decoder: Audio::Decoder, silero_factory: nil,
97
+ aligner_factory: nil)
98
+ @options = options
99
+ @progress = progress
100
+ @model_provider = model_provider || ModelProvider.new
101
+ @decoder = decoder
102
+ @silero_factory = silero_factory || ->(**keywords) { VAD::Silero.new(**keywords) }
103
+ @aligner_factory = aligner_factory || ->(**keywords) { Alignment::Aligner.new(**keywords) }
104
+ @gate = Mutex.new
105
+ @progress_gate = Mutex.new
106
+ @closed = false
107
+ @active = false
108
+ @identity = nil
109
+ @resources = ModelResources.new
110
+ @native_session = nil
111
+ @aligner = nil
112
+ @preparation_sileros = []
113
+ @preparation_silero_gate = Mutex.new
114
+ end
115
+
116
+ def transcribe(audio, raise_on_error: false, runtime_import_seconds: 0.0, serialization_wait_seconds: 0.0)
117
+ if Thread.current.thread_variable_get(THREAD_PROGRESS_KEY)
118
+ raise TranscriberBusyError, "Reentrant transcription from a progress callback is not supported"
119
+ end
120
+ if Thread.current.thread_variable_get(THREAD_ACTIVE_KEY)
121
+ raise TranscriberBusyError, "Reentrant transcription in one process is not supported"
122
+ end
123
+
124
+ started = monotonic
125
+ wait_started = monotonic
126
+ @gate.synchronize do
127
+ measurements = Measurements.new(
128
+ runtime_import_seconds: runtime_import_seconds,
129
+ serialization_wait_seconds: serialization_wait_seconds + monotonic - wait_started
130
+ )
131
+ raise TranscriberClosedError, "This Transcriber has been closed" if @closed
132
+ raise TranscriberBusyError, "Reentrant transcription with one Transcriber is not supported" if @active
133
+
134
+ @active = true
135
+ begin
136
+ process_wait = monotonic
137
+ PROCESS_GATE.synchronize do
138
+ measurements.serialization_wait_seconds += monotonic - process_wait
139
+ previous_active = Thread.current.thread_variable_get(THREAD_ACTIVE_KEY)
140
+ Thread.current.thread_variable_set(THREAD_ACTIVE_KEY, true)
141
+ run = execute(audio, measurements, started)
142
+ raise BatchTranscriptionError, run if raise_on_error && !run.ok?
143
+
144
+ run
145
+ ensure
146
+ Thread.current.thread_variable_set(THREAD_ACTIVE_KEY, previous_active)
147
+ end
148
+ rescue ProgressCallbackError, BatchTranscriptionError, TranscriptionConfigurationError,
149
+ TranscriptionInputError, TranscriberBusyError, TranscriberClosedError
150
+ raise
151
+ rescue FatalRuntimeError => e
152
+ evict_inference_sessions
153
+ raise TranscriptionRuntimeError, e.message
154
+ rescue TranscriptionRuntimeError
155
+ evict_inference_sessions
156
+ raise
157
+ rescue Interrupt
158
+ evict_inference_sessions
159
+ raise
160
+ rescue SystemExit => e
161
+ evict_inference_sessions
162
+ detail = e.message.to_s
163
+ detail = "Transcription setup failed" if detail.empty? || detail == "SystemExit"
164
+ raise TranscriptionRuntimeError, detail
165
+ rescue ScriptError => e
166
+ evict_inference_sessions
167
+ raise TranscriptionRuntimeError, "#{e.class}: #{e.message}"
168
+ rescue StandardError => e
169
+ evict_inference_sessions
170
+ raise TranscriptionRuntimeError, "#{e.class}: #{e.message}"
171
+ ensure
172
+ @active = false
173
+ end
174
+ end
175
+ end
176
+
177
+ def close
178
+ if Thread.current.thread_variable_get(THREAD_PROGRESS_KEY) ||
179
+ Thread.current.thread_variable_get(THREAD_ACTIVE_KEY)
180
+ raise TranscriberBusyError, "Cannot close a Transcriber while transcription is active"
181
+ end
182
+
183
+ @gate.synchronize do
184
+ return if @closed
185
+ raise TranscriberBusyError, "Cannot close a Transcriber while its transcription is active" if @active
186
+
187
+ begin
188
+ @resources.close
189
+ ensure
190
+ @native_session = nil
191
+ evict_aligner
192
+ end
193
+ @preparation_sileros.clear
194
+ @closed = true
195
+ end
196
+ nil
197
+ end
198
+
199
+ private
200
+
201
+ def execute(audio, measurements, started)
202
+ validation_started = monotonic
203
+ validate_configuration!
204
+ if options.adapter
205
+ raise TranscriptionConfigurationError,
206
+ "PEFT/LoRA adapters are not supported by the native Ruby Dense runtime"
207
+ end
208
+ entries = Input.expand(audio, recursive: options.recursive)
209
+ preliminary_options = resolve_configuration
210
+ # Preserve filesystem/preflight failures before any remote model
211
+ # identity work, then bind durable contracts to the resolved model.
212
+ preliminary_plans = Output::Publication.plan(
213
+ entries,
214
+ state_contract_options(preliminary_options)
215
+ )
216
+ preliminary_profile_binding = Output::Publication.bind_profile_path(
217
+ preliminary_options.publication&.profile_json
218
+ )
219
+ @identity ||= resolve_identity
220
+ resolved_options = resolve_configuration(model_identity: @identity)
221
+ preliminary_plans.each_value do |plan|
222
+ plan.directory_bindings.each(&:verify!)
223
+ end
224
+ preliminary_profile_binding&.verify!
225
+ plans = Output::Publication.plan(entries, state_contract_options(resolved_options))
226
+ profile_binding = Output::Publication.bind_profile_path(
227
+ resolved_options.publication&.profile_json
228
+ )
229
+ Output::Publication.verify_plan_directory_continuity!(preliminary_plans, plans)
230
+ Output::Publication.verify_profile_directory_continuity!(
231
+ preliminary_profile_binding,
232
+ profile_binding
233
+ )
234
+ measurements.input_validation_seconds += monotonic - validation_started
235
+
236
+ preflight_skips = {}
237
+ preparation_items = entries.each_with_index.filter_map do |entry, index|
238
+ plan = plans.fetch(entry.path)
239
+ outcome = preflight_outcome(
240
+ index,
241
+ entry,
242
+ plan,
243
+ resolved_options,
244
+ measurements
245
+ )
246
+ next outcome if outcome.is_a?(PreparationItem)
247
+
248
+ preflight_skips[index] = outcome
249
+ nil
250
+ end
251
+ memory_byte_limit = [(resolved_options.audio_memory_gb * (1024**3)).to_i, 1].max
252
+ pipeline = Preparation::Pipeline.new(
253
+ preparation_items,
254
+ memory_byte_limit: memory_byte_limit,
255
+ requested_workers: resolved_options.preprocess_workers,
256
+ enabled: resolved_options.pipeline_preparation,
257
+ worker_limit: vad_file_concurrency_limit(resolved_options)
258
+ ) do |item, decoded_byte_limit, worker_slot|
259
+ prepare_entry(
260
+ item,
261
+ resolved_options,
262
+ decoded_byte_limit: decoded_byte_limit,
263
+ worker_slot: worker_slot
264
+ )
265
+ end
266
+
267
+ preparation_mode = pipeline.pipelined? ? "on" : "off"
268
+ unless preparation_items.empty?
269
+ report(
270
+ ProgressEvent.new(
271
+ stage: "message",
272
+ message: " preparing (workers=#{pipeline.effective_workers}, " \
273
+ "next-group=#{preparation_mode}, " \
274
+ "PCM group cap=#{pipeline.group_byte_limit} bytes)"
275
+ )
276
+ )
277
+ end
278
+ fully_preflight_skipped = preparation_items.empty? &&
279
+ preflight_skips.length == entries.length &&
280
+ preflight_skips.values.all? do |result|
281
+ result.respond_to?(:status) && result.status == "skipped"
282
+ end
283
+ results = if fully_preflight_skipped
284
+ entries.each_index.map { |index| preflight_skips.fetch(index) }.freeze
285
+ elsif resolved_options.alignment == "word"
286
+ process_word_pipeline(
287
+ pipeline,
288
+ total: entries.length,
289
+ preflight_skips: preflight_skips,
290
+ resolved_options: resolved_options,
291
+ measurements: measurements,
292
+ memory_byte_limit: memory_byte_limit
293
+ )
294
+ else
295
+ process_standard_pipeline(
296
+ pipeline,
297
+ total: entries.length,
298
+ preflight_skips: preflight_skips,
299
+ resolved_options: resolved_options,
300
+ measurements: measurements
301
+ )
302
+ end
303
+ measurements.preparation_wait_seconds += pipeline.wait_seconds
304
+ record_cuda_memory(measurements, @native_session, start: false)
305
+ evict_native_session if @resources.asr_circuit_broken?
306
+
307
+ run = build_run(
308
+ results,
309
+ resolved_options,
310
+ measurements,
311
+ elapsed: monotonic - started
312
+ )
313
+ if (profile = resolved_options.publication&.profile_json)
314
+ begin
315
+ Output::Publication.write_profile(
316
+ profile,
317
+ run,
318
+ runtime_metrics: profile_runtime_metrics(measurements),
319
+ directory_binding: profile_binding
320
+ )
321
+ rescue TranscriptionError => e
322
+ run = run.with(errors: (run.errors + [e.message]).freeze)
323
+ end
324
+ end
325
+ public_run(run)
326
+ end
327
+
328
+ def process_standard_pipeline(pipeline, total:, preflight_skips:, resolved_options:, measurements:)
329
+ results = []
330
+ cursor = 0
331
+ while cursor < total && preflight_skips.key?(cursor)
332
+ results << preflight_skips.fetch(cursor)
333
+ cursor += 1
334
+ report(ProgressEvent.new(stage: "files", current: cursor, total: total))
335
+ end
336
+
337
+ pipeline.each do |prepared|
338
+ target = prepared.item.index
339
+ while cursor < target
340
+ raise TranscriptionRuntimeError, "Preparation order became inconsistent" unless preflight_skips.key?(cursor)
341
+
342
+ results << preflight_skips.fetch(cursor)
343
+ cursor += 1
344
+ report(ProgressEvent.new(stage: "files", current: cursor, total: total))
345
+ end
346
+
347
+ raise TranscriptionRuntimeError, "Preparation order became inconsistent" unless cursor == target
348
+
349
+ results << process_prepared_entry(prepared, resolved_options, measurements)
350
+ cursor += 1
351
+ report(ProgressEvent.new(stage: "files", current: cursor, total: total))
352
+ end
353
+ while cursor < total
354
+ raise TranscriptionRuntimeError, "Preparation omitted an input" unless preflight_skips.key?(cursor)
355
+
356
+ results << preflight_skips.fetch(cursor)
357
+ cursor += 1
358
+ report(ProgressEvent.new(stage: "files", current: cursor, total: total))
359
+ end
360
+ results.freeze
361
+ end
362
+
363
+ def preflight_outcome(index, entry, plan, resolved_options, measurements)
364
+ Output::Publication.with_plan_lock(plan) do
365
+ decision = Output::Publication.revalidate(plan, resolved_options)
366
+ case decision.action
367
+ when :skip
368
+ skipped_result(entry, plan)
369
+ when :resume
370
+ preflight_resumed_entry(
371
+ index,
372
+ entry,
373
+ plan,
374
+ decision,
375
+ resolved_options,
376
+ measurements
377
+ )
378
+ else
379
+ PreparationItem.new(index: index, entry: entry, plan: plan)
380
+ end
381
+ end
382
+ end
383
+
384
+ def preflight_resumed_entry(index, entry, plan, decision, resolved_options, measurements)
385
+ prepared = Preparation::PreparedEntry.new(
386
+ item: PreparationItem.new(index: index, entry: entry, plan: plan),
387
+ snapshot: source_snapshot(entry.path),
388
+ decoded: nil,
389
+ duration: decision.checkpoint.duration,
390
+ segment_times: decision.checkpoint.segment_times,
391
+ speech_spans: decision.checkpoint.speech_spans,
392
+ vad_details: nil,
393
+ decode_seconds: 0.0,
394
+ vad_seconds: 0.0,
395
+ error: nil
396
+ )
397
+ process_resumed_entry(
398
+ prepared,
399
+ decision.checkpoint,
400
+ decision.generation_id,
401
+ resolved_options,
402
+ measurements,
403
+ defer_word_alignment: resolved_options.alignment == "word"
404
+ )
405
+ rescue FatalRuntimeError, ProgressCallbackError
406
+ raise
407
+ rescue Interrupt, SystemExit
408
+ raise
409
+ rescue StandardError => e
410
+ failed_result(entry, decision.checkpoint.duration, e)
411
+ end
412
+
413
+ def process_word_pipeline(pipeline, total:, preflight_skips:, resolved_options:, measurements:,
414
+ memory_byte_limit:)
415
+ alignment_state = { error: nil }
416
+ post_asr_started = nil
417
+ redecoder = WordPipeline::ConcreteRedecoder.new(
418
+ decoder: @decoder,
419
+ sample_rate: SAMPLE_RATE,
420
+ memory_byte_limit: memory_byte_limit,
421
+ skip: ->(_work) { !alignment_state.fetch(:error).nil? },
422
+ validate: lambda do |work|
423
+ ensure_source_unchanged!(work.entry.path, work.source_snapshot)
424
+ end
425
+ )
426
+ WordPipeline::Coordinator.new(
427
+ total: total,
428
+ fixed_results: preflight_skips,
429
+ evict_asr: method(:evict_current_asr_owner),
430
+ reload: redecoder,
431
+ memory_byte_limit: memory_byte_limit,
432
+ start_alignment: lambda do |works|
433
+ post_asr_started ||= monotonic
434
+ preload_word_aligner(works, resolved_options, measurements, alignment_state)
435
+ end,
436
+ align: lambda do |work, reload_result|
437
+ process_word_alignment(
438
+ work,
439
+ reload_result,
440
+ resolved_options,
441
+ measurements,
442
+ alignment_state
443
+ )
444
+ end,
445
+ completed: lambda do |_index, current, event_total, _result|
446
+ report(ProgressEvent.new(stage: "files", current: current, total: event_total))
447
+ end
448
+ ).run(pipeline) do |prepared|
449
+ process_word_asr_entry(prepared, resolved_options, measurements)
450
+ end
451
+ ensure
452
+ measurements.post_asr_seconds += monotonic - post_asr_started if post_asr_started
453
+ # Python's MMS owner is one-shot. Closing it after the alignment
454
+ # phase also prevents a different reusable Transcriber from loading
455
+ # Dense while this engine retains an idle aligner.
456
+ evict_aligner
457
+ end
458
+
459
+ def resolve_identity
460
+ @model_provider.resolve(options)
461
+ rescue TranscriptionConfigurationError
462
+ raise
463
+ rescue ArgumentError, TypeError, Hub::Error => e
464
+ raise TranscriptionConfigurationError, e.message
465
+ end
466
+
467
+ def validate_configuration!
468
+ Configuration.validate!(options)
469
+ rescue TranscriptionConfigurationError
470
+ raise
471
+ rescue ArgumentError, TypeError, NoMethodError => e
472
+ raise TranscriptionConfigurationError, e.message
473
+ end
474
+
475
+ def resolve_configuration(model_identity: nil)
476
+ resolved = Configuration.resolved(options, model_identity: model_identity)
477
+ Precision.resolve(resolved)
478
+ rescue TranscriptionConfigurationError
479
+ raise
480
+ rescue ArgumentError, TypeError, NoMethodError => e
481
+ raise TranscriptionConfigurationError, e.message
482
+ end
483
+
484
+ def state_contract_options(resolved_options)
485
+ return resolved_options unless resolved_options.vad == "silero"
486
+
487
+ resolved_options.with(vad_engine: options.vad_engine)
488
+ end
489
+
490
+ def prepare_entry(item, resolved_options, decoded_byte_limit:, worker_slot:)
491
+ snapshot = nil
492
+ duration = nil
493
+ decoded = nil
494
+ segment_times = nil
495
+ speech_spans = nil
496
+ vad_details = nil
497
+ decode_seconds = 0.0
498
+ vad_seconds = 0.0
499
+
500
+ snapshot = source_snapshot(item.entry.path)
501
+ decode_started = monotonic
502
+ begin
503
+ decoded = @decoder.decode(
504
+ item.entry.path,
505
+ backend: resolved_options.audio_backend,
506
+ sample_rate: SAMPLE_RATE,
507
+ max_decoded_bytes: decoded_byte_limit
508
+ )
509
+ ensure
510
+ decode_seconds += monotonic - decode_started
511
+ end
512
+ duration = decoded.samples.length.fdiv(SAMPLE_RATE)
513
+
514
+ vad_started = monotonic
515
+ begin
516
+ segment_times, speech_spans, vad_details = segment(
517
+ decoded.samples,
518
+ resolved_options,
519
+ silero: preparation_silero(worker_slot, resolved_options)
520
+ )
521
+ ensure
522
+ vad_seconds += monotonic - vad_started
523
+ end
524
+
525
+ Preparation::PreparedEntry.new(
526
+ item: item,
527
+ snapshot: snapshot,
528
+ decoded: decoded,
529
+ duration: duration,
530
+ segment_times: segment_times,
531
+ speech_spans: speech_spans,
532
+ vad_details: vad_details,
533
+ decode_seconds: decode_seconds,
534
+ vad_seconds: vad_seconds,
535
+ error: nil
536
+ )
537
+ rescue Interrupt, SystemExit
538
+ raise
539
+ rescue StandardError => e
540
+ Preparation::PreparedEntry.new(
541
+ item: item,
542
+ snapshot: snapshot,
543
+ decoded: nil,
544
+ duration: duration,
545
+ segment_times: nil,
546
+ speech_spans: nil,
547
+ vad_details: nil,
548
+ decode_seconds: decode_seconds,
549
+ vad_seconds: vad_seconds,
550
+ error: e
551
+ )
552
+ end
553
+
554
+ def process_prepared_entry(prepared, resolved_options, measurements)
555
+ item = prepared.item
556
+ entry = item.entry
557
+ plan = item.plan
558
+ duration = prepared.duration
559
+ measurements.decode_seconds += prepared.decode_seconds
560
+ measurements.vad_seconds += prepared.vad_seconds
561
+ record_vad_measurements(measurements, prepared.vad_details)
562
+ Output::Publication.with_plan_lock(plan) do
563
+ decision = Output::Publication.revalidate(plan, resolved_options)
564
+ return skipped_result(entry, plan) if decision.action == :skip
565
+
566
+ if prepared.error
567
+ raise prepared.error if prepared.error.is_a?(FatalRuntimeError) ||
568
+ prepared.error.is_a?(ProgressCallbackError)
569
+ unless decision.action == :resume && resolved_options.alignment != "word"
570
+ return failed_result(entry, duration, prepared.error)
571
+ end
572
+ end
573
+
574
+ if decision.action == :resume
575
+ return process_resumed_entry(
576
+ prepared,
577
+ decision.checkpoint,
578
+ decision.generation_id,
579
+ resolved_options,
580
+ measurements
581
+ )
582
+ end
583
+
584
+ process_fresh_entry(prepared, resolved_options, measurements)
585
+ end
586
+ rescue FatalRuntimeError, ProgressCallbackError
587
+ raise
588
+ rescue Interrupt, SystemExit
589
+ raise
590
+ rescue StandardError => e
591
+ failed_result(entry, duration, e)
592
+ end
593
+
594
+ def process_word_asr_entry(prepared, resolved_options, measurements)
595
+ item = prepared.item
596
+ entry = item.entry
597
+ plan = item.plan
598
+ duration = prepared.duration
599
+ measurements.decode_seconds += prepared.decode_seconds
600
+ measurements.vad_seconds += prepared.vad_seconds
601
+ record_vad_measurements(measurements, prepared.vad_details)
602
+ outcome = Output::Publication.with_plan_lock(plan) do
603
+ decision = Output::Publication.revalidate(plan, resolved_options)
604
+ if decision.action == :skip
605
+ WordPipeline::Final.new(index: item.index, result: skipped_result(entry, plan))
606
+ else
607
+ if prepared.error
608
+ raise prepared.error if prepared.error.is_a?(ProgressCallbackError)
609
+
610
+ resumable = decision.action == :resume && prepared.snapshot
611
+ unless resumable
612
+ raise prepared.error if prepared.error.is_a?(FatalRuntimeError)
613
+
614
+ next WordPipeline::Final.new(
615
+ index: item.index,
616
+ result: failed_result(entry, duration, prepared.error)
617
+ )
618
+ end
619
+ end
620
+
621
+ if decision.action == :resume
622
+ process_resumed_entry(
623
+ prepared,
624
+ decision.checkpoint,
625
+ decision.generation_id,
626
+ resolved_options,
627
+ measurements,
628
+ defer_word_alignment: true
629
+ )
630
+ else
631
+ process_fresh_entry(
632
+ prepared,
633
+ resolved_options,
634
+ measurements,
635
+ defer_word_alignment: true
636
+ )
637
+ end
638
+ end
639
+ end
640
+ return outcome if outcome.is_a?(WordPipeline::AlignmentWork) || outcome.is_a?(WordPipeline::Final)
641
+
642
+ raise TranscriptionRuntimeError, "Word ASR phase produced an invalid outcome for #{entry.path}"
643
+ rescue FatalRuntimeError, ProgressCallbackError
644
+ raise
645
+ rescue Interrupt, SystemExit
646
+ raise
647
+ rescue StandardError => e
648
+ WordPipeline::Final.new(index: item.index, result: failed_result(entry, duration, e))
649
+ end
650
+
651
+ def process_word_alignment(work, reload_result, resolved_options, measurements, alignment_state)
652
+ entry = work.entry
653
+ plan = work.plan
654
+ Output::Publication.with_plan_lock(plan) do
655
+ decision = Output::Publication.revalidate(plan, resolved_options)
656
+ return skipped_result(entry, plan) if decision.action == :skip
657
+ if (alignment_error = alignment_state.fetch(:error))
658
+ return failed_result(entry, work.result.duration, alignment_error)
659
+ end
660
+ return failed_result(entry, work.result.duration, reload_result.error) unless reload_result.ok?
661
+
662
+ ensure_source_unchanged!(entry.path, work.source_snapshot)
663
+ rendered = render_and_publish(
664
+ work.result,
665
+ reload_result.decoded&.samples || [],
666
+ work.segment_times,
667
+ work.speech_spans,
668
+ plan,
669
+ resolved_options,
670
+ measurements,
671
+ work.generation_id,
672
+ asr_evicted: true
673
+ )
674
+ ensure_source_unchanged!(entry.path, work.source_snapshot)
675
+ rendered
676
+ end
677
+ rescue Alignment::BackendUnavailable => e
678
+ alignment_state[:error] = e
679
+ failed_result(entry, work.result.duration, e)
680
+ rescue FatalRuntimeError, ProgressCallbackError
681
+ raise
682
+ rescue Interrupt, SystemExit
683
+ raise
684
+ rescue StandardError => e
685
+ failed_result(entry, work.result.duration, e)
686
+ end
687
+
688
+ def preload_word_aligner(works, resolved_options, measurements, alignment_state)
689
+ return unless works.any?(&:audio_required)
690
+
691
+ aligner = ensure_aligner(resolved_options)
692
+ before = alignment_measurements(aligner)
693
+ aligner.load! if aligner.respond_to?(:load!)
694
+ rescue Interrupt, SystemExit, ProgressCallbackError
695
+ raise
696
+ rescue StandardError => e
697
+ alignment_state[:error] = e
698
+ evict_aligner
699
+ ensure
700
+ record_alignment_measurements(measurements, before, aligner) if before && aligner
701
+ end
702
+
703
+ def process_fresh_entry(prepared, resolved_options, measurements, defer_word_alignment: false)
704
+ item = prepared.item
705
+ entry = item.entry
706
+ plan = item.plan
707
+ snapshot = prepared.snapshot
708
+ decoded = prepared.decoded
709
+ duration = prepared.duration
710
+ segment_times = prepared.segment_times
711
+ speech_spans = prepared.speech_spans
712
+ vad_details = prepared.vad_details
713
+ record_file_segmentation(measurements, entry.path, segment_times, speech_spans)
714
+
715
+ segments = []
716
+ repetition_stopped_segments = []
717
+ truncation_retried_segments = []
718
+ token_limit_segments = []
719
+ generated_tokens = []
720
+ report(ProgressEvent.new(stage: "ASR", current: 0, total: segment_times.length))
721
+ generation_outcomes = transcribe_segments(
722
+ decoded.samples,
723
+ segment_times,
724
+ language: resolved_options.language,
725
+ measurements: measurements,
726
+ resolved_options: resolved_options
727
+ )
728
+ segment_times.each_with_index do |(start_time, end_time), segment_index|
729
+ outcome = generation_outcomes.fetch(segment_index)
730
+ native = outcome.native
731
+ retried = outcome.retried
732
+ hit_token_limit = outcome.hit_token_limit
733
+ repetition_stopped_segments << segment_index if native.repetition_stopped
734
+ truncation_retried_segments << segment_index if retried
735
+ token_limit_segments << segment_index if hit_token_limit
736
+ text = PythonText.strip(native.text.to_s)
737
+ segments << TranscriptionSegment.new(
738
+ index: segment_index,
739
+ start: start_time,
740
+ end: end_time,
741
+ text: text
742
+ )
743
+ token_count = native.generated_tokens
744
+ generated_tokens << [segment_index, token_count]
745
+ report(ProgressEvent.new(stage: "ASR", current: segment_index + 1, total: segment_times.length))
746
+ end
747
+
748
+ ensure_source_unchanged!(entry.path, snapshot)
749
+ text = segments.map(&:text).reject(&:empty?).join("\n")
750
+ provenance = provenance_for(
751
+ decoded: decoded,
752
+ vad_details: vad_details,
753
+ repetition_stopped_segments: repetition_stopped_segments,
754
+ truncation_retried_segments: truncation_retried_segments,
755
+ token_limit_segments: token_limit_segments,
756
+ generated_tokens: generated_tokens,
757
+ published: false
758
+ )
759
+ result = TranscriptionResult.new(
760
+ path: entry.path,
761
+ relative_path: entry.relative_path,
762
+ status: "completed",
763
+ text: text,
764
+ duration: duration,
765
+ segments: segments,
766
+ provenance: provenance
767
+ )
768
+ checkpoint_started = nil
769
+ generation_id = if plan.checkpoint_path
770
+ checkpoint_started = monotonic
771
+ begin
772
+ State.write_asr_checkpoint(
773
+ path: plan.checkpoint_path,
774
+ result: result,
775
+ source_snapshot: plan.source_snapshot,
776
+ asr_contract_key: plan.asr_contract_key,
777
+ speech_spans: speech_spans,
778
+ vad_provider_options: vad_provider_options(vad_details),
779
+ directory_binding: plan.directory_bindings.last,
780
+ guard_bindings: plan.directory_bindings
781
+ )
782
+ ensure
783
+ measurements.checkpoint_seconds += monotonic - checkpoint_started
784
+ end
785
+ end
786
+ measurements.checkpoint_written_files += 1 if generation_id
787
+ if defer_word_alignment
788
+ return word_alignment_work(
789
+ prepared,
790
+ result,
791
+ segment_times,
792
+ speech_spans,
793
+ generation_id
794
+ )
795
+ end
796
+
797
+ render_and_publish(
798
+ result,
799
+ decoded.samples,
800
+ segment_times,
801
+ speech_spans,
802
+ plan,
803
+ resolved_options,
804
+ measurements,
805
+ generation_id
806
+ )
807
+ end
808
+
809
+ def process_resumed_entry(prepared, checkpoint, generation_id, resolved_options, measurements,
810
+ defer_word_alignment: false)
811
+ item = prepared.item
812
+ entry = item.entry
813
+ plan = item.plan
814
+ record_file_segmentation(
815
+ measurements,
816
+ entry.path,
817
+ checkpoint.segment_times,
818
+ checkpoint.speech_spans
819
+ )
820
+ segments = checkpoint.segment_times.each_with_index.map do |(start_time, end_time), index|
821
+ TranscriptionSegment.new(
822
+ index: index,
823
+ start: start_time,
824
+ end: end_time,
825
+ text: checkpoint.segment_texts.fetch(index)
826
+ )
827
+ end
828
+ text = segments.map(&:text).reject(&:empty?).join("\n")
829
+ provenance = checkpoint_provenance(checkpoint, resolved_options)
830
+ result = TranscriptionResult.new(
831
+ path: entry.path,
832
+ relative_path: entry.relative_path,
833
+ status: "completed",
834
+ text: text,
835
+ duration: checkpoint.duration,
836
+ segments: segments,
837
+ provenance: provenance
838
+ )
839
+ if defer_word_alignment
840
+ return word_alignment_work(
841
+ prepared,
842
+ result,
843
+ checkpoint.segment_times,
844
+ checkpoint.speech_spans,
845
+ generation_id,
846
+ expected_sample_count: (checkpoint.duration * SAMPLE_RATE).round(half: :even)
847
+ )
848
+ end
849
+
850
+ samples = prepared.decoded&.samples || []
851
+ render_and_publish(
852
+ result,
853
+ samples,
854
+ checkpoint.segment_times,
855
+ checkpoint.speech_spans,
856
+ plan,
857
+ resolved_options,
858
+ measurements,
859
+ generation_id
860
+ )
861
+ end
862
+
863
+ def word_alignment_work(prepared, result, segment_times, speech_spans, generation_id,
864
+ expected_sample_count: nil)
865
+ WordPipeline::AlignmentWork.new(
866
+ index: prepared.item.index,
867
+ entry: prepared.item.entry,
868
+ plan: prepared.item.plan,
869
+ result: result,
870
+ segment_times: segment_times,
871
+ speech_spans: speech_spans,
872
+ generation_id: generation_id,
873
+ decode_backend: result.provenance.decode_backend,
874
+ expected_sample_count: expected_sample_count || prepared.decoded.samples.length,
875
+ audio_required: result.segments.any? { |segment| !segment.text.empty? },
876
+ source_snapshot: prepared.snapshot
877
+ )
878
+ end
879
+
880
+ def render_and_publish(result, samples, segment_times, speech_spans, plan, resolved_options,
881
+ measurements, generation_id, asr_evicted: false)
882
+ progressive_started = monotonic unless resolved_options.alignment == "word"
883
+ words, fallback_alignment_segments = words_for_segments(
884
+ samples,
885
+ segment_times,
886
+ result.segments,
887
+ speech_spans,
888
+ resolved_options,
889
+ measurements,
890
+ asr_evicted: asr_evicted
891
+ )
892
+ cues = if resolved_options.alignment == "none"
893
+ [].freeze
894
+ else
895
+ Output::Rendering.build_cues(
896
+ words,
897
+ max_chars: resolved_options.max_chars,
898
+ max_duration: resolved_options.max_cue_dur,
899
+ max_gap: resolved_options.max_gap,
900
+ media_duration: result.duration
901
+ )
902
+ end
903
+ result = result.with(
904
+ words: words,
905
+ cues: cues,
906
+ provenance: result.provenance.with(
907
+ fallback_alignment_segments: fallback_alignment_segments
908
+ )
909
+ )
910
+ outputs = Output::Publication.write(
911
+ plan,
912
+ result,
913
+ resolved_options,
914
+ generation_id: generation_id,
915
+ speech_spans: speech_spans
916
+ )
917
+ if outputs.any?
918
+ result = result.with(
919
+ outputs: outputs,
920
+ provenance: result.provenance.with(published: true)
921
+ )
922
+ end
923
+ result
924
+ ensure
925
+ measurements.progressive_output_seconds += monotonic - progressive_started if progressive_started
926
+ end
927
+
928
+ def checkpoint_provenance(checkpoint, resolved_options)
929
+ requested_vad = resolved_options.vad == "silero" ? options.vad_engine : nil
930
+ TranscriptionProvenance.new(
931
+ model_id: @identity&.model_id,
932
+ model_revision: @identity&.model_revision,
933
+ model_format: @identity&.model_format&.to_s,
934
+ adapter_id: @identity&.adapter_id,
935
+ adapter_revision: @identity&.adapter_revision,
936
+ decode_backend: checkpoint.decode_backend,
937
+ decode_fallback_reason: checkpoint.decode_fallback_reason,
938
+ vad_engine_requested: requested_vad,
939
+ vad_engine_actual: checkpoint.vad_engine_actual,
940
+ vad_provider: checkpoint.vad_provider,
941
+ vad_fallback_reason: checkpoint.vad_fallback_reason,
942
+ repetition_stopped_segments: checkpoint.repetition_stopped_segments,
943
+ truncation_retried_segments: checkpoint.truncation_retried_segments,
944
+ token_limit_segments: checkpoint.token_limit_segments,
945
+ generated_tokens_by_segment: checkpoint.generated_tokens_by_segment,
946
+ resumed_from_asr_checkpoint: true,
947
+ published: false
948
+ )
949
+ end
950
+
951
+ def vad_provider_options(vad_details)
952
+ vad_details&.fetch(:provider_options, nil)
953
+ end
954
+
955
+ def segment(samples, resolved_options, silero: nil)
956
+ duration = samples.length.fdiv(SAMPLE_RATE)
957
+ case resolved_options.vad
958
+ when "none"
959
+ spans = Audio::Segmentation.fixed(samples, resolved_options.max_dur)
960
+ [spans, spans, { requested: nil, actual: "none (fixed windows)", provider: nil, fallback: nil }]
961
+ when "auditok"
962
+ spans = Audio::Segmentation.energy(
963
+ samples,
964
+ min_duration: resolved_options.min_dur,
965
+ max_duration: resolved_options.max_dur,
966
+ max_silence: resolved_options.max_silence,
967
+ threshold: resolved_options.energy_threshold
968
+ )
969
+ spans = Audio::Segmentation.validate(spans, duration, max_duration: resolved_options.max_dur)
970
+ [spans, spans, { requested: nil, actual: "auditok", provider: nil, fallback: nil }]
971
+ when "silero"
972
+ silero ||= preparation_silero(0, resolved_options)
973
+ timestamps = silero.speech_timestamps(
974
+ samples,
975
+ sampling_rate: SAMPLE_RATE,
976
+ threshold: resolved_options.vad_threshold,
977
+ min_speech_duration_ms: (resolved_options.min_dur * 1000).round(half: :even),
978
+ max_speech_duration_s: resolved_options.max_dur,
979
+ min_silence_duration_ms: resolved_options.min_silence_ms,
980
+ speech_pad_ms: resolved_options.speech_pad_ms
981
+ )
982
+ raw = Audio::Segmentation.samples_to_seconds(timestamps, samples.length)
983
+ spans = resolved_options.vad_merge ? Audio::Segmentation.merge_speech(raw, resolved_options.max_dur) : raw
984
+ spans = Audio::Segmentation.validate(spans, duration, max_duration: resolved_options.max_dur)
985
+ requested = options.vad_engine
986
+ fallback = unless %w[auto onnx].include?(requested)
987
+ "Ruby maps the requested Silero #{requested} executor to the equivalent packaged ONNX graph"
988
+ end
989
+ actual = "onnx"
990
+ provider_options = silero.provider_options if silero.respond_to?(:provider_options)
991
+ execution = silero.last_execution if silero.respond_to?(:last_execution)
992
+ intraop_threads = silero.intra_op_threads if silero.respond_to?(:intra_op_threads)
993
+ [
994
+ spans,
995
+ raw,
996
+ {
997
+ requested: requested,
998
+ actual: actual,
999
+ provider: silero.provider,
1000
+ provider_options: provider_options,
1001
+ execution: execution,
1002
+ intraop_threads: intraop_threads,
1003
+ fallback: fallback
1004
+ }
1005
+ ]
1006
+ else
1007
+ raise TranscriptionConfigurationError, "Unsupported VAD mode: #{resolved_options.vad.inspect}"
1008
+ end
1009
+ rescue VAD::SileroBackendUnavailable => e
1010
+ raise FatalRuntimeError, "Silero VAD is unavailable: #{e.message}"
1011
+ end
1012
+
1013
+ def preparation_silero(worker_slot, resolved_options)
1014
+ return nil unless resolved_options.vad == "silero"
1015
+
1016
+ @preparation_silero_gate.synchronize do
1017
+ cached = @preparation_sileros[worker_slot]
1018
+ return cached.session if cached && cached.thread.equal?(Thread.current)
1019
+
1020
+ keywords = silero_runtime_options(resolved_options)
1021
+ session = if keywords.empty? || !silero_factory_accepts_keywords?
1022
+ @silero_factory.call
1023
+ else
1024
+ @silero_factory.call(**keywords)
1025
+ end
1026
+ @preparation_sileros[worker_slot] = PreparationSilero.new(
1027
+ thread: Thread.current,
1028
+ session: session
1029
+ )
1030
+ session
1031
+ end
1032
+ end
1033
+
1034
+ def silero_runtime_options(resolved_options)
1035
+ return {} unless packed_silero_compatibility?(resolved_options)
1036
+
1037
+ {
1038
+ block_frames: resolved_options.vad_block_frames,
1039
+ threads: resolved_options.vad_threads || VAD::Silero::DEFAULT_INTRA_OP_THREADS
1040
+ }
1041
+ end
1042
+
1043
+ def silero_factory_accepts_keywords?
1044
+ parameters = if @silero_factory.respond_to?(:parameters)
1045
+ @silero_factory.parameters
1046
+ else
1047
+ @silero_factory.method(:call).parameters
1048
+ end
1049
+ parameters.any? { |kind, _name| %i[key keyreq keyrest].include?(kind) }
1050
+ rescue NameError
1051
+ false
1052
+ end
1053
+
1054
+ def vad_file_concurrency_limit(resolved_options)
1055
+ resolved_options.vad_batch_size if packed_silero_compatibility?(resolved_options)
1056
+ end
1057
+
1058
+ def packed_silero_compatibility?(resolved_options)
1059
+ resolved_options.vad == "silero" && %w[auto torch].include?(options.vad_engine)
1060
+ end
1061
+
1062
+ def transcribe_segments(samples, segment_times, language:, measurements:, resolved_options:)
1063
+ return [] if segment_times.empty?
1064
+
1065
+ # A retained MMS session from an earlier file/run must not overlap
1066
+ # the much larger Dense ASR checkpoint.
1067
+ evict_aligner if resolved_options.alignment == "word"
1068
+ session = ensure_native_session(measurements, resolved_options)
1069
+ work = segment_times.each_with_index.map do |(start_time, end_time), index|
1070
+ { index: index, start: start_time, end: end_time, duration: end_time - start_time }
1071
+ end
1072
+ work.sort_by! { |row| [-row.fetch(:duration), row.fetch(:index)] }
1073
+ controller = ensure_batch_controller(session, resolved_options, work.map { |row| row.fetch(:duration) })
1074
+ telemetry = ASR::BatchTelemetry.new
1075
+ outcomes = Array.new(segment_times.length)
1076
+
1077
+ begin
1078
+ until work.empty?
1079
+ count = if work.first.fetch(:duration) > 35.0
1080
+ 1
1081
+ else
1082
+ controller.take_count(work) { |row| row.fetch(:duration) }
1083
+ end
1084
+ rows = work.shift(count)
1085
+ batch_outcomes = transcribe_batch_generation(
1086
+ session,
1087
+ samples,
1088
+ rows,
1089
+ language: language,
1090
+ measurements: measurements,
1091
+ resolved_options: resolved_options,
1092
+ max_new_tokens: resolved_options.max_new_tokens,
1093
+ controller: controller,
1094
+ telemetry: telemetry
1095
+ )
1096
+ batch_outcomes.each { |index, outcome| outcomes[index] = outcome }
1097
+ end
1098
+ ensure
1099
+ merge_batch_telemetry(measurements, telemetry)
1100
+ end
1101
+
1102
+ outcomes.freeze
1103
+ end
1104
+
1105
+ def ensure_batch_controller(session, resolved_options, durations)
1106
+ controller = @resources.batch_controller
1107
+ return controller.configure_group(resolved_options, durations) if controller
1108
+
1109
+ physical_max = if session.respond_to?(:batch_capacity)
1110
+ Integer(session.batch_capacity)
1111
+ elsif session.respond_to?(:transcribe_batch)
1112
+ 8
1113
+ else
1114
+ 1
1115
+ end
1116
+ raise TranscriptionRuntimeError, "Native session reported an invalid batch capacity" unless physical_max.positive?
1117
+
1118
+ controller = ASR::BatchController.create(
1119
+ resolved_options,
1120
+ device: resolved_options.device,
1121
+ durations: durations,
1122
+ memory: native_memory(session),
1123
+ physical_max: physical_max
1124
+ )
1125
+ @resources.install_batch_controller(controller)
1126
+ end
1127
+
1128
+ def transcribe_batch_generation(session, samples, rows, language:, measurements:, resolved_options:,
1129
+ max_new_tokens:, controller:, telemetry:, retry_cap: nil)
1130
+ executor = ASR::BatchExecutor.new(
1131
+ controller,
1132
+ memory: (-> { session.memory } if session.respond_to?(:memory)),
1133
+ operation_metrics: (-> { session.last_batch_metrics } if session.respond_to?(:last_batch_metrics)),
1134
+ telemetry: telemetry
1135
+ )
1136
+ execution = executor.execute(
1137
+ rows,
1138
+ max_new_tokens: max_new_tokens,
1139
+ base_max_new_tokens: resolved_options.max_new_tokens,
1140
+ retry_cap: retry_cap
1141
+ ) do |attempt_rows|
1142
+ call_native_batch(
1143
+ session,
1144
+ samples,
1145
+ attempt_rows,
1146
+ language: language,
1147
+ measurements: measurements,
1148
+ max_new_tokens: max_new_tokens
1149
+ )
1150
+ end
1151
+ raise execution.errors.compact.first if execution.errors.any?
1152
+
1153
+ natives = execution.values
1154
+ outcomes = {}
1155
+ retry_groups = Hash.new { |hash, limit| hash[limit] = [] }
1156
+
1157
+ rows.each_with_index do |row, lane|
1158
+ native = natives.fetch(lane)
1159
+ unless native.stopped_by_max_tokens
1160
+ outcomes[row.fetch(:index)] = GenerationOutcome.new(
1161
+ native: native,
1162
+ retried: false,
1163
+ hit_token_limit: false
1164
+ )
1165
+ next
1166
+ end
1167
+
1168
+ next_limit = retry_token_limit(
1169
+ max_new_tokens,
1170
+ resolved_options.max_retry_tokens,
1171
+ native.generation_limit,
1172
+ native.generation_capacity
1173
+ )
1174
+ if resolved_options.truncation_policy == "retry" && next_limit > max_new_tokens
1175
+ retry_groups[next_limit] << row
1176
+ else
1177
+ outcomes[row.fetch(:index)] = GenerationOutcome.new(
1178
+ native: native,
1179
+ retried: false,
1180
+ hit_token_limit: true
1181
+ )
1182
+ end
1183
+ end
1184
+
1185
+ retry_groups.each do |next_limit, retry_rows|
1186
+ measurements.truncation_retries += retry_rows.length
1187
+ group_retry_cap = retry_cap || ASR::RetryBatchCap.new(retry_rows.length)
1188
+ retried = transcribe_batch_generation(
1189
+ session,
1190
+ samples,
1191
+ retry_rows,
1192
+ language: language,
1193
+ measurements: measurements,
1194
+ resolved_options: resolved_options,
1195
+ max_new_tokens: next_limit,
1196
+ controller: controller,
1197
+ telemetry: telemetry,
1198
+ retry_cap: group_retry_cap
1199
+ )
1200
+ retried.each do |index, outcome|
1201
+ outcomes[index] = GenerationOutcome.new(
1202
+ native: outcome.native,
1203
+ retried: true,
1204
+ hit_token_limit: outcome.hit_token_limit
1205
+ )
1206
+ end
1207
+ end
1208
+ outcomes.freeze
1209
+ end
1210
+
1211
+ def call_native_batch(session, samples, rows, language:, measurements:, max_new_tokens:)
1212
+ slices = []
1213
+ offsets = []
1214
+ rows.each do |row|
1215
+ start_sample = (row.fetch(:start) * SAMPLE_RATE).round(half: :even).clamp(0, samples.length)
1216
+ end_sample = (row.fetch(:end) * SAMPLE_RATE).round(half: :even).clamp(start_sample, samples.length)
1217
+ slices << samples[start_sample...end_sample].dup
1218
+ offsets << row.fetch(:start)
1219
+ end
1220
+
1221
+ asr_started = monotonic
1222
+ if rows.length == 1 || !session.respond_to?(:transcribe_batch)
1223
+ [session.transcribe(
1224
+ slices.first,
1225
+ language: language,
1226
+ offset: offsets.first,
1227
+ max_new_tokens: max_new_tokens
1228
+ )]
1229
+ else
1230
+ session.transcribe_batch(
1231
+ slices,
1232
+ language: language,
1233
+ offsets: offsets,
1234
+ max_new_tokens: max_new_tokens
1235
+ )
1236
+ end
1237
+ ensure
1238
+ if asr_started
1239
+ elapsed = monotonic - asr_started
1240
+ measurements.asr_seconds += elapsed
1241
+ measurements.asr_generation_call_seconds += elapsed
1242
+ end
1243
+ record_cuda_memory(measurements, session, start: false)
1244
+ end
1245
+
1246
+ def native_memory(session)
1247
+ session.memory if session.respond_to?(:memory)
1248
+ rescue StandardError
1249
+ nil
1250
+ end
1251
+
1252
+ def merge_batch_telemetry(measurements, telemetry)
1253
+ measurements.asr_batches += telemetry.asr_batches
1254
+ measurements.asr_processor_rows += telemetry.processor_rows
1255
+ measurements.generated_tokens += telemetry.generated_tokens
1256
+ measurements.oom_retries += telemetry.oom_retries
1257
+ measurements.asr_feature_worker_seconds += telemetry.feature_worker_seconds
1258
+ measurements.asr_h2d_seconds += telemetry.h2d_seconds
1259
+ measurements.asr_generate_device_seconds += telemetry.generate_device_seconds
1260
+ measurements.asr_generation_analysis_seconds += telemetry.generation_analysis_seconds
1261
+ if telemetry.effective_batch_min.positive?
1262
+ measurements.effective_batch_min = if measurements.effective_batch_min.zero?
1263
+ telemetry.effective_batch_min
1264
+ else
1265
+ [measurements.effective_batch_min,
1266
+ telemetry.effective_batch_min].min
1267
+ end
1268
+ end
1269
+ measurements.effective_batch_max = [
1270
+ measurements.effective_batch_max,
1271
+ telemetry.effective_batch_max
1272
+ ].max
1273
+ measurements.final_batch_size = telemetry.final_batch_size
1274
+ measurements.final_batch_cap = telemetry.final_batch_cap
1275
+ measurements.batch_history.concat(telemetry.batch_history)
1276
+ end
1277
+
1278
+ def retry_token_limit(current_limit, requested_maximum, effective_limit, generation_capacity)
1279
+ # A lower native effective limit means the decoder's positional
1280
+ # context, rather than the user limit, was exhausted. Retrying cannot
1281
+ # create more decoder positions.
1282
+ return current_limit if effective_limit.positive? && effective_limit < current_limit
1283
+
1284
+ ceiling = generation_capacity.positive? ? [requested_maximum, generation_capacity].min : requested_maximum
1285
+ return current_limit if ceiling <= current_limit
1286
+
1287
+ proposed = [ceiling, [current_limit + 128, current_limit * 2].max].min
1288
+ ceiling - proposed < 128 ? ceiling : proposed
1289
+ end
1290
+
1291
+ def ensure_native_session(measurements, resolved_options)
1292
+ loaded_seconds = 0.0
1293
+ retained = @resources.asr_session
1294
+ if retained
1295
+ @native_session = retained
1296
+ record_cuda_memory(measurements, retained, start: true)
1297
+ return retained
1298
+ end
1299
+
1300
+ evict_aligner
1301
+ session, = @resources.acquire_asr(asr_resource_key(resolved_options)) do
1302
+ started = monotonic
1303
+ begin
1304
+ @model_provider.open(@identity, resolved_options)
1305
+ ensure
1306
+ loaded_seconds += monotonic - started
1307
+ end
1308
+ end
1309
+ @native_session = session
1310
+ record_cuda_memory(measurements, session, start: true)
1311
+ session
1312
+ rescue TranscriptionError => e
1313
+ raise FatalRuntimeError, e.message
1314
+ rescue StandardError => e
1315
+ raise FatalRuntimeError, "Cannot initialize Dense Cohere inference: #{e.class}: #{e.message}"
1316
+ ensure
1317
+ measurements.asr_load_seconds += loaded_seconds if loaded_seconds
1318
+ end
1319
+
1320
+ def asr_resource_key(resolved_options)
1321
+ [
1322
+ resolved_options.device,
1323
+ resolved_options.dtype,
1324
+ @identity.model_id,
1325
+ @identity.model_revision,
1326
+ @identity.model_format,
1327
+ @identity.adapter_id,
1328
+ @identity.adapter_revision
1329
+ ].freeze
1330
+ end
1331
+
1332
+ def uniform_words_for_segment(text, start_time, end_time, segment_index, speech_spans, resolved_options)
1333
+ return [] if text.empty?
1334
+
1335
+ within = Output::Timing.spans_within(speech_spans, start_time, end_time)
1336
+ if resolved_options.vad_merge && within.any?
1337
+ Output::Timing.uniform_words_across_spans(
1338
+ text, within, segment_index, "uniform_speech_spans"
1339
+ )
1340
+ else
1341
+ Output::Timing.uniform_words(text, start_time, end_time, segment_index)
1342
+ end
1343
+ end
1344
+
1345
+ def words_for_segments(samples, segment_times, segments, speech_spans, resolved_options, measurements,
1346
+ asr_evicted: false)
1347
+ case resolved_options.alignment
1348
+ when "none"
1349
+ [[], 0]
1350
+ when "word"
1351
+ align_words(
1352
+ samples,
1353
+ segment_times,
1354
+ segments,
1355
+ resolved_options,
1356
+ measurements,
1357
+ asr_evicted: asr_evicted
1358
+ )
1359
+ else
1360
+ words = segments.flat_map do |segment|
1361
+ uniform_words_for_segment(
1362
+ segment.text,
1363
+ segment.start,
1364
+ segment.end,
1365
+ segment.index,
1366
+ speech_spans,
1367
+ resolved_options
1368
+ )
1369
+ end
1370
+ [words.freeze, 0]
1371
+ end
1372
+ end
1373
+
1374
+ def align_words(samples, segment_times, segments, resolved_options, measurements, asr_evicted: false)
1375
+ return [[], 0] unless segments.any? { |segment| !segment.text.empty? }
1376
+
1377
+ # Match the reference runtime's memory invariant: the 2B ASR and
1378
+ # 300M MMS checkpoints must not be resident at the same time.
1379
+ evict_current_asr_owner unless asr_evicted
1380
+ report(ProgressEvent.new(stage: "alignment", current: 0, total: segments.length))
1381
+ aligner = ensure_aligner(resolved_options)
1382
+ before = alignment_measurements(aligner)
1383
+ result = aligner.align(
1384
+ samples,
1385
+ segment_times,
1386
+ segments.map(&:text),
1387
+ language: resolved_options.language
1388
+ )
1389
+ report(ProgressEvent.new(stage: "alignment", current: segments.length, total: segments.length))
1390
+ result
1391
+ rescue StandardError
1392
+ evict_aligner unless asr_evicted
1393
+ raise
1394
+ ensure
1395
+ record_alignment_measurements(measurements, before, aligner) if before && aligner
1396
+ end
1397
+
1398
+ def ensure_aligner(resolved_options)
1399
+ @aligner ||= @aligner_factory.call(
1400
+ dtype: resolved_options.align_dtype,
1401
+ device: resolved_options.device,
1402
+ batch_size: resolved_options.align_batch_size
1403
+ )
1404
+ rescue TranscriptionError
1405
+ raise
1406
+ rescue StandardError => e
1407
+ raise TranscriptionRuntimeError, "Cannot initialize MMS word alignment: #{e.class}: #{e.message}"
1408
+ end
1409
+
1410
+ def alignment_measurements(aligner)
1411
+ [aligner.load_seconds.to_f, aligner.emissions_seconds.to_f, aligner.viterbi_seconds.to_f]
1412
+ end
1413
+
1414
+ def record_alignment_measurements(measurements, before, aligner)
1415
+ after = alignment_measurements(aligner)
1416
+ measurements.aligner_load_seconds += [after.fetch(0) - before.fetch(0), 0.0].max
1417
+ measurements.emissions_seconds += [after.fetch(1) - before.fetch(1), 0.0].max
1418
+ measurements.viterbi_seconds += [after.fetch(2) - before.fetch(2), 0.0].max
1419
+ end
1420
+
1421
+ def skipped_result(entry, plan)
1422
+ TranscriptionResult.new(
1423
+ path: entry.path,
1424
+ relative_path: entry.relative_path,
1425
+ status: "skipped",
1426
+ text: nil,
1427
+ duration: skipped_duration(entry.path),
1428
+ outputs: plan.paths.values,
1429
+ provenance: provenance_for(published: true)
1430
+ )
1431
+ end
1432
+
1433
+ def skipped_duration(path)
1434
+ @decoder.probe_duration(path) if @decoder.respond_to?(:probe_duration)
1435
+ rescue StandardError
1436
+ nil
1437
+ end
1438
+
1439
+ def failed_result(entry, duration, error)
1440
+ TranscriptionResult.new(
1441
+ path: entry.path,
1442
+ relative_path: entry.relative_path,
1443
+ status: "failed",
1444
+ text: nil,
1445
+ duration: duration,
1446
+ error: "#{error.class}: #{error.message}",
1447
+ provenance: provenance_for(published: false)
1448
+ )
1449
+ end
1450
+
1451
+ def provenance_for(published:, decoded: nil, vad_details: nil, fallback_alignment_segments: 0,
1452
+ repetition_stopped_segments: [], truncation_retried_segments: [],
1453
+ token_limit_segments: [], generated_tokens: [])
1454
+ TranscriptionProvenance.new(
1455
+ model_id: @identity&.model_id,
1456
+ model_revision: @identity&.model_revision,
1457
+ model_format: @identity&.model_format&.to_s,
1458
+ adapter_id: @identity&.adapter_id,
1459
+ adapter_revision: @identity&.adapter_revision,
1460
+ decode_backend: decoded&.backend,
1461
+ decode_fallback_reason: decoded&.fallback_reason,
1462
+ vad_engine_requested: vad_details&.fetch(:requested, nil),
1463
+ vad_engine_actual: vad_details&.fetch(:actual, nil),
1464
+ vad_provider: vad_details&.fetch(:provider, nil),
1465
+ vad_fallback_reason: vad_details&.fetch(:fallback, nil),
1466
+ fallback_alignment_segments: fallback_alignment_segments,
1467
+ repetition_stopped_segments: repetition_stopped_segments,
1468
+ truncation_retried_segments: truncation_retried_segments,
1469
+ token_limit_segments: token_limit_segments,
1470
+ generated_tokens_by_segment: generated_tokens,
1471
+ published: published
1472
+ )
1473
+ end
1474
+
1475
+ def build_run(results, resolved_options, measurements, elapsed:)
1476
+ successful_seconds = results.sum do |result|
1477
+ result.status == "completed" ? result.duration.to_f : 0.0
1478
+ end
1479
+ statistics = TranscriptionStatistics.new(
1480
+ elapsed_seconds: elapsed,
1481
+ successful_audio_seconds: successful_seconds,
1482
+ real_time_factor_x: elapsed.positive? ? successful_seconds / elapsed : 0.0,
1483
+ runtime_import_seconds: measurements.runtime_import_seconds,
1484
+ serialization_wait_seconds: measurements.serialization_wait_seconds,
1485
+ input_validation_seconds: measurements.input_validation_seconds,
1486
+ decode_seconds: measurements.decode_seconds,
1487
+ vad_seconds: measurements.vad_seconds,
1488
+ asr_load_seconds: measurements.asr_load_seconds,
1489
+ asr_seconds: measurements.asr_seconds,
1490
+ aligner_load_seconds: measurements.aligner_load_seconds,
1491
+ emissions_seconds: measurements.emissions_seconds,
1492
+ viterbi_seconds: measurements.viterbi_seconds,
1493
+ peak_cuda_allocated_gib: 0.0,
1494
+ peak_cuda_reserved_gib: 0.0,
1495
+ asr_batches: measurements.asr_batches,
1496
+ asr_processor_rows: measurements.asr_processor_rows,
1497
+ generated_tokens: measurements.generated_tokens,
1498
+ oom_retries: measurements.oom_retries,
1499
+ truncation_retries: measurements.truncation_retries
1500
+ )
1501
+ TranscriptionRun.new(
1502
+ results: results,
1503
+ requested_options: options,
1504
+ resolved_options: resolved_options,
1505
+ statistics: statistics
1506
+ )
1507
+ end
1508
+
1509
+ def profile_runtime_metrics(measurements)
1510
+ {
1511
+ vad_model_load_seconds: measurements.vad_model_load_seconds,
1512
+ vad_inference_seconds: measurements.vad_inference_seconds,
1513
+ vad_postprocess_seconds: measurements.vad_postprocess_seconds,
1514
+ preparation_wait_seconds: measurements.preparation_wait_seconds,
1515
+ asr_generation_call_seconds: measurements.asr_generation_call_seconds,
1516
+ asr_feature_worker_seconds: measurements.asr_feature_worker_seconds,
1517
+ asr_h2d_seconds: measurements.asr_h2d_seconds,
1518
+ asr_generate_device_seconds: measurements.asr_generate_device_seconds,
1519
+ asr_generation_analysis_seconds: measurements.asr_generation_analysis_seconds,
1520
+ post_asr_seconds: measurements.post_asr_seconds,
1521
+ checkpoint_seconds: measurements.checkpoint_seconds,
1522
+ progressive_output_seconds: measurements.progressive_output_seconds,
1523
+ cuda_total_gib: measurements.cuda_total_gib,
1524
+ cuda_free_start_gib: measurements.cuda_free_start_gib,
1525
+ cuda_free_end_gib: measurements.cuda_free_end_gib,
1526
+ effective_batch_min: measurements.effective_batch_min,
1527
+ effective_batch_max: measurements.effective_batch_max,
1528
+ final_batch_size: measurements.final_batch_size,
1529
+ final_batch_cap: measurements.final_batch_cap,
1530
+ batch_history: measurements.batch_history,
1531
+ checkpoint_written_files: measurements.checkpoint_written_files,
1532
+ vad_prepared_groups: measurements.vad_prepared_groups,
1533
+ vad_model_calls: measurements.vad_model_calls,
1534
+ vad_valid_frames: measurements.vad_valid_frames,
1535
+ vad_padded_frames: measurements.vad_padded_frames,
1536
+ vad_max_files_per_call: measurements.vad_max_files_per_call,
1537
+ vad_effective_block_frames: measurements.vad_effective_block_frames,
1538
+ vad_intraop_threads: measurements.vad_intraop_threads,
1539
+ file_segmentation: measurements.file_segmentation
1540
+ }.freeze
1541
+ end
1542
+
1543
+ def record_vad_measurements(measurements, vad_details)
1544
+ intraop_threads = vad_details&.fetch(:intraop_threads, nil)
1545
+ measurements.vad_intraop_threads = intraop_threads if intraop_threads
1546
+ execution = vad_details&.fetch(:execution, nil)
1547
+ return unless execution
1548
+
1549
+ measurements.vad_prepared_groups += 1
1550
+ measurements.vad_model_load_seconds += execution.model_load_seconds if execution.respond_to?(:model_load_seconds)
1551
+ measurements.vad_inference_seconds += execution.inference_seconds if execution.respond_to?(:inference_seconds)
1552
+ measurements.vad_postprocess_seconds += execution.postprocess_seconds if execution.respond_to?(:postprocess_seconds)
1553
+ measurements.vad_model_calls += execution.model_calls
1554
+ measurements.vad_valid_frames += execution.valid_frames
1555
+ measurements.vad_padded_frames += execution.padded_frames
1556
+ measurements.vad_max_files_per_call = [
1557
+ measurements.vad_max_files_per_call,
1558
+ execution.max_files_per_call
1559
+ ].max
1560
+ effective = execution.effective_block_frames
1561
+ return unless effective&.positive?
1562
+
1563
+ current = measurements.vad_effective_block_frames
1564
+ measurements.vad_effective_block_frames = current.zero? ? effective : [current, effective].min
1565
+ end
1566
+
1567
+ def record_file_segmentation(measurements, path, segment_times, speech_spans)
1568
+ immutable_spans = lambda do |spans|
1569
+ spans.map { |start_time, end_time| [start_time.to_f, end_time.to_f].freeze }.freeze
1570
+ end
1571
+ measurements.file_segmentation[path.to_s] = {
1572
+ segment_times: immutable_spans.call(segment_times),
1573
+ speech_spans: immutable_spans.call(speech_spans)
1574
+ }.freeze
1575
+ end
1576
+
1577
+ def record_cuda_memory(measurements, session, start:)
1578
+ return unless session.respond_to?(:memory)
1579
+ return if session.respond_to?(:device) && session.device.to_s != "cuda"
1580
+
1581
+ free_bytes, total_bytes = native_memory(session)
1582
+ free_bytes = Integer(free_bytes)
1583
+ total_bytes = Integer(total_bytes)
1584
+ return unless free_bytes.between?(0, total_bytes) && total_bytes.positive?
1585
+
1586
+ measurements.cuda_total_gib = total_bytes.fdiv(1024**3)
1587
+ free_gib = free_bytes.fdiv(1024**3)
1588
+ if start
1589
+ measurements.cuda_free_start_gib ||= free_gib
1590
+ else
1591
+ measurements.cuda_free_end_gib = free_gib
1592
+ end
1593
+ rescue TypeError, ArgumentError
1594
+ nil
1595
+ end
1596
+
1597
+ # Publication, checkpoints, and profiles retain every ASR row so their
1598
+ # duration and generation telemetry remains complete. The stable public
1599
+ # API mirrors Python by omitting blank transcript rows without
1600
+ # renumbering the surviving segment indices.
1601
+ def public_run(run)
1602
+ changed = false
1603
+ results = run.results.map do |result|
1604
+ next result if result.segments.empty?
1605
+
1606
+ visible = result.segments.reject { |segment| PythonText.blank?(segment.text.to_s) }
1607
+ next result if visible.length == result.segments.length
1608
+
1609
+ changed = true
1610
+ result.with(segments: visible.freeze)
1611
+ end.freeze
1612
+ changed ? run.with(results: results) : run
1613
+ end
1614
+
1615
+ def report(event)
1616
+ return unless @progress
1617
+
1618
+ @progress_gate.synchronize do
1619
+ previous = Thread.current.thread_variable_get(THREAD_PROGRESS_KEY)
1620
+ Thread.current.thread_variable_set(THREAD_PROGRESS_KEY, true)
1621
+ begin
1622
+ @progress.call(event)
1623
+ rescue ScriptError, StandardError => e
1624
+ evict_inference_sessions
1625
+ raise ProgressCallbackError, e
1626
+ ensure
1627
+ Thread.current.thread_variable_set(THREAD_PROGRESS_KEY, previous)
1628
+ end
1629
+ end
1630
+ end
1631
+
1632
+ def source_snapshot(path)
1633
+ stat = path.stat
1634
+ [stat.dev, stat.ino, stat.size, stat.mtime.to_r, stat.ctime.to_r]
1635
+ end
1636
+
1637
+ def ensure_source_unchanged!(path, before)
1638
+ unless source_snapshot(path) == before
1639
+ raise TranscriptionRuntimeError,
1640
+ "Source changed while processing: #{path}"
1641
+ end
1642
+ rescue SystemCallError => e
1643
+ raise TranscriptionRuntimeError, "Cannot re-check source #{path}: #{e.message}"
1644
+ end
1645
+
1646
+ def evict_native_session
1647
+ @resources.evict_asr
1648
+ ensure
1649
+ @native_session = nil
1650
+ end
1651
+
1652
+ def evict_current_asr_owner
1653
+ ModelResources.evict_current_asr_owner
1654
+ ensure
1655
+ @native_session = @resources.asr_session
1656
+ end
1657
+
1658
+ def evict_aligner
1659
+ @aligner&.close
1660
+ ensure
1661
+ @aligner = nil
1662
+ end
1663
+
1664
+ def evict_inference_sessions
1665
+ evict_native_session
1666
+ ensure
1667
+ evict_aligner
1668
+ end
1669
+
1670
+ def monotonic
1671
+ Process.clock_gettime(Process::CLOCK_MONOTONIC)
1672
+ end
1673
+ end
1674
+ end
1675
+ end
1676
+ end