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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +21 -0
- data/LICENSE.txt +201 -0
- data/NOTICE +5 -0
- data/README.md +265 -0
- data/THIRD_PARTY_NOTICES.md +67 -0
- data/exe/cohere-transcribe +8 -0
- data/exe/cohere-transcribe-doctor +8 -0
- data/ext/cohere_transcribe_native/CMakeLists.txt +262 -0
- data/ext/cohere_transcribe_native/README.md +26 -0
- data/ext/cohere_transcribe_native/audio_abi.cpp +1416 -0
- data/ext/cohere_transcribe_native/audio_exports.macos +5 -0
- data/ext/cohere_transcribe_native/audio_exports.map +10 -0
- data/ext/cohere_transcribe_native/cohere_abi.cpp +1209 -0
- data/ext/cohere_transcribe_native/exports.macos +40 -0
- data/ext/cohere_transcribe_native/exports.map +45 -0
- data/ext/cohere_transcribe_native/extconf.rb +89 -0
- data/ext/cohere_transcribe_native/test/abi_smoke.rb +232 -0
- data/ext/cohere_transcribe_native/test/audio_matrix_smoke.cpp +121 -0
- data/ext/cohere_transcribe_native/test/audio_reliability_smoke.cpp +198 -0
- data/ext/cohere_transcribe_native/test/native_abi_reliability_smoke.cpp +186 -0
- data/ext/cohere_transcribe_native/test/native_batch_projection_probe.rb +81 -0
- data/ext/cohere_transcribe_native/test/native_cancellation_smoke.cpp +194 -0
- data/lib/cohere/transcribe/alignment/ATTRIBUTION.md +23 -0
- data/lib/cohere/transcribe/alignment/LICENSE.ctc-forced-aligner +407 -0
- data/lib/cohere/transcribe/alignment/LICENSE.torchaudio +25 -0
- data/lib/cohere/transcribe/alignment/LICENSE.uroman +26 -0
- data/lib/cohere/transcribe/alignment/aligner.rb +476 -0
- data/lib/cohere/transcribe/alignment/ctc.rb +224 -0
- data/lib/cohere/transcribe/alignment/text.rb +237 -0
- data/lib/cohere/transcribe/alignment/uroman_data.rb +4950 -0
- data/lib/cohere/transcribe/api.rb +173 -0
- data/lib/cohere/transcribe/asr/batching.rb +472 -0
- data/lib/cohere/transcribe/asr/failure_policy.rb +112 -0
- data/lib/cohere/transcribe/asr/native.rb +676 -0
- data/lib/cohere/transcribe/audio/ATTRIBUTION.md +8 -0
- data/lib/cohere/transcribe/audio/LICENSE.auditok +21 -0
- data/lib/cohere/transcribe/audio/decoder.rb +315 -0
- data/lib/cohere/transcribe/audio/ffmpeg_native.rb +248 -0
- data/lib/cohere/transcribe/audio/segmentation.rb +260 -0
- data/lib/cohere/transcribe/cli.rb +727 -0
- data/lib/cohere/transcribe/configuration.rb +282 -0
- data/lib/cohere/transcribe/constants.rb +14 -0
- data/lib/cohere/transcribe/dense_converter.rb +548 -0
- data/lib/cohere/transcribe/doctor.rb +576 -0
- data/lib/cohere/transcribe/errors.rb +57 -0
- data/lib/cohere/transcribe/gguf_writer.rb +268 -0
- data/lib/cohere/transcribe/hub.rb +436 -0
- data/lib/cohere/transcribe/input.rb +110 -0
- data/lib/cohere/transcribe/licenses/crispasr.txt +21 -0
- data/lib/cohere/transcribe/loader.rb +128 -0
- data/lib/cohere/transcribe/model_identity.rb +440 -0
- data/lib/cohere/transcribe/output/publication.rb +1118 -0
- data/lib/cohere/transcribe/output/rendering.rb +105 -0
- data/lib/cohere/transcribe/output/timing.rb +86 -0
- data/lib/cohere/transcribe/python_text.rb +70 -0
- data/lib/cohere/transcribe/pytorch_checkpoint.rb +1180 -0
- data/lib/cohere/transcribe/runtime/engine.rb +1676 -0
- data/lib/cohere/transcribe/runtime/model_provider.rb +390 -0
- data/lib/cohere/transcribe/runtime/precision.rb +57 -0
- data/lib/cohere/transcribe/runtime/preparation.rb +215 -0
- data/lib/cohere/transcribe/runtime/resources.rb +165 -0
- data/lib/cohere/transcribe/runtime/word_pipeline.rb +364 -0
- data/lib/cohere/transcribe/safetensors.rb +579 -0
- data/lib/cohere/transcribe/state/checkpoint.rb +224 -0
- data/lib/cohere/transcribe/state/contracts.rb +141 -0
- data/lib/cohere/transcribe/state/io.rb +727 -0
- data/lib/cohere/transcribe/state/locking.rb +211 -0
- data/lib/cohere/transcribe/state/manifest.rb +155 -0
- data/lib/cohere/transcribe/state.rb +7 -0
- data/lib/cohere/transcribe/types.rb +535 -0
- data/lib/cohere/transcribe/vad/ATTRIBUTION.md +14 -0
- data/lib/cohere/transcribe/vad/LICENSE.faster-whisper +21 -0
- data/lib/cohere/transcribe/vad/LICENSE.silero-vad +21 -0
- data/lib/cohere/transcribe/vad/silero.rb +344 -0
- data/lib/cohere/transcribe/vad/silero_vad_v6.onnx +0 -0
- data/lib/cohere/transcribe/vad/timestamps.rb +219 -0
- data/lib/cohere/transcribe/version.rb +7 -0
- data/lib/cohere/transcribe.rb +26 -0
- data/sig/cohere/transcribe.rbs +250 -0
- data/vendor/crispasr/AUTHORS +510 -0
- data/vendor/crispasr/LICENSE +21 -0
- data/vendor/crispasr/UPSTREAM.md +9 -0
- data/vendor/crispasr/VERSION +1 -0
- data/vendor/crispasr/ggml/AUTHORS +335 -0
- data/vendor/crispasr/ggml/CMakeLists.txt +512 -0
- data/vendor/crispasr/ggml/LICENSE +21 -0
- data/vendor/crispasr/ggml/README.md +49 -0
- data/vendor/crispasr/ggml/cmake/FindNCCL.cmake +36 -0
- data/vendor/crispasr/ggml/cmake/GitVars.cmake +22 -0
- data/vendor/crispasr/ggml/cmake/common.cmake +50 -0
- data/vendor/crispasr/ggml/cmake/ggml-config.cmake.in +191 -0
- data/vendor/crispasr/ggml/ggml.pc.in +10 -0
- data/vendor/crispasr/ggml/include/ggml-alloc.h +85 -0
- data/vendor/crispasr/ggml/include/ggml-backend.h +431 -0
- data/vendor/crispasr/ggml/include/ggml-blas.h +25 -0
- data/vendor/crispasr/ggml/include/ggml-cann.h +123 -0
- data/vendor/crispasr/ggml/include/ggml-cpp.h +39 -0
- data/vendor/crispasr/ggml/include/ggml-cpu.h +151 -0
- data/vendor/crispasr/ggml/include/ggml-cuda.h +50 -0
- data/vendor/crispasr/ggml/include/ggml-hexagon.h +19 -0
- data/vendor/crispasr/ggml/include/ggml-metal.h +61 -0
- data/vendor/crispasr/ggml/include/ggml-opencl.h +26 -0
- data/vendor/crispasr/ggml/include/ggml-openvino.h +37 -0
- data/vendor/crispasr/ggml/include/ggml-opt.h +256 -0
- data/vendor/crispasr/ggml/include/ggml-rpc.h +35 -0
- data/vendor/crispasr/ggml/include/ggml-sycl.h +49 -0
- data/vendor/crispasr/ggml/include/ggml-virtgpu.h +14 -0
- data/vendor/crispasr/ggml/include/ggml-vulkan.h +29 -0
- data/vendor/crispasr/ggml/include/ggml-webgpu.h +19 -0
- data/vendor/crispasr/ggml/include/ggml-zdnn.h +17 -0
- data/vendor/crispasr/ggml/include/ggml-zendnn.h +22 -0
- data/vendor/crispasr/ggml/include/ggml.h +2887 -0
- data/vendor/crispasr/ggml/include/gguf.h +204 -0
- data/vendor/crispasr/ggml/src/CMakeLists.txt +493 -0
- data/vendor/crispasr/ggml/src/ggml-alloc.c +1323 -0
- data/vendor/crispasr/ggml/src/ggml-backend-dl.cpp +48 -0
- data/vendor/crispasr/ggml/src/ggml-backend-dl.h +44 -0
- data/vendor/crispasr/ggml/src/ggml-backend-impl.h +275 -0
- data/vendor/crispasr/ggml/src/ggml-backend-meta.cpp +2145 -0
- data/vendor/crispasr/ggml/src/ggml-backend-reg.cpp +586 -0
- data/vendor/crispasr/ggml/src/ggml-backend.cpp +2437 -0
- data/vendor/crispasr/ggml/src/ggml-common.h +1900 -0
- data/vendor/crispasr/ggml/src/ggml-cpu/CMakeLists.txt +718 -0
- data/vendor/crispasr/ggml/src/ggml-cpu/amx/amx.cpp +249 -0
- data/vendor/crispasr/ggml/src/ggml-cpu/amx/amx.h +8 -0
- data/vendor/crispasr/ggml/src/ggml-cpu/amx/common.h +115 -0
- data/vendor/crispasr/ggml/src/ggml-cpu/amx/mmq.cpp +2512 -0
- data/vendor/crispasr/ggml/src/ggml-cpu/amx/mmq.h +10 -0
- data/vendor/crispasr/ggml/src/ggml-cpu/arch/arm/cpu-feats.cpp +98 -0
- data/vendor/crispasr/ggml/src/ggml-cpu/arch/arm/quants.c +4244 -0
- data/vendor/crispasr/ggml/src/ggml-cpu/arch/arm/repack.cpp +5156 -0
- data/vendor/crispasr/ggml/src/ggml-cpu/arch/loongarch/quants.c +2158 -0
- data/vendor/crispasr/ggml/src/ggml-cpu/arch/powerpc/cpu-feats.cpp +82 -0
- data/vendor/crispasr/ggml/src/ggml-cpu/arch/powerpc/quants.c +2304 -0
- data/vendor/crispasr/ggml/src/ggml-cpu/arch/riscv/cpu-feats.cpp +38 -0
- data/vendor/crispasr/ggml/src/ggml-cpu/arch/riscv/quants.c +4455 -0
- data/vendor/crispasr/ggml/src/ggml-cpu/arch/riscv/repack.cpp +1703 -0
- data/vendor/crispasr/ggml/src/ggml-cpu/arch/s390/cpu-feats.cpp +50 -0
- data/vendor/crispasr/ggml/src/ggml-cpu/arch/s390/quants.c +1465 -0
- data/vendor/crispasr/ggml/src/ggml-cpu/arch/wasm/quants.c +1220 -0
- data/vendor/crispasr/ggml/src/ggml-cpu/arch/x86/cpu-feats.cpp +327 -0
- data/vendor/crispasr/ggml/src/ggml-cpu/arch/x86/quants.c +3970 -0
- data/vendor/crispasr/ggml/src/ggml-cpu/arch/x86/repack.cpp +6407 -0
- data/vendor/crispasr/ggml/src/ggml-cpu/arch-fallback.h +349 -0
- data/vendor/crispasr/ggml/src/ggml-cpu/binary-ops.cpp +154 -0
- data/vendor/crispasr/ggml/src/ggml-cpu/binary-ops.h +16 -0
- data/vendor/crispasr/ggml/src/ggml-cpu/cmake/FindSIMD.cmake +100 -0
- data/vendor/crispasr/ggml/src/ggml-cpu/common.h +95 -0
- data/vendor/crispasr/ggml/src/ggml-cpu/ggml-cpu-impl.h +539 -0
- data/vendor/crispasr/ggml/src/ggml-cpu/ggml-cpu.c +3791 -0
- data/vendor/crispasr/ggml/src/ggml-cpu/ggml-cpu.cpp +703 -0
- data/vendor/crispasr/ggml/src/ggml-cpu/hbm.cpp +55 -0
- data/vendor/crispasr/ggml/src/ggml-cpu/hbm.h +8 -0
- data/vendor/crispasr/ggml/src/ggml-cpu/kleidiai/kernels.cpp +939 -0
- data/vendor/crispasr/ggml/src/ggml-cpu/kleidiai/kernels.h +90 -0
- data/vendor/crispasr/ggml/src/ggml-cpu/kleidiai/kleidiai.cpp +1513 -0
- data/vendor/crispasr/ggml/src/ggml-cpu/kleidiai/kleidiai.h +17 -0
- data/vendor/crispasr/ggml/src/ggml-cpu/llamafile/sgemm.cpp +4051 -0
- data/vendor/crispasr/ggml/src/ggml-cpu/llamafile/sgemm.h +25 -0
- data/vendor/crispasr/ggml/src/ggml-cpu/ops.cpp +11662 -0
- data/vendor/crispasr/ggml/src/ggml-cpu/ops.h +121 -0
- data/vendor/crispasr/ggml/src/ggml-cpu/quants.c +1288 -0
- data/vendor/crispasr/ggml/src/ggml-cpu/quants.h +103 -0
- data/vendor/crispasr/ggml/src/ggml-cpu/repack.cpp +4836 -0
- data/vendor/crispasr/ggml/src/ggml-cpu/repack.h +245 -0
- data/vendor/crispasr/ggml/src/ggml-cpu/simd-gemm.h +226 -0
- data/vendor/crispasr/ggml/src/ggml-cpu/simd-mappings.h +1329 -0
- data/vendor/crispasr/ggml/src/ggml-cpu/spacemit/ime.cpp +1025 -0
- data/vendor/crispasr/ggml/src/ggml-cpu/spacemit/ime.h +13 -0
- data/vendor/crispasr/ggml/src/ggml-cpu/spacemit/ime1_kernels.cpp +3196 -0
- data/vendor/crispasr/ggml/src/ggml-cpu/spacemit/ime_kernels.h +26 -0
- data/vendor/crispasr/ggml/src/ggml-cpu/traits.cpp +36 -0
- data/vendor/crispasr/ggml/src/ggml-cpu/traits.h +38 -0
- data/vendor/crispasr/ggml/src/ggml-cpu/unary-ops.cpp +336 -0
- data/vendor/crispasr/ggml/src/ggml-cpu/unary-ops.h +35 -0
- data/vendor/crispasr/ggml/src/ggml-cpu/vec.cpp +681 -0
- data/vendor/crispasr/ggml/src/ggml-cpu/vec.h +1606 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/CMakeLists.txt +272 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/acc.cu +61 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/acc.cuh +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/add-id.cu +58 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/add-id.cuh +3 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/arange.cu +34 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/arange.cuh +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/argmax.cu +91 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/argmax.cuh +3 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/argsort.cu +265 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/argsort.cuh +19 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/binbcast.cu +534 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/binbcast.cuh +12 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/clamp.cu +45 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/clamp.cuh +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/col2im-1d.cu +81 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/col2im-1d.cuh +3 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/common.cuh +1489 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/concat.cu +204 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/concat.cuh +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/conv-transpose-1d.cu +97 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/conv-transpose-1d.cuh +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/conv2d-dw.cu +161 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/conv2d-dw.cuh +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/conv2d-transpose.cu +115 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/conv2d-transpose.cuh +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/conv2d.cu +166 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/conv2d.cuh +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/convert.cu +892 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/convert.cuh +66 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/count-equal.cu +64 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/count-equal.cuh +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/cp-async.cuh +57 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/cpy-utils.cuh +217 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/cpy.cu +581 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/cpy.cuh +7 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/cross-entropy-loss.cu +177 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/cross-entropy-loss.cuh +7 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/cumsum.cu +307 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/cumsum.cuh +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/dequantize.cuh +99 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/diag.cu +77 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/diag.cuh +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/diagmask.cu +40 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/diagmask.cuh +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/fattn-common.cuh +1212 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/fattn-mma-f16.cuh +1860 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/fattn-tile.cu +57 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/fattn-tile.cuh +1309 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/fattn-vec.cuh +600 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/fattn-wmma-f16.cu +696 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/fattn-wmma-f16.cuh +51 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/fattn.cu +620 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/fattn.cuh +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/fill.cu +37 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/fill.cuh +3 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/gated_delta_net.cu +273 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/gated_delta_net.cuh +4 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/getrows.cu +332 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/getrows.cuh +15 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/ggml-cuda.cu +5580 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/gla.cu +93 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/gla.cuh +3 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/im2col.cu +274 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/im2col.cuh +6 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/mean.cu +75 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/mean.cuh +3 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/mma.cuh +1333 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/mmf.cu +191 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/mmf.cuh +908 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/mmid.cu +164 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/mmid.cuh +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/mmq.cu +372 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/mmq.cuh +4175 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/mmvf.cu +862 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/mmvf.cuh +14 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/mmvq.cu +1161 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/mmvq.cuh +16 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/norm.cu +756 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/norm.cuh +20 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/opt-step-adamw.cu +78 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/opt-step-adamw.cuh +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/opt-step-sgd.cu +49 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/opt-step-sgd.cuh +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/out-prod.cu +68 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/out-prod.cuh +3 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/pad.cu +106 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/pad.cuh +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/pad_reflect_1d.cu +91 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/pad_reflect_1d.cuh +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/pool2d.cu +94 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/pool2d.cuh +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/quantize.cu +443 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/quantize.cuh +41 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/reduce_rows.cuh +39 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/roll.cu +67 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/roll.cuh +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/rope.cu +665 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/rope.cuh +9 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/scale.cu +34 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/scale.cuh +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/set-rows.cu +330 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/set-rows.cuh +7 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/set.cu +39 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/set.cuh +7 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/softcap.cu +34 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/softcap.cuh +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/softmax.cu +472 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/softmax.cuh +7 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/solve_tri.cu +275 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/solve_tri.cuh +3 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/ssm-conv.cu +197 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/ssm-conv.cuh +3 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/ssm-scan.cu +342 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/ssm-scan.cuh +3 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/sum.cu +41 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/sum.cuh +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/sumrows.cu +43 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/sumrows.cuh +4 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-mma-f16-instance-ncols1_1-ncols2_16.cu +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-mma-f16-instance-ncols1_1-ncols2_32.cu +6 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-mma-f16-instance-ncols1_1-ncols2_8.cu +11 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-mma-f16-instance-ncols1_16-ncols2_1.cu +10 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-mma-f16-instance-ncols1_16-ncols2_2.cu +10 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-mma-f16-instance-ncols1_16-ncols2_4.cu +12 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-mma-f16-instance-ncols1_2-ncols2_16.cu +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-mma-f16-instance-ncols1_2-ncols2_32.cu +6 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-mma-f16-instance-ncols1_2-ncols2_4.cu +12 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-mma-f16-instance-ncols1_2-ncols2_8.cu +11 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-mma-f16-instance-ncols1_32-ncols2_1.cu +10 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-mma-f16-instance-ncols1_32-ncols2_2.cu +10 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-mma-f16-instance-ncols1_4-ncols2_16.cu +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-mma-f16-instance-ncols1_4-ncols2_2.cu +10 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-mma-f16-instance-ncols1_4-ncols2_4.cu +12 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-mma-f16-instance-ncols1_4-ncols2_8.cu +11 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-mma-f16-instance-ncols1_64-ncols2_1.cu +10 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-mma-f16-instance-ncols1_8-ncols2_1.cu +10 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-mma-f16-instance-ncols1_8-ncols2_2.cu +10 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-mma-f16-instance-ncols1_8-ncols2_4.cu +12 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-mma-f16-instance-ncols1_8-ncols2_8.cu +11 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-tile-instance-dkq112-dv112.cu +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-tile-instance-dkq128-dv128.cu +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-tile-instance-dkq256-dv256.cu +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-tile-instance-dkq320-dv256.cu +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-tile-instance-dkq40-dv40.cu +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-tile-instance-dkq512-dv512.cu +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-tile-instance-dkq576-dv512.cu +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-tile-instance-dkq64-dv64.cu +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-tile-instance-dkq72-dv72.cu +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-tile-instance-dkq80-dv80.cu +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-tile-instance-dkq96-dv96.cu +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-bf16-bf16.cu +7 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-bf16-f16.cu +7 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-bf16-q4_0.cu +7 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-bf16-q4_1.cu +7 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-bf16-q5_0.cu +7 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-bf16-q5_1.cu +7 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-bf16-q8_0.cu +7 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-f16-bf16.cu +7 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-f16-f16.cu +7 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-f16-q4_0.cu +7 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-f16-q4_1.cu +7 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-f16-q5_0.cu +7 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-f16-q5_1.cu +7 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-f16-q8_0.cu +7 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-q4_0-bf16.cu +7 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-q4_0-f16.cu +7 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-q4_0-q4_0.cu +7 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-q4_0-q4_1.cu +7 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-q4_0-q5_0.cu +7 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-q4_0-q5_1.cu +7 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-q4_0-q8_0.cu +7 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-q4_1-bf16.cu +7 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-q4_1-f16.cu +7 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-q4_1-q4_0.cu +7 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-q4_1-q4_1.cu +7 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-q4_1-q5_0.cu +7 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-q4_1-q5_1.cu +7 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-q4_1-q8_0.cu +7 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-q5_0-bf16.cu +7 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-q5_0-f16.cu +7 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-q5_0-q4_0.cu +7 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-q5_0-q4_1.cu +7 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-q5_0-q5_0.cu +7 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-q5_0-q5_1.cu +7 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-q5_0-q8_0.cu +7 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-q5_1-bf16.cu +7 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-q5_1-f16.cu +7 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-q5_1-q4_0.cu +7 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-q5_1-q4_1.cu +7 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-q5_1-q5_0.cu +7 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-q5_1-q5_1.cu +7 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-q5_1-q8_0.cu +7 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-q8_0-bf16.cu +7 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-q8_0-f16.cu +7 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-q8_0-q4_0.cu +7 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-q8_0-q4_1.cu +7 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-q8_0-q5_0.cu +7 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-q8_0-q5_1.cu +7 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-q8_0-q8_0.cu +7 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/mmf-instance-ncols_1.cu +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/mmf-instance-ncols_10.cu +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/mmf-instance-ncols_11.cu +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/mmf-instance-ncols_12.cu +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/mmf-instance-ncols_13.cu +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/mmf-instance-ncols_14.cu +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/mmf-instance-ncols_15.cu +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/mmf-instance-ncols_16.cu +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/mmf-instance-ncols_2.cu +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/mmf-instance-ncols_3.cu +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/mmf-instance-ncols_4.cu +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/mmf-instance-ncols_5.cu +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/mmf-instance-ncols_6.cu +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/mmf-instance-ncols_7.cu +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/mmf-instance-ncols_8.cu +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/mmf-instance-ncols_9.cu +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/mmq-instance-iq1_s.cu +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/mmq-instance-iq2_s.cu +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/mmq-instance-iq2_xs.cu +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/mmq-instance-iq2_xxs.cu +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/mmq-instance-iq3_s.cu +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/mmq-instance-iq3_xxs.cu +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/mmq-instance-iq4_nl.cu +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/mmq-instance-iq4_xs.cu +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/mmq-instance-mxfp4.cu +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/mmq-instance-nvfp4.cu +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/mmq-instance-q1_0.cu +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/mmq-instance-q2_k.cu +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/mmq-instance-q3_k.cu +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/mmq-instance-q4_0.cu +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/mmq-instance-q4_1.cu +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/mmq-instance-q4_k.cu +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/mmq-instance-q5_0.cu +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/mmq-instance-q5_1.cu +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/mmq-instance-q5_k.cu +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/mmq-instance-q6_k.cu +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/mmq-instance-q8_0.cu +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/top-k.cu +95 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/top-k.cuh +3 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/topk-moe.cu +415 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/topk-moe.cuh +27 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/tri.cu +136 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/tri.cuh +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/tsembd.cu +47 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/tsembd.cuh +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/unary.cu +644 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/unary.cuh +116 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/upscale.cu +293 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/upscale.cuh +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/vecdotq.cuh +1317 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/vendors/cuda.h +28 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/vendors/hip.h +306 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/vendors/musa.h +148 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/wkv.cu +199 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/wkv.cuh +7 -0
- data/vendor/crispasr/ggml/src/ggml-impl.h +783 -0
- data/vendor/crispasr/ggml/src/ggml-metal/CMakeLists.txt +124 -0
- data/vendor/crispasr/ggml/src/ggml-metal/ggml-metal-common.cpp +457 -0
- data/vendor/crispasr/ggml/src/ggml-metal/ggml-metal-common.h +52 -0
- data/vendor/crispasr/ggml/src/ggml-metal/ggml-metal-context.h +41 -0
- data/vendor/crispasr/ggml/src/ggml-metal/ggml-metal-context.m +778 -0
- data/vendor/crispasr/ggml/src/ggml-metal/ggml-metal-device.cpp +2117 -0
- data/vendor/crispasr/ggml/src/ggml-metal/ggml-metal-device.h +299 -0
- data/vendor/crispasr/ggml/src/ggml-metal/ggml-metal-device.m +2093 -0
- data/vendor/crispasr/ggml/src/ggml-metal/ggml-metal-impl.h +1267 -0
- data/vendor/crispasr/ggml/src/ggml-metal/ggml-metal-ops.cpp +5023 -0
- data/vendor/crispasr/ggml/src/ggml-metal/ggml-metal-ops.h +111 -0
- data/vendor/crispasr/ggml/src/ggml-metal/ggml-metal.cpp +954 -0
- data/vendor/crispasr/ggml/src/ggml-metal/ggml-metal.metal +11756 -0
- data/vendor/crispasr/ggml/src/ggml-opt.cpp +1094 -0
- data/vendor/crispasr/ggml/src/ggml-quants.c +5491 -0
- data/vendor/crispasr/ggml/src/ggml-quants.h +112 -0
- data/vendor/crispasr/ggml/src/ggml-threading.cpp +12 -0
- data/vendor/crispasr/ggml/src/ggml-threading.h +14 -0
- data/vendor/crispasr/ggml/src/ggml.c +7925 -0
- data/vendor/crispasr/ggml/src/ggml.cpp +26 -0
- data/vendor/crispasr/ggml/src/gguf.cpp +1556 -0
- data/vendor/crispasr/src/cohere-arch.h +137 -0
- data/vendor/crispasr/src/cohere.cpp +5642 -0
- data/vendor/crispasr/src/cohere.h +327 -0
- data/vendor/crispasr/src/cohere_batch_planner.h +82 -0
- data/vendor/crispasr/src/cohere_chunking.h +64 -0
- data/vendor/crispasr/src/cohere_decoder_batch_layout.h +60 -0
- data/vendor/crispasr/src/cohere_encoder_padded_layout.h +27 -0
- data/vendor/crispasr/src/cohere_frontend.cpp +189 -0
- data/vendor/crispasr/src/cohere_frontend.h +31 -0
- data/vendor/crispasr/src/cohere_ragged_controller.h +149 -0
- data/vendor/crispasr/src/cohere_token_renderer.h +181 -0
- data/vendor/crispasr/src/core/attention.h +924 -0
- data/vendor/crispasr/src/core/audio_chunking.h +97 -0
- data/vendor/crispasr/src/core/beam_decode.h +486 -0
- data/vendor/crispasr/src/core/cpu_ops.h +135 -0
- data/vendor/crispasr/src/core/gguf_loader.cpp +1021 -0
- data/vendor/crispasr/src/core/gguf_loader.h +216 -0
- data/vendor/crispasr/src/core/gpu_backend_pref.h +119 -0
- data/vendor/crispasr/src/core/mel.cpp +519 -0
- data/vendor/crispasr/src/core/mel.h +265 -0
- data/vendor/crispasr/src/core/ngram_loop_fix.h +173 -0
- data/vendor/crispasr/src/core/repetition_loop_guard.h +54 -0
- data/vendor/crispasr/src/crispasr_imatrix.cpp +255 -0
- data/vendor/crispasr/src/crispasr_imatrix.h +38 -0
- 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
|