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,1180 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+ require "stringio"
5
+ require "zlib"
6
+
7
+ require_relative "safetensors"
8
+
9
+ module Cohere
10
+ module Transcribe
11
+ # A non-executing reader for dense PyTorch state dictionaries. It accepts
12
+ # the ZIP serialization used by current torch.save and the immediately
13
+ # preceding pickle-plus-storage stream. Pickle GLOBAL/REDUCE operations are
14
+ # interpreted through a small allowlist; no Python classes or code run.
15
+ module PyTorchCheckpoint
16
+ class Error < StandardError; end
17
+
18
+ MAGIC_NUMBER = 119_547_037_146_038_801_333_356
19
+ PROTOCOL_VERSION = 1001
20
+ PICKLE_LIMIT = 64 * 1024 * 1024
21
+ PICKLE_OPCODE_LIMIT = 1_000_000
22
+ PICKLE_LONG_BYTES_LIMIT = 16
23
+ ZIP_TAIL_LIMIT = 65_557
24
+ ZIP_ENTRY_LIMIT = 1_000_000
25
+ ZIP_CENTRAL_LIMIT = 256 * 1024 * 1024
26
+ ZIP_COMPRESSED_OVERHEAD = 1024 * 1024
27
+ ZIP_INFLATE_CHUNK = 16 * 1024
28
+ ZIP_EOCD = 0x0605_4b50
29
+ ZIP64_EOCD = 0x0606_4b50
30
+ ZIP64_LOCATOR = 0x0706_4b50
31
+ ZIP_CENTRAL = 0x0201_4b50
32
+ ZIP_LOCAL = 0x0403_4b50
33
+
34
+ STORAGE_DTYPES = {
35
+ "FloatStorage" => ["F32", 4],
36
+ "HalfStorage" => ["F16", 2],
37
+ "BFloat16Storage" => ["BF16", 2],
38
+ "LongStorage" => ["I64", 8]
39
+ }.freeze
40
+ FLOAT_DTYPES = %w[BF16 F16 F32].freeze
41
+
42
+ Global = Data.define(:module_name, :name)
43
+ StorageRef = Data.define(:dtype, :key, :elements, :base_offset)
44
+ TensorSpec = Data.define(:storage, :storage_offset, :shape, :stride)
45
+ Entry = Data.define(
46
+ :name, :compression_method, :flags, :crc32, :compressed_size, :size,
47
+ :local_header_offset, :data_offset
48
+ )
49
+ Tensor = Data.define(
50
+ :reader, :name, :dtype, :shape, :storage_key, :data_start, :nbytes,
51
+ :storage_data_start, :storage_elements, :storage_offset, :stride
52
+ ) do
53
+ def element_count
54
+ shape.empty? ? 1 : shape.reduce(1, :*)
55
+ end
56
+
57
+ def floating_point?
58
+ FLOAT_DTYPES.include?(dtype)
59
+ end
60
+ end
61
+
62
+ # Minimal ZIP/ZIP64 central-directory reader. Torch stores tensor records
63
+ # without compression, allowing bounded direct reads from multi-GB files.
64
+ class ZipArchive
65
+ attr_reader :path, :entries
66
+
67
+ def initialize(path)
68
+ @path = Pathname(path).expand_path
69
+ @entries = read_entries.freeze
70
+ end
71
+
72
+ def fetch(name)
73
+ entries.fetch(name)
74
+ rescue KeyError
75
+ raise Error, "PyTorch archive #{path} has no entry #{name.inspect}"
76
+ end
77
+
78
+ def read(name, limit: PICKLE_LIMIT)
79
+ entry = fetch(name)
80
+ raise Error, "PyTorch archive entry #{name.inspect} exceeds the #{limit}-byte size limit" if entry.size > limit
81
+ if entry.compressed_size > limit + ZIP_COMPRESSED_OVERHEAD
82
+ raise Error,
83
+ "PyTorch archive entry #{name.inspect} has an oversized compressed representation"
84
+ end
85
+ if entry.compression_method.zero? && entry.compressed_size != entry.size
86
+ raise Error, "Stored PyTorch archive entry #{name.inspect} has inconsistent sizes"
87
+ end
88
+
89
+ compressed = read_range(entry.data_offset, entry.compressed_size)
90
+ payload = case entry.compression_method
91
+ when 0
92
+ compressed
93
+ when 8
94
+ inflate_bounded(compressed, entry, limit)
95
+ else
96
+ raise Error,
97
+ "PyTorch archive entry #{name.inspect} uses unsupported ZIP method " \
98
+ "#{entry.compression_method}"
99
+ end
100
+ if payload.bytesize != entry.size
101
+ raise Error,
102
+ "PyTorch archive entry #{name.inspect} decoded to #{payload.bytesize} bytes; expected #{entry.size}"
103
+ end
104
+ raise Error, "PyTorch archive entry #{name.inspect} failed its CRC check" unless Zlib.crc32(payload) == entry.crc32
105
+
106
+ payload
107
+ rescue Zlib::Error => e
108
+ raise Error, "Cannot inflate PyTorch archive entry #{name.inspect}: #{e.message}"
109
+ end
110
+
111
+ private
112
+
113
+ def inflate_bounded(compressed, entry, limit)
114
+ inflater = Zlib::Inflate.new(-Zlib::MAX_WBITS)
115
+ payload = +"".b
116
+ offset = 0
117
+ chunk_size = [[entry.size / 1024, 1].max, ZIP_INFLATE_CHUNK].min
118
+ begin
119
+ while offset < compressed.bytesize
120
+ chunk = compressed.byteslice(offset, chunk_size)
121
+ payload << inflater.inflate(chunk)
122
+ if payload.bytesize > entry.size || payload.bytesize > limit
123
+ raise Error,
124
+ "PyTorch archive entry #{entry.name.inspect} expands beyond its declared size"
125
+ end
126
+ offset += chunk.bytesize
127
+ end
128
+ payload << inflater.finish
129
+ if payload.bytesize > entry.size || payload.bytesize > limit
130
+ raise Error,
131
+ "PyTorch archive entry #{entry.name.inspect} expands beyond its declared size"
132
+ end
133
+ payload
134
+ ensure
135
+ # Ruby 4 warns when an unfinished zstream is closed and performs
136
+ # this reset implicitly. Expansion-limit and corrupt-stream
137
+ # failures are expected rejection paths, so reset explicitly
138
+ # before closing without masking the original checkpoint error.
139
+ inflater.reset unless inflater.finished?
140
+ inflater.close
141
+ end
142
+ end
143
+
144
+ def read_entries
145
+ size = path.size
146
+ raise Error, "PyTorch archive #{path} is too short" if size < 22
147
+
148
+ tail_length = [size, ZIP_TAIL_LIMIT].min
149
+ tail_start = size - tail_length
150
+ tail = read_range(tail_start, tail_length)
151
+ signature = [ZIP_EOCD].pack("V")
152
+ relative = nil
153
+ cursor = tail.bytesize - 22
154
+ while cursor >= 0
155
+ candidate = tail.rindex(signature, cursor)
156
+ break unless candidate
157
+
158
+ comment_length = tail.byteslice(candidate + 20, 2)&.unpack1("v")
159
+ if comment_length && candidate + 22 + comment_length == tail.bytesize
160
+ relative = candidate
161
+ break
162
+ end
163
+ cursor = candidate - 1
164
+ end
165
+ raise Error, "PyTorch archive #{path} has no end-of-central-directory record" unless relative
166
+
167
+ eocd_offset = tail_start + relative
168
+ eocd = tail.byteslice(relative, 22)
169
+ raise Error, "PyTorch archive #{path} has a truncated end record" unless eocd&.bytesize == 22
170
+
171
+ fields = eocd.unpack("VvvvvVVv")
172
+ _signature, disk, central_disk, disk_entries, total_entries,
173
+ central_size, central_offset, comment_length = fields
174
+ raise Error, "PyTorch archive #{path} has a truncated ZIP comment" if relative + 22 + comment_length > tail.bytesize
175
+ unless disk.zero? && central_disk.zero? && disk_entries == total_entries
176
+ raise Error, "Multi-disk PyTorch ZIP archives are not supported"
177
+ end
178
+
179
+ if [disk_entries, total_entries].include?(0xffff) ||
180
+ [central_size, central_offset].include?(0xffff_ffff)
181
+ total_entries, central_size, central_offset = zip64_directory(eocd_offset)
182
+ end
183
+ if total_entries > ZIP_ENTRY_LIMIT || central_size > ZIP_CENTRAL_LIMIT ||
184
+ central_size > size || central_offset > size - central_size
185
+ raise Error, "PyTorch archive #{path} has invalid central-directory bounds"
186
+ end
187
+
188
+ parse_central_directory(
189
+ read_range(central_offset, central_size),
190
+ expected_entries: total_entries,
191
+ archive_size: size,
192
+ data_limit: central_offset
193
+ )
194
+ rescue Errno::ENOENT
195
+ raise Error, "PyTorch checkpoint #{path} does not exist"
196
+ rescue Errno::EACCES => e
197
+ raise Error, "Cannot read PyTorch checkpoint #{path}: #{e.message}"
198
+ end
199
+
200
+ def zip64_directory(eocd_offset)
201
+ raise Error, "PyTorch archive #{path} has no ZIP64 locator" if eocd_offset < 20
202
+
203
+ locator = read_range(eocd_offset - 20, 20)
204
+ signature, disk, record_offset, disks = locator.unpack("VVQ<V")
205
+ raise Error, "PyTorch archive #{path} has an invalid ZIP64 locator" unless signature == ZIP64_LOCATOR && disk.zero? && disks == 1
206
+
207
+ record = read_range(record_offset, 56)
208
+ signature, record_size, _made, _needed, disk, central_disk,
209
+ disk_entries, total_entries, central_size, central_offset = record.unpack("VQ<vvVVQ<Q<Q<Q<")
210
+ unless signature == ZIP64_EOCD && record_size >= 44 && disk.zero? && central_disk.zero? &&
211
+ disk_entries == total_entries && record_offset + 12 + record_size == eocd_offset - 20
212
+ raise Error, "PyTorch archive #{path} has an invalid ZIP64 end record"
213
+ end
214
+
215
+ [total_entries, central_size, central_offset]
216
+ end
217
+
218
+ def parse_central_directory(bytes, expected_entries:, archive_size:, data_limit:)
219
+ offset = 0
220
+ result = {}
221
+ expected_entries.times do
222
+ fixed = bytes.byteslice(offset, 46)
223
+ unless fixed&.bytesize == 46 && fixed.unpack1("V") == ZIP_CENTRAL
224
+ raise Error, "PyTorch archive #{path} has a malformed central directory"
225
+ end
226
+
227
+ values = fixed.unpack("VvvvvvvVVVvvvvvVV")
228
+ flags = values[3]
229
+ method = values[4]
230
+ crc32 = values[7]
231
+ compressed_size = values[8]
232
+ size = values[9]
233
+ name_length = values[10]
234
+ extra_length = values[11]
235
+ comment_length = values[12]
236
+ disk = values[13]
237
+ local_offset = values[16]
238
+ offset += 46
239
+
240
+ variable_length = name_length + extra_length + comment_length
241
+ variable = bytes.byteslice(offset, variable_length)
242
+ unless variable&.bytesize == variable_length
243
+ raise Error,
244
+ "PyTorch archive #{path} has a truncated central entry"
245
+ end
246
+
247
+ raw_name = variable.byteslice(0, name_length)
248
+ extra = variable.byteslice(name_length, extra_length)
249
+ offset += variable_length
250
+ size, compressed_size, local_offset, disk = apply_zip64_extra(
251
+ extra, size: size, compressed_size: compressed_size,
252
+ local_offset: local_offset, disk: disk
253
+ )
254
+ raise Error, "Multi-disk PyTorch ZIP archives are not supported" unless disk.zero?
255
+ raise Error, "Encrypted PyTorch ZIP entries are not supported" if flags.anybits?(0x1)
256
+
257
+ name = decode_name(raw_name, utf8: flags.anybits?(0x800))
258
+ validate_entry_name!(name)
259
+ raise Error, "PyTorch archive #{path} contains duplicate entry #{name.inspect}" if result.key?(name)
260
+
261
+ data_offset = local_data_offset(
262
+ local_offset,
263
+ name,
264
+ archive_size,
265
+ central_flags: flags,
266
+ central_method: method
267
+ )
268
+ if local_offset >= data_limit || compressed_size > archive_size ||
269
+ data_offset > archive_size - compressed_size || data_offset + compressed_size > data_limit
270
+ raise Error, "PyTorch archive entry #{name.inspect} points outside #{path}"
271
+ end
272
+
273
+ result[name] = Entry.new(
274
+ name: name.freeze,
275
+ compression_method: method,
276
+ flags: flags,
277
+ crc32: crc32,
278
+ compressed_size: compressed_size,
279
+ size: size,
280
+ local_header_offset: local_offset,
281
+ data_offset: data_offset
282
+ )
283
+ end
284
+ raise Error, "PyTorch archive #{path} central-directory size is inconsistent" unless offset == bytes.bytesize
285
+
286
+ result
287
+ end
288
+
289
+ def apply_zip64_extra(extra, size:, compressed_size:, local_offset:, disk:)
290
+ values = nil
291
+ cursor = 0
292
+ while cursor < extra.bytesize
293
+ raise Error, "PyTorch archive #{path} has a truncated ZIP extra field" if cursor + 4 > extra.bytesize
294
+
295
+ tag, length = extra.byteslice(cursor, 4).unpack("vv")
296
+ cursor += 4
297
+ payload = extra.byteslice(cursor, length)
298
+ raise Error, "PyTorch archive #{path} has a truncated ZIP extra payload" unless payload&.bytesize == length
299
+
300
+ values = payload if tag == 0x0001
301
+ cursor += length
302
+ end
303
+
304
+ required = [size, compressed_size, local_offset, disk].count do |value|
305
+ [0xffff_ffff, 0xffff].include?(value)
306
+ end
307
+ return [size, compressed_size, local_offset, disk] if required.zero?
308
+ raise Error, "PyTorch archive #{path} lacks required ZIP64 size data" unless values
309
+
310
+ cursor = 0
311
+ take_qword = lambda do
312
+ raise Error, "PyTorch archive #{path} has truncated ZIP64 data" if cursor + 8 > values.bytesize
313
+
314
+ value = values.byteslice(cursor, 8).unpack1("Q<")
315
+ cursor += 8
316
+ value
317
+ end
318
+ take_dword = lambda do
319
+ raise Error, "PyTorch archive #{path} has truncated ZIP64 data" if cursor + 4 > values.bytesize
320
+
321
+ value = values.byteslice(cursor, 4).unpack1("V")
322
+ cursor += 4
323
+ value
324
+ end
325
+ size = take_qword.call if size == 0xffff_ffff
326
+ compressed_size = take_qword.call if compressed_size == 0xffff_ffff
327
+ local_offset = take_qword.call if local_offset == 0xffff_ffff
328
+ disk = take_dword.call if disk == 0xffff
329
+ [size, compressed_size, local_offset, disk]
330
+ end
331
+
332
+ def local_data_offset(local_offset, central_name, archive_size, central_flags:, central_method:)
333
+ raise Error, "PyTorch archive entry #{central_name.inspect} has an invalid local header" if local_offset > archive_size - 30
334
+
335
+ fixed = read_range(local_offset, 30)
336
+ signature, _needed, flags, method, _time, _date, _crc, _compressed, _size,
337
+ name_length, extra_length = fixed.unpack("VvvvvvVVVvv")
338
+ raise Error, "PyTorch archive entry #{central_name.inspect} has no local header" unless signature == ZIP_LOCAL
339
+
340
+ local_name = read_range(local_offset + 30, name_length)
341
+ raise Error, "PyTorch archive central/local names disagree for #{central_name.inspect}" unless local_name == central_name.b
342
+ unless flags == central_flags && method == central_method
343
+ raise Error,
344
+ "PyTorch archive central/local metadata disagree for #{central_name.inspect}"
345
+ end
346
+
347
+ local_offset + 30 + name_length + extra_length
348
+ end
349
+
350
+ def decode_name(bytes, utf8:)
351
+ value = bytes.dup
352
+ value.force_encoding(utf8 ? Encoding::UTF_8 : Encoding::BINARY)
353
+ raise Error, "PyTorch archive #{path} contains an invalid entry name" unless value.valid_encoding?
354
+
355
+ value.encode(Encoding::UTF_8)
356
+ rescue EncodingError
357
+ raise Error, "PyTorch archive #{path} contains a non-UTF-8 entry name"
358
+ end
359
+
360
+ def validate_entry_name!(name)
361
+ raise Error, "PyTorch archive #{path} contains an invalid entry name #{name.inspect}" if name.empty? || name.include?("\0")
362
+
363
+ candidate = Pathname(name)
364
+ return if !candidate.absolute? &&
365
+ candidate.each_filename.none? { |part| part == ".." }
366
+
367
+ raise Error, "PyTorch archive #{path} contains an invalid entry name #{name.inspect}"
368
+ end
369
+
370
+ def read_range(offset, length)
371
+ File.open(path, "rb") do |file|
372
+ file.seek(offset)
373
+ value = file.read(length)
374
+ return value if value && value.bytesize == length
375
+ end
376
+ raise Error, "PyTorch archive #{path} ended unexpectedly"
377
+ end
378
+ end
379
+
380
+ # Restricted pickle virtual machine for tensor state dictionaries.
381
+ class RestrictedUnpickler
382
+ MARKER = Object.new.freeze
383
+
384
+ attr_reader :storages
385
+
386
+ def initialize(io, limit: PICKLE_LIMIT, storages: nil)
387
+ @io = io
388
+ @limit = limit
389
+ @start = io.pos
390
+ @stack = []
391
+ @memo = {}
392
+ @storages = storages || {}
393
+ @next_memo = 0
394
+ @opcode_count = 0
395
+ end
396
+
397
+ def load
398
+ loop do
399
+ @opcode_count += 1
400
+ raise Error, "Restricted PyTorch pickle exceeds the opcode-count limit" if @opcode_count > PICKLE_OPCODE_LIMIT
401
+
402
+ opcode = read_exact(1).getbyte(0)
403
+ case opcode
404
+ when 0x80 then protocol!
405
+ when 0x95 then read_uint64 # FRAME
406
+ when 0x2e then return pop # STOP
407
+ when 0x28 then push(MARKER)
408
+ when 0x30 then pop
409
+ when 0x31 then pop_mark
410
+ when 0x32 then push(peek)
411
+ when 0x4e then push(nil)
412
+ when 0x88 then push(true)
413
+ when 0x89 then push(false)
414
+ when 0x4a then push(read_exact(4).unpack1("l<"))
415
+ when 0x4b then push(read_exact(1).unpack1("C"))
416
+ when 0x4d then push(read_exact(2).unpack1("v"))
417
+ when 0x49 then push(parse_ascii_integer(read_line))
418
+ when 0x4c then push(Integer(read_line.delete_suffix("L"), 10))
419
+ when 0x8a then push(parse_long(read_long_bytes(1)))
420
+ when 0x8b then push(parse_long(read_long_bytes(4)))
421
+ when 0x46 then push(Float(read_line))
422
+ when 0x47 then push(read_exact(8).unpack1("G"))
423
+ when 0x58 then push(read_utf8(read_exact(read_exact(4).unpack1("V"))))
424
+ when 0x8c then push(read_utf8(read_exact(read_exact(1).unpack1("C"))))
425
+ when 0x8d then push(read_utf8(read_exact(read_uint64)))
426
+ when 0x54 then push(read_exact(read_exact(4).unpack1("V")))
427
+ when 0x55 then push(read_exact(read_exact(1).unpack1("C")))
428
+ when 0x42 then push(read_exact(read_exact(4).unpack1("V")))
429
+ when 0x43 then push(read_exact(read_exact(1).unpack1("C")))
430
+ when 0x8e then push(read_exact(read_uint64))
431
+ when 0x96 then push(read_exact(read_uint64).dup)
432
+ when 0x53 then push(parse_quoted_string(read_line))
433
+ when 0x5d then push([])
434
+ when 0x6c then push(pop_mark)
435
+ when 0x61
436
+ value = pop
437
+ list_target << value
438
+ when 0x65
439
+ values = pop_mark
440
+ list_target.concat(values)
441
+ when 0x7d then push({})
442
+ when 0x64 then push(Hash[*pop_mark])
443
+ when 0x73 then set_pair
444
+ when 0x75 then set_pairs
445
+ when 0x29 then push([])
446
+ when 0x74 then push(pop_mark)
447
+ when 0x85 then push([pop])
448
+ when 0x86 then push(pop(2))
449
+ when 0x87 then push(pop(3))
450
+ when 0x8f then push([]) # EMPTY_SET; arrays suffice for metadata
451
+ when 0x90
452
+ values = pop_mark
453
+ list_target.concat(values).uniq!
454
+ when 0x91 then push(pop_mark.uniq.freeze)
455
+ when 0x63 then push(read_global)
456
+ when 0x93
457
+ module_name, name = pop(2)
458
+ raise Error, "Restricted PyTorch pickle has an invalid STACK_GLOBAL" unless module_name.is_a?(String) && name.is_a?(String)
459
+
460
+ push(Global.new(module_name: module_name, name: name))
461
+ when 0x52 then reduce!
462
+ when 0x51 then push(persistent_load(pop))
463
+ when 0x50 then push(persistent_load(read_line))
464
+ when 0x62 then build!
465
+ when 0x81 then new_object!
466
+ when 0x92 then new_object_with_keywords!
467
+ when 0x71 then memo_store(read_exact(1).unpack1("C"))
468
+ when 0x72 then memo_store(read_exact(4).unpack1("V"))
469
+ when 0x70 then memo_store(Integer(read_line, 10))
470
+ when 0x94 then memo_store_next
471
+ when 0x68 then push(memo_fetch(read_exact(1).unpack1("C")))
472
+ when 0x6a then push(memo_fetch(read_exact(4).unpack1("V")))
473
+ when 0x67 then push(memo_fetch(Integer(read_line, 10)))
474
+ else
475
+ raise Error, format("Restricted PyTorch pickle uses unsupported opcode 0x%02x", opcode)
476
+ end
477
+ end
478
+ rescue EOFError
479
+ raise Error, "PyTorch pickle ended before STOP"
480
+ rescue ArgumentError, TypeError, RangeError => e
481
+ raise Error, "Invalid restricted PyTorch pickle: #{e.message}"
482
+ end
483
+
484
+ private
485
+
486
+ def protocol!
487
+ version = read_exact(1).unpack1("C")
488
+ raise Error, "PyTorch pickle protocol #{version} is unsupported" unless version.between?(2, 5)
489
+ end
490
+
491
+ def read_global
492
+ module_name = read_line
493
+ name = read_line
494
+ Global.new(module_name: module_name.freeze, name: name.freeze)
495
+ end
496
+
497
+ def reduce!
498
+ arguments = pop
499
+ callable = pop
500
+ raise Error, "Restricted PyTorch pickle attempted a non-global reduction" unless callable.is_a?(Global) && arguments.is_a?(Array)
501
+
502
+ push(apply_reducer(callable, arguments))
503
+ end
504
+
505
+ def apply_reducer(callable, arguments)
506
+ key = [callable.module_name, callable.name]
507
+ case key
508
+ when %w[collections OrderedDict]
509
+ pairs = arguments.first || []
510
+ return {} if pairs.empty?
511
+ unless pairs.is_a?(Array) && pairs.all? { |pair| pair.is_a?(Array) && pair.length == 2 }
512
+ raise Error, "Restricted PyTorch pickle has an invalid OrderedDict"
513
+ end
514
+
515
+ pairs.to_h
516
+ when %w[torch Size]
517
+ Array(arguments.first)
518
+ when %w[builtins set], %w[__builtin__ set]
519
+ Array(arguments.first).uniq
520
+ when ["torch._utils", "_rebuild_tensor"],
521
+ ["torch._utils", "_rebuild_tensor_v2"],
522
+ ["torch._utils", "_rebuild_tensor_v3"]
523
+ rebuild_tensor(arguments)
524
+ when ["torch._utils", "_rebuild_parameter"],
525
+ ["torch._utils", "_rebuild_parameter_with_state"]
526
+ tensor = arguments.first
527
+ raise Error, "Restricted PyTorch pickle has an invalid parameter" unless tensor.is_a?(TensorSpec)
528
+
529
+ tensor
530
+ else
531
+ raise Error,
532
+ "Restricted PyTorch pickle refuses reducer #{callable.module_name}.#{callable.name}"
533
+ end
534
+ end
535
+
536
+ def rebuild_tensor(arguments)
537
+ storage, offset, shape, stride = arguments.first(4)
538
+ unless storage.is_a?(StorageRef) && offset.is_a?(Integer) &&
539
+ shape.is_a?(Array) && stride.is_a?(Array)
540
+ raise Error, "Restricted PyTorch pickle has invalid tensor reconstruction arguments"
541
+ end
542
+
543
+ TensorSpec.new(
544
+ storage: storage,
545
+ storage_offset: offset,
546
+ shape: integer_vector(shape, "tensor shape"),
547
+ stride: integer_vector(stride, "tensor stride")
548
+ )
549
+ end
550
+
551
+ def persistent_load(identifier)
552
+ unless identifier.is_a?(Array) && identifier.length.between?(5, 6) && identifier[0].to_s == "storage"
553
+ raise Error, "Restricted PyTorch pickle contains an unsupported persistent object"
554
+ end
555
+
556
+ storage_global, key, _location, elements, view = identifier[1..]
557
+ unless storage_global.is_a?(Global) && storage_global.module_name == "torch"
558
+ raise Error, "Restricted PyTorch pickle has an invalid storage type"
559
+ end
560
+
561
+ dtype, = STORAGE_DTYPES.fetch(storage_global.name) do
562
+ raise Error, "PyTorch storage #{storage_global.name.inspect} is unsupported"
563
+ end
564
+ unless (key.is_a?(String) || key.is_a?(Integer)) && elements.is_a?(Integer) && elements >= 0
565
+ raise Error, "Restricted PyTorch pickle has invalid storage metadata"
566
+ end
567
+
568
+ key = key.to_s
569
+ raise Error, "Restricted PyTorch pickle has an invalid storage key #{key.inspect}" unless /\A[0-9A-Za-z_.-]+\z/.match?(key)
570
+
571
+ existing = storages[key]
572
+ if existing && (existing.dtype != dtype || existing.elements != elements)
573
+ raise Error, "PyTorch storage #{key.inspect} has conflicting metadata"
574
+ end
575
+
576
+ root = existing || StorageRef.new(dtype: dtype, key: key.freeze, elements: elements, base_offset: 0)
577
+ storages[key] ||= root
578
+ return root unless view
579
+
580
+ unless view.is_a?(Array) && view.length == 3 &&
581
+ view[1].is_a?(Integer) && view[2].is_a?(Integer) &&
582
+ view[1] >= 0 && view[2] >= 0 && view[1] + view[2] <= elements
583
+ raise Error, "Restricted PyTorch pickle has invalid storage-view metadata"
584
+ end
585
+
586
+ StorageRef.new(dtype: dtype, key: key.freeze, elements: view[2], base_offset: view[1])
587
+ end
588
+
589
+ def build!
590
+ state = pop
591
+ object = peek
592
+ return if state.is_a?(Hash) && (object.is_a?(Hash) || object.is_a?(TensorSpec))
593
+
594
+ raise Error, "Restricted PyTorch pickle refuses object BUILD state"
595
+
596
+ # OrderedDict's _metadata and Tensor's backward state do not affect
597
+ # weight bytes. They are deliberately validated as a Hash then ignored.
598
+ end
599
+
600
+ def new_object!
601
+ arguments = pop
602
+ callable = pop
603
+ raise Error, "Restricted PyTorch pickle attempted invalid NEWOBJ" unless callable.is_a?(Global) && arguments.is_a?(Array)
604
+
605
+ push(apply_reducer(callable, arguments))
606
+ end
607
+
608
+ def new_object_with_keywords!
609
+ keywords = pop
610
+ arguments = pop
611
+ callable = pop
612
+ unless keywords == {} && callable.is_a?(Global) && arguments.is_a?(Array)
613
+ raise Error, "Restricted PyTorch pickle refuses NEWOBJ_EX keyword construction"
614
+ end
615
+
616
+ push(apply_reducer(callable, arguments))
617
+ end
618
+
619
+ def integer_vector(values, description)
620
+ raise Error, "Restricted PyTorch pickle has an invalid #{description}" unless values.all?(Integer)
621
+
622
+ values.map(&:to_i).freeze
623
+ end
624
+
625
+ def set_pair
626
+ value = pop
627
+ key = pop
628
+ target = peek
629
+ raise Error, "Restricted PyTorch pickle SETITEM target is not a Hash" unless target.is_a?(Hash)
630
+
631
+ target[key] = value
632
+ end
633
+
634
+ def set_pairs
635
+ pairs = pop_mark
636
+ raise Error, "Restricted PyTorch pickle has an odd SETITEMS payload" unless pairs.length.even?
637
+
638
+ target = peek
639
+ raise Error, "Restricted PyTorch pickle SETITEMS target is not a Hash" unless target.is_a?(Hash)
640
+
641
+ pairs.each_slice(2) { |key, value| target[key] = value }
642
+ end
643
+
644
+ def list_target
645
+ target = peek
646
+ raise Error, "Restricted PyTorch pickle list target is invalid" unless target.is_a?(Array)
647
+
648
+ target
649
+ end
650
+
651
+ def memo_store(index)
652
+ @memo[index] = peek
653
+ @next_memo = [@next_memo, index + 1].max
654
+ end
655
+
656
+ def memo_store_next
657
+ @memo[@next_memo] = peek
658
+ @next_memo += 1
659
+ end
660
+
661
+ def memo_fetch(index)
662
+ @memo.fetch(index)
663
+ rescue KeyError
664
+ raise Error, "Restricted PyTorch pickle references missing memo #{index}"
665
+ end
666
+
667
+ def push(value)
668
+ @stack << value
669
+ end
670
+
671
+ def peek
672
+ raise Error, "Restricted PyTorch pickle stack is empty" if @stack.empty?
673
+
674
+ @stack.last
675
+ end
676
+
677
+ def pop(count = nil)
678
+ return @stack.pop unless count
679
+ raise Error, "Restricted PyTorch pickle stack underflow" if @stack.length < count
680
+
681
+ @stack.pop(count)
682
+ end
683
+
684
+ def pop_mark
685
+ index = @stack.rindex(MARKER)
686
+ raise Error, "Restricted PyTorch pickle has no MARK" unless index
687
+
688
+ values = @stack.slice!(index + 1, @stack.length - index - 1)
689
+ @stack.pop
690
+ values
691
+ end
692
+
693
+ def read_exact(length)
694
+ raise Error, "Restricted PyTorch pickle requests an invalid byte count" if length.negative?
695
+ raise Error, "PyTorch pickle exceeds the #{@limit}-byte size limit" if @io.pos - @start + length > @limit
696
+
697
+ value = @io.read(length)
698
+ raise EOFError unless value && value.bytesize == length
699
+
700
+ value
701
+ end
702
+
703
+ def read_line
704
+ remaining = @limit - (@io.pos - @start)
705
+ raise Error, "PyTorch pickle exceeds the #{@limit}-byte size limit" unless remaining.positive?
706
+
707
+ value = @io.gets("\n", remaining + 1)
708
+ raise EOFError unless value&.end_with?("\n")
709
+
710
+ value.delete_suffix("\n")
711
+ end
712
+
713
+ def read_uint64
714
+ value = read_exact(8).unpack1("Q<")
715
+ raise Error, "Restricted PyTorch pickle declares an oversized field" if value > @limit
716
+
717
+ value
718
+ end
719
+
720
+ def read_long_bytes(length_width)
721
+ length = read_exact(length_width).unpack1(length_width == 1 ? "C" : "V")
722
+ if length > PICKLE_LONG_BYTES_LIMIT
723
+ raise Error,
724
+ "Restricted PyTorch pickle integer exceeds the #{PICKLE_LONG_BYTES_LIMIT}-byte size limit"
725
+ end
726
+
727
+ read_exact(length)
728
+ end
729
+
730
+ def read_utf8(bytes)
731
+ value = bytes.dup.force_encoding(Encoding::UTF_8)
732
+ raise Error, "Restricted PyTorch pickle contains invalid UTF-8" unless value.valid_encoding?
733
+
734
+ value.freeze
735
+ end
736
+
737
+ def parse_ascii_integer(value)
738
+ return true if value == "01"
739
+ return false if value == "00"
740
+
741
+ Integer(value, 10)
742
+ end
743
+
744
+ def parse_long(bytes)
745
+ value = bytes.bytes.each_with_index.sum { |byte, index| byte << (8 * index) }
746
+ value -= 1 << (8 * bytes.bytesize) if !bytes.empty? && bytes.getbyte(-1).anybits?(0x80)
747
+ value
748
+ end
749
+
750
+ def parse_quoted_string(value)
751
+ unless value.bytesize >= 2 && ["'", '"'].include?(value[0]) && value[-1] == value[0]
752
+ raise Error, "Restricted PyTorch pickle contains an invalid STRING"
753
+ end
754
+
755
+ body = value.byteslice(1, value.bytesize - 2)
756
+ body.gsub(/\\(?:x([0-9a-fA-F]{2})|([\\'"nrt]))/) do
757
+ if Regexp.last_match(1)
758
+ Regexp.last_match(1).to_i(16).chr
759
+ else
760
+ { "\\" => "\\", "'" => "'", '"' => '"', "n" => "\n", "r" => "\r", "t" => "\t" }
761
+ .fetch(Regexp.last_match(2))
762
+ end
763
+ end
764
+ end
765
+ end
766
+
767
+ # One checkpoint shard. Tensor bytes are never materialized as a whole.
768
+ class Reader
769
+ attr_reader :path, :tensors
770
+
771
+ def initialize(path)
772
+ @path = Pathname(path).expand_path
773
+ @storages = {}
774
+ @tensors = read_checkpoint.freeze
775
+ end
776
+
777
+ def names
778
+ tensors.keys
779
+ end
780
+
781
+ def key?(name)
782
+ tensors.key?(name)
783
+ end
784
+
785
+ def fetch(name)
786
+ tensors.fetch(name)
787
+ rescue KeyError
788
+ raise Error, "Tensor #{name.inspect} is not present in #{path}"
789
+ end
790
+
791
+ def write_tensor(tensor, output, target_dtype:, converter: Safetensors::DTypeConverter.default,
792
+ chunk_bytes: Safetensors::DEFAULT_CHUNK_BYTES)
793
+ raise ArgumentError, "Tensor #{tensor.name.inspect} belongs to a different PyTorch reader" unless tensor.reader.equal?(self)
794
+
795
+ target_dtype = target_dtype.to_s.upcase
796
+ unless FLOAT_DTYPES.include?(tensor.dtype) && FLOAT_DTYPES.include?(target_dtype)
797
+ raise Error, "Cannot convert #{tensor.name.inspect} from #{tensor.dtype} to #{target_dtype}"
798
+ end
799
+ raise ArgumentError, "chunk_bytes must be positive" unless chunk_bytes.positive?
800
+
801
+ source_width = Safetensors::DTYPE_BYTES.fetch(tensor.dtype)
802
+ elements_per_chunk = [chunk_bytes / source_width, 1].max
803
+ bytes_per_chunk = elements_per_chunk * source_width
804
+ unless contiguous?(tensor)
805
+ return write_strided_tensor(
806
+ tensor, output, target_dtype: target_dtype, converter: converter,
807
+ elements_per_chunk: elements_per_chunk, source_width: source_width
808
+ )
809
+ end
810
+
811
+ remaining = tensor.nbytes
812
+ written = 0
813
+ File.open(path, "rb") do |source|
814
+ source.seek(tensor.data_start)
815
+ while remaining.positive?
816
+ requested = [remaining, bytes_per_chunk].min
817
+ chunk = source.read(requested)
818
+ raise Error, "Unexpected end of #{path} while reading #{tensor.name.inspect}" if chunk.nil? || chunk.bytesize != requested
819
+
820
+ converted = converter.convert(chunk, from: tensor.dtype, to: target_dtype)
821
+ output.write(converted)
822
+ written += converted.bytesize
823
+ remaining -= requested
824
+ end
825
+ end
826
+ expected = tensor.element_count * Safetensors::DTYPE_BYTES.fetch(target_dtype)
827
+ return written if written == expected
828
+
829
+ raise Error, "Converted tensor #{tensor.name.inspect} wrote #{written} bytes; expected #{expected}"
830
+ end
831
+
832
+ private
833
+
834
+ def read_checkpoint
835
+ signature = File.binread(path, 4)
836
+ if signature == [ZIP_LOCAL].pack("V")
837
+ read_zip
838
+ elsif path.size >= 262 && File.binread(path, 5, 257) == "ustar"
839
+ raise Error,
840
+ "Legacy tar PyTorch checkpoints are not accepted by the restricted weights-only reader; " \
841
+ "re-save this dense state dictionary as Safetensors or a current torch.save ZIP"
842
+ else
843
+ read_legacy_stream
844
+ end
845
+ rescue Errno::ENOENT
846
+ raise Error, "PyTorch checkpoint #{path} does not exist"
847
+ rescue Errno::EACCES => e
848
+ raise Error, "Cannot read PyTorch checkpoint #{path}: #{e.message}"
849
+ end
850
+
851
+ def read_zip
852
+ archive = ZipArchive.new(path)
853
+ data_entries = archive.entries.keys.select { |name| name.end_with?("/data.pkl") }
854
+ raise Error, "PyTorch archive #{path} must contain exactly one data.pkl" unless data_entries.length == 1
855
+
856
+ data_name = data_entries.first
857
+ prefix = data_name.delete_suffix("data.pkl")
858
+ byteorder_name = "#{prefix}byteorder"
859
+ if archive.entries.key?(byteorder_name)
860
+ byteorder = archive.read(byteorder_name, limit: 16)
861
+ raise Error, "Big-endian PyTorch checkpoints are not supported" unless byteorder == "little"
862
+ end
863
+ root = RestrictedUnpickler.new(
864
+ StringIO.new(archive.read(data_name)), storages: @storages
865
+ ).load
866
+ build_tensors(root) do |storage|
867
+ entry = archive.fetch("#{prefix}data/#{storage.key}")
868
+ unless entry.compression_method.zero? && entry.compressed_size == entry.size
869
+ raise Error, "PyTorch tensor storage #{storage.key.inspect} is compressed and cannot be streamed"
870
+ end
871
+
872
+ expected = @storages.fetch(storage.key).elements * dtype_width(storage.dtype)
873
+ unless entry.size == expected
874
+ raise Error, "PyTorch storage #{storage.key.inspect} has #{entry.size} bytes; expected #{expected}"
875
+ end
876
+
877
+ entry.data_offset
878
+ end
879
+ end
880
+
881
+ def read_legacy_stream
882
+ File.open(path, "rb") do |file|
883
+ magic = RestrictedUnpickler.new(file, limit: 64).load
884
+ raise Error, "#{path} is not a supported PyTorch state-dictionary checkpoint" unless magic == MAGIC_NUMBER
885
+
886
+ version = RestrictedUnpickler.new(file, limit: 64).load
887
+ raise Error, "PyTorch legacy protocol #{version.inspect} is unsupported" unless version == PROTOCOL_VERSION
888
+
889
+ system_info = RestrictedUnpickler.new(file, limit: 4096).load
890
+ unless system_info.is_a?(Hash) && system_info["little_endian"] != false
891
+ raise Error, "Big-endian PyTorch checkpoints are not supported"
892
+ end
893
+
894
+ root = RestrictedUnpickler.new(file, storages: @storages).load
895
+ storage_keys = RestrictedUnpickler.new(file, limit: PICKLE_LIMIT).load
896
+ unless storage_keys.is_a?(Array) && storage_keys.map(&:to_s).uniq.length == storage_keys.length
897
+ raise Error, "PyTorch legacy checkpoint has an invalid storage-key list"
898
+ end
899
+
900
+ offsets = {}
901
+ storage_keys.each do |raw_key|
902
+ key = raw_key.to_s
903
+ storage = @storages.fetch(key) do
904
+ raise Error, "PyTorch legacy checkpoint lists unknown storage #{key.inspect}"
905
+ end
906
+ raw_count = file.read(8)
907
+ unless raw_count&.bytesize == 8
908
+ raise Error,
909
+ "PyTorch legacy checkpoint ended before storage #{key.inspect}"
910
+ end
911
+
912
+ count = raw_count.unpack1("Q<")
913
+ unless count == storage.elements
914
+ raise Error, "PyTorch legacy storage #{key.inspect} has #{count} elements; expected #{storage.elements}"
915
+ end
916
+
917
+ offsets[key] = file.pos
918
+ bytes = count * dtype_width(storage.dtype)
919
+ file.seek(bytes, IO::SEEK_CUR)
920
+ raise Error, "PyTorch legacy storage #{key.inspect} points beyond #{path}" if file.pos > path.size
921
+ end
922
+ extra = @storages.keys - offsets.keys
923
+ raise Error, "PyTorch legacy checkpoint omits storage(s): #{extra.first(8).join(", ")}" unless extra.empty?
924
+
925
+ build_tensors(root) { |storage| offsets.fetch(storage.key) }
926
+ end
927
+ end
928
+
929
+ def build_tensors(root)
930
+ state = extract_state_dict(root)
931
+ state.to_h do |name, specification|
932
+ validate_tensor_spec!(name, specification)
933
+ storage = specification.storage
934
+ root_storage = @storages.fetch(storage.key)
935
+ width = dtype_width(storage.dtype)
936
+ count = element_count(specification.shape)
937
+ relative_elements = storage.base_offset + specification.storage_offset
938
+ last_element = contiguous_extent(specification.shape, specification.stride, relative_elements)
939
+ if relative_elements.negative? || last_element > root_storage.elements
940
+ raise Error, "Tensor #{name.inspect} points outside storage #{storage.key.inspect}"
941
+ end
942
+
943
+ base_offset = yield(storage)
944
+ tensor = Tensor.new(
945
+ reader: self,
946
+ name: name.freeze,
947
+ dtype: storage.dtype.freeze,
948
+ shape: specification.shape.freeze,
949
+ storage_key: storage.key,
950
+ data_start: base_offset + (relative_elements * width),
951
+ nbytes: count * width,
952
+ storage_data_start: base_offset,
953
+ storage_elements: root_storage.elements,
954
+ storage_offset: relative_elements,
955
+ stride: specification.stride.freeze
956
+ )
957
+ [name, tensor]
958
+ end
959
+ end
960
+
961
+ def extract_state_dict(root)
962
+ candidates = [root]
963
+ candidates << root["state_dict"] if root.is_a?(Hash)
964
+ state = candidates.compact.find do |candidate|
965
+ candidate.is_a?(Hash) && !candidate.empty? &&
966
+ candidate.all? { |name, value| name.is_a?(String) && value.is_a?(TensorSpec) }
967
+ end
968
+ return state if state
969
+
970
+ raise Error, "PyTorch checkpoint #{path} does not contain a plain tensor state dictionary"
971
+ end
972
+
973
+ def validate_tensor_spec!(name, specification)
974
+ unless name.is_a?(String) && !name.empty? && specification.is_a?(TensorSpec)
975
+ raise Error, "PyTorch checkpoint #{path} contains invalid tensor metadata"
976
+ end
977
+
978
+ shape = specification.shape
979
+ stride = specification.stride
980
+ unless shape.length == stride.length && shape.all? { |dimension| dimension >= 0 } &&
981
+ specification.storage_offset >= 0
982
+ raise Error, "Tensor #{name.inspect} has an invalid shape, stride, or storage offset"
983
+ end
984
+ return if stride.all? { |value| value >= 0 }
985
+
986
+ raise Error, "Tensor #{name.inspect} has a negative stride"
987
+ end
988
+
989
+ def contiguous_extent(shape, stride, start)
990
+ return start if shape.any?(&:zero?)
991
+
992
+ start + shape.each_index.sum { |index| (shape[index] - 1) * stride[index] } + 1
993
+ end
994
+
995
+ def element_count(shape)
996
+ shape.empty? ? 1 : shape.reduce(1, :*)
997
+ end
998
+
999
+ def dtype_width(dtype)
1000
+ Safetensors::DTYPE_BYTES.fetch(dtype)
1001
+ rescue KeyError
1002
+ raise Error, "PyTorch dtype #{dtype.inspect} is unsupported"
1003
+ end
1004
+
1005
+ def contiguous?(tensor)
1006
+ expected = 1
1007
+ (tensor.shape.length - 1).downto(0) do |index|
1008
+ dimension = tensor.shape[index]
1009
+ return false if dimension > 1 && tensor.stride[index] != expected
1010
+
1011
+ expected *= dimension unless dimension.zero?
1012
+ end
1013
+ true
1014
+ end
1015
+
1016
+ # General strided tensors occur in legitimate torch state dictionaries
1017
+ # (notably transposed projection weights). A read-only file mapping
1018
+ # keeps heap usage bounded while preserving logical row-major order.
1019
+ def write_strided_tensor(tensor, output, target_dtype:, converter:, elements_per_chunk:, source_width:)
1020
+ total = tensor.element_count
1021
+ return 0 if total.zero?
1022
+
1023
+ page_size = IO::Buffer::PAGE_SIZE
1024
+ map_offset = (tensor.storage_data_start / page_size) * page_size
1025
+ map_delta = tensor.storage_data_start - map_offset
1026
+ map_length = map_delta + (tensor.storage_elements * source_width)
1027
+ coordinates = Array.new(tensor.shape.length, 0)
1028
+ source_element = tensor.storage_offset
1029
+ written = 0
1030
+ previous_warning = Warning[:experimental]
1031
+ Warning[:experimental] = false
1032
+ File.open(path, "rb") do |source|
1033
+ mapped = IO::Buffer.map(source, map_length, map_offset, IO::Buffer::READONLY)
1034
+ begin
1035
+ remaining = total
1036
+ while remaining.positive?
1037
+ count = [remaining, elements_per_chunk].min
1038
+ gathered = IO::Buffer.new(count * source_width)
1039
+ begin
1040
+ count.times do |index|
1041
+ gathered.copy(
1042
+ mapped,
1043
+ index * source_width,
1044
+ source_width,
1045
+ map_delta + (source_element * source_width)
1046
+ )
1047
+ next unless index + 1 < count || remaining > count
1048
+
1049
+ source_element = advance_index!(
1050
+ coordinates, tensor.shape, tensor.stride, source_element
1051
+ )
1052
+ end
1053
+ raw = gathered.get_string(0, count * source_width)
1054
+ ensure
1055
+ gathered.free
1056
+ end
1057
+ converted = converter.convert(raw, from: tensor.dtype, to: target_dtype)
1058
+ output.write(converted)
1059
+ written += converted.bytesize
1060
+ remaining -= count
1061
+ end
1062
+ ensure
1063
+ mapped.free
1064
+ end
1065
+ end
1066
+ expected = total * Safetensors::DTYPE_BYTES.fetch(target_dtype)
1067
+ return written if written == expected
1068
+
1069
+ raise Error, "Converted tensor #{tensor.name.inspect} wrote #{written} bytes; expected #{expected}"
1070
+ ensure
1071
+ Warning[:experimental] = previous_warning unless previous_warning.nil?
1072
+ end
1073
+
1074
+ def advance_index!(coordinates, shape, stride, source_element)
1075
+ (shape.length - 1).downto(0) do |dimension|
1076
+ coordinates[dimension] += 1
1077
+ source_element += stride[dimension]
1078
+ return source_element if coordinates[dimension] < shape[dimension]
1079
+
1080
+ source_element -= stride[dimension] * shape[dimension]
1081
+ coordinates[dimension] = 0
1082
+ end
1083
+ source_element
1084
+ end
1085
+ end
1086
+
1087
+ # One unsharded pytorch_model.bin or its Transformers shard index.
1088
+ class TensorSet
1089
+ attr_reader :directory, :readers, :tensors
1090
+
1091
+ def self.from_directory(directory)
1092
+ directory = Pathname(directory).expand_path
1093
+ single = directory.join("pytorch_model.bin")
1094
+ return new(directory, [Reader.new(single)]) if single.file?
1095
+
1096
+ index = directory.join("pytorch_model.bin.index.json")
1097
+ raise Error, "#{directory} does not contain pytorch_model.bin or its index" unless index.file?
1098
+
1099
+ from_index(directory, index)
1100
+ end
1101
+
1102
+ def self.from_index(directory, index_path)
1103
+ payload = JSON.parse(index_path.read(encoding: "UTF-8"))
1104
+ weight_map = payload["weight_map"]
1105
+ unless weight_map.is_a?(Hash) && !weight_map.empty? &&
1106
+ weight_map.all? { |name, file| name.is_a?(String) && file.is_a?(String) }
1107
+ raise Error, "PyTorch index #{index_path} has an invalid weight_map"
1108
+ end
1109
+
1110
+ shard_names = weight_map.values.uniq
1111
+ shard_names.each { |name| validate_shard_name!(name, index_path) }
1112
+ readers = shard_names.to_h do |name|
1113
+ shard = directory.join(name)
1114
+ raise Error, "PyTorch shard #{shard} is missing" unless shard.file?
1115
+
1116
+ [name, Reader.new(shard)]
1117
+ end
1118
+ mapped = weight_map.to_h do |name, shard|
1119
+ reader = readers.fetch(shard)
1120
+ raise Error, "PyTorch index maps missing tensor #{name.inspect} to #{shard}" unless reader.key?(name)
1121
+
1122
+ [name, reader.fetch(name)]
1123
+ end
1124
+ readers.each do |shard, reader|
1125
+ indexed = weight_map.filter_map { |name, filename| name if filename == shard }
1126
+ unindexed = reader.names - indexed
1127
+ next if unindexed.empty?
1128
+
1129
+ raise Error,
1130
+ "PyTorch shard #{reader.path} contains tensors absent from its index: " \
1131
+ "#{unindexed.first(8).map(&:inspect).join(", ")}"
1132
+ end
1133
+ new(directory, readers.values, tensors: mapped)
1134
+ rescue JSON::ParserError, EncodingError => e
1135
+ raise Error, "Invalid PyTorch index #{index_path}: #{e.message}"
1136
+ rescue Errno::EACCES, Errno::ENOENT => e
1137
+ raise Error, "Cannot read PyTorch index #{index_path}: #{e.message}"
1138
+ end
1139
+
1140
+ def self.validate_shard_name!(name, index_path)
1141
+ raise Error, "PyTorch index #{index_path} contains an invalid shard path #{name.inspect}" if name.empty? || name.include?("\0")
1142
+
1143
+ candidate = Pathname(name)
1144
+ return if name.end_with?(".bin") && !candidate.absolute? &&
1145
+ candidate.each_filename.none? { |part| part == ".." }
1146
+
1147
+ raise Error, "PyTorch index #{index_path} contains an invalid shard path #{name.inspect}"
1148
+ end
1149
+ private_class_method :validate_shard_name!
1150
+
1151
+ def initialize(directory, readers, tensors: nil)
1152
+ @directory = Pathname(directory).expand_path
1153
+ @readers = readers.freeze
1154
+ @tensors = (tensors || readers.flat_map { |reader| reader.tensors.to_a }.to_h).freeze
1155
+ end
1156
+
1157
+ def names
1158
+ tensors.keys
1159
+ end
1160
+
1161
+ def key?(name)
1162
+ tensors.key?(name)
1163
+ end
1164
+
1165
+ def fetch(name)
1166
+ tensors.fetch(name)
1167
+ rescue KeyError
1168
+ raise Error, "Tensor #{name.inspect} is not present in #{directory}"
1169
+ end
1170
+
1171
+ def fetch_any(names)
1172
+ name = names.find { |candidate| tensors.key?(candidate) }
1173
+ return tensors.fetch(name) if name
1174
+
1175
+ raise Error, "None of the tensor aliases are present: #{names.map(&:inspect).join(", ")}"
1176
+ end
1177
+ end
1178
+ end
1179
+ end
1180
+ end