Skip to main content
Pre-release

This release is a pre-release and may not be stable for production use.

 loom-py

import loom

model = loom.Model.from_pretrained("loom-ai-org/lfm2-350m-monolithic-loom")
print(model.text2text.infer("The capital of France is", max_new_tokens=14))
# ':\nA) Paris\nB) Lyon\nC) Marseille\nD'

Two APIs, and which one you want

The high-level API is one door per task, named for the modality pair it maps between. Every model carries all of them; the ones it does not answer to say so when called, naming what it actually is.

model.text2text.infer("The capital of France is")        # -> str
model.speech2text.infer(waveform, language="en")         # -> Transcription
model.text2speech.infer("hello world")                   # -> Audio

Which door a model answers is read off the file, not guessed from its name:

model.task            # 'automatic-speech-recognition'
model.capabilities    # ('speech2text',)
model.text2speech.infer("hi")
# UnsupportedTask: this model is speech2text (automatic-speech-recognition), not text2speech.

Each door does the whole job, including the parts you cannot do from outside. speech2text windows audio for a model whose graph is built at one clip length, decodes with the early stop armed, splits the output into timestamped segments and seeks to where the model closed its last segment — so an utterance straddling a window edge is re-decoded whole rather than arriving as two fragments:

r = model.speech2text.infer(audio, language="en", timestamps=True)
r.text                        # the joined transcript
r.segments[0].start, .end     # seconds, whole-file
r.timestamped                 # whether those are boundaries the model chose

text2speech returns audio with its rate attached, because a bare list of floats played at the wrong rate does not fail — it plays at the wrong speed:

audio = model.text2speech.infer("hello world", steps=8, seed=1)
audio.sample_rate             # 22050
audio.save("out.wav")

The low-level API is the driver's own entry point, and stays raw. infer passes your arguments straight through, so which ones a model takes is a property of the model rather than of this package:

model.tokenize("The capital of France is")   # [1, 1098, 5706, 803, 4481, 856]
model.detokenize([1, 1098, 5706])            # '<|startoftext|>The capital'
model.infer(tokens=[16, 40, 22, 30], n_steps=4, seed=1234)   # the driver's own inputs
print(model.driver_source)                   # the Lua that will run, and what it accepts

Use it when you want a knob the high-level door does not name — VITS's noise_scale_w, a specific voice vector, a model's own second entry point. That is the boundary between the two: a knob with no canonical role is reachable through infer and nowhere else.

Text to speech, and the one step that is not in the file

Every TTS model here takes text now, but two of them get there differently. Supertonic encodes graphemes itself. The other four consume phoneme ids — and the symbol table that turns phonemes into their ids ships in the GGUF, so model.tokenizer is a real vocabulary for all of them:

model.tokenizer                      # <loom.Tokenizer 'phonemes' size=159>
model.tokenize("h\u0259\u02c8lo\u028a")             # -> ids, with the model's own BOS/blank/EOS assembly

What is not in the file is grapheme-to-phoneme, because that is a property of the language rather than of any checkpoint. It is an optional extra:

pip install "loom-py-rt[phonemes]"

With it, text2speech.infer("hello world") works on all five. Without it, passing phonemes= or tokens= works exactly as before and only the text door is absent, with an error naming the install. loom.phonemizers.register("ipa", fn) substitutes your own.

Choosing a device

A wheel built with a GPU backend uses it by default; one built without has only a CPU to find, so nothing changes.

model = loom.Model.from_file("qwen3.gguf")                  # decide for me (or $LOOM_DEVICE)
model = loom.Model.from_file("qwen3.gguf", device="cpu")    # pin it
model = loom.Model.from_file("qwen3.gguf", device="gpu")    # demand one; raises if there is none
model.device, model.device_description   # ('Vulkan0', 'AMD Radeon Vega 3 Graphics (RADV RAVEN2)')

"gpu" raises rather than falling back, because a caller who spelled it out is asking a question about the machine and a silent CPU run is how a large slowdown goes unnoticed. "auto" — the default — is the one that falls back.

The base wheel is CPU-only, and an accelerator is a separate install rather than a different wheel:

pip install "loom-py-rt[vulkan]"

That adds one small package holding one libggml-vulkan.so, which this package finds at import; device="auto" then uses it and nothing about the base wheel changes. The reason it works this way — rather than a full wheel per accelerator, which is the more familiar shape — is that a Vulkan backend is 46.5 MB and CUDA is larger, so the per-accelerator matrix does not fit PyPI's 100 MB per-file ceiling. See packaging/README.md.

loom.devices()   # [{'name': 'Vulkan0', 'description': 'AMD Radeon Vega 3 Graphics (RADV RAVEN2)', ...}]

Worth calling after installing one, because a backend whose driver is too old — or which finds no supported device — loads without error and registers nothing, and the only other symptom is a model running at CPU speed. Note that with this build every backend is loaded at run time, the CPU included, so an empty device list means no backend library was found at all rather than no accelerator.

Which ops fall back to the CPU, and why some always will, is documented in loom.cpp's own build notes.

Why there is so little API

A loom GGUF carries its own graph topologies and its own driver script alongside its weights, so this package contains no per-architecture code at all. Loading a model registers whatever topologies the file declares and attaches a KV cache to the ones that say they need it; running one calls the driver the file shipped with. A model this library has never heard of works the day loom-exporter can produce it.

That is also why infer takes **kwargs: its arguments are the driver's arguments, and which ones a model takes is a property of the model. model.driver_source prints the Lua that will run, whose header comment documents its inputs — that is the authority.

model = loom.Model.from_file("granite_speech_mil.gguf")
model.architecture          # 'granite-speech'
model.topologies            # ['encoder', 'embed', 'decoder', 'lm_head']
model.hparam("samples_per_chunk")   # 192000
print(model.driver_source)  # what infer() will run, and what it accepts

Supported models

Seventeen, published at huggingface.co/loom-ai-org and loadable by id with from_pretrained (needs the [hub] extra). This package has no per-architecture code, so the list is a property of loom-exporter, not of anything here.

Language models

Model Exported from
loom-ai-org/qwen3-0.6b-base-loom Qwen/Qwen3-0.6B-Base
loom-ai-org/lfm2-350m-monolithic-loom LiquidAI/LFM2-350M
loom-ai-org/lfm2-350m-modular-loom LiquidAI/LFM2-350M
loom-ai-org/smollm2-360m-instruct-loom HuggingFaceTB/SmolLM2-360M-Instruct
loom-ai-org/gemma-3-270m-it-loom google/gemma-3-270m-it

These are the models text2text answers to; model.generate(...) is the same call under the name it shipped with.

Speech recognition

Model Exported from
loom-ai-org/whisper-small-loom openai/whisper-small
loom-ai-org/conformer-ctc-small-loom nvidia/stt_en_conformer_ctc_small
loom-ai-org/parakeet-tdt-0.6b-loom nvidia/parakeet-tdt-0.6b-v3
loom-ai-org/parakeet-rnnt-0.6b-loom nvidia/parakeet-rnnt-0.6b
loom-ai-org/gigaam-v3-rnnt-loom ai-sage/GigaAM-v3
loom-ai-org/qwen3-asr-0.6b-loom Qwen/Qwen3-ASR-0.6B
loom-ai-org/granite-speech-4.0-1b-loom ibm-granite/granite-4.0-1b-speech

model.speech2text.infer(waveform, language="en") — the mel frontend is inside the graph, so a raw waveform is the input, and long audio is windowed and seeked for you.

Speech synthesis

Model Exported from
loom-ai-org/kokoro-82m-loom hexgrad/Kokoro-82M
loom-ai-org/matcha-tts-ljspeech-loom Matcha-TTS (LJSpeech checkpoint)
loom-ai-org/supertonic-2-loom Supertone/supertonic-2
loom-ai-org/vits-piper-en-gb-miro-loom OpenVoiceOS/pipertts_en-GB_miro
loom-ai-org/styletts2-ljspeech-loom yl4579/StyleTTS2-LJSpeech

All five take text through model.text2speech.infer(...); the four phoneme-input ones need the [phonemes] extra for the G2P step (see above), and every one of them accepts phonemes= or tokens= without it. Kokoro and Supertonic ship a default voice, so a published file speaks on its own. model.driver_source is the authority on what each driver accepts.

The three repos

loom.cpp the engine, vendored here as a submodule
loom-exporter produces the GGUFs this runs
loom-py this one

Installing

pip install loom-py-rt           # once published -- `loom-py` on PyPI clashes with `loompy`,
                                  # and `loom-engine` normalizes to the already-taken `loomengine`
pip install loom-py-rt[hub]      # + from_pretrained()

From a checkout — note --recursive, since the engine is a submodule:

git clone --recursive https://github.com/loom-ai-org/loom-py
cd loom-py && pip install -e .

No runtime dependencies. Arrays cross the boundary as plain sequences of floats, so numpy is something you may use rather than something this package makes you install — list, array.array, numpy arrays and torch tensors all work.

Testing

pytest tests/ci      # the Python layer: coercion and error paths. No model. What CI runs.
pytest tests/gate    # a real exported GGUF, end to end.
export LOOM_TEST_MODEL=~/loom-fixtures/matcha_mil.gguf
export LOOM_TEST_MODEL_INPUTS='{"tokens":[16,40,22,30,12,3],"n_steps":4,"seed":1234}'
pytest tests/gate -q

The gate suite is written against no particular architecture on purpose: it asserts the shape of what a loom model is, which is the whole of what this package knows. A test that expected one model's inputs would be this package learning about a model, which is the thing the design exists to avoid.

Roadmap

Shared with loom.cpp, because three of the four are the engine's and this package inherits them by having no per-architecture code of its own.

1. GPUs and NPUs — the packaging is built; the backends beyond Vulkan are what remain. The engine schedules a graph across a device backend and a CPU fallback, this package exposes the choice as device= (above), and the wheel shape that lets an accelerator ship at all now exists.

The shape that is NOT wanted is a wheel per accelerator per architecture — PyPI's wheel tags have no accelerator dimension, so that is torch's cu121 arrangement, and it multiplies every future backend by every existing platform. GGML_BACKEND_DL makes the better shape possible and this package is now built that way: one arch-tagged base wheel, plus small backend packages that drop a .so where ggml looks for it, so pip install "loom-py-rt[cuda]" means "also fetch that backend", device="auto" finds it, and a Raspberry Pi installs nothing extra. packaging/rt-vulkan/ is the worked example; a CUDA package is that directory with two strings changed, waiting only on a machine with an NVIDIA GPU to build and test against. Tracked as BACKLOG.md P4.8, along with which backends are reachable at all (CUDA, OpenVINO and Qualcomm's are already in the pinned ggml; CoreML and RKNPU2 are not).

2. Wheels for more platforms. Linux x86-64 today; next macOS on Intel, macOS on Apple Silicon and Linux on ARM. This is the item most visible from here, since it is what pip install loom-py-rt can resolve to.

3. More models — P5 in the ledger, ordered by coverage per unit of effort: BERT token classifiers (the smallest possible template, and the first non-audio task) → codec decoders → CNN+CTC and SANM encoders → the remaining TTS families → text encoder-decoders → small classifiers → music. Each lands here for free: a model this package has never heard of works the day the exporter can produce it.

4. The follow-ups the docs already nameBACKLOG.md is the ledger for all three repos and the authority. The ones that would show up in this API: a permissively-licensed phonemiser, which is what would give the four phoneme-input TTS models a tokenize; and the KvCache memory redesign and quantized KV cache, which decide how large a model this can run on a given machine.

Licence

MIT — see LICENSE.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

loom_py_rt-1.0.0rc4.tar.gz (1.8 MB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

loom_py_rt-1.0.0rc4-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl (7.6 MB view details)

Uploaded CPython 3.13manylinux: glibc 2.27+ x86-64manylinux: glibc 2.28+ x86-64

loom_py_rt-1.0.0rc4-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl (4.2 MB view details)

Uploaded CPython 3.13manylinux: glibc 2.27+ ARM64manylinux: glibc 2.28+ ARM64

loom_py_rt-1.0.0rc4-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl (7.6 MB view details)

Uploaded CPython 3.12manylinux: glibc 2.27+ x86-64manylinux: glibc 2.28+ x86-64

loom_py_rt-1.0.0rc4-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl (4.2 MB view details)

Uploaded CPython 3.12manylinux: glibc 2.27+ ARM64manylinux: glibc 2.28+ ARM64

loom_py_rt-1.0.0rc4-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl (7.6 MB view details)

Uploaded CPython 3.11manylinux: glibc 2.27+ x86-64manylinux: glibc 2.28+ x86-64

loom_py_rt-1.0.0rc4-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl (4.2 MB view details)

Uploaded CPython 3.11manylinux: glibc 2.27+ ARM64manylinux: glibc 2.28+ ARM64

loom_py_rt-1.0.0rc4-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl (7.6 MB view details)

Uploaded CPython 3.10manylinux: glibc 2.27+ x86-64manylinux: glibc 2.28+ x86-64

loom_py_rt-1.0.0rc4-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl (4.2 MB view details)

Uploaded CPython 3.10manylinux: glibc 2.27+ ARM64manylinux: glibc 2.28+ ARM64

File details

Details for the file loom_py_rt-1.0.0rc4.tar.gz.

File metadata

  • Download URL: loom_py_rt-1.0.0rc4.tar.gz
  • Upload date:
  • Size: 1.8 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for loom_py_rt-1.0.0rc4.tar.gz
Algorithm Hash digest
SHA256 6debcd1979863d00033c71f777b8b9bad0ddb87fe685c6d96c51932ab7bdc2cd
MD5 2d12c04b01408ea077f19cbba5700f34
BLAKE2b-256 2b54d77509d72a4e365c1901e59b0c42255ce8f071fccbf2f0f9e2f674dd422a

See more details on using hashes here.

Provenance

The following attestation bundles were made for loom_py_rt-1.0.0rc4.tar.gz:

Publisher: wheels.yml on loom-ai-org/loom-py

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file loom_py_rt-1.0.0rc4-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for loom_py_rt-1.0.0rc4-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 3e76c8cbfdb92fa0c2f3ec6a0a5b12d870e9cd32fd1fa76bad1c5257d9cd63a2
MD5 d3ddc65f47db0781959bc247a1b9b134
BLAKE2b-256 41573e9f0ed3ebdc33aedd2981b05fcff11fd6c206d6e21ea48c12dd3828ca9b

See more details on using hashes here.

Provenance

The following attestation bundles were made for loom_py_rt-1.0.0rc4-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl:

Publisher: wheels.yml on loom-ai-org/loom-py

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file loom_py_rt-1.0.0rc4-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl.

File metadata

File hashes

Hashes for loom_py_rt-1.0.0rc4-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl
Algorithm Hash digest
SHA256 52803e31defb98fd55daeb53d6cbc8b08a3102ae09c14be15607e74e0411a389
MD5 83d5992a90359535cdd185ff843e1872
BLAKE2b-256 76908815a66336c2c672cc0fa0c37d16aea63a071c02019be107ac0a358037a3

See more details on using hashes here.

Provenance

The following attestation bundles were made for loom_py_rt-1.0.0rc4-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl:

Publisher: wheels.yml on loom-ai-org/loom-py

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file loom_py_rt-1.0.0rc4-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for loom_py_rt-1.0.0rc4-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 8625b14df34d4c068fcccad61764a3ab03f2bbc345eb14a92686a4bf430f013e
MD5 4a4e0643232839669210348fffd26155
BLAKE2b-256 1957e3a28f91936c23a3cc20328899bccc0ffff5ecb76b12b1d7b0e0fa50201f

See more details on using hashes here.

Provenance

The following attestation bundles were made for loom_py_rt-1.0.0rc4-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl:

Publisher: wheels.yml on loom-ai-org/loom-py

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file loom_py_rt-1.0.0rc4-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl.

File metadata

File hashes

Hashes for loom_py_rt-1.0.0rc4-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl
Algorithm Hash digest
SHA256 55a913f09e1a8d482f9aa6ae16b3ede70ed8c261ac71776801a453835183f2c0
MD5 29c9f2cb4ead09d081b733da2af0f675
BLAKE2b-256 55ce276eca7026661b353a28d7c162ef931b2a23cb133db003f7cc0cc33dd102

See more details on using hashes here.

Provenance

The following attestation bundles were made for loom_py_rt-1.0.0rc4-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl:

Publisher: wheels.yml on loom-ai-org/loom-py

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file loom_py_rt-1.0.0rc4-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for loom_py_rt-1.0.0rc4-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 0a354bbb5465621ae8efd3f1ee0552cbe93df4d6bb1682f389e0aaea6e65cef1
MD5 d001c3b999cdbc9c04b2310e0b1a0560
BLAKE2b-256 fb227356bb282c2960bb81b5c4a4ee9ed66514843956a871b346417964d5f602

See more details on using hashes here.

Provenance

The following attestation bundles were made for loom_py_rt-1.0.0rc4-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl:

Publisher: wheels.yml on loom-ai-org/loom-py

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file loom_py_rt-1.0.0rc4-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl.

File metadata

File hashes

Hashes for loom_py_rt-1.0.0rc4-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl
Algorithm Hash digest
SHA256 848f3feda4d844baeac13f5f10a9a9ea8a1ffc62608160bf503f8fa25be951ef
MD5 00bcca198b7c62c027ba795abb292bb6
BLAKE2b-256 dad7242637a0bda1af9be8f9c614e62b36f88c76d758327208c32a10891a4674

See more details on using hashes here.

Provenance

The following attestation bundles were made for loom_py_rt-1.0.0rc4-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl:

Publisher: wheels.yml on loom-ai-org/loom-py

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file loom_py_rt-1.0.0rc4-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for loom_py_rt-1.0.0rc4-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 33c7b880f5f8ab12c3019393298f60984c659011f3cf10378b954cd1606cf155
MD5 162d8cc0ab772c698bfab0eb84769207
BLAKE2b-256 82a36605982eb2dc93abcafd86de970644418c96237690940ac8d798e9ba7c59

See more details on using hashes here.

Provenance

The following attestation bundles were made for loom_py_rt-1.0.0rc4-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl:

Publisher: wheels.yml on loom-ai-org/loom-py

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file loom_py_rt-1.0.0rc4-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl.

File metadata

File hashes

Hashes for loom_py_rt-1.0.0rc4-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl
Algorithm Hash digest
SHA256 f090805b17e4ee7a8e3e0dc8738111a40a9ab19275f8571640ee1a99d22e419e
MD5 5d1969fd96edfc62b2852aed640c1bba
BLAKE2b-256 672c7039d1fb2050bd7e5a094b5c85c50995cd7b04cfb863a86432a2c90d73e8

See more details on using hashes here.

Provenance

The following attestation bundles were made for loom_py_rt-1.0.0rc4-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl:

Publisher: wheels.yml on loom-ai-org/loom-py

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.
Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page