Semlith

Models and pinned downloads

The embedding, rescoring and image models Semlith runs on your machine, the accelerator packs it can fetch, and how to check each download by its digest.

Every model and accelerator component semlith downloads, with the commit, digest and size it is pinned to. Read it to check what runs on your machine or to verify a download yourself.

semlith computes vectors with three ONNX models: granite for text, a cross-encoder for optional rescoring, and CLIP for images. They are fetched from Hugging Face on first use, cached under ~/.cache/semlith/models (or SEMLITH_MODEL_CACHE), and never fetched again.

Each model is pinned to a commit, and each file is verified against a SHA-256 recorded in the source. A model that changed upstream would change every answer without changing anything you can see. A file whose digest does not match is refused by name with both digests printed, and nothing is loaded. If that happens twice on a clean cache, the upstream bytes have changed and semlith needs a new release, not another download.

Text: granite-embedding-small-english-r2

The default model: 384 dimensions, int8, Apache-2.0. semlith fetches these files itself and verifies each as it is read.

FileSHA-256
tokenizer.jsonfeeb83348dcb033bc6b9d2e1f7906ca9eb2d122845000c9416d894d7c2927149
config.json1a1710c20911da8c96179716bf44058e54cca6fa7952cce77be83ef05edae3ee
special_tokens_map.jsonea97ecdbcc73713039d8d64dbb05e3689495c96657fbd9a18f5bed381be81049
tokenizer_config.jsonce06781b38bb393db68c9e0709bddd31ef5d88f2c6fbb3fd9f369778fb85e451
onnx/model_quantized.onnxa3fad524afc3f060216a8ddbb1ac89c9b6498fba8995b5718bde879076a2e9ba
onnx/model_quantized.onnx_data1f4cf47e4adec7f7ae09db03d071ba8667e07f9a4203142c7efa8d37fe453597

The fp16 and fp32 exports

Two more exports of the same model, at the same commit and with the same tokenizer files. Recorded in src/embed.rs as Variant::files; the fp16 pair is also in src/gpu.rs as FP16_FILES, with the sizes the download is checked against.

VariantFileSHA-256Bytes
fp16onnx/model_fp16.onnxee200de55cb2f94e858aabca54be7697a9c0805a14c858ee26ad0922b05f57d7200 792
fp16onnx/model_fp16.onnx_data28d16e29cd623f25cc6fa0968700c5bc31036466091a5fa06d1353c1777f050e97 402 880
fp32onnx/model.onnxcddb145cd1147ec24a3908b2ca2602b98b20a3d198365cff270b7cb26c98179e
fp32onnx/model.onnx_data86a3a705d4598615894d89540ea71a3d9bbdb17a315e79edcd5dfc737222834b
  • fp16 is what the GPU lanes run. The int8 graph does not load on WebGPU. The fp16 pair is downloaded with the WebGPU plugin, the first time a run starts on a machine with a hardware GPU and the GPU lane on, or when CUDA is turned on. Both files are stored in the plugin’s directory in the model cache, not in the Hugging Face snapshot. fp16 and int8 vectors of the same text agree at cosine 0.987 (2026-09-23 corpus). A store records how many chunks each variant embedded in its variants meta row.
  • fp32 is what the known-answer fixture was made with. tests/fixtures/gpu/ holds 32 chunks and their CPU fp32 vectors; semlith doctor --gpu, and every GPU lane before its first real batch, compare against them. The product never downloads fp32. A harness fetches it with SEMLITH_EMBED_VARIANT=fp32, which is not part of the documented environment.

Accelerator components

Not models, but pinned and verified the same way. Each download is streamed through SHA-256 and deleted if its digest does not match. Each set is kept under accel/ in the model cache, in a directory named by its pinned version. Turning a lane off leaves the directory in place; semlith accel remove <gpu | cuda> deletes it. Under --airgap each download is refused unless its directory was seeded beforehand.

The WebGPU plugin

Microsoft’s WebGPU plugin execution provider, onnxruntime-ep-webgpu 0.4.0 (MIT), taken from the wheel Microsoft publishes on PyPI. semlith extracts the plugin library, plus the two shader compilers beside it on Windows, into accel/webgpu-0.4.0/. Recorded in src/gpu.rs as WEBGPU_VERSION and wheel().

PlatformWheelSHA-256Bytes
macOS (universal2)onnxruntime_ep_webgpu-0.4.0-py3-none-macosx_14_0_universal2.whl8d0a91d44b43d931c3068b9134aed9770f27803e353c2b58c93e1700bb68bcca5 200 679
Linux x86_64onnxruntime_ep_webgpu-0.4.0-py3-none-manylinux_2_28_x86_64.whl5d8c961eda91a88961cad1fb7621f389f006b18497d6ea9ecf96f729f066cbfb6 892 120
Linux aarch64onnxruntime_ep_webgpu-0.4.0-py3-none-manylinux_2_28_aarch64.whl17f660db53b1a509c63e721ac6784a2daa1c4e91a968523ee9986505ee5b0a556 096 222
Windows x86_64onnxruntime_ep_webgpu-0.4.0-py3-none-win_amd64.whl7db646669d1a2390551da675115ea125e68cb3d4d2c3a2d6e463372bf8d6ea8713 149 847

It is fetched only after a hardware adapter is found. A machine whose only adapter is a software renderer (Mesa lavapipe or llvmpipe, SwiftShader, Microsoft WARP or the Basic Render Driver) counts as having no GPU, and nothing is downloaded.

The CUDA pack

x86_64 Linux only. Pack 1.24.4-cu12.8, recorded in src/cuda.rs as PACK_VERSION and PARTS, with PACK_BYTES asserted against the sum of the sizes in tests/cuda.rs. It is downloaded only after CUDA is turned on (semlith accel on cuda or the switch on the Machine limits card), and the size is stated before the download starts: 1 891 522 804 bytes (1.89 GB) when the system supplies none of the libraries. A library the system already has, at the pinned version or a newer one in the same major version, is used from the system and its wheel is not fetched. Parts are listed in load order.

PartVersionSourceSHA-256BytesSupplies
ONNX Runtime GPU build1.24.4GitHub releasec5f804ff5d239b436fa59e9f2fb288a39f7eb9552f6a636c8b71e792e91a8808205 429 115libonnxruntime.so.1.24.4, libonnxruntime_providers_shared.so, libonnxruntime_providers_cuda.so
nvidia-cuda-runtime-cu1212.8.90PyPI wheeladade8dcbd0edf427b7204d480d6066d33902cab2a4707dcfc48a2d0fd44ab90954 765libcudart.so.12
nvidia-nvjitlink-cu1212.8.93PyPI wheel81ff63371a7ebd6e6451970684f916be2eab07321b73c9d244dc2b4da7f73b8839 254 836libnvJitLink.so.12
nvidia-cublas-cu1212.8.4.1PyPI wheel8ac4e771d5a348c551b2a426eda6193c19aa630236b418086020df5ba9667142594 346 921libcublasLt.so.12, libcublas.so.12
nvidia-cufft-cu1211.3.3.83PyPI wheel4d2dd21ec0b88cf61b62e6b43564355e5222e4a3fb394cac0db101f2dd0d4f74193 118 695libcufft.so.11
nvidia-curand-cu1210.3.9.90PyPI wheelb32331d4f4df5d6eefa0554c565b626c7216f87a06a4f56fab27c3b68a830ec963 619 976libcurand.so.10
nvidia-cuda-nvrtc-cu1212.8.93PyPI wheela7756528852ef889772a84c6cd89d41dfa74667e24cca16bb31f8f061e3e999488 040 029libnvrtc-builtins.so.12.8, libnvrtc.so.12
nvidia-cudnn-cu129.10.2.21PyPI wheel949452be657fa16687d0930933f032835951ef0892b37d2d53824d1a84dc97a8706 758 467libcudnn.so.9 and its seven sub-libraries

The NVIDIA versions are the CUDA 12.8 set that PyTorch 2.8’s cu128 wheels pin; ONNX Runtime’s onnxruntime-gpu[cuda,cudnn] extras resolve to the same set. The card needs driver 525.60.13 or newer, the minimum for CUDA 12.x on Linux x86_64; a card on an older driver is reported with that minimum and not used. On Windows, an NVIDIA card is used through WebGPU on D3D12.

Licences. ONNX Runtime is Microsoft’s MIT-licensed release, downloaded from its GitHub release. NVIDIA’s libraries never pass through semlith: your machine downloads each wheel from files.pythonhosted.org, where NVIDIA publishes it — the same bytes from the same place pip install nvidia-cudnn-cu12 would fetch — and semlith never hosts, mirrors or ships a copy. Your use of the libraries is governed by the licence each wheel carries (“NVIDIA Proprietary Software”), the terms a pip install of the same wheel accepts. When a wheel includes that licence text, semlith keeps it beside the libraries in licences/. Because semlith does not redistribute the libraries, the redistribution terms in the CUDA Toolkit EULA and the cuDNN Software License Agreement do not apply to it.

The Core ML pack

Apple silicon only, for the Neural Engine lane and the Core ML GPU lane. semlith builds it: .github/workflows/packs.yml runs packs/coreml/convert.py on a macOS runner and publishes the zip to this repository’s pack-coreml-v2 release, and src/packs.rs pins it by URL, SHA-256 and size. granite at ibm-granite/granite-embedding-small-english-r2@2ab6fa8e is converted twice:

  • for the Neural Engine: batch 4; sequence buckets 128, 192, 256, 320, 384, 512;
  • for the GPU, in the standard layout: batch 8; buckets 128, 192, 256, 320, 400 (no chunk is longer than 400 tokens). These run with fp16 accumulation, which leaves the fixture’s cosine against fp32 at 0.999999.

Each layout is one multifunction model with a function per bucket, so the weights are stored once per layout.

AssetSHA-256Bytes
semlith-coreml-2.zip799e4f7e8a4b1aab8335ab969bc385fcfb7baa8d62bfd14e02c8be9b0188a082147 950 175

On the M1 Air, every Neural Engine function places 99.85 % of its operations on the Neural Engine, and its vectors agree with the fp32 reference at cosine 0.99998 or better on the 512-chunk fixture in tests/fixtures/coreml.

The first load on a Mac compiles the models for that machine. The lane is ready once the longest bucket has compiled (39 s on the M1) and compiles the other five behind it, about three minutes in all. macOS keeps the result for the program that compiled it, which is a copy of semlith kept beside the models, so a later start is ready in about 2 s and an upgrade does not compile again. macOS empties that cache when the disk runs low: with 13 GB free on the M1 it had done so within ten minutes, and the next start compiled again.

The llama.cpp pack

Experimental. ggml-org’s own llama-server build b11146 for the platform, and granite as GGUF f16, which semlith builds with llama.cpp’s converter at the same build (packs/llama/convert.sh) and publishes to pack-llama-v1.

AssetSHA-256Bytes
llama-b11146-bin-macos-arm64.tar.gz1ad3f9eff80edb9dbef4259ad564d1720612ef7eea48fa4afed0e54f5f3d571111 189 714
llama-b11146-bin-ubuntu-vulkan-x64.tar.gzd3ce40fce7403cc93bcf5718fc46c6efb61ed9709f8e5d9f10c86bf0e30e8fb330 598 492
llama-b11146-bin-win-vulkan-x64.zip55a378aa095b466979d85075234f66d7655c7a7483222af0c006c0e55b4d7bd632 127 004
granite-embedding-small-english-r2-f16.ggufe9d55517cdadcf30eb18a65ea069778dd134914dee2554959bcedd5717d8faa897 119 616

TensorRT for RTX and OpenVINO

Experimental, x86_64 Windows and Linux. Each is the vendor’s own ONNX Runtime plugin execution provider, downloaded from PyPI where the vendor publishes it; semlith never hosts a copy.

PluginPlatformWheelSHA-256Bytes
TensorRT for RTX 0.4.0 (cu12)Linuxonnxruntime_ep_nv_tensorrt_rtx_cu12-0.4.0-…manylinux_2_28_x86_64.whl221807b1797a6270f37dfd7981f66e25ca7483f3883bfe23ef2b6a4198fe2b10167 721 016
TensorRT for RTX 0.4.0 (cu12)Windowsonnxruntime_ep_nv_tensorrt_rtx_cu12-0.4.0-…win_amd64.whl5d9dba6aafd8e0863f34d0e5f51ad7c7ae9a5d6a92a55fe45d9637d3a5f1e1a7105 822 356
OpenVINO 1.7.0Linuxonnxruntime_ep_openvino-1.7.0-…manylinux_2_28_x86_64.whl7016c5ba4154f2a2b6da87e8b8654e5e811151186fb97cc29bde5d2fabde6c9351 725 826
OpenVINO 1.7.0Windowsonnxruntime_ep_openvino-1.7.0-…win_amd64.whl0b48601ff6720a9fdbf06de99073665cec9b8d7983226455d4f766aa4d8be16c67 675 843

Intel’s plugin offers Intel hardware only: on a machine with an AMD CPU and no Intel GPU or NPU it offers no device, and the lane says so.

Rescoring: jina-reranker-v1-turbo-en

A cross-encoder: 37 M parameters, int8, Apache-2.0. It reads the query and a candidate together and reorders the head of the fused list. It embeds nothing and adds no candidates, so a store’s vectors do not depend on it.

It is off unless you set SEMLITH_RERANK=on. A search over one store takes 8.2 ms, and 132.2 ms with rescoring over twelve candidates — sixteen times slower — for a small gain at the top of the list. Turn it on when an answer matters more than a tenth of a second.

semlith setup fetches it beside the embedding model, so turning it on never pauses a query for a download. semlith stats and semlith doctor both say which ranking a search used.

FileSHA-256
tokenizer.json0046da43cc8c424b317f56b092b0512aaaa65c4f925d2f16af9d9eeb4d0ef902
config.jsone050ff6a15ae9295e84882fa0e98051bd8754856cd5201395ebf00ce9f2d609b
special_tokens_map.json06e405a36dfe4b9604f484f6a1e619af1a7f7d09e34a8555eb0b77b66318067f
tokenizer_config.jsond291c6652d96d56ffdbcf1ea19d9bae5ed79003f7648c627e725a619227ce8fa
onnx/model_quantized.onnx3defdef1ae34e119bd704216087743e79665934c96aebabcb6077c239dc3ae66

Images: CLIP ViT-B/32

Two repositories, a vision encoder and a text encoder, fixed as a pair. A query about a picture goes through CLIP’s own text encoder, never through the store’s model, because a granite vector and a CLIP vector have different lengths and describe different things.

These are fastembed’s built-in models, so fastembed resolves and caches them through its own client and semlith cannot hand it a revision. Instead, semlith checks every snapshot in the cache, not only the pinned one, before either encoder is used and again after a fetch, and refuses to load bytes that are not the ones this release was built against.

RepositoryFileSHA-256
visionmodel.onnxc68d3d9a200ddd2a8c8a5510b576d4c94d1ae383bf8b36dd8c084f94e1fb4d63
visionconfig.json43bfed060ab82f57833bdd09acdcc2731995cb984651732a9d2b399e63113b9c
visionpreprocessor_config.jsonce945ef831c9972c135b5b198a03d8eeb70478cd69c0238f24caf1903a9965e6
textmodel.onnx4dbe762b11e36488304471e439cde89da053ad7acaddbf9e096745d142ec8d8b
textconfig.json4d5923d94bbc4e29864de837df14c138c12b93a0b738c64f3ca41f0c539e17b7
texttokenizer.jsonb68d571997a1f81bf521fb73806740ddb91e4ed6666cb6e996c066bb289cf55b
textspecial_tokens_map.json2cdb3b8331a60c92fc1e55a13e9fd61fd2293c5a51275fdcccd62b780052530e
texttokenizer_config.json6bdcee9ccce2a16ca2b4c0c5ed00b42c50ea225f4472a8c4c1e963a2902c2881
textvocab.json5047b556ce86ccaf6aa22b3ffccfc52d391ea4accdab9c2f2407da5b742d4363
textmerges.txt9fd691f7c8039210e0fced15865466c65820d09b63988b0174bfe25de299051a

The cache directory

semlith refuses to load weights from a model cache owned by another account or writable by other users, and names the chown or chmod that fixes it. A directory somebody else can write to is a model somebody else chooses.

Checking a digest yourself

Hugging Face publishes the SHA-256 of every LFS file in its tree API, and the small files can be hashed directly:

curl -sSL "https://huggingface.co/onnx-community/granite-embedding-small-english-r2-ONNX/resolve/1dc7835ba0cb9c76a3618d0bf0c427c97671b3c8/tokenizer.json" \
  | shasum -a 256
curl -sS "https://huggingface.co/api/models/Qdrant/clip-ViT-B-32-vision/tree/e0c24ed0fa57fa3e4f97f30de74c51d944036ace?recursive=true" \
  | jq -r '.[] | select(.lfs) | "\(.path) \(.lfs.oid)"'

Updating a pin

Changing a model is a release, not a patch to a table. The commit and every digest move together, docs/models.md and the constants are updated in the same change, and CHANGELOG.md says which model moved and why. A store’s vectors are comparable only with vectors from the model that built it.