zsfm-rs — Zero-Shot Forecasters & Tabular Foundation Models in Rust
One
zsfmbinary, 16 foundation models, no Python. Download real weights from Hugging Face, convert to a single self-contained GGUF file, and run zero-shot inference locally via Burn.
Repo: amaye15/zsfm-rs (renamed from zero-shot-forecasters-gguf) · Binary: zsfm · Workspace: zsfm-rs/
What is this?
zsfm-rs is a Rust workspace that ports 11 zero-shot time-series forecasters and 5 tabular foundation models to a unified GGUF + Burn stack. Each port is verified bit-exact (or within F32 rounding, ~1e-6–1e-7) tensor-for-tensor against the original PyTorch checkpoint before being considered done — no re-training, no approximations, just the original published weights re-encoded.
There are two halves — plus a cleanup helper:
- Convert —
zsfm <model> convertdownloads the model's real weights from Hugging Face and writes a single*.gguffile. - Infer —
zsfm <model> infer --gguf <file> < request.jsonloads that GGUF and returns a forecast / tabular prediction. No PyTorch, no Python runtime. - Delete —
zsfm <model> deleteremoves the cachedmodel-f32.gguf+config.json(and optionally an output GGUF) to free disk (zsfm chronos delete --output gguf/chronos-f16.gguf).
The same GGUF file works with any GGUF consumer; infer just happens to use Burn.
Docs: User Guide (mdBook) · API docs (rustdoc) · Benchmark
Supported models
Time-series forecasters (11)
All read a numeric context (past values) + horizon (steps to predict) and return an OpenAI-compatible { point, quantiles } forecast. Toto / Moirai / Moirai-2 also support batch and multivariate input.
Tabular foundation models (5)
Zero-shot in-context classification/regression: give a small labeled support set + query rows, get predictions in one forward pass — no fine-tuning.
| Model | HF weights | Original code | Paper | License | Class. | Regr. |
|---|---|---|---|---|---|---|
| Mitra (AutoGluon) | autogluon/mitra-classifier / mitra-regressor | autogluon/autogluon | Mitra | Apache-2.0 | ✅ | ✅ |
| TabDPT (Layer6) | Layer6/TabDPT | layer6ai-labs/TabDPT-inference | TabDPT | Apache-2.0 | ✅ | ✅ |
| TabICL v2 | jingang/TabICL | soda-inria/tabicl | TabICL / v2 | BSD-3-Clause | ✅ | ❌ |
| TabPFN-3 (Prior Labs) | Prior-Labs/tabpfn_3 | PriorLabs/TabPFN | TabPFN-3 | Non-commercial ⚠️ | ✅ | ❌ |
| TabFM (Google) | google/tabfm-1.0.0-pytorch | google-research/tabfm | Blog post | Non-commercial ⚠️ | ✅ | ✅ |
⚠️ Moirai / TabPFN-3 / TabFM are non-commercial only (research & internal evaluation). TiRex is free commercially unless your org's consolidated annual revenue exceeds €100M and you ship TiRex in a commercial product/service. See Licensing and docs-guide/src/licensing.md.
Installation
Option 1: cargo install (like cargo install ripgrep)
From crates.io (requires a recent stable Rust toolchain from rustup.rs):
cargo install zsfm --locked
zsfm --help
From git (latest main):
cargo install --git https://github.com/amaye15/zsfm-rs zsfm --locked
# or pin a tag:
cargo install --git https://github.com/amaye15/zsfm-rs --tag v0.3.1 zsfm --locked
From a local checkout:
git clone https://github.com/amaye15/zsfm-rs.git
cargo install --path zsfm-rs/crates/zsfm --locked
# or, build without installing:
cargo build --release -p zsfm --manifest-path zsfm-rs/Cargo.toml
./zsfm-rs/target/release/zsfm --help
--lockeduses theCargo.locktested in CI for reproducible builds. Omit it if you want the latest compatible dependencies.
Option 2: pre-built binary
Binaries for Linux (x86_64-unknown-linux-gnu) and macOS Apple Silicon (aarch64-apple-darwin) are published on the Releases page:
tar xzf zsfm-v0.3.1-aarch64-apple-darwin.tar.gz
sudo mv zsfm*/*zsfm /usr/local/bin/
zsfm --help
macOS Intel (
x86_64-apple-darwin) is not currently published (GitHub's free Intel runner capacity was cut). Usecargo installabove instead — the workspace builds fine on Intel Macs.
On macOS the release profile automatically links Apple's Accelerate framework for faster BLAS; on Linux/Windows it falls back to the portable CPU backend with no extra setup.
Verify:
zsfm --help # one subcommand per model
zsfm chronos --help # per-model: convert / infer / upload / inspect-tensors / delete
zsfm chronos delete --help # per-model cache deletion
Quick start
Time-series forecasting (Moirai-2, ~45 MB)
# 1. Download + convert to GGUF (cached at models/Salesforce__moirai-2.0-R-small/model-f32.gguf)
zsfm moirai2 convert
# 2. Build a request (Moirai batches series; univariate models take a flat array)
cat > request.json <<'EOF'
{
"context": [[10.0, 10.5, 11.0, 10.8, 11.2, 11.5, 11.3, 11.8, 12.0, 12.2]],
"horizon": 4
}
EOF
# 3. Forecast
zsfm moirai2 infer --gguf gguf/moirai2-f32.gguf < request.json
Response (OpenAI-compatible):
{
"id": "forecast-...",
"object": "forecast",
"model": "moirai-2",
"choices": [{ "index": 0, "forecast": { "point": [12.35, 12.51, 12.64, 12.72] } }]
}
Univariate models use a flat array instead:
zsfm chronos convert
echo '{"context": [1,2,3,4,5,6,7,8], "horizon": 4}' | zsfm chronos infer --gguf gguf/chronos-f16.gguf
Tabular prediction (Mitra)
zsfm mitra convert --model autogluon/mitra-classifier --task classification
cat > request.json <<'EOF'
{
"x_support": [[0.1, 1.2], [0.9, -0.3], [-1.1, 0.4], [1.5, 1.1]],
"y_support": [0, 1, 1, 0],
"x_query": [[0.2, 0.9], [-0.8, 0.1]],
"n_classes": 2
}
EOF
zsfm mitra infer --gguf gguf/mitra-classification-f32.gguf < request.json
# {"task":"classification","probabilities":[[0.83,0.17],[0.21,0.79]]}
See Time-series forecasters and Tabular foundation models for every model's exact request/response shape, flags, and caveats.
Python (uv + pyo3)
The same models are available from Python (uv + maturin + pyo3):
# from a local checkout (editable, recommended for development)
uv sync && uv run maturin develop # or: pip install -e . (needs maturin)
uv run python -c "import zsfm; print(zsfm.list_models())"
# from git / crates.io (once published)
uv pip install "zsfm @ git+https://github.com/amaye15/zsfm-rs"
uv pip install zsfm # crates.io / PyPI
import zsfm
print(zsfm.__version__, zsfm.list_forecasters()[:3])
# download + convert (like `zsfm ttm convert`)
zsfm.convert("ttm", dtype="f32")
# load and forecast (like `zsfm ttm infer`)
model = zsfm.TtmModel("gguf/ttm-f32.gguf", config="models/ibm-granite__granite-timeseries-ttm-r2/config.json")
print(model.forecast([10.0, 10.5, 11.0, 10.8], horizon=4))
# tabular (like `zsfm mitra infer`)
mitra = zsfm.MitraModel("gguf/mitra-classification-f32.gguf", task="classification")
print(mitra.predict_classification([[0.1, 1.2]], [0], [[0.2, 0.9]], n_classes=2))
# delete cache (like `zsfm ttm delete`)
zsfm.delete("ttm")
See Python bindings for the full API (11 forecasters + 5 tabular, numpy arrays, convert/delete dispatch).
CLI overview
Every model is a subcommand: zsfm <model> <action>.
| Action | What it does |
|---|---|
convert |
Download from Hugging Face and write a GGUF file |
infer |
Load a GGUF and run inference on JSON from stdin → JSON to stdout |
upload |
Push source + GGUF to a Hugging Face repo you control |
inspect-tensors |
Print every tensor name/shape/dtype in a local checkpoint |
delete |
Remove the cached canonical GGUF + config for this model (and optionally an output GGUF) |
zsfm <model> --help
zsfm <model> convert --help
zsfm <model> infer --help
zsfm <model> delete --help
Model caching
The first convert for a model:
- Downloads raw weights from HF.
- Writes a canonical F32 GGUF to
<model-dir>/<owner>__<repo>/model-f32.gguf(defaultmodel-dirismodels/). - Deletes the raw weight files (they're redundant once the canonical GGUF exists).
Later converts for the same model — at any --dtype — recast from that cache with no network:
zsfm chronos convert --dtype f32 # downloads
zsfm chronos convert --dtype f16 # <1s, from cache
zsfm chronos convert --dtype q8 # <1s, from cache (Q8_0 block-quant; tiny tensors stay F32)
zsfm chronos convert --redownload # force a fresh download
To free disk, remove the cache (and optionally an output file):
zsfm chronos delete # removes models/amazon__chronos-2/
zsfm chronos delete --output gguf/chronos-f16.gguf # also removes the converted file
zsfm chronos delete --model custom/repo --model-dir /tmp/models # custom locations
Most convert commands accept --dtype f32|f16|q8. BF16 is available only through the generic converter (the model loaders support F32/F16/Q8_0).
Generic converter
TabICL and TabPFN-3 ship checkpoints whose tensor names are used unchanged, so they go through a model-agnostic path (also works for any HF repo):
zsfm convert --repo jingang/TabICL --file classifier-v2 --format ckpt -o gguf/tabicl-v2-f32.gguf
zsfm convert --repo Prior-Labs/tabpfn_3 --file classifier-v3_default --format ckpt -o gguf/tabpfn-v3-f32.gguf
# re-quantize an existing GGUF:
zsfm convert existing.gguf -o smaller.gguf --dtype q8
--file is a case-insensitive substring to disambiguate repos that publish multiple checkpoints.
Benchmark
zsfm-bench (zsfm-rs/crates/zsfm-bench/) links all 11 forecasters in-process via the shared zsfm_core::Forecaster trait — each GGUF is loaded once and independent models run concurrently.
# convert the models you want to evaluate first
zsfm ttm convert && zsfm moirai2 convert
# single dataset
zsfm-bench run --dataset ETTh1 --models ttm,moirai2 --windows 30
# full sweep (21 datasets × models × horizons/contexts) → writes ../benchmark.md
zsfm-bench report
Results are cached in benchmark/bench_cache.json (gitignored) so re-runs and interruptions only compute what's missing. The full pre-computed results live in benchmark.md:
- 21 datasets across Energy, Climate, Astronomy, Health, Finance, Transport, Hydrology — from ETTh/m (Transformer benchmark) through Monash and Informer collections to Jena/Saugeen.
- 30 rolling windows at
context=512, horizon=96for the main table, plus horizon sweeps (96/192/336/720) and context sweeps (96/256/512/1024). - 11 ensemble strategies evaluated over all non-trivial model subsets (mean, median, inverse-MAE weighted, trimmed mean, softmax-weighted, geometric mean, online adaptive, Hedge, EMA-smoothed, greedy selection, per-horizon and uncertainty-weighted).
- Metrics: MAE, RMSE, MASE (scale-free vs. naïve 1-step baseline), and per-window inference latency.
Highlights from benchmark.md:140-167 (best ensemble vs. best solo, context=512, horizon=96, 30 windows): ensembles beat the best single model on 19/21 datasets (up to −9.7% on Wind), with the median-based strategy (~) winning most often; only aus_electricity and weather favor a solo model. The appendix in benchmark.md:212-952 has per-dataset Top-10 ensembles.
See also benchmark/optimisation_log.md for inference latency optimisation history (LTO, Accelerate, etc.).
Project structure
.
├── Cargo.toml # root workspace (so `cargo install --git https://github.com/amaye15/zsfm-rs zsfm` works)
├── pyproject.toml # Python project (uv + maturin + pyo3, module `zsfm`)
├── zsfm-rs/ # Rust workspace (all code lives here; also usable as `cargo build -p zsfm --manifest-path zsfm-rs/Cargo.toml`)
│ ├── Cargo.toml # workspace + shared [profile.release] (lto=fat, opt-level=3, strip)
│ ├── crates/
│ │ ├── zsfm/ # `zsfm` binary — one subcommand module per model (`cargo install zsfm`)
│ │ ├── zsfm-python/ # `zsfm` Python extension (pyo3, `import zsfm`)
│ │ ├── zsfm-gguf/ # GGUF reader/writer
│ │ ├── zsfm-hub/ # HF download/upload + canonical-F32 cache helpers
│ │ ├── zsfm-checkpoint/ # checkpoint loader (safetensors/pickle/ONNX/HDF5/npz/ckpt/gguf) + dtype casting
│ │ ├── zsfm-core/ # `Forecaster` trait, JSON envelope, config helpers
│ │ ├── zsfm-burn/ # shared Burn tensor ops + direct GGUF weight loading
│ │ ├── zsfm-bench/ # in-process benchmark + ensembling
│ │ └── models/ # one crate per model (arch + weight mapping + Burn inference)
│ │ ├── toto/ chronos/ timesfm/ sundial/ ttm/ lag_llama/ moment/
│ │ ├── moirai/ moirai2/ flowstate/ tirex/ # forecasters
│ │ └── mitra/ tabdpt/ tabicl/ tabpfn/ tabfm/ # tabular
│ ├── scripts/publish-crates.sh
│ └── tools/export-weights.py
├── docs-guide/ # mdBook user guide (published to GitHub Pages /guide/)
│ └── src/
│ ├── introduction.md
│ ├── installation.md
│ ├── quickstart.md
│ ├── cli-overview.md
│ ├── models/time-series.md
│ ├── models/tabular.md
│ ├── python.md # Python bindings (uv + pyo3 + maturin)
│ ├── licensing.md
│ └── development.md
├── benchmark/
│ ├── data/ # evaluation datasets (not all tracked; see .gitignore)
│ └── optimisation_log.md
├── .github/
│ ├── workflows/ci.yml # cargo build + cargo test (Linux + macOS)
│ ├── workflows/release.yml # cross-platform binaries + crates.io publish (on v* tag)
│ └── workflows/docs.yml # mdBook + rustdoc → GitHub Pages
│ └── pages/ # custom landing page (guide + API cards)
├── tests/
│ └── python/test_zsfm.py # pytest for Python bindings
├── benchmark.md # pre-computed benchmark report (21 datasets, sweeps, ensembles)
└── LICENSE (MIT)
Each model crate under zsfm-rs/crates/models/ owns its architecture, checkpoint weight-name mapping, and Burn inference; zsfm (zsfm-rs/crates/zsfm/) only wires them to the CLI.
Development
# Rust
cd zsfm-rs
cargo build --release --workspace
cargo test --release --workspace # 190 tests (GGUF round-trip, dtype casting, arch shapes, bench logic)
# Python (uv + pyo3 + maturin)
uv sync
uv run maturin develop # or: maturin develop --manifest-path zsfm-rs/crates/zsfm-python/Cargo.toml
uv run pytest tests/python -v # Python binding tests (import zsfm, list_models, delete, etc.)
uv run python -c "import zsfm; print(zsfm.list_models())"
CI (.github/workflows/ci.yml:1) runs both on every push to main and every PR (Linux + macOS) — Rust (cargo build/cargo test) and Python (uv run maturin develop + pytest).
Adding a new model
- New crate at
zsfm-rs/crates/models/<name>/(weight mapping + Burn inference + request/response types). - Module at
zsfm/src/<name>.rs(zsfm-rs/crates/zsfm/src/<name>.rs) withconvert/infersubcommands — follow the caching pattern in ThezsfmCLI (zsfm_hub::canonical_gguf_path+zsfm_checkpoint::recast). - Wire it into
zsfm/src/main.rs:ModelCommand(zsfm-rs/crates/zsfm/src/main.rs). - Add a page under
docs-guide/src/models/and a row in the model table.
See Development for benchmarking and release notes. Pushing a v* tag triggers .github/workflows/release.yml:1 (cross-platform binaries via cargo build -p zsfm + GitHub Release + optional crates.io publish via zsfm-rs/scripts/publish-crates.sh — cargo publish -p zsfm is last).
Documentation
- User guide (mdBook):
docs-guide/→ https://amaye15.github.io/zsfm-rs/guide/ — installation, quick start, CLI overview, per-model pages with exact JSON shapes and links to papers/code/weights. - API reference (rustdoc):
cargo docfor every crate → https://amaye15.github.io/zsfm-rs/api/ - Landing page:
.github/pages/index.html— cards linking to guide + per-crate rustdoc.
Build the guide locally:
cargo install mdbook
mdbook serve docs-guide
Licensing
Code in this repository (converters, GGUF writer/reader, CLI, Burn inference, bench) is MIT — see LICENSE.
Model weights keep the license their original authors published. Converting with zsfm <model> convert downloads the original weights and re-encodes them as GGUF — it does not change ownership or terms. Check before using a GGUF beyond local experimentation:
| License | Models | Commercial use |
|---|---|---|
| Apache-2.0 | Toto-2, Chronos-2, TimesFM 2.5, Sundial, TTM, Lag-Llama, Mitra, TabDPT, FlowState-R1 | ✅ unrestricted |
| MIT | MOMENT | ✅ unrestricted |
| BSD-3-Clause | TabICL | ✅ unrestricted |
| NXAI Community | TiRex | ✅ unless your org's annual revenue > €100M and you ship TiRex in a commercial product/service |
| CC-BY-NC-4.0 | Moirai 1.0, Moirai 2.0 | ❌ non-commercial only |
| tabpfn-3-license-v1.0 | TabPFN-3 | ❌ non-commercial only |
| TabFM Non-Commercial v1.0 | TabFM | ❌ non-commercial only |
For the restricted models, see the license file in each HF repo (linked in the tables above) and docs-guide/src/licensing.md. This project does not interpret or enforce weight licenses.
Acknowledgments
This workspace is a packaging and portability layer — all modeling credit belongs to the original authors. Each crate and each page in Time-series forecasters / Tabular foundation models links back to the original Hugging Face weights, source repository, and paper. If you use these models in academic work, please cite the relevant paper(s) from those pages.
Built on Burn and the GGUF format (ggml/ggerganov).
Maintained at amaye15/zsfm-rs. Issues and PRs welcome — see Development. Feedback on the tooling itself: https://github.com/anomalyco/opencode.
Metadata
Release files for zsfm 0.3.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| zsfm-0.3.1.tar.gz | 306.8 kB | Without provenance | Details |
Built distributions (wheels)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| zsfm-0.3.1-cp312-cp312-manylinux_2_28_x86_64.whl | CPython 3.12 | CPython 3.12 | Linux glibc 2.28+ x86-64 | Details |
| zsfm-0.3.1-cp312-cp312-macosx_11_0_arm64.whl | CPython 3.12 | CPython 3.12 | macOS 11.0+ ARM64 | Details |
Total release size: 10.1 MB
Release files / zsfm-0.3.1.tar.gz
| Download URL | zsfm-0.3.1.tar.gz |
|---|---|
| Size | 306.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
1c2b623406f47d4d11f4497ed18ac29fe58f7e5cccb8f995b39c431e9ea44be2
|
|
BLAKE2b-256 checksum How to use checksums |
3bc1e2c5b5c441543e9c715d459d40f2b6ead03683e4e2cae44a41ec5750c0bd
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.15
|
Release files / zsfm-0.3.1-cp312-cp312-manylinux_2_28_x86_64.whl
| Download URL | zsfm-0.3.1-cp312-cp312-manylinux_2_28_x86_64.whl |
|---|---|
| Size | 5.4 MB |
| Tags | CPython 3.12 Linux glibc 2.28+ x86-64 |
|
SHA-256 checksum How to use checksums |
aae0578830add45334b9d49a7b81be660968984d76d806ced59b201d69dedb31
|
|
BLAKE2b-256 checksum How to use checksums |
36fc288cc4a791575db43ef435cf2ada120e31cbde237d03924839f964a0979f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.15
|
Release files / zsfm-0.3.1-cp312-cp312-macosx_11_0_arm64.whl
| Download URL | zsfm-0.3.1-cp312-cp312-macosx_11_0_arm64.whl |
|---|---|
| Size | 4.4 MB |
| Tags | CPython 3.12 macOS 11.0+ ARM64 |
|
SHA-256 checksum How to use checksums |
dd7adbdedf961ed389934461bd5f141c351ac7717f60062f6f09421474293fe9
|
|
BLAKE2b-256 checksum How to use checksums |
cf790262804f68149391c5560142fbe9982bb32316b3cdf78939b36e24575ee3
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.15
|
Security for zsfm 0.3.1
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.