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,1118 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "digest"
4
+ require "etc"
5
+ require "json"
6
+ require "rbconfig"
7
+ require "securerandom"
8
+ require "tempfile"
9
+ require_relative "../python_text"
10
+
11
+ module Cohere
12
+ module Transcribe
13
+ module Output
14
+ PublicationPlan = Data.define(
15
+ :paths,
16
+ :state_path,
17
+ :checkpoint_path,
18
+ :source_snapshot,
19
+ :asr_contract_key,
20
+ :render_contract_key,
21
+ :lock_target,
22
+ :directory_bindings,
23
+ :skipped,
24
+ :generation_id
25
+ )
26
+ PublicationDecision = Data.define(:action, :checkpoint, :generation_id, :reason)
27
+
28
+ module Publication
29
+ module_function
30
+
31
+ OUTPUT_SCHEMA_VERSION = 8
32
+ PROFILE_SCHEMA_VERSION = 9
33
+ REPETITION_DETECTOR_VERSION = 3
34
+ SILERO_VERSION = "6.2.1"
35
+ SUCCESSFUL_BATCH_PROFILE_FIELDS = %w[
36
+ segments processor_rows max_new_tokens generated_tokens generated_tokens_by_row
37
+ prepare_seconds h2d_seconds generation_call_wall_seconds generate_device_seconds
38
+ generation_analysis_seconds padded_audio_seconds padding_ratio peak_allocated_gib
39
+ peak_reserved_gib
40
+ ].freeze
41
+
42
+ def plan(entries, options)
43
+ publication = options.publication
44
+ unless publication
45
+ return entries.to_h do |entry|
46
+ [
47
+ entry.path,
48
+ PublicationPlan.new(
49
+ paths: {}.freeze,
50
+ state_path: nil,
51
+ checkpoint_path: nil,
52
+ source_snapshot: nil,
53
+ asr_contract_key: nil,
54
+ render_contract_key: nil,
55
+ lock_target: nil,
56
+ directory_bindings: [].freeze,
57
+ skipped: false,
58
+ generation_id: nil
59
+ )
60
+ ]
61
+ end.freeze
62
+ end
63
+
64
+ root = publication.output_dir&.expand_path
65
+ if root
66
+ root_binding = State.ensure_bound_directory(root)
67
+ root = root_binding.canonical_path
68
+ end
69
+
70
+ claimed = {}
71
+ input_paths = entries.to_h { |entry| [entry.path.expand_path.to_s, true] }
72
+ profile_path = publication.profile_json&.expand_path
73
+ profile_key = nil
74
+ if profile_path
75
+ if profile_path.symlink? || (profile_path.exist? && !profile_path.file?)
76
+ raise TranscriptionInputError, "Profile path is not a regular file: #{profile_path}"
77
+ end
78
+
79
+ profile_key = prospective_realpath(profile_path).to_s
80
+ if input_paths.key?(profile_key)
81
+ raise TranscriptionInputError, "Profile path collides with an input audio file: #{profile_path}"
82
+ end
83
+ end
84
+ asr_contract_key = State.asr_contract_key(options)
85
+ render_contract_key = State.render_contract_key(options)
86
+ plans = entries.to_h do |entry|
87
+ source_snapshot = source_record(entry.path)
88
+ parent = root ? root.join(entry.relative_path.dirname) : entry.path.dirname
89
+ ensure_within_output_root!(prospective_realpath(parent), root, parent) if root
90
+ parent_binding = State.ensure_bound_directory(parent, root_binding: root_binding)
91
+ parent = parent_binding.canonical_path
92
+ ensure_within_output_root!(parent, root, parent) if root
93
+ directory_bindings = [root_binding, parent_binding].compact.uniq.freeze
94
+ stem = entry.relative_path.basename(entry.relative_path.extname).to_s
95
+ paths = publication.formats.to_h do |format|
96
+ output_path = parent.join("#{stem}.#{format}")
97
+ validate_output_path!(output_path, input_paths, entry.path)
98
+ canonical = output_path.expand_path.to_s
99
+ if (previous = claimed[canonical]) && previous != entry.path
100
+ raise TranscriptionInputError,
101
+ "Output collision between #{previous} and #{entry.path}: #{output_path}"
102
+ end
103
+ if profile_key == canonical
104
+ raise TranscriptionInputError,
105
+ "Profile path collides with a transcript output: #{output_path}"
106
+ end
107
+ claimed[canonical] = entry.path
108
+ [format, output_path.freeze]
109
+ end.freeze
110
+ state_path = State.state_path_for_outputs(paths)
111
+ checkpoint_path = State.checkpoint_path_for_outputs(paths)
112
+ lock_target = State.lock_target_for_outputs(paths)
113
+ {
114
+ "State marker" => state_path,
115
+ "ASR checkpoint" => checkpoint_path
116
+ }.each do |label, reserved_path|
117
+ if reserved_path.symlink? || (reserved_path.exist? && !reserved_path.file?)
118
+ raise TranscriptionInputError, "#{label} is not a regular file: #{reserved_path}"
119
+ end
120
+
121
+ reserved_key = reserved_path.expand_path.to_s
122
+ if profile_key == reserved_key
123
+ raise TranscriptionInputError,
124
+ "Profile path collides with a reserved #{label.downcase}: #{reserved_path}"
125
+ end
126
+ if input_paths.key?(reserved_key) || claimed.key?(reserved_key)
127
+ raise TranscriptionInputError, "#{label} collides with an input or output: #{reserved_path}"
128
+ end
129
+
130
+ claimed[reserved_key] = entry.path
131
+ end
132
+
133
+ existing = paths.values.select(&:exist?)
134
+ if existing.any? && publication.existing == "error"
135
+ rendered = existing.map { |path| " #{path}" }.join("\n")
136
+ raise TranscriptionInputError,
137
+ "Output already exists:\n#{rendered}\n" \
138
+ "Use existing: 'overwrite' or existing: 'skip'."
139
+ end
140
+ verification = if publication.existing == "skip" && existing.length == paths.length
141
+ State.verify_published_outputs(
142
+ source_snapshot: source_snapshot,
143
+ output_paths: paths,
144
+ state_path: state_path,
145
+ asr_contract_key: asr_contract_key,
146
+ render_contract_key: render_contract_key,
147
+ directory_binding: directory_bindings.last,
148
+ guard_bindings: directory_bindings
149
+ )
150
+ end
151
+ skipped = verification&.verified? || false
152
+ [
153
+ entry.path,
154
+ PublicationPlan.new(
155
+ paths: paths,
156
+ state_path: state_path.freeze,
157
+ checkpoint_path: checkpoint_path.freeze,
158
+ source_snapshot: source_snapshot,
159
+ asr_contract_key: asr_contract_key,
160
+ render_contract_key: render_contract_key,
161
+ lock_target: lock_target,
162
+ directory_bindings: directory_bindings,
163
+ skipped: skipped,
164
+ generation_id: verification&.generation_id
165
+ )
166
+ ]
167
+ end
168
+ plans.freeze
169
+ rescue SystemCallError, ArgumentError, TranscriptionRuntimeError => e
170
+ detail = e.message.to_s.delete("\0")
171
+ raise TranscriptionInputError, "Cannot prepare output paths: #{detail}"
172
+ end
173
+
174
+ def with_plan_lock(plan, &block)
175
+ return block.call unless plan.lock_target
176
+
177
+ State.with_output_lock(plan.lock_target, &block)
178
+ end
179
+
180
+ def verify_plan_directory_continuity!(before_plans, after_plans)
181
+ raise TranscriptionRuntimeError, "Publication inputs changed between planning passes" unless before_plans.keys == after_plans.keys
182
+
183
+ before_plans.each do |path, before_plan|
184
+ after_plan = after_plans.fetch(path)
185
+ before_plan.directory_bindings.each(&:verify!)
186
+ after_plan.directory_bindings.each(&:verify!)
187
+ next if before_plan.directory_bindings == after_plan.directory_bindings
188
+
189
+ raise TranscriptionRuntimeError,
190
+ "Publication parent identity changed between planning passes: #{path}"
191
+ end
192
+ nil
193
+ end
194
+
195
+ def verify_profile_directory_continuity!(before_binding, after_binding)
196
+ return if before_binding.nil? && after_binding.nil?
197
+
198
+ before_binding&.verify!
199
+ after_binding&.verify!
200
+ return if before_binding == after_binding
201
+
202
+ raise TranscriptionRuntimeError,
203
+ "Profile parent identity changed between planning passes"
204
+ end
205
+
206
+ def revalidate(plan, options)
207
+ return PublicationDecision.new(action: :process, checkpoint: nil, generation_id: nil, reason: nil) if plan.paths.empty?
208
+
209
+ plan.directory_bindings.each(&:verify!)
210
+ State.ensure_source_unchanged!(plan.source_snapshot)
211
+ validate_reserved_path!(plan.state_path, "State marker")
212
+ validate_reserved_path!(plan.checkpoint_path, "ASR checkpoint")
213
+ plan.paths.each_value { |path| validate_runtime_output_path!(path) }
214
+ existing = plan.paths.values.select(&:exist?)
215
+ if existing.any? && options.publication.existing == "error"
216
+ rendered = existing.map { |path| " #{path}" }.join("\n")
217
+ raise TranscriptionInputError,
218
+ "Output already exists:\n#{rendered}\n" \
219
+ "Use existing: 'overwrite' or existing: 'skip'."
220
+ end
221
+
222
+ if options.publication.existing == "skip" && existing.length == plan.paths.length
223
+ verification = State.verify_published_outputs(
224
+ source_snapshot: plan.source_snapshot,
225
+ output_paths: plan.paths,
226
+ state_path: plan.state_path,
227
+ asr_contract_key: plan.asr_contract_key,
228
+ render_contract_key: plan.render_contract_key,
229
+ directory_binding: plan.directory_bindings.last,
230
+ guard_bindings: plan.directory_bindings
231
+ )
232
+ if verification.verified?
233
+ return PublicationDecision.new(
234
+ action: :skip,
235
+ checkpoint: nil,
236
+ generation_id: verification.generation_id,
237
+ reason: nil
238
+ )
239
+ end
240
+ publication_reason = verification.reason
241
+ elsif existing.any? && options.publication.existing == "skip"
242
+ publication_reason = "requested output set is incomplete"
243
+ end
244
+
245
+ checkpoint = State.restore_asr_checkpoint(
246
+ path: plan.checkpoint_path,
247
+ source_snapshot: plan.source_snapshot,
248
+ asr_contract_key: plan.asr_contract_key,
249
+ directory_binding: plan.directory_bindings.last,
250
+ guard_bindings: plan.directory_bindings
251
+ )
252
+ if checkpoint.restored?
253
+ return PublicationDecision.new(
254
+ action: :resume,
255
+ checkpoint: checkpoint.checkpoint,
256
+ generation_id: checkpoint.checkpoint.generation_id,
257
+ reason: publication_reason
258
+ )
259
+ end
260
+
261
+ reasons = [publication_reason, checkpoint.reason].compact
262
+ PublicationDecision.new(
263
+ action: :process,
264
+ checkpoint: nil,
265
+ generation_id: nil,
266
+ reason: reasons.empty? ? nil : reasons.join("; ").freeze
267
+ )
268
+ ensure
269
+ plan.directory_bindings.each(&:verify!) if plan&.paths&.any?
270
+ end
271
+
272
+ def write(plan, result, options, generation_id: nil, speech_spans: nil)
273
+ return [].freeze if plan.paths.empty?
274
+
275
+ contents = plan.paths.to_h do |format, _path|
276
+ [format, render(format, result, options, speech_spans: speech_spans)]
277
+ end
278
+ snapshot = plan.source_snapshot || source_record(result.path)
279
+ generation_id = SecureRandom.hex(16) if generation_id.to_s.empty?
280
+ manifest = State.published_manifest_content(
281
+ source_snapshot: snapshot,
282
+ output_paths: plan.paths,
283
+ contents: contents,
284
+ asr_contract_key: plan.asr_contract_key || State.asr_contract_key(options),
285
+ render_contract_key: plan.render_contract_key || State.render_contract_key(options),
286
+ generation_id: generation_id
287
+ )
288
+ transaction_paths = plan.paths.merge("__manifest__" => plan.state_path).freeze
289
+ transaction_contents = contents.merge("__manifest__" => manifest).freeze
290
+ atomic_write_set(
291
+ transaction_paths,
292
+ transaction_contents,
293
+ before_commit: -> { State.ensure_source_unchanged!(snapshot) },
294
+ directory_bindings: plan.directory_bindings
295
+ )
296
+ plan.paths.values.freeze
297
+ end
298
+
299
+ def write_profile(path, run, runtime_metrics: nil, directory_binding: nil)
300
+ return unless path
301
+
302
+ destination = Pathname(path).expand_path
303
+ directory_binding ||= State.ensure_bound_directory(destination.dirname)
304
+ payload = profile_payload(run, runtime_metrics: runtime_metrics)
305
+ atomic_write_set(
306
+ { "json" => destination },
307
+ { "json" => Rendering.json(payload) },
308
+ directory_bindings: [directory_binding]
309
+ )
310
+ destination
311
+ rescue Interrupt, SystemExit
312
+ raise
313
+ rescue StandardError => e
314
+ raise TranscriptionRuntimeError,
315
+ "profile output failed: #{e.class}: Cannot write #{path}: #{e.message}"
316
+ end
317
+
318
+ def bind_profile_path(path)
319
+ return unless path
320
+
321
+ destination = Pathname(path).expand_path
322
+ State.ensure_bound_directory(destination.dirname)
323
+ rescue SystemCallError, ArgumentError, TranscriptionRuntimeError => e
324
+ raise TranscriptionInputError, "Cannot prepare profile output path #{path}: #{e.message}"
325
+ end
326
+
327
+ def profile_payload(run, runtime_metrics: nil)
328
+ requested = run.requested_options
329
+ resolved = run.resolved_options
330
+ statistics = run.statistics
331
+ runtime_metrics ||= {}
332
+ file_segmentation = runtime_metrics.fetch(:file_segmentation, {})
333
+ results = run.results
334
+ successful = results.reject { |result| result.status == "failed" }
335
+ representative = results.find { |result| result.provenance.model_id } || results.first
336
+ all_durations = results.flat_map do |result|
337
+ profile_segment_durations(result, file_segmentation[result.path.to_s])
338
+ end
339
+ inferred_durations = results.reject do |result|
340
+ result.provenance.resumed_from_asr_checkpoint
341
+ end.flat_map do |result|
342
+ profile_segment_durations(result, file_segmentation[result.path.to_s])
343
+ end
344
+ requested_engines = results.filter_map(&:provenance)
345
+ .filter_map(&:vad_engine_requested).uniq.sort
346
+ actual_engines = results.filter_map(&:provenance)
347
+ .filter_map(&:vad_engine_actual).uniq.sort
348
+ vad_prepared_groups = runtime_metrics.fetch(:vad_prepared_groups, 0)
349
+ vad_model_calls = runtime_metrics.fetch(:vad_model_calls, 0)
350
+ vad_valid_frames = runtime_metrics.fetch(:vad_valid_frames, 0)
351
+ vad_padded_frames = runtime_metrics.fetch(:vad_padded_frames, 0)
352
+ vad_max_files_per_call = runtime_metrics.fetch(:vad_max_files_per_call, 0)
353
+ vad_effective_block_frames = runtime_metrics.fetch(:vad_effective_block_frames, 0)
354
+ vad_intraop_threads = runtime_metrics.fetch(:vad_intraop_threads, 0)
355
+ vad_padding_ratio = if vad_padded_frames.zero?
356
+ 0.0
357
+ else
358
+ 1.0 - vad_valid_frames.fdiv(vad_padded_frames)
359
+ end
360
+
361
+ {
362
+ "schema_version" => PROFILE_SCHEMA_VERSION,
363
+ "created_unix_seconds" => Time.now.to_f,
364
+ "implementation" => implementation_payload,
365
+ "models" => profile_models_payload(representative, resolved),
366
+ "environment" => environment_payload(resolved, runtime_metrics),
367
+ "configuration" => configuration_payload(requested, results, resolved: false),
368
+ "resolved_configuration" => configuration_payload(resolved, results, resolved: true),
369
+ "run" => {
370
+ "elapsed_seconds" => statistics.elapsed_seconds,
371
+ "successful_files" => successful.length,
372
+ "failed_files" => results.count { |result| result.status == "failed" },
373
+ "successful_audio_seconds" => statistics.successful_audio_seconds,
374
+ "real_time_factor_x" => statistics.real_time_factor_x
375
+ },
376
+ "timings" => timings_payload(statistics, runtime_metrics),
377
+ "vad" => {
378
+ "requested_engines" => requested_engines,
379
+ "actual_engines" => actual_engines,
380
+ "torch_device" => vad_prepared_groups.positive? ? "cpu" : nil,
381
+ # This reference-schema field reports the effective CPU
382
+ # intra-op count for Ruby's sequence-ONNX substitution.
383
+ "torch_intraop_threads" => vad_intraop_threads.positive? ? vad_intraop_threads : nil,
384
+ "configured_file_batch_size" => resolved.vad_batch_size,
385
+ "configured_block_frames" => resolved.vad_block_frames,
386
+ "effective_block_frames" => vad_effective_block_frames.positive? ? vad_effective_block_frames : nil,
387
+ "prepared_groups" => vad_prepared_groups,
388
+ "model_calls" => vad_model_calls,
389
+ "valid_frames" => vad_valid_frames,
390
+ "padded_frames" => vad_padded_frames,
391
+ "padding_ratio" => vad_padding_ratio,
392
+ "max_files_per_call" => vad_max_files_per_call
393
+ },
394
+ "asr" => {
395
+ "batches" => statistics.asr_batches,
396
+ "processor_rows" => statistics.asr_processor_rows,
397
+ "generated_tokens" => statistics.generated_tokens,
398
+ "valid_feature_frames" => nil,
399
+ "padded_feature_frames" => nil,
400
+ "discarded_processor_rows" => 0,
401
+ "discarded_valid_feature_frames" => 0,
402
+ "discarded_padded_feature_frames" => 0,
403
+ "padding_ratio" => nil,
404
+ "effective_batch_min" => runtime_metrics[:effective_batch_min],
405
+ "effective_batch_max" => runtime_metrics[:effective_batch_max],
406
+ "final_batch_size" => runtime_metrics[:final_batch_size],
407
+ "final_batch_cap" => runtime_metrics[:final_batch_cap],
408
+ "oom_retries" => statistics.oom_retries,
409
+ "truncation_retries" => statistics.truncation_retries,
410
+ "discarded_feature_batches" => 0,
411
+ "pin_memory_fallbacks" => 0,
412
+ "all_segment_duration_seconds" => duration_quantiles(all_durations),
413
+ "inferred_segment_duration_seconds" => duration_quantiles(inferred_durations),
414
+ "batch_history" => profile_batch_history(runtime_metrics[:batch_history]),
415
+ "checkpoint_resumed_files" => results.count do |result|
416
+ result.provenance.resumed_from_asr_checkpoint
417
+ end,
418
+ "checkpoint_written_files" => runtime_metrics.fetch(:checkpoint_written_files, 0)
419
+ },
420
+ "cuda_memory" => {
421
+ "total_gib" => runtime_metrics[:cuda_total_gib],
422
+ "free_start_gib" => runtime_metrics[:cuda_free_start_gib],
423
+ "free_end_gib" => runtime_metrics[:cuda_free_end_gib],
424
+ "peak_allocated_gib" => nil,
425
+ "peak_reserved_gib" => nil
426
+ },
427
+ "files" => results.map do |result|
428
+ profile_file_payload(
429
+ result,
430
+ resolved,
431
+ segmentation: file_segmentation[result.path.to_s]
432
+ )
433
+ end
434
+ }
435
+ end
436
+
437
+ def render(format, result, options, speech_spans: nil)
438
+ case format
439
+ when "txt"
440
+ lines = result.segments.empty? ? [result.text.to_s] : result.segments.map(&:text)
441
+ Rendering.plain_text(lines)
442
+ when "srt" then Rendering.srt(result.cues)
443
+ when "vtt" then Rendering.vtt(result.cues)
444
+ when "json" then Rendering.json(result_payload(result, options, speech_spans: speech_spans))
445
+ else
446
+ raise TranscriptionRuntimeError, "Unsupported publication format: #{format.inspect}"
447
+ end
448
+ end
449
+
450
+ def result_payload(result, options, speech_spans: nil)
451
+ provenance = result.provenance
452
+ {
453
+ "schema_version" => OUTPUT_SCHEMA_VERSION,
454
+ "implementation" => implementation_payload,
455
+ "source" => {
456
+ "path" => result.path.to_s,
457
+ "duration_seconds" => result.duration,
458
+ "sample_rate" => SAMPLE_RATE,
459
+ "decode_backend" => provenance.decode_backend,
460
+ "decode_fallback_reason" => provenance.decode_fallback_reason
461
+ },
462
+ "language" => options.language,
463
+ "segmentation" => options.vad,
464
+ "segmentation_details" => {
465
+ "mode" => options.vad,
466
+ "requested_engine" => provenance.vad_engine_requested,
467
+ "actual_engine" => provenance.vad_engine_actual,
468
+ "provider" => provenance.vad_provider,
469
+ "provider_options" => vad_provider_options(provenance, options),
470
+ "fallback_reason" => provenance.vad_fallback_reason,
471
+ "merge" => options.vad_merge,
472
+ "parameters" => segmentation_parameters(options),
473
+ "speech_spans" => speech_spans_payload(result, speech_spans)
474
+ },
475
+ "timing" => options.alignment,
476
+ "models" => {
477
+ "asr" => {
478
+ "id" => provenance.model_id,
479
+ "revision" => provenance.model_revision,
480
+ "format" => provenance.model_format,
481
+ "quantization" => nil,
482
+ "adapter" => provenance.adapter_id && {
483
+ "id" => provenance.adapter_id,
484
+ "revision" => provenance.adapter_revision
485
+ }
486
+ },
487
+ "vad" => output_vad_model_payload(options, provenance),
488
+ "aligner" => aligner_payload(options)
489
+ },
490
+ "fallback_alignment_segments" => provenance.fallback_alignment_segments,
491
+ "repetition_detector_version" => REPETITION_DETECTOR_VERSION,
492
+ "repetition_stopped_segments" => provenance.repetition_stopped_segments,
493
+ "truncation_retried_segments" => provenance.truncation_retried_segments,
494
+ "token_limit_segments" => provenance.token_limit_segments,
495
+ "generated_tokens_by_segment" => provenance.generated_tokens_by_segment.map do |index, tokens|
496
+ { "segment_index" => index, "tokens" => tokens }
497
+ end,
498
+ "transcript" => transcript_lines(result),
499
+ "segments" => result.segments.filter_map do |segment|
500
+ text = PythonText.strip(segment.text.to_s)
501
+ next if text.empty?
502
+
503
+ {
504
+ "segment_index" => segment.index,
505
+ "start" => segment.start,
506
+ "end" => segment.end,
507
+ "text" => text
508
+ }
509
+ end,
510
+ "words" => result.words.map do |word|
511
+ {
512
+ "start" => word.start,
513
+ "end" => word.end,
514
+ "text" => word.text,
515
+ "segment_index" => word.segment_index,
516
+ "segment_word_index" => word.segment_word_index,
517
+ "timing_source" => word.timing_source
518
+ }
519
+ end,
520
+ "cues" => result.cues.map do |cue|
521
+ { "start" => cue.start, "end" => cue.end, "text" => cue.text }
522
+ end
523
+ }
524
+ end
525
+
526
+ def implementation_payload
527
+ @implementation_payload ||= begin
528
+ behavior_root = Pathname(__dir__).parent
529
+ package_root = behavior_root.parent.parent
530
+ suffixes = %w[.rb .onnx .so .bundle .dylib .dll]
531
+ artifacts = Dir.glob(behavior_root.join("**", "*").to_s).filter_map do |name|
532
+ path = Pathname(name)
533
+ next unless path.file? && suffixes.include?(path.extname)
534
+
535
+ [path.relative_path_from(package_root).to_s.tr(File::SEPARATOR, "/"), Digest::SHA256.file(path).hexdigest]
536
+ end.to_h
537
+ artifacts.each do |key, value|
538
+ key.freeze
539
+ value.freeze
540
+ end
541
+ artifacts.freeze
542
+ {
543
+ "package_version" => VERSION,
544
+ "artifacts_sha256" => artifacts
545
+ }.freeze
546
+ end
547
+ end
548
+
549
+ def configuration_payload(options, results, resolved:)
550
+ publication = options.publication
551
+ formats = publication&.formats
552
+ formats ||= options.text_only || options.alignment == "none" ? ["txt"] : %w[txt srt vtt]
553
+ payload = options.class.members.each_with_object({}) do |member, values|
554
+ next if member == :publication
555
+
556
+ values[member.to_s] = json_value(options.public_send(member))
557
+ end
558
+ representative = results.find { |result| result.provenance.model_id }
559
+ payload.merge(
560
+ "audio" => results.map { |result| result.path.to_s },
561
+ "formats" => formats,
562
+ "output_dir" => publication&.output_dir&.to_s,
563
+ "existing" => publication&.existing || "error",
564
+ "profile_json" => publication&.profile_json&.to_s,
565
+ "model_format" => resolved ? representative&.provenance&.model_format : nil,
566
+ "model_quantization" => nil
567
+ )
568
+ end
569
+ private_class_method :configuration_payload
570
+
571
+ def timings_payload(statistics, runtime_metrics)
572
+ {
573
+ "runtime_import_seconds" => statistics.runtime_import_seconds,
574
+ "serialization_wait_seconds" => statistics.serialization_wait_seconds,
575
+ "input_validation_seconds" => statistics.input_validation_seconds,
576
+ "decode_worker_seconds" => statistics.decode_seconds,
577
+ "vad_worker_seconds" => statistics.vad_seconds,
578
+ "vad_model_load_seconds" => runtime_metrics[:vad_model_load_seconds],
579
+ "vad_inference_seconds" => runtime_metrics[:vad_inference_seconds],
580
+ "vad_postprocess_seconds" => runtime_metrics[:vad_postprocess_seconds],
581
+ "preparation_wait_seconds" => runtime_metrics[:preparation_wait_seconds],
582
+ "asr_load_seconds" => statistics.asr_load_seconds,
583
+ "asr_wall_seconds" => statistics.asr_seconds,
584
+ "asr_feature_worker_seconds" => runtime_metrics[:asr_feature_worker_seconds],
585
+ "asr_discarded_feature_seconds" => 0.0,
586
+ "asr_feature_wait_seconds" => 0.0,
587
+ "asr_h2d_seconds" => runtime_metrics[:asr_h2d_seconds],
588
+ "asr_generation_call_wall_seconds" => runtime_metrics[:asr_generation_call_seconds],
589
+ "asr_generate_device_seconds" => runtime_metrics[:asr_generate_device_seconds],
590
+ "asr_generation_analysis_seconds" => runtime_metrics[:asr_generation_analysis_seconds],
591
+ "asr_decode_seconds" => nil,
592
+ "aligner_load_seconds" => statistics.aligner_load_seconds,
593
+ "emissions_seconds" => statistics.emissions_seconds,
594
+ "viterbi_seconds" => statistics.viterbi_seconds,
595
+ "post_asr_seconds" => runtime_metrics[:post_asr_seconds],
596
+ "checkpoint_seconds" => runtime_metrics[:checkpoint_seconds],
597
+ "progressive_output_seconds" => runtime_metrics[:progressive_output_seconds]
598
+ }
599
+ end
600
+ private_class_method :timings_payload
601
+
602
+ def environment_payload(options, runtime_metrics)
603
+ specs = Gem.loaded_specs
604
+ packages = {
605
+ "cohere-transcribe" => VERSION,
606
+ "numo-narray" => specs["numo-narray"]&.version&.to_s,
607
+ "onnxruntime" => specs["onnxruntime"]&.version&.to_s
608
+ }.compact
609
+ environment = {
610
+ "python" => nil,
611
+ "executable" => RbConfig.ruby,
612
+ "platform" => RUBY_PLATFORM,
613
+ "machine" => RbConfig::CONFIG["host_cpu"],
614
+ "processor" => RbConfig::CONFIG["host_cpu"],
615
+ "cpu_count" => Etc.nprocessors,
616
+ "device" => options.device,
617
+ "dtype" => options.dtype,
618
+ "packages" => packages,
619
+ "torch_cuda_build" => nil,
620
+ "pytorch_alloc_conf" => nil,
621
+ "pytorch_cuda_alloc_conf" => nil,
622
+ "pytorch_effective_alloc_conf" => nil,
623
+ "torch_intraop_threads" => nil,
624
+ "torch_interop_threads" => nil
625
+ }
626
+ if options.device == "cuda"
627
+ environment["cuda"] = {
628
+ "device_index" => nil,
629
+ "name" => nil,
630
+ "compute_capability" => nil,
631
+ "total_memory_gib" => runtime_metrics[:cuda_total_gib],
632
+ "free_memory_at_profile_gib" => runtime_metrics[:cuda_free_end_gib] ||
633
+ runtime_metrics[:cuda_free_start_gib],
634
+ "driver_visible_device_count" => nil,
635
+ "cudnn_version" => nil,
636
+ "allocator_backend" => nil
637
+ }
638
+ end
639
+ environment
640
+ end
641
+ private_class_method :environment_payload
642
+
643
+ def profile_batch_history(entries)
644
+ return entries unless entries
645
+
646
+ entries.map do |entry|
647
+ next entry unless entry.is_a?(Hash) && !entry.key?("event")
648
+
649
+ SUCCESSFUL_BATCH_PROFILE_FIELDS.to_h { |field| [field, entry[field]] }.merge(entry)
650
+ end
651
+ end
652
+ private_class_method :profile_batch_history
653
+
654
+ def profile_models_payload(result, options)
655
+ provenance = result&.provenance || TranscriptionProvenance.new
656
+ {
657
+ "asr" => {
658
+ "id" => provenance.model_id || json_value(options.model),
659
+ "revision" => provenance.model_revision || options.model_revision,
660
+ "format" => provenance.model_format,
661
+ "quantization" => nil,
662
+ "adapter" => provenance.adapter_id && {
663
+ "id" => provenance.adapter_id,
664
+ "revision" => provenance.adapter_revision
665
+ }
666
+ },
667
+ "vad" => profile_vad_model_payload(options),
668
+ "aligner" => aligner_payload(options)
669
+ }
670
+ end
671
+ private_class_method :profile_models_payload
672
+
673
+ def output_vad_model_payload(options, provenance)
674
+ return nil unless options.vad == "silero"
675
+
676
+ {
677
+ "source" => "silero-vad",
678
+ "source_version" => SILERO_VERSION,
679
+ "distribution" => "cohere-transcribe",
680
+ "version" => VERSION,
681
+ "weight_asset" => "cohere/transcribe/vad/silero_vad_v6.onnx",
682
+ "implementation" => provenance.vad_engine_actual&.start_with?("onnx") ? "sequence-onnx" : nil
683
+ }
684
+ end
685
+ private_class_method :output_vad_model_payload
686
+
687
+ def profile_vad_model_payload(options)
688
+ return nil unless options.vad == "silero"
689
+
690
+ {
691
+ "source" => "silero-vad",
692
+ "source_version" => SILERO_VERSION,
693
+ "distribution" => "cohere-transcribe",
694
+ "version" => VERSION,
695
+ "torch_weight_asset" => nil,
696
+ "onnx_weight_asset" => "cohere/transcribe/vad/silero_vad_v6.onnx",
697
+ "packed_torch_implementation" => nil
698
+ }
699
+ end
700
+ private_class_method :profile_vad_model_payload
701
+
702
+ def aligner_payload(options)
703
+ return nil unless options.alignment == "word"
704
+
705
+ {
706
+ "id" => Alignment::ModelProvider::SOURCE_REPOSITORY,
707
+ "revision" => Alignment::ModelProvider::SOURCE_REVISION,
708
+ "kernel" => {
709
+ "distribution" => "cohere-transcribe",
710
+ "operation" => "Cohere::Transcribe::Alignment::CTC.forced_align",
711
+ "version" => VERSION
712
+ },
713
+ "utility_package" => {
714
+ "distribution" => "cohere-transcribe",
715
+ "location" => "cohere/transcribe/alignment",
716
+ "repository" => Alignment::ModelProvider::UTILITY_REPOSITORY,
717
+ "revision" => Alignment::ModelProvider::UTILITY_REVISION
718
+ },
719
+ "romanizer" => {
720
+ "distribution" => "cohere-transcribe",
721
+ "version" => Alignment::ModelProvider::UROMAN_COMPATIBILITY_VERSION
722
+ }
723
+ }
724
+ end
725
+ private_class_method :aligner_payload
726
+
727
+ def vad_provider_options(provenance, _options)
728
+ providers = provenance.vad_provider.to_s.split(",").reject(&:empty?)
729
+ return nil if providers.empty?
730
+
731
+ providers.to_h { |provider| [provider, {}] }
732
+ end
733
+ private_class_method :vad_provider_options
734
+
735
+ def segmentation_parameters(options)
736
+ parameters = { "max_duration_seconds" => options.max_dur }
737
+ case options.vad
738
+ when "silero"
739
+ parameters.merge!(
740
+ "min_duration_seconds" => options.min_dur,
741
+ "threshold" => options.vad_threshold,
742
+ "min_silence_ms" => options.min_silence_ms,
743
+ "speech_pad_ms" => options.speech_pad_ms
744
+ )
745
+ when "auditok"
746
+ parameters.merge!(
747
+ "min_duration_seconds" => options.min_dur,
748
+ "max_silence_seconds" => options.max_silence,
749
+ "energy_threshold" => options.energy_threshold
750
+ )
751
+ end
752
+ parameters
753
+ end
754
+ private_class_method :segmentation_parameters
755
+
756
+ def speech_spans_payload(result, speech_spans)
757
+ spans = speech_spans || result.segments.map { |segment| [segment.start, segment.end] }
758
+ spans.map { |start_time, end_time| { "start" => start_time, "end" => end_time } }
759
+ end
760
+ private_class_method :speech_spans_payload
761
+
762
+ def transcript_lines(result)
763
+ lines = result.segments.map(&:text)
764
+ lines = result.text.to_s.lines if lines.empty?
765
+ lines.filter_map do |line|
766
+ stripped = PythonText.strip(line.to_s)
767
+ stripped unless stripped.empty?
768
+ end
769
+ end
770
+ private_class_method :transcript_lines
771
+
772
+ def profile_file_payload(result, options, segmentation: nil)
773
+ segment_times = segmentation&.fetch(:segment_times, nil)
774
+ speech_spans = segmentation&.fetch(:speech_spans, nil)
775
+ durations = segment_times ? span_durations(segment_times) : segment_durations(result)
776
+ raw_available = !speech_spans.nil? || !options.vad_merge
777
+ raw_durations = speech_spans ? span_durations(speech_spans) : durations
778
+ {
779
+ "path" => result.path.to_s,
780
+ "relative_path" => result.relative_path.to_s,
781
+ "duration_seconds" => result.duration,
782
+ "segment_count" => segment_times ? segment_times.length : result.segments.length,
783
+ "raw_speech_span_count" => raw_available ? raw_durations.length : nil,
784
+ "raw_speech_seconds" => raw_available ? raw_durations.sum : nil,
785
+ "selected_audio_seconds" => durations.sum,
786
+ "decode_backend" => result.provenance.decode_backend,
787
+ "decode_fallback_reason" => result.provenance.decode_fallback_reason,
788
+ "vad_engine" => result.provenance.vad_engine_actual,
789
+ "vad_provider" => result.provenance.vad_provider,
790
+ "vad_provider_options" => vad_provider_options(result.provenance, options),
791
+ "vad_fallback_reason" => result.provenance.vad_fallback_reason,
792
+ "generated_tokens" => result.provenance.generated_tokens_by_segment.sum { |_index, count| count },
793
+ "repetition_stopped_segments" => result.provenance.repetition_stopped_segments,
794
+ "truncation_retried_segments" => result.provenance.truncation_retried_segments,
795
+ "token_limit_segments" => result.provenance.token_limit_segments,
796
+ "fallback_alignment_segments" => result.provenance.fallback_alignment_segments,
797
+ "outputs" => result.outputs.map(&:to_s),
798
+ "resumed_from_asr_checkpoint" => result.provenance.resumed_from_asr_checkpoint,
799
+ "published" => result.provenance.published,
800
+ "error" => result.error
801
+ }
802
+ end
803
+ private_class_method :profile_file_payload
804
+
805
+ def segment_durations(result)
806
+ result.segments.map { |segment| [segment.end - segment.start, 0.0].max }
807
+ end
808
+ private_class_method :segment_durations
809
+
810
+ def profile_segment_durations(result, segmentation)
811
+ return segment_durations(result) unless segmentation
812
+
813
+ span_durations(segmentation.fetch(:segment_times))
814
+ end
815
+ private_class_method :profile_segment_durations
816
+
817
+ def span_durations(spans)
818
+ spans.map { |start_time, end_time| [end_time - start_time, 0.0].max }
819
+ end
820
+ private_class_method :span_durations
821
+
822
+ def duration_quantiles(values)
823
+ sorted = values.map(&:to_f).sort
824
+ return nil if sorted.empty?
825
+
826
+ {
827
+ "min" => sorted.first,
828
+ "p50" => quantile(sorted, 0.50),
829
+ "p90" => quantile(sorted, 0.90),
830
+ "p99" => quantile(sorted, 0.99),
831
+ "max" => sorted.last
832
+ }
833
+ end
834
+ private_class_method :duration_quantiles
835
+
836
+ def quantile(sorted, fraction)
837
+ position = (sorted.length - 1) * fraction
838
+ lower = position.floor
839
+ upper = position.ceil
840
+ return sorted.fetch(lower) if lower == upper
841
+
842
+ sorted.fetch(lower) + ((sorted.fetch(upper) - sorted.fetch(lower)) * (position - lower))
843
+ end
844
+ private_class_method :quantile
845
+
846
+ def atomic_write_set(paths, contents, before_commit: nil, rename: nil,
847
+ directory_bindings: nil, operation_hook: nil)
848
+ staged = {}
849
+ backups = {}
850
+ committed = []
851
+ preserved_backups = {}
852
+ open_handles = []
853
+ failed = false
854
+ bound_directories = publication_bound_directories(paths, directory_bindings)
855
+ operation_hook&.call(:directories_opened, nil)
856
+ paths.each do |format, supplied_destination|
857
+ destination = Pathname(supplied_destination).expand_path
858
+ bound = bound_directories.fetch(destination.dirname)
859
+ bound.verify!
860
+ output_mode = bound_output_mode(bound, destination.basename.to_s)
861
+ temporary_name = nil
862
+ temporary = nil
863
+ Thread.handle_interrupt(State::DEFERRED_PUBLICATION_EXCEPTIONS) do
864
+ temporary_name, temporary = bound.create_temporary(destination.basename.to_s, ".tmp")
865
+ staged[destination] = [bound, temporary_name].freeze
866
+ open_handles << temporary
867
+ end
868
+ begin
869
+ temporary.chmod(output_mode)
870
+ operation_hook&.call(:before_stage_write, destination)
871
+ temporary.write(contents.fetch(format))
872
+ temporary.flush
873
+ temporary.fsync
874
+ ensure
875
+ temporary.close unless temporary.closed?
876
+ end
877
+ bound.verify!
878
+ end
879
+
880
+ staged.each do |destination, (bound, _temporary_name)|
881
+ operation_hook&.call(:before_backup_open, destination)
882
+ source = nil
883
+ Thread.handle_interrupt(State::DEFERRED_PUBLICATION_EXCEPTIONS) do
884
+ source = begin
885
+ bound.open_regular(destination.basename.to_s)
886
+ rescue Errno::ENOENT
887
+ nil
888
+ end
889
+ open_handles << source if source
890
+ end
891
+ unless source
892
+ backups[destination] = nil
893
+ next
894
+ end
895
+
896
+ backup_name = nil
897
+ backup = nil
898
+ Thread.handle_interrupt(State::DEFERRED_PUBLICATION_EXCEPTIONS) do
899
+ backup_name, backup = bound.create_temporary(destination.basename.to_s, ".bak")
900
+ backups[destination] = [bound, backup_name].freeze
901
+ open_handles << backup
902
+ end
903
+ begin
904
+ backup.chmod(source.stat.mode & 0o7777)
905
+ operation_hook&.call(:before_backup_copy, destination)
906
+ IO.copy_stream(source, backup)
907
+ backup.flush
908
+ backup.fsync
909
+ ensure
910
+ source.close
911
+ backup.close
912
+ end
913
+ bound.verify!
914
+ end
915
+ before_commit&.call
916
+ bound_directories.each_value(&:verify!)
917
+ staged.each do |destination, (bound, temporary_name)|
918
+ operation_hook&.call(:before_rename, destination)
919
+ if rename
920
+ Thread.handle_interrupt(State::DEFERRED_PUBLICATION_EXCEPTIONS) do
921
+ committed << destination
922
+ rename.call(bound.display_path(temporary_name), destination)
923
+ bound.rename(temporary_name, destination.basename.to_s) if bound.regular_entry?(temporary_name)
924
+ end
925
+ else
926
+ Thread.handle_interrupt(State::DEFERRED_PUBLICATION_EXCEPTIONS) do
927
+ bound.rename(temporary_name, destination.basename.to_s)
928
+ committed << destination
929
+ end
930
+ end
931
+ operation_hook&.call(:after_rename, destination)
932
+ bound.verify!
933
+ end
934
+ bound_directories.each_value(&:fsync)
935
+ bound_directories.each_value(&:verify!)
936
+ rescue Exception => e # rubocop:disable Lint/RescueException -- rollback must include interrupts
937
+ failed = true
938
+ rollback_errors = []
939
+ committed.reverse_each do |destination|
940
+ backup = backups[destination]
941
+ bound, backup_name = backup
942
+ bound ||= staged.fetch(destination).first
943
+ begin
944
+ Thread.handle_interrupt(State::DEFERRED_PUBLICATION_EXCEPTIONS) do
945
+ bound.unlink(destination.basename.to_s, missing_ok: true)
946
+ bound.rename(backup_name, destination.basename.to_s) if backup_name
947
+ end
948
+ rescue SystemCallError, TranscriptionRuntimeError => rollback_error
949
+ preserved_backups[backup] = true if backup_name
950
+ rollback_errors << "#{destination}: #{rollback_error.message}"
951
+ end
952
+ end
953
+ begin
954
+ bound_directories&.each_value(&:fsync)
955
+ rescue SystemCallError, TranscriptionRuntimeError => rollback_error
956
+ rollback_errors << "directory sync: #{rollback_error.message}"
957
+ end
958
+ if rollback_errors.any?
959
+ detail = rollback_errors.join("; ")
960
+ retained = preserved_backups.keys.filter_map do |backup|
961
+ backup&.then { |bound, name| bound.display_path(name).to_s }
962
+ end.sort
963
+ raise TranscriptionRuntimeError,
964
+ "Output commit failed and rollback was incomplete (#{detail}); " \
965
+ "preserved backups: #{retained}",
966
+ cause: e
967
+ end
968
+ raise e
969
+ ensure
970
+ cleanup_errors = []
971
+ staged&.each_value do |bound, name|
972
+ bound.unlink(name, missing_ok: true)
973
+ rescue SystemCallError, TranscriptionRuntimeError => e
974
+ cleanup_errors << e
975
+ end
976
+ backups&.each_value do |backup|
977
+ next unless backup && !preserved_backups&.key?(backup)
978
+
979
+ bound, name = backup
980
+ bound.unlink(name, missing_ok: true)
981
+ rescue SystemCallError, TranscriptionRuntimeError => e
982
+ cleanup_errors << e
983
+ end
984
+ open_handles&.each { |handle| handle.close unless handle.closed? }
985
+ bound_directories&.each_value(&:close)
986
+ raise cleanup_errors.first if cleanup_errors.any? && !failed
987
+ end
988
+
989
+ def publication_bound_directories(paths, directory_bindings)
990
+ guards = Array(directory_bindings).compact.uniq.freeze
991
+ bound_directories = {}
992
+ paths.each_value.map { |path| Pathname(path).expand_path.dirname }.uniq.each do |parent|
993
+ binding = guards.rfind do |candidate|
994
+ candidate.access_path == parent || candidate.canonical_path == parent
995
+ end
996
+ binding ||= State.ensure_bound_directory(parent)
997
+ active_guards = (guards + [binding]).uniq
998
+ Thread.handle_interrupt(State::DEFERRED_PUBLICATION_EXCEPTIONS) do
999
+ bound_directories[parent] = State::BoundDirectory.open(binding, guards: active_guards)
1000
+ end
1001
+ end
1002
+ bound_directories.freeze
1003
+ rescue Exception # rubocop:disable Lint/RescueException -- close partially opened directory handles
1004
+ bound_directories&.each_value(&:close)
1005
+ raise
1006
+ end
1007
+ private_class_method :publication_bound_directories
1008
+
1009
+ def bound_output_mode(bound, basename)
1010
+ existing = nil
1011
+ Thread.handle_interrupt(State::DEFERRED_PUBLICATION_EXCEPTIONS) do
1012
+ existing = bound.open_regular(basename)
1013
+ end
1014
+ existing.stat.mode & 0o7777
1015
+ rescue Errno::ENOENT
1016
+ current_umask = File.umask
1017
+ File.umask(current_umask)
1018
+ 0o666 & ~current_umask
1019
+ ensure
1020
+ existing&.close
1021
+ end
1022
+ private_class_method :bound_output_mode
1023
+
1024
+ def output_mode(destination)
1025
+ return destination.stat.mode & 0o7777 if destination.exist?
1026
+
1027
+ current_umask = File.umask
1028
+ File.umask(current_umask)
1029
+ 0o666 & ~current_umask
1030
+ end
1031
+ private_class_method :output_mode
1032
+
1033
+ def fsync_directories(directories)
1034
+ directories.each do |directory|
1035
+ File.open(directory, File::RDONLY, &:fsync)
1036
+ rescue Errno::EACCES, Errno::EBADF, Errno::EINVAL, Errno::EISDIR,
1037
+ Errno::ENOTSUP, Errno::EPERM
1038
+ next
1039
+ end
1040
+ end
1041
+ private_class_method :fsync_directories
1042
+
1043
+ def verified_publication?(source_snapshot, paths, state_path, options)
1044
+ State.verify_published_outputs(
1045
+ source_snapshot: source_snapshot,
1046
+ output_paths: paths,
1047
+ state_path: state_path,
1048
+ asr_contract_key: State.asr_contract_key(options),
1049
+ render_contract_key: State.render_contract_key(options)
1050
+ ).verified?
1051
+ end
1052
+ private_class_method :verified_publication?
1053
+
1054
+ def source_record(path)
1055
+ State::SourceSnapshot.capture(path)
1056
+ end
1057
+ private_class_method :source_record
1058
+
1059
+ def json_value(value)
1060
+ case value
1061
+ when Pathname then value.to_s
1062
+ when Array then value.map { |item| json_value(item) }
1063
+ when Hash then value.to_h { |key, item| [key.to_s, json_value(item)] }
1064
+ else value
1065
+ end
1066
+ end
1067
+ private_class_method :json_value
1068
+
1069
+ def validate_reserved_path!(path, label)
1070
+ return unless path.symlink? || (path.exist? && !path.file?)
1071
+
1072
+ raise TranscriptionInputError, "#{label} is not a regular file: #{path}"
1073
+ end
1074
+ private_class_method :validate_reserved_path!
1075
+
1076
+ def validate_runtime_output_path!(path)
1077
+ return unless path.symlink? || (path.exist? && !path.file?)
1078
+
1079
+ raise TranscriptionInputError, "Output path is not a regular file: #{path}"
1080
+ end
1081
+ private_class_method :validate_runtime_output_path!
1082
+
1083
+ def validate_output_path!(path, input_paths, source)
1084
+ raise TranscriptionInputError, "Output path is not a regular file: #{path}" if path.symlink? || (path.exist? && !path.file?)
1085
+ return unless input_paths.key?(path.expand_path.to_s)
1086
+
1087
+ raise TranscriptionInputError, "Output path for #{source} collides with an input: #{path}"
1088
+ end
1089
+ private_class_method :validate_output_path!
1090
+
1091
+ def prospective_realpath(path)
1092
+ path = Pathname(path).expand_path
1093
+ missing = []
1094
+ cursor = path
1095
+ until cursor.exist? || cursor.symlink?
1096
+ parent = cursor.dirname
1097
+ raise ArgumentError, "Cannot resolve output directory #{path}" if parent == cursor
1098
+
1099
+ missing.unshift(cursor.basename)
1100
+ cursor = parent
1101
+ end
1102
+ missing.reduce(cursor.realpath) { |resolved, component| resolved.join(component) }
1103
+ end
1104
+ private_class_method :prospective_realpath
1105
+
1106
+ def ensure_within_output_root!(path, root, supplied)
1107
+ relative = Pathname(path).relative_path_from(Pathname(root))
1108
+ return unless relative.each_filename.first == ".."
1109
+
1110
+ raise TranscriptionInputError, "Output directory escapes output root: #{supplied} -> #{path}"
1111
+ rescue ArgumentError
1112
+ raise TranscriptionInputError, "Output directory escapes output root: #{supplied} -> #{path}"
1113
+ end
1114
+ private_class_method :ensure_within_output_root!
1115
+ end
1116
+ end
1117
+ end
1118
+ end