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,924 @@
1
+ // src/core/attention.h — shared multi-head attention helpers (header-only).
2
+ //
3
+ // Replaces the Q/K/V-projection + reshape + RoPE + GQA-expand + flash-attn +
4
+ // output-projection block that every LLM-based model in src/ has 1–2 copies
5
+ // of. The helper is header-only so the compiler inlines it straight into
6
+ // each caller, producing the exact same ggml op sequence as the original
7
+ // inline code and preserving bit-identical graph execution.
8
+ //
9
+ // Scope of the initial version (this commit):
10
+ //
11
+ // core_attn::llama_self_attn_kv() — the classic Llama / Mistral LLM
12
+ // attention block: RMSNorm weights applied by caller, no biases on
13
+ // Q/K/V/O, NEOX RoPE, optional GQA expansion, ggml_flash_attn_ext with
14
+ // a caller-supplied causal-or-sliding-window mask, reshape + output
15
+ // projection. Used by voxtral, voxtral4b, qwen3 (without Q/K norm),
16
+ // and granite LLM decoders.
17
+ //
18
+ // Follow-up variants (to be added when their first consumer migrates):
19
+ //
20
+ // * post-projection Q/K RMSNorm (qwen3)
21
+ // * separate audio-encoder variant with biases + no RoPE (voxtral audio)
22
+ // * adaptive scale / residual_multiplier (granite µP)
23
+ // * KV-cache lookup that returns a (K, V) pair instead of taking
24
+ // pre-permuted inputs (needed when KV cache is stored in a different
25
+ // layout than (head_dim, T, n_heads))
26
+ //
27
+ // This staged approach keeps the first commit narrow and verifiable. Every
28
+ // new caller either fits the existing helper or adds a new sibling helper
29
+ // with its own regression test.
30
+
31
+ #pragma once
32
+
33
+ #include "ggml-backend.h"
34
+ #include "ggml.h"
35
+
36
+ #include <cstddef>
37
+ #include <cstdint>
38
+ #include <cstdio>
39
+ #include <cstdlib>
40
+ #include <cstring>
41
+ #include <vector>
42
+
43
+ // Declared in ggml-backend-impl.h, which is not on the public include path,
44
+ // but the symbol is GGML_API-exported by the ggml backend library. Returns
45
+ // true iff the backend performed a direct device-to-device tensor copy
46
+ // (GH #161 — used to pick the KV-snapshot storage mode below).
47
+ extern "C" bool ggml_backend_buffer_copy_tensor(const struct ggml_tensor* src, struct ggml_tensor* dst);
48
+
49
+ namespace core_attn {
50
+
51
+ // PLAN #60e: KV cache dtype selection from `CRISPASR_KV_QUANT`.
52
+ //
53
+ // Default returns `GGML_TYPE_F16` so any backend that calls this in
54
+ // its `*_kv_init` is bit-identical to legacy behaviour until the user
55
+ // opts in. Recognised values: `f16` (default), `q8_0`, `q4_0`. Anything
56
+ // else logs a warning to stderr and falls back to F16.
57
+ //
58
+ // Pairs with the `core_attn::kv_self_attn` write- and read-path
59
+ // quant-safety: when the cache type is quantised, the helper switches
60
+ // to `ggml_set_rows` for the write (vs `ggml_cpy` for F16, which
61
+ // requires contig dst that quant slices never satisfy) and uses
62
+ // `ggml_cast(...,F32)` to dequantise on read (CPU-backend safe; F16
63
+ // would be metal-only).
64
+ //
65
+ // `backend_tag` is the prefix on the warning line so a misconfigured
66
+ // env var points at a specific backend rather than a generic message.
67
+ // Parse a single KV-quant string. Internal helper.
68
+ inline ggml_type kv_dtype_parse(const char* s, const char* backend_tag, const char* env_name, ggml_type fallback) {
69
+ if (!s || !*s)
70
+ return fallback;
71
+ if (std::strcmp(s, "f16") == 0 || std::strcmp(s, "F16") == 0)
72
+ return GGML_TYPE_F16;
73
+ if (std::strcmp(s, "f32") == 0 || std::strcmp(s, "F32") == 0)
74
+ return GGML_TYPE_F32;
75
+ if (std::strcmp(s, "q8_0") == 0 || std::strcmp(s, "Q8_0") == 0)
76
+ return GGML_TYPE_Q8_0;
77
+ if (std::strcmp(s, "q4_0") == 0 || std::strcmp(s, "Q4_0") == 0)
78
+ return GGML_TYPE_Q4_0;
79
+ std::fprintf(stderr, "%s: %s='%s' unrecognised, defaulting to f16\n", backend_tag, env_name, s);
80
+ return GGML_TYPE_F16;
81
+ }
82
+
83
+ inline ggml_type kv_dtype_from_env(const char* backend_tag) {
84
+ return kv_dtype_parse(std::getenv("CRISPASR_KV_QUANT"), backend_tag, "CRISPASR_KV_QUANT", GGML_TYPE_F16);
85
+ }
86
+
87
+ // Asymmetric K/V cache types. PLAN #69e — llama.cpp-style independent
88
+ // `--cache-type-k` / `--cache-type-v`. The two halves of the KV cache
89
+ // have very different sensitivity profiles:
90
+ //
91
+ // * V quantises down well: it gets used as `softmax(QK^T) · V`, where
92
+ // softmax already concentrates probability mass and per-element
93
+ // errors get averaged across attended positions. q4_0 V is usually
94
+ // indistinguishable from F16.
95
+ // * K is the fragile half: errors in `QK^T / sqrt(d)` distort *which*
96
+ // positions get attended to (the softmax exponentiates errors).
97
+ // K typically wants q8_0 or higher for the same PPL floor.
98
+ //
99
+ // Common llama.cpp recipe is `-ctk q8_0 -ctv q4_0`, ~40 % KV memory
100
+ // savings vs symmetric Q8_0 with PPL barely moved on Llama-class
101
+ // models. The legacy CRISPASR_KV_QUANT remains the default for both
102
+ // halves; the per-half overrides take precedence.
103
+ struct kv_dtype_pair {
104
+ ggml_type k;
105
+ ggml_type v;
106
+ };
107
+
108
+ inline kv_dtype_pair kv_dtype_pair_from_env(const char* backend_tag) {
109
+ const ggml_type both = kv_dtype_from_env(backend_tag);
110
+ const ggml_type k = kv_dtype_parse(std::getenv("CRISPASR_KV_QUANT_K"), backend_tag, "CRISPASR_KV_QUANT_K", both);
111
+ const ggml_type v = kv_dtype_parse(std::getenv("CRISPASR_KV_QUANT_V"), backend_tag, "CRISPASR_KV_QUANT_V", both);
112
+ return {k, v};
113
+ }
114
+
115
+ // PLAN #69b — pick the backend on which to allocate the KV cache.
116
+ // Default: same backend as the model weights (`gpu_backend`). When
117
+ // `CRISPASR_KV_ON_CPU=1` is set, allocate on `cpu_backend` instead so
118
+ // users with very long context + tight VRAM can spill the cache to
119
+ // system RAM. The cost is per-step GPU↔CPU copy of the KV slice, which
120
+ // is typically slower than just running with `CRISPASR_KV_QUANT=q4_0`
121
+ // to fit KV in VRAM — try KV_QUANT first.
122
+ //
123
+ // `backend_tag` is the prefix on the warning line. Returns gpu_backend
124
+ // when neither offload is requested, cpu_backend when it is.
125
+ inline ggml_backend_t kv_backend_from_env(ggml_backend_t gpu_backend, ggml_backend_t cpu_backend,
126
+ const char* backend_tag) {
127
+ const char* s = std::getenv("CRISPASR_KV_ON_CPU");
128
+ if (!s || !*s || std::strcmp(s, "0") == 0)
129
+ return gpu_backend;
130
+ if (!cpu_backend) {
131
+ std::fprintf(stderr, "%s: CRISPASR_KV_ON_CPU=%s requested but no CPU backend available, falling back to GPU\n",
132
+ backend_tag, s);
133
+ return gpu_backend;
134
+ }
135
+ return cpu_backend;
136
+ }
137
+
138
+ // GH #161 — on-device KV-cache snapshot for branched beam search.
139
+ //
140
+ // `core_beam_decode::run_with_probs_branched` snapshots and restores the
141
+ // decoder's KV cache once per surviving beam per step. The original
142
+ // per-backend lambdas did this through host memory
143
+ // (`ggml_backend_tensor_get` → std::vector → `ggml_backend_tensor_set`),
144
+ // which on a GPU backend means a full KV round-trip over PCIe *plus* a
145
+ // blocking device synchronize on every copy. With the default beam_size=5
146
+ // that is ~B×(K+V) bytes of GPU↔CPU traffic per decode step — the bulk of
147
+ // the #161 cohere regression (one core pegged in a sync-spin, time spent
148
+ // entirely outside the profiled compute).
149
+ //
150
+ // `kv_snapshot_pool` instead keeps the snapshot resident on the *same*
151
+ // buffer type as the cache, so save/restore are device-to-device blits with
152
+ // no host transfer and no sync-to-host. It snapshots an arbitrary list of
153
+ // live tensors, so it serves both the single `kv_k`/`kv_v` layout (cohere,
154
+ // canary, kyutai_stt, omniasr) and the per-layer `kv.k[il]`/`kv.v[il]`
155
+ // layout (moonshine).
156
+ //
157
+ // Allocating a fresh backend buffer per snapshot is itself expensive on a
158
+ // GPU backend (MTLBuffer creation / cudaMalloc can synchronize the device),
159
+ // and beam search saves O(B) snapshots *per step*. The pool therefore
160
+ // recycles slots: at most ~beam_size+1 snapshots are ever live at once, so
161
+ // after the first few steps every `save` reuses an idle slot.
162
+ //
163
+ // Two storage modes, chosen once from whether the cache backend supports a
164
+ // direct device-to-device tensor copy:
165
+ // * DEVICE — discrete-VRAM backends (CUDA, Vulkan, ROCm). The snapshot
166
+ // lives in VRAM and save/restore are on-device blits: no PCIe transfer,
167
+ // no sync-to-host. This is the path that fixes #161.
168
+ // * HOST — unified-memory / CPU backends (Metal, CPU), where the cache
169
+ // is already host-addressable and an on-device blit isn't implemented
170
+ // (and `ggml_backend_tensor_copy` would silently round-trip through a
171
+ // freshly malloc'd staging buffer). Here we snapshot into a recycled
172
+ // host buffer, identical cost to the original code minus the per-step
173
+ // allocation churn.
174
+ struct kv_snapshot {
175
+ ggml_backend_buffer_t buf = nullptr; // DEVICE mode (all tensors packed here)
176
+ ggml_context* meta = nullptr; // DEVICE mode
177
+ std::vector<ggml_tensor*> dev; // DEVICE mode: per-source snapshot tensor
178
+ std::vector<std::vector<uint8_t>> host; // HOST mode: per-source snapshot bytes
179
+ bool in_use = false;
180
+ };
181
+
182
+ struct kv_snapshot_pool {
183
+ std::vector<ggml_tensor*> live; // tensors to snapshot, in order
184
+ enum { UNKNOWN, DEVICE, HOST } mode = UNKNOWN;
185
+ std::vector<kv_snapshot*> slots;
186
+
187
+ explicit kv_snapshot_pool(std::vector<ggml_tensor*> tensors) : live(std::move(tensors)) {}
188
+ kv_snapshot_pool(ggml_tensor* k, ggml_tensor* v) : live{k, v} {}
189
+ kv_snapshot_pool(const kv_snapshot_pool&) = delete;
190
+ kv_snapshot_pool& operator=(const kv_snapshot_pool&) = delete;
191
+
192
+ ~kv_snapshot_pool() {
193
+ for (kv_snapshot* s : slots) {
194
+ if (s->buf)
195
+ ggml_backend_buffer_free(s->buf);
196
+ if (s->meta)
197
+ ggml_free(s->meta);
198
+ delete s;
199
+ }
200
+ }
201
+
202
+ void alloc_device(kv_snapshot* s) {
203
+ const ggml_init_params ip = {ggml_tensor_overhead() * (live.size() + 1) + 256, nullptr, /*no_alloc=*/true};
204
+ s->meta = ggml_init(ip);
205
+ size_t total = 0;
206
+ for (ggml_tensor* t : live)
207
+ total += ggml_nbytes(t);
208
+ ggml_backend_buffer_type_t buft = ggml_backend_buffer_get_type(live[0]->buffer);
209
+ s->buf = ggml_backend_buft_alloc_buffer(buft, total);
210
+ char* base = (char*)ggml_backend_buffer_get_base(s->buf);
211
+ size_t off = 0;
212
+ s->dev.resize(live.size());
213
+ for (size_t i = 0; i < live.size(); i++) {
214
+ s->dev[i] = ggml_new_tensor(s->meta, live[i]->type, GGML_MAX_DIMS, live[i]->ne);
215
+ ggml_backend_tensor_alloc(s->buf, s->dev[i], base + off);
216
+ off += ggml_nbytes(live[i]);
217
+ }
218
+ }
219
+
220
+ void free_device(kv_snapshot* s) {
221
+ if (s->buf)
222
+ ggml_backend_buffer_free(s->buf);
223
+ if (s->meta)
224
+ ggml_free(s->meta);
225
+ s->buf = nullptr;
226
+ s->meta = nullptr;
227
+ s->dev.clear();
228
+ }
229
+
230
+ kv_snapshot* save() {
231
+ kv_snapshot* s = nullptr;
232
+ for (kv_snapshot* slot : slots) {
233
+ if (!slot->in_use) {
234
+ s = slot;
235
+ break;
236
+ }
237
+ }
238
+ const bool fresh = (s == nullptr);
239
+ if (fresh) {
240
+ s = new kv_snapshot();
241
+ slots.push_back(s);
242
+ }
243
+ s->in_use = true;
244
+
245
+ // Decide the storage mode the first time we see a real copy: try a
246
+ // device-to-device blit; if the backend can't do it, fall back to a
247
+ // host snapshot for this and every subsequent slot.
248
+ if (mode == UNKNOWN) {
249
+ alloc_device(s);
250
+ bool ok = true;
251
+ for (size_t i = 0; i < live.size(); i++)
252
+ ok = ok && ggml_backend_buffer_copy_tensor(live[i], s->dev[i]);
253
+ if (ok) {
254
+ mode = DEVICE;
255
+ return s;
256
+ }
257
+ mode = HOST;
258
+ free_device(s);
259
+ }
260
+
261
+ if (mode == DEVICE) {
262
+ if (fresh)
263
+ alloc_device(s);
264
+ for (size_t i = 0; i < live.size(); i++)
265
+ ggml_backend_buffer_copy_tensor(live[i], s->dev[i]);
266
+ } else {
267
+ s->host.resize(live.size());
268
+ for (size_t i = 0; i < live.size(); i++) {
269
+ s->host[i].resize(ggml_nbytes(live[i]));
270
+ ggml_backend_tensor_get(live[i], s->host[i].data(), 0, s->host[i].size());
271
+ }
272
+ }
273
+ return s;
274
+ }
275
+
276
+ void restore(const kv_snapshot* s) {
277
+ if (mode == DEVICE) {
278
+ for (size_t i = 0; i < live.size(); i++)
279
+ ggml_backend_buffer_copy_tensor(s->dev[i], live[i]);
280
+ } else {
281
+ for (size_t i = 0; i < live.size(); i++)
282
+ ggml_backend_tensor_set(live[i], s->host[i].data(), 0, s->host[i].size());
283
+ }
284
+ }
285
+
286
+ void release(kv_snapshot* s) {
287
+ if (s)
288
+ s->in_use = false;
289
+ }
290
+ };
291
+
292
+ // PLAN #73 — quant-safe per-step KV cache write. Replaces the inline
293
+ // `ggml_cpy(K_perm, ggml_view_4d(kv_k, …))` pattern that several
294
+ // backends use (canary, cohere, kyutai_stt, …). Works for any cache
295
+ // dtype: F16 / F32 take the strided-view + ggml_cpy path (preserved
296
+ // for bit-exactness with the legacy code), Q8_0 / Q4_0 take a
297
+ // ggml_set_rows path keyed by a runtime indices tensor.
298
+ //
299
+ // Caller responsibilities:
300
+ // * `K_perm` / `V_perm` shape: [head_dim, T, n_kv_heads], i.e.
301
+ // already permuted from the QKV layout into cache layout.
302
+ // * `kv_k` / `kv_v` shape: [head_dim, max_ctx, n_kv_heads, n_layers].
303
+ // * `indices` is an I32 tensor of length T containing the cache
304
+ // positions to write to (typically `[n_past, n_past+T)` — the
305
+ // same tensor used as RoPE positions). Required for quant cache;
306
+ // may be nullptr for F16/F32 (the fast static-offset path will
307
+ // be used).
308
+ //
309
+ // Adds the K and V writes to `gf` via ggml_build_forward_expand. The
310
+ // returned bool is informational: true if the quant path was taken,
311
+ // false for the F16 fast path.
312
+ inline bool kv_cache_write(ggml_context* ctx, ggml_cgraph* gf, ggml_tensor* K_perm, ggml_tensor* V_perm,
313
+ ggml_tensor* kv_k, ggml_tensor* kv_v, int layer_idx, int n_past, int T,
314
+ ggml_tensor* indices) {
315
+ const bool quant_k = ggml_is_quantized(kv_k->type);
316
+ const bool quant_v = ggml_is_quantized(kv_v->type);
317
+ const bool quant_any = quant_k || quant_v;
318
+
319
+ if (!quant_any) {
320
+ // Legacy F16/F32 path: strided view + ggml_cpy. Bit-identical
321
+ // to the inline code that callers used to have.
322
+ const int hd = (int)kv_k->ne[0];
323
+ const int nh = (int)kv_k->ne[2];
324
+ ggml_tensor* k_dst = ggml_view_4d(ctx, kv_k, hd, T, nh, 1, kv_k->nb[1], kv_k->nb[2], kv_k->nb[3],
325
+ (size_t)layer_idx * kv_k->nb[3] + (size_t)n_past * kv_k->nb[1]);
326
+ ggml_build_forward_expand(gf, ggml_cpy(ctx, K_perm, k_dst));
327
+ ggml_tensor* v_dst = ggml_view_4d(ctx, kv_v, hd, T, nh, 1, kv_v->nb[1], kv_v->nb[2], kv_v->nb[3],
328
+ (size_t)layer_idx * kv_v->nb[3] + (size_t)n_past * kv_v->nb[1]);
329
+ ggml_build_forward_expand(gf, ggml_cpy(ctx, V_perm, v_dst));
330
+ return false;
331
+ }
332
+
333
+ // Quant path: ggml_set_rows expects rows along ne[0] of the dst.
334
+ // The cache layout is [hd, max_ctx, n_kv, n_layers]; rows are along
335
+ // max_ctx (dim 1). So we view the layer slice as [hd, max_ctx, nh],
336
+ // then ggml_set_rows writes T rows at positions given by `indices`.
337
+ GGML_ASSERT(indices && "kv_cache_write: quant cache requires indices tensor");
338
+ const int hd = (int)kv_k->ne[0];
339
+ const int nh = (int)kv_k->ne[2];
340
+ ggml_tensor* k_layer =
341
+ ggml_view_3d(ctx, kv_k, hd, (int)kv_k->ne[1], nh, kv_k->nb[1], kv_k->nb[2], (size_t)layer_idx * kv_k->nb[3]);
342
+ ggml_tensor* v_layer =
343
+ ggml_view_3d(ctx, kv_v, hd, (int)kv_v->ne[1], nh, kv_v->nb[1], kv_v->nb[2], (size_t)layer_idx * kv_v->nb[3]);
344
+ ggml_build_forward_expand(gf, ggml_set_rows(ctx, k_layer, K_perm, indices));
345
+ ggml_build_forward_expand(gf, ggml_set_rows(ctx, v_layer, V_perm, indices));
346
+ (void)n_past; // unused on quant path; indices carries position info
347
+ return true;
348
+ }
349
+
350
+ // Parameters that differ from call to call. Everything here is a plain
351
+ // value type so the compiler can inline the caller's constants into the
352
+ // helper's ggml_* op chain.
353
+ struct LlamaSelfAttnParams {
354
+ int n_heads; // query heads (== n_kv_heads for MHA)
355
+ int n_kv_heads; // key/value heads; with GQA, n_heads / n_kv_heads > 1
356
+ int head_dim; // per-head dimension
357
+ int n_kv_grp; // == n_heads / n_kv_heads (caller precomputes)
358
+ int n_ctx_orig; // rope n_ctx_orig (usually llm_max_pos)
359
+ float rope_theta;
360
+ float attn_scale; // usually 1 / sqrt(head_dim); pass explicitly
361
+ };
362
+
363
+ // Llama / Mistral-style self-attention with optional GQA, NEOX RoPE, and
364
+ // ggml_flash_attn_ext.
365
+ //
366
+ // Inputs:
367
+ // x [d_model, T] — RMSNormed input for this layer (the
368
+ // caller does the norm + the learned
369
+ // scale multiplication)
370
+ // q_w,k_w,v_w Q/K/V weight tensors (no biases in the LLM case)
371
+ // o_w output projection weight
372
+ // positions [T] — RoPE position ids
373
+ // mask [ctx, T] F16 — causal / sliding-window mask or nullptr
374
+ // for no-mask (voxtral audio case)
375
+ //
376
+ // Output:
377
+ // attn [d_model, T] — post-output-projection tensor. The
378
+ // caller adds it to the residual.
379
+ // Fused QKV variant: if qkv_w is non-null, do a single matmul and split.
380
+ // qkv_w shape: [d_model, n_q*hd + 2*n_kv*hd] — concatenated Q, K, V weights.
381
+ // Falls back to 3 separate matmuls when qkv_w is null (backward compat).
382
+ static inline ggml_tensor* llama_self_attn(ggml_context* ctx, ggml_tensor* x, ggml_tensor* q_w, ggml_tensor* k_w,
383
+ ggml_tensor* v_w, ggml_tensor* o_w, ggml_tensor* positions,
384
+ ggml_tensor* mask, const LlamaSelfAttnParams& p,
385
+ ggml_tensor* qkv_w = nullptr) {
386
+ const int hd = p.head_dim;
387
+ const int n_q = p.n_heads;
388
+ const int n_kv = p.n_kv_heads;
389
+ const int n_ctx = p.n_ctx_orig;
390
+ const int grp = p.n_kv_grp;
391
+
392
+ ggml_tensor* Q;
393
+ ggml_tensor* K;
394
+ ggml_tensor* V;
395
+
396
+ if (qkv_w) {
397
+ // Single fused matmul: one mul_mat instead of three.
398
+ // qkv_w: [d_model, q_dim + k_dim + v_dim]
399
+ // Output: [q_dim + k_dim + v_dim, T]
400
+ ggml_tensor* qkv = ggml_mul_mat(ctx, qkv_w, x);
401
+ const int q_dim = n_q * hd;
402
+ const int kv_dim = n_kv * hd;
403
+ const int T = (int)x->ne[1];
404
+ // Split along ne[0]: Q=[0..q_dim), K=[q_dim..q_dim+kv_dim), V=[q_dim+kv_dim..)
405
+ Q = ggml_view_2d(ctx, qkv, q_dim, T, qkv->nb[1], 0);
406
+ K = ggml_view_2d(ctx, qkv, kv_dim, T, qkv->nb[1], q_dim * ggml_type_size(qkv->type));
407
+ V = ggml_view_2d(ctx, qkv, kv_dim, T, qkv->nb[1], (q_dim + kv_dim) * ggml_type_size(qkv->type));
408
+ } else {
409
+ // Standard 3 separate matmuls (backward compat).
410
+ Q = ggml_mul_mat(ctx, q_w, x);
411
+ K = ggml_mul_mat(ctx, k_w, x);
412
+ V = ggml_mul_mat(ctx, v_w, x);
413
+ }
414
+
415
+ // T is the time dim of x; ggml stores [d_model, T] as ne = [d_model, T].
416
+ const int T = (int)x->ne[1];
417
+
418
+ Q = ggml_reshape_3d(ctx, Q, hd, n_q, T);
419
+ K = ggml_reshape_3d(ctx, K, hd, n_kv, T);
420
+ V = ggml_reshape_3d(ctx, V, hd, n_kv, T);
421
+
422
+ // NEOX RoPE on Q and K. Same args as the original inline code in
423
+ // voxtral / voxtral4b / qwen3 / granite LLM blocks.
424
+ Q = ggml_rope_ext(ctx, Q, positions, /*freq_factors*/ nullptr, hd, GGML_ROPE_TYPE_NEOX, n_ctx, p.rope_theta,
425
+ /*freq_scale*/ 1.0f, /*ext_factor*/ 0.0f,
426
+ /*attn_factor*/ 1.0f, /*beta_fast*/ 32.0f, /*beta_slow*/ 1.0f);
427
+ K = ggml_rope_ext(ctx, K, positions, nullptr, hd, GGML_ROPE_TYPE_NEOX, n_ctx, p.rope_theta, 1.0f, 0.0f, 1.0f, 32.0f,
428
+ 1.0f);
429
+
430
+ // GQA expansion: replicate each KV head `grp` times along a new dim so
431
+ // K/V have n_heads rows instead of n_kv_heads, then flatten back.
432
+ if (grp > 1) {
433
+ ggml_tensor* K4 = ggml_reshape_4d(ctx, K, hd, 1, n_kv, T);
434
+ ggml_tensor* V4 = ggml_reshape_4d(ctx, V, hd, 1, n_kv, T);
435
+ K4 = ggml_repeat_4d(ctx, K4, hd, grp, n_kv, T);
436
+ V4 = ggml_repeat_4d(ctx, V4, hd, grp, n_kv, T);
437
+ K = ggml_cont(ctx, ggml_reshape_3d(ctx, K4, hd, n_q, T));
438
+ V = ggml_cont(ctx, ggml_reshape_3d(ctx, V4, hd, n_q, T));
439
+ }
440
+
441
+ // Permute to flash-attention layout: (head_dim, T, n_heads).
442
+ Q = ggml_cont(ctx, ggml_permute(ctx, Q, 0, 2, 1, 3));
443
+ K = ggml_cont(ctx, ggml_permute(ctx, K, 0, 2, 1, 3));
444
+ V = ggml_cont(ctx, ggml_permute(ctx, V, 0, 2, 1, 3));
445
+
446
+ // Flash attention. Output shape = (head_dim, n_heads, T, 1).
447
+ ggml_tensor* attn =
448
+ ggml_flash_attn_ext(ctx, Q, K, V, mask, p.attn_scale, /*max_bias*/ 0.0f, /*logit_softcap*/ 0.0f);
449
+
450
+ // Back to (d_model, T).
451
+ attn = ggml_reshape_2d(ctx, attn, hd * n_q, T);
452
+
453
+ // Output projection (no bias).
454
+ return ggml_mul_mat(ctx, o_w, attn);
455
+ }
456
+
457
+ // ---------------------------------------------------------------------------
458
+ // Encoder self-attention — biased Q/K/V/O projections, optional RoPE.
459
+ //
460
+ // Covers architectures like the Whisper audio encoder (voxtral 3B) and the
461
+ // causal RoPE+SwiGLU audio encoder (voxtral4b). Key differences from the
462
+ // LLM llama_self_attn():
463
+ // - Q, K, V, O projections can each have an optional bias (nullptr = skip)
464
+ // - RoPE is optional: pass positions == nullptr to skip
465
+ // - GQA expansion is included for architectures that use it
466
+ //
467
+ // The caller still handles the pre-attention norm and post-attention
468
+ // residual add.
469
+ // ---------------------------------------------------------------------------
470
+
471
+ struct EncoderSelfAttnParams {
472
+ int n_heads; // query heads
473
+ int n_kv_heads; // key/value heads (usually == n_heads for encoders)
474
+ int head_dim;
475
+ int n_kv_grp; // n_heads / n_kv_heads (1 for MHA)
476
+ float attn_scale; // usually 1/sqrt(head_dim)
477
+ // RoPE params (only used when positions != nullptr)
478
+ int n_ctx_orig;
479
+ float rope_theta;
480
+ // When true (default), wrap ggml_permute() in ggml_cont() before
481
+ // flash_attn_ext. voxtral 3B needs this; voxtral4b does not (its
482
+ // encoder was written without cont and changing it would alter the
483
+ // ggml graph structure). Set to false for voxtral4b compatibility.
484
+ bool permute_cont = true;
485
+ };
486
+
487
+ static inline ggml_tensor* encoder_self_attn(ggml_context* ctx, ggml_tensor* x, ggml_tensor* q_w, ggml_tensor* q_b,
488
+ ggml_tensor* k_w, ggml_tensor* k_b, ggml_tensor* v_w, ggml_tensor* v_b,
489
+ ggml_tensor* o_w, ggml_tensor* o_b, ggml_tensor* positions,
490
+ ggml_tensor* mask, const EncoderSelfAttnParams& p) {
491
+ const int hd = p.head_dim;
492
+ const int n_q = p.n_heads;
493
+ const int n_kv = p.n_kv_heads;
494
+ const int grp = p.n_kv_grp;
495
+ const int T = (int)x->ne[1];
496
+
497
+ // Q/K/V projections with optional biases.
498
+ ggml_tensor* Q = ggml_mul_mat(ctx, q_w, x);
499
+ if (q_b)
500
+ Q = ggml_add(ctx, Q, q_b);
501
+ ggml_tensor* K = ggml_mul_mat(ctx, k_w, x);
502
+ if (k_b)
503
+ K = ggml_add(ctx, K, k_b);
504
+ ggml_tensor* V = ggml_mul_mat(ctx, v_w, x);
505
+ if (v_b)
506
+ V = ggml_add(ctx, V, v_b);
507
+
508
+ Q = ggml_reshape_3d(ctx, Q, hd, n_q, T);
509
+ K = ggml_reshape_3d(ctx, K, hd, n_kv, T);
510
+ V = ggml_reshape_3d(ctx, V, hd, n_kv, T);
511
+
512
+ // Optional RoPE (skip for encoders with learned positional embeddings).
513
+ if (positions) {
514
+ Q = ggml_rope_ext(ctx, Q, positions, nullptr, hd, GGML_ROPE_TYPE_NEOX, p.n_ctx_orig, p.rope_theta, 1.0f, 0.0f,
515
+ 1.0f, 0.0f, 0.0f);
516
+ K = ggml_rope_ext(ctx, K, positions, nullptr, hd, GGML_ROPE_TYPE_NEOX, p.n_ctx_orig, p.rope_theta, 1.0f, 0.0f,
517
+ 1.0f, 0.0f, 0.0f);
518
+ }
519
+
520
+ // GQA expansion (when n_kv_heads < n_heads).
521
+ if (grp > 1) {
522
+ ggml_tensor* K4 = ggml_reshape_4d(ctx, K, hd, 1, n_kv, T);
523
+ ggml_tensor* V4 = ggml_reshape_4d(ctx, V, hd, 1, n_kv, T);
524
+ K4 = ggml_repeat_4d(ctx, K4, hd, grp, n_kv, T);
525
+ V4 = ggml_repeat_4d(ctx, V4, hd, grp, n_kv, T);
526
+ K = ggml_cont(ctx, ggml_reshape_3d(ctx, K4, hd, n_q, T));
527
+ V = ggml_cont(ctx, ggml_reshape_3d(ctx, V4, hd, n_q, T));
528
+ }
529
+
530
+ // Permute to flash-attention layout: (head_dim, T, n_heads).
531
+ Q = ggml_permute(ctx, Q, 0, 2, 1, 3);
532
+ K = ggml_permute(ctx, K, 0, 2, 1, 3);
533
+ V = ggml_permute(ctx, V, 0, 2, 1, 3);
534
+ if (p.permute_cont) {
535
+ Q = ggml_cont(ctx, Q);
536
+ K = ggml_cont(ctx, K);
537
+ V = ggml_cont(ctx, V);
538
+ }
539
+
540
+ // Flash attention (bidirectional if mask==nullptr, causal/SWA otherwise).
541
+ ggml_tensor* attn = ggml_flash_attn_ext(ctx, Q, K, V, mask, p.attn_scale, 0.0f, 0.0f);
542
+ attn = ggml_reshape_2d(ctx, attn, hd * n_q, T);
543
+
544
+ // Output projection with optional bias.
545
+ attn = ggml_mul_mat(ctx, o_w, attn);
546
+ if (o_b)
547
+ attn = ggml_add(ctx, attn, o_b);
548
+ return attn;
549
+ }
550
+
551
+ // ---------------------------------------------------------------------------
552
+ // KV-cached self-attention for the LLM decoders (qwen3-asr, voxtral,
553
+ // voxtral4b, granite-speech).
554
+ //
555
+ // Replaces the Q/K/V-proj + [optional Q/K norm] + RoPE + persistent-KV-cache
556
+ // write + cache read + [manual GQA expansion] + flash-attn-ext + output-proj
557
+ // block that each of the four models has its own copy of. The helper does
558
+ // NOT do the pre-attention RMSNorm or the post-attention residual add —
559
+ // callers do those inline so the helper stays focused on the attention
560
+ // block proper (which is where the per-model knobs live).
561
+ //
562
+ // KV cache layout convention: ne = (head_dim, max_ctx, n_kv_heads, n_layers).
563
+ // Every consumer already stores its cache this way, which is why this helper
564
+ // is shareable in the first place.
565
+ // ---------------------------------------------------------------------------
566
+
567
+ // GQA expansion strategy.
568
+ //
569
+ // qwen3 and voxtral (3b) manually expand each KV head into `n_kv_grp` query
570
+ // heads via reshape_4d -> repeat_4d -> reshape_3d, and then wrap the final
571
+ // reshape in ggml_cont. voxtral4b also manually expands, but skips the final
572
+ // ggml_cont. granite skips the manual expansion entirely and relies on
573
+ // ggml_flash_attn_ext's native GQA support (pass Kfull/Vfull with n_kv heads
574
+ // directly, flash-attn handles the repeat internally).
575
+ //
576
+ // Each mode produces slightly different graph ops. Picking the wrong one
577
+ // breaks bit-identity on the regression sweep, so this is an explicit knob.
578
+ enum GqaMode {
579
+ GQA_MANUAL_CONT = 0, // reshape_4d / repeat_4d / reshape_3d + ggml_cont
580
+ GQA_MANUAL_NOCONT = 1, // reshape_4d / repeat_4d / reshape_3d, no final cont
581
+ GQA_NATIVE = 2, // no expansion; flash_attn_ext handles GQA itself
582
+ };
583
+
584
+ struct KvSelfAttnParams {
585
+ int n_heads; // query heads
586
+ int n_kv_heads; // key/value heads (== n_heads for MHA)
587
+ int head_dim; // per-head dimension
588
+ int n_kv_grp; // n_heads / n_kv_heads (caller precomputes)
589
+ int n_ctx_orig; // RoPE n_ctx_orig (some models 0, some llm_max_pos)
590
+ float rope_theta;
591
+ float rope_beta_fast; // RoPE extrapolation beta_fast (qwen3/voxtral: 32, others: 0)
592
+ float rope_beta_slow; // RoPE extrapolation beta_slow (qwen3/voxtral: 1, others: 0)
593
+ float attn_scale; // usually 1/sqrt(head_dim); granite uses µP scale
594
+ float qk_norm_eps; // RMSNorm epsilon for optional Q/K norm (qwen3); unused otherwise
595
+ GqaMode gqa_mode;
596
+ int rope_type = GGML_ROPE_TYPE_NEOX; // NEOX for most models, NORMAL for fairseq2/omniasr
597
+ // Partial-rotary RoPE: number of head-dim entries to rotate. The
598
+ // remaining `head_dim - n_rot` entries pass through unchanged. Used
599
+ // by Gemma4 full-attention layers (`partial_rotary_factor=0.25`,
600
+ // i.e. n_rot = head_dim/4) and Phi-3-style models. Default 0 means
601
+ // rotate the entire head_dim — matches every existing caller.
602
+ int n_rot = 0;
603
+ // Apply RMSNorm-without-learned-weight to V before the cache write.
604
+ // Gemma4's `v_norm` is constructed with `with_scale=False`, i.e.
605
+ // RMSNorm with no learned scale tensor — there is no weight to load,
606
+ // we just need to run the normalisation op on V. Default false → no
607
+ // op, matches every other consumer.
608
+ bool v_rms_norm = false;
609
+ // Optional per-dimension RoPE frequency factors (e.g. Llama 3 scaling).
610
+ ggml_tensor* rope_freq_factors = nullptr;
611
+ // Force the cached K/V to be cast to F32 before the GQA repeat/expansion,
612
+ // exactly like the global CRISPASR_KV_READ_F32 knob but per-call. Needed on
613
+ // Vulkan, where REPEAT has no f16→f16 pipeline (the GQA head-expansion
614
+ // `ggml_repeat_4d` on an F16 cache aborts with "Missing op: REPEAT for f16
615
+ // to f16"; #192). Casting to F32 first lowers it to a supported F32 REPEAT.
616
+ // Default false → legacy F16 fast path on Metal/CPU. Caller sets it true only
617
+ // for the Vulkan-native graph.
618
+ bool force_kv_read_f32 = false;
619
+ };
620
+
621
+ // KV-cached self-attention. Writes the new K/V into the persistent cache
622
+ // slice at [n_past, n_past + T) for layer `il`, then reads the full history
623
+ // [0, n_past + T) back out and runs flash-attention against Q.
624
+ //
625
+ // Inputs:
626
+ // x [d_model, T] — pre-attention normalized activations
627
+ // q_w,k_w,v_w projection weights (no biases for the Llama case)
628
+ // o_w output projection weight (no bias)
629
+ // q_norm_w [head_dim] Q-norm weight, or nullptr to skip (non-qwen3)
630
+ // k_norm_w [head_dim] K-norm weight, or nullptr to skip
631
+ // positions [T] I32 — absolute positions n_past, n_past+1, ...
632
+ // causal_mask [Lk, T] F16 or nullptr (decode path uses nullptr)
633
+ // kv_k, kv_v persistent cache, ne = (hd, max_ctx, n_kv, n_layers)
634
+ // il layer index into the cache's trailing dim
635
+ // n_past number of tokens already in the cache
636
+ //
637
+ // Output:
638
+ // attn [d_model, T] — post-output-projection tensor. Caller adds
639
+ // it to the residual.
640
+ // fixed_kv_len > 0: override the KV-read length (Lk) to a constant, keeping
641
+ // topology identical across calls with different n_past. Unwritten slots are
642
+ // masked to -inf by causal_mask so they never affect output.
643
+ //
644
+ // kv_indices != nullptr: scatter the new K/V into the cache via ggml_set_rows
645
+ // keyed by the runtime indices tensor instead of the default static-offset
646
+ // ggml_cpy. Required for graph-cache reuse across calls at different n_past:
647
+ // the static-offset path bakes n_past into the graph as a literal byte offset,
648
+ // so a cached graph built at n_past=A would write to slot A even when reused at
649
+ // n_past=B; the dynamic-index path makes the destination a runtime input. Pass
650
+ // the same `positions` tensor that's already populated with [n_past, n_past+T)
651
+ // for RoPE — the indices required for set_rows are bit-equivalent.
652
+ //
653
+ // q_b/k_b/v_b/o_b: optional projection biases. Qwen2 (mimo-asr LM) sets
654
+ // `attention_bias=true` and ships per-layer Q/K/V biases; Qwen3 / Llama /
655
+ // granite / voxtral / gemma4 do not. Default nullptr keeps the graph
656
+ // bit-identical for those callers.
657
+ //
658
+ // qkv_b: optional fused-bias for the Qwen2 fused-QKV path. When qkv_w is
659
+ // non-null and the GGUF stores a fused `attn.qkv.bias` (length q_dim +
660
+ // 2*kv_dim), pass it here — it's added to the fused matmul output before
661
+ // the Q/K/V split. q_b/k_b/v_b should be nullptr in that case (the
662
+ // caller emits one fused tensor instead of three). Algebraically
663
+ // identical to per-projection bias adds; one ggml_add op instead of
664
+ // three.
665
+ static inline ggml_tensor* kv_self_attn(ggml_context* ctx0, ggml_cgraph* gf, ggml_tensor* x, ggml_tensor* q_w,
666
+ ggml_tensor* k_w, ggml_tensor* v_w, ggml_tensor* o_w, ggml_tensor* q_norm_w,
667
+ ggml_tensor* k_norm_w, ggml_tensor* positions, ggml_tensor* causal_mask,
668
+ ggml_tensor* kv_k, ggml_tensor* kv_v, int il, int n_past,
669
+ const KvSelfAttnParams& p, ggml_tensor* qkv_w = nullptr, int fixed_kv_len = 0,
670
+ ggml_tensor* kv_indices = nullptr, ggml_tensor* q_b = nullptr,
671
+ ggml_tensor* k_b = nullptr, ggml_tensor* v_b = nullptr,
672
+ ggml_tensor* o_b = nullptr, ggml_tensor* qkv_b = nullptr) {
673
+ const int hd = p.head_dim;
674
+ const int n_q = p.n_heads;
675
+ const int n_kv = p.n_kv_heads;
676
+ const int grp = p.n_kv_grp;
677
+ const int T = (int)x->ne[1];
678
+ const int Lk = fixed_kv_len > 0 ? fixed_kv_len : (n_past + T);
679
+
680
+ // ---- Q/K/V projections ----
681
+ ggml_tensor* Q;
682
+ ggml_tensor* K;
683
+ ggml_tensor* V;
684
+
685
+ if (qkv_w) {
686
+ // Fused: one matmul, then split output. The 2D views below are
687
+ // strided (each T-row leaves gaps for the other Q/K/V), so for T>1
688
+ // the downstream ggml_reshape_3d would fail its contiguity assert.
689
+ // ggml_cont materialises each into its own contiguous buffer; for
690
+ // T=1 the cont is a no-op (single row is already contiguous).
691
+ ggml_tensor* qkv = ggml_mul_mat(ctx0, qkv_w, x);
692
+ if (qkv_b) {
693
+ // Fused bias (1D, length q_dim + 2*kv_dim) added before the
694
+ // split so each Q/K/V chunk picks up its own slice. One add
695
+ // instead of three; algebraically identical to per-proj adds.
696
+ qkv = ggml_add(ctx0, qkv, qkv_b);
697
+ }
698
+ const int q_dim = n_q * hd;
699
+ const int kv_dim = n_kv * hd;
700
+ const size_t ts = ggml_type_size(qkv->type);
701
+ Q = ggml_view_2d(ctx0, qkv, q_dim, T, qkv->nb[1], 0);
702
+ K = ggml_view_2d(ctx0, qkv, kv_dim, T, qkv->nb[1], q_dim * ts);
703
+ V = ggml_view_2d(ctx0, qkv, kv_dim, T, qkv->nb[1], (q_dim + kv_dim) * ts);
704
+ if (T > 1) {
705
+ Q = ggml_cont(ctx0, Q);
706
+ K = ggml_cont(ctx0, K);
707
+ V = ggml_cont(ctx0, V);
708
+ }
709
+ } else {
710
+ Q = ggml_mul_mat(ctx0, q_w, x);
711
+ K = ggml_mul_mat(ctx0, k_w, x);
712
+ V = ggml_mul_mat(ctx0, v_w, x);
713
+ }
714
+
715
+ // Optional Q/K/V projection biases (Qwen2). Applied before reshape so
716
+ // the bias broadcasts along the time dim; q_b/k_b/v_b are 1D.
717
+ if (q_b)
718
+ Q = ggml_add(ctx0, Q, q_b);
719
+ if (k_b)
720
+ K = ggml_add(ctx0, K, k_b);
721
+ if (v_b)
722
+ V = ggml_add(ctx0, V, v_b);
723
+
724
+ Q = ggml_reshape_3d(ctx0, Q, hd, n_q, T);
725
+ K = ggml_reshape_3d(ctx0, K, hd, n_kv, T);
726
+ V = ggml_reshape_3d(ctx0, V, hd, n_kv, T);
727
+
728
+ // ---- Optional Q/K RMSNorm (qwen3) ----
729
+ if (q_norm_w) {
730
+ Q = ggml_rms_norm(ctx0, Q, p.qk_norm_eps);
731
+ Q = ggml_mul(ctx0, Q, q_norm_w);
732
+ }
733
+ if (k_norm_w) {
734
+ K = ggml_rms_norm(ctx0, K, p.qk_norm_eps);
735
+ K = ggml_mul(ctx0, K, k_norm_w);
736
+ }
737
+
738
+ // ---- Optional V RMSNorm without learned weight (gemma4) ----
739
+ // gemma4's v_norm is `Gemma4RMSNorm(head_dim, with_scale=False)`,
740
+ // so there is no weight tensor — we just normalise V along its
741
+ // last (head_dim) axis, exactly as ggml_rms_norm does.
742
+ if (p.v_rms_norm) {
743
+ V = ggml_rms_norm(ctx0, V, p.qk_norm_eps);
744
+ }
745
+
746
+ // ---- RoPE (NEOX for most models, NORMAL for fairseq2/omniasr) ----
747
+ // p.n_rot > 0 selects partial-rotary mode (only the first n_rot
748
+ // entries of each head are rotated; the rest pass through). 0
749
+ // means rotate the entire head_dim, which matches every existing
750
+ // caller's prior behaviour.
751
+ // Skip RoPE when rope_theta <= 0 (e.g. VoxCPM2 RALM has no positional
752
+ // encoding). powf(0, -2/d) = inf → NaN (#164).
753
+ if (p.rope_theta > 0.0f) {
754
+ const int n_rot = p.n_rot > 0 ? p.n_rot : hd;
755
+ Q = ggml_rope_ext(ctx0, Q, positions, p.rope_freq_factors, n_rot, p.rope_type, p.n_ctx_orig, p.rope_theta,
756
+ /*freq_scale*/ 1.0f, /*ext_factor*/ 0.0f,
757
+ /*attn_factor*/ 1.0f, p.rope_beta_fast, p.rope_beta_slow);
758
+ K = ggml_rope_ext(ctx0, K, positions, p.rope_freq_factors, n_rot, p.rope_type, p.n_ctx_orig, p.rope_theta, 1.0f,
759
+ 0.0f, 1.0f, p.rope_beta_fast, p.rope_beta_slow);
760
+ }
761
+
762
+ // ---- Permute new K/V to (hd, T, n_kv) for cache write ----
763
+ ggml_tensor* K_new_perm = ggml_permute(ctx0, K, 0, 2, 1, 3);
764
+ ggml_tensor* V_new_perm = ggml_permute(ctx0, V, 0, 2, 1, 3);
765
+
766
+ // ---- Write into the persistent KV cache at [n_past, n_past+T) ----
767
+ // The default ggml_cpy(F32, slice-of-cache) path requires the
768
+ // destination to be contiguous when the source/dst types differ
769
+ // (CPU backend's `dup_to_q` aborts otherwise, and Metal's CPY also
770
+ // skips non-contiguous quantised dst). For a quantised cache —
771
+ // PLAN #60e CRISPASR_KV_QUANT={q8_0,q4_0} — we instead always use
772
+ // `ggml_set_rows` with a per-token row-index tensor, which both
773
+ // backends accept for F32→Q* directly. When the caller already
774
+ // supplies `kv_indices` (cached-graph reuse path) we honour that;
775
+ // otherwise we synthesise the indices from `positions` (which is
776
+ // [n_past..n_past+T) by construction for RoPE — exactly the row
777
+ // ids set_rows needs).
778
+ const bool quant_kv = ggml_is_quantized(kv_k->type);
779
+ // When the write goes through ggml_set_rows we keep the result tensors so
780
+ // the read view below can be based on them (see the read path) — that gives
781
+ // the scheduler an explicit write→read dependency edge. Without it the read
782
+ // views the bare cache and the set_rows nodes become graph dead-ends, so on
783
+ // Metal the KV read races the in-place set_rows write and reads stale/garbage
784
+ // (the Lk-bucket single-step decode in orpheus/parler hits this). Mirrors
785
+ // parler_tts's bucket read path.
786
+ ggml_tensor* sr_k = nullptr;
787
+ ggml_tensor* sr_v = nullptr;
788
+ if (kv_indices || quant_kv) {
789
+ ggml_tensor* eff_idx = kv_indices ? kv_indices : positions;
790
+ ggml_tensor* k_layer =
791
+ ggml_view_3d(ctx0, kv_k, hd, kv_k->ne[1], n_kv, kv_k->nb[1], kv_k->nb[2], (size_t)il * kv_k->nb[3]);
792
+ ggml_tensor* v_layer =
793
+ ggml_view_3d(ctx0, kv_v, hd, kv_v->ne[1], n_kv, kv_v->nb[1], kv_v->nb[2], (size_t)il * kv_v->nb[3]);
794
+ sr_k = ggml_set_rows(ctx0, k_layer, K_new_perm, eff_idx);
795
+ sr_v = ggml_set_rows(ctx0, v_layer, V_new_perm, eff_idx);
796
+ ggml_build_forward_expand(gf, sr_k);
797
+ ggml_build_forward_expand(gf, sr_v);
798
+ } else {
799
+ ggml_tensor* k_view = ggml_view_4d(ctx0, kv_k, hd, T, n_kv, 1, kv_k->nb[1], kv_k->nb[2], kv_k->nb[3],
800
+ (size_t)il * kv_k->nb[3] + (size_t)n_past * kv_k->nb[1]);
801
+ ggml_tensor* v_view = ggml_view_4d(ctx0, kv_v, hd, T, n_kv, 1, kv_v->nb[1], kv_v->nb[2], kv_v->nb[3],
802
+ (size_t)il * kv_v->nb[3] + (size_t)n_past * kv_v->nb[1]);
803
+ ggml_build_forward_expand(gf, ggml_cpy(ctx0, K_new_perm, k_view));
804
+ ggml_build_forward_expand(gf, ggml_cpy(ctx0, V_new_perm, v_view));
805
+ }
806
+
807
+ // ---- Read full K/V history from cache ----
808
+ // Cache may be allocated as F16 (default) or as a quantized type
809
+ // (Q8_0 / Q4_0 / etc., per CRISPASR_KV_QUANT — PLAN #60e). For the
810
+ // default F16 path the strided per-layer view becomes a contiguous
811
+ // F16 tensor via ggml_cont (a CPY F16→F16 op). For a quantized
812
+ // cache the equivalent CPY (Q8_0→Q8_0 etc.) isn't supported by
813
+ // Metal, so we use ggml_cast(...,F32) which lowers to CPY Q*→F32
814
+ // — supported on both Metal and the CPU backend (the CPU `dup`
815
+ // dispatch only implements `Q*→F32` for the dequant-on-read path,
816
+ // not `Q*→F16`; so F32 is the only safe target if the scheduler
817
+ // splits the op). The cache *storage* still uses ~half the bytes
818
+ // (for Q8_0); reads pay one dequant pass per layer per step.
819
+ // Flash-attn-ext on Metal accepts F32 K/V natively (and F16 / quant
820
+ // too) but mixing types across K and V isn't supported, so both
821
+ // sides cast to the same dtype.
822
+ // Read from the set_rows RESULT when we wrote via set_rows (sr_k/sr_v are
823
+ // in-place views of the layer slice, so offset 0 == this layer's data);
824
+ // otherwise read the bare cache at the per-layer offset (ggml_cpy path).
825
+ ggml_tensor* k_read_src = sr_k ? sr_k : kv_k;
826
+ ggml_tensor* v_read_src = sr_v ? sr_v : kv_v;
827
+ const size_t k_read_off = sr_k ? 0 : (size_t)il * kv_k->nb[3];
828
+ const size_t v_read_off = sr_v ? 0 : (size_t)il * kv_v->nb[3];
829
+ ggml_tensor* k_layer_view = ggml_view_3d(ctx0, k_read_src, hd, Lk, n_kv, kv_k->nb[1], kv_k->nb[2], k_read_off);
830
+ ggml_tensor* v_layer_view = ggml_view_3d(ctx0, v_read_src, hd, Lk, n_kv, kv_v->nb[1], kv_v->nb[2], v_read_off);
831
+ // CRISPASR_KV_READ_F32=1 forces the cache read to dequantise (or
832
+ // upcast F16) to F32 before flash_attn. Useful when F16 attention
833
+ // accumulator drift on Metal sends the sampler off the rails for
834
+ // sensitive models (chatterbox T3 — see LEARNINGS §82). Default
835
+ // off to preserve legacy bit-exactness with the F16 fast path.
836
+ static const bool s_kv_read_f32 = []() {
837
+ const char* s = std::getenv("CRISPASR_KV_READ_F32");
838
+ return s && *s && std::strcmp(s, "0") != 0;
839
+ }();
840
+ // Vulkan has no f16→f16 REPEAT pipeline, so the GQA head-expansion
841
+ // (ggml_repeat_4d below) on an F16 cache aborts with "Missing op: REPEAT
842
+ // for f16 to f16" (issue #200/#192). When the cache lives on a Vulkan
843
+ // buffer AND we're about to take the manual-repeat path on an F16 cache,
844
+ // force the F32 read so the repeat lowers to a supported F32 REPEAT. This
845
+ // central detection covers every kv_self_attn caller automatically (no
846
+ // per-backend Vulkan plumbing needed). Scoped to exactly the crash
847
+ // condition so Metal/CPU and the GQA_NATIVE / MHA / quantized / F32-cache
848
+ // paths stay bit-identical even on Vulkan.
849
+ const bool gqa_manual_repeat = (p.gqa_mode != GQA_NATIVE) && (grp > 1);
850
+ const bool kv_f16_repeat_on_vulkan = gqa_manual_repeat && (kv_k->type == GGML_TYPE_F16) && kv_k->buffer && [&]() {
851
+ const char* bn = ggml_backend_buft_name(ggml_backend_buffer_get_type(kv_k->buffer));
852
+ return bn && std::strstr(bn, "Vulkan") != nullptr;
853
+ }();
854
+ const bool want_f32_read = s_kv_read_f32 || p.force_kv_read_f32 || kv_f16_repeat_on_vulkan;
855
+ const bool need_dequant_k = ggml_is_quantized(kv_k->type) || (want_f32_read && kv_k->type != GGML_TYPE_F32);
856
+ const bool need_dequant_v = ggml_is_quantized(kv_v->type) || (want_f32_read && kv_v->type != GGML_TYPE_F32);
857
+ ggml_tensor* Kfull = need_dequant_k ? ggml_cast(ctx0, k_layer_view, GGML_TYPE_F32) : ggml_cont(ctx0, k_layer_view);
858
+ ggml_tensor* Vfull = need_dequant_v ? ggml_cast(ctx0, v_layer_view, GGML_TYPE_F32) : ggml_cont(ctx0, v_layer_view);
859
+
860
+ // ---- GQA expansion ----
861
+ if (p.gqa_mode != GQA_NATIVE && grp > 1) {
862
+ ggml_tensor* K4 = ggml_reshape_4d(ctx0, Kfull, hd, Lk, 1, n_kv);
863
+ ggml_tensor* V4 = ggml_reshape_4d(ctx0, Vfull, hd, Lk, 1, n_kv);
864
+ K4 = ggml_repeat_4d(ctx0, K4, hd, Lk, grp, n_kv);
865
+ V4 = ggml_repeat_4d(ctx0, V4, hd, Lk, grp, n_kv);
866
+ if (p.gqa_mode == GQA_MANUAL_CONT) {
867
+ Kfull = ggml_cont(ctx0, ggml_reshape_3d(ctx0, K4, hd, Lk, n_q));
868
+ Vfull = ggml_cont(ctx0, ggml_reshape_3d(ctx0, V4, hd, Lk, n_q));
869
+ } else {
870
+ Kfull = ggml_reshape_3d(ctx0, K4, hd, Lk, n_q);
871
+ Vfull = ggml_reshape_3d(ctx0, V4, hd, Lk, n_q);
872
+ }
873
+ }
874
+
875
+ // CrispASR debug hook (#83 bisect): when CRISPASR_CORE_ATTN_DUMP_FA_LAYER
876
+ // matches the current layer index, name + add the FA inputs and output as
877
+ // graph outputs so chatterbox.cpp's run_t3_kv post-compute dumper can
878
+ // fetch them. Negligible perf cost when the env knob is unset.
879
+ auto dbg_dump_il_env = std::getenv("CRISPASR_CORE_ATTN_DUMP_FA_LAYER");
880
+ const int dbg_dump_il = dbg_dump_il_env ? (int)std::strtol(dbg_dump_il_env, nullptr, 10) : -1;
881
+ const bool dbg_dump = ((int)il == dbg_dump_il);
882
+ if (dbg_dump) {
883
+ ggml_tensor* Q_pre = ggml_cont(ctx0, Q);
884
+ ggml_set_name(Q_pre, "DBG_Q_post_rope");
885
+ ggml_set_output(Q_pre);
886
+ ggml_build_forward_expand(gf, Q_pre);
887
+
888
+ ggml_tensor* Kfull_dbg = ggml_cast(ctx0, Kfull, GGML_TYPE_F32);
889
+ ggml_set_name(Kfull_dbg, "DBG_Kfull");
890
+ ggml_set_output(Kfull_dbg);
891
+ ggml_build_forward_expand(gf, Kfull_dbg);
892
+
893
+ ggml_tensor* Vfull_dbg = ggml_cast(ctx0, Vfull, GGML_TYPE_F32);
894
+ ggml_set_name(Vfull_dbg, "DBG_Vfull");
895
+ ggml_set_output(Vfull_dbg);
896
+ ggml_build_forward_expand(gf, Vfull_dbg);
897
+ }
898
+
899
+ // ---- Permute Q to (hd, T, n_q) for flash-attn ----
900
+ Q = ggml_cont(ctx0, ggml_permute(ctx0, Q, 0, 2, 1, 3));
901
+
902
+ // ---- Flash attention + reshape + output projection ----
903
+ ggml_tensor* attn = ggml_flash_attn_ext(ctx0, Q, Kfull, Vfull, causal_mask, p.attn_scale, /*max_bias*/ 0.0f,
904
+ /*logit_softcap*/ 0.0f);
905
+ if (dbg_dump) {
906
+ ggml_set_name(attn, "DBG_fa_out");
907
+ ggml_set_output(attn);
908
+ ggml_build_forward_expand(gf, attn);
909
+ }
910
+ attn = ggml_reshape_2d(ctx0, attn, hd * n_q, T);
911
+
912
+ if (dbg_dump) {
913
+ ggml_set_name(attn, "DBG_fa_reshaped");
914
+ ggml_set_output(attn);
915
+ ggml_build_forward_expand(gf, attn);
916
+ }
917
+
918
+ ggml_tensor* out = ggml_mul_mat(ctx0, o_w, attn);
919
+ if (o_b)
920
+ out = ggml_add(ctx0, out, o_b);
921
+ return out;
922
+ }
923
+
924
+ } // namespace core_attn