This release is a pre-release and may not be stable for production use.
linguonnx
CPU-first language technology on ONNX Runtime, in the spirit of
onnx-asr (speech recognition) and
phoonnx (text to speech).
Two packages under one namespace:
linguonnx.detect— language identification over ONNX exports of four fastText classifiers: GlotLID, fastText's classic lid.176, OpenLID and OpenLID-v2.linguonnx.translate— machine translation over Marian (opus-mt), M2M100, NLLB-200 and MADLAD exports, with a routing graph that decides which model, or which chain of models, connects a language pair.
Runtime dependencies are onnxruntime, numpy, sentencepiece,
huggingface_hub and langcodes. No torch, at any point. The
encoder-decoder generation loop, beam search and KV cache included, is written
against the raw ONNX graphs, because pulling in torch to run a 150 MB
quantised model is a trade nobody on a small device wants to make.
Install
pip install linguonnx
pip install linguonnx[distance] # adds orthography2ipa, for pivot ranking
By default every session runs on CPUExecutionProvider. onnxruntime and
onnxruntime-gpu provide the same onnxruntime import namespace and must
never both be installed, so the gpu extra cannot pull onnxruntime-gpu in
automatically without breaking that constraint - it is a no-op label. A GPU
host swaps the runtime itself, in the same environment linguonnx is already
installed in:
pip uninstall onnxruntime
pip install onnxruntime-gpu
export LINGUONNX_ONNX_PROVIDERS=auto # or a CSV list, e.g. CUDAExecutionProvider
auto picks the best provider the installed ONNX Runtime build actually
offers, always falling back to CPU. A requested provider that fails to
initialize - a CUDA build with no CUDA device, say - is something ONNX Runtime
does not raise on; it silently runs the next provider in the list instead.
linguonnx checks this after building each session and logs a warning when
the active provider is not the one that was asked for, so a misconfigured GPU
deployment does not run on CPU indefinitely without anyone noticing. See
linguonnx/providers.py. Whether a given model architecture actually runs on
a non-CPU provider depends on the ops in its exported ONNX graph and has to be
verified per architecture on real hardware; provider selection here is honest
about what got selected, not a claim that every export runs on it.
Models download from HuggingFace on first use and are cached under
~/.cache/linguonnx/models/<model_id>/. Set LINGUONNX_CACHE to put that
somewhere else — on a server the weights are tens of gigabytes and $HOME is
usually the small root volume:
export LINGUONNX_CACHE=/mnt/bulk/linguonnx
Identify a language
from linguonnx import load_detector
det = load_detector() # glotlid-int8, 425 MB on first use
print(det.detect("Egun on, zer moduz?")) # 'eu'
print(det.detect_probs("Bon dia a tothom", top_k=3)) # {'ca': 0.998, ...}
print(det.detect_raw("وش لونك يا خوي")) # ('ars_Arab', 0.99)
print(det.detect("وش لونك يا خوي", collapse_varieties=True)) # 'ar'
GlotLID labels 2102 varieties, not macrolanguages, so colloquial Arabic
comes back as a dialect (ars, Najdi) and Chinese may come back as Cantonese.
That is free text-side dialect identification when you want it and a nuisance
when you do not, which is what collapse_varieties is for. See
docs/detect.md.
Translate
from linguonnx import load_translator
tx = load_translator()
print(tx.translate("bom dia, como estás?", src="pt", tgt="en"))
# 'Good morning, how are you?' via opus-mt-pt-en-int8, 172 MB
A pair no single model covers is chained through a third language, and the
Route comes back with the translation so a pivot is never silent:
route = tx.route("pt", "eu", prefer="dedicated")
print(route.model_ids) # ('opus-mt-pt-ca-int8', 'mt-hitz-ca-eu-int8')
print(route.pivots) # ('ca',) — it went through Catalan
print(route.license_tier) # 'permissive' — the worst licence on the chain
A per-model size budget turns a single large hop into a chain of small ones, for a host that cannot afford the download:
frugal = load_translator(max_model_mb=500) # or LINGUONNX_MAX_MODEL_MB
See docs/translate.md for the API and docs/routing.md for how a route is chosen.
What ships
| Language identification | 5 models, 176 to 2102 labels, 33 MB to 1.7 GB |
| Translation | 377 registry entries — 188 int8 and 189 fp32 across 189 models |
| Reachable languages | 593, over the default graph |
| Default LID model | glotlid-int8 — Apache-2.0, the only permissive LID option |
| Default translation graph | every permissive int8 model, fewest hops, capped at 2 |
| Licences | Apache-2.0, MIT and CC-BY-4.0 by default; GPL-3.0 and CC-BY-NC-4.0 must be asked for by name |
linguonnx itself is Apache-2.0 and downloads no model you did not ask for.
Some of the models are not: OpenLID is GPL-3.0 and NLLB-200 is CC-BY-NC-4.0,
so non-commercial models are kept out of the default translation graph
entirely. docs/licences.md explains what that costs and how
to opt in.
Documentation
- docs/detect.md — language identification: the four models, the variety labels, hierarchical softmax, BCP-47 mapping.
- docs/translate.md — the translation API, and how each architecture picks its target language. Get that wrong and nothing raises.
- docs/routing.md — capabilities rather than edges, the
preferpolicies, hop caps, the size budget, pivot ranking, pinning a route yourself. - docs/models.md — the registry, what is in it, and the
generated
sync_registry.pyworkflow that keeps it honest. - docs/licences.md — the licence tiers and what
NoRouteErrortells you when a licence is what blocks a pair.
Runnable scripts live in examples/. Each says in its docstring
what it demonstrates and what it downloads.
Use it from OpenVoiceOS
ovos-plugin-linguonnx
wraps this library as two OVOS plugins from one install: a language detector
(opm.lang.detect, id ovos-lang-detect-plugin-linguonnx) and a translator
(opm.lang.translate, id ovos-translate-plugin-linguonnx). Both load their
models on first use, and every knob in load_detector and load_translator is
reachable from mycroft.conf.
Development
uv pip install -e .[test]
pytest test/ -m "not network" # unit tests: no download, no model
pytest test/ # also runs the real model downloads
python scripts/check_docs.py # execute every code sample in these docs
# (needs network: it downloads real models,
# same as `pytest -m network`; not run in CI)
Routing and decoding are tested without any real model. The graph is pure
data, and the decode loop runs against a few-kilobyte ONNX seq2seq built in the
test file, using the same past_key_values.* / present.* naming the real
exports use. The network-marked tests then compare the hand-written decoder to
transformers and optimum on the real graphs, string for string — those two
are test-only dependencies and must never appear in linguonnx/.
Related projects
linguonnx answers "what language is this text in?" and "say it in another
one". These siblings answer the neighbouring questions, and are worth reaching
for instead of stretching this library to cover them:
- scriptconv — the writing
system rather than the language: zero-dependency ISO-15924 script detection
and metadata, plus conversions between phoneme notations (IPA ↔ ARPABET,
X-SAMPA, Kirshenbaum, Cotovía, RFE), Buckwalter ↔ Arabic, Hangul → jamo and
kana. A GlotLID label carries a script subtag (
zho_Hans,srp_Cyrl); use scriptconv when the script itself is the thing you need to identify or transliterate. - ovos-lang-parser —
language names rather than text: parses a spoken or written language name
into a BCP-47 code, and renders a BCP-47 code back into a spoken name. Pair
it with
linguonnxwhen a user says or reads a language name ("translate this to Brazilian Portuguese") and you need the tag, or when you want to speak a detected tag back to them. - phoonnx — text to speech on ONNX Runtime, 1000+ languages.
- onnx-asr — speech to text on ONNX Runtime; the structural model this library follows.
Metadata
Release files for linguonnx 0.14.2a1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| linguonnx-0.14.2a1.tar.gz | 293.2 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| linguonnx-0.14.2a1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 452.2 kB
Release files / linguonnx-0.14.2a1.tar.gz
| Download URL | linguonnx-0.14.2a1.tar.gz |
|---|---|
| Size | 293.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
4f74bab9a7fc80d50a6b00ba364452dcf5c6f6a426be800671f4430e8ff4d57d
|
|
BLAKE2b-256 checksum How to use checksums |
a10a07c34e47e72e029b70d763dad5464865c8e22cac3b58592155dc57cde691
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Release files / linguonnx-0.14.2a1-py3-none-any.whl
| Download URL | linguonnx-0.14.2a1-py3-none-any.whl |
|---|---|
| Size | 159.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
10093ab66fec1f60d195f2f3134372c7311e3242ad0ad4e731cc9a40d083b6ac
|
|
BLAKE2b-256 checksum How to use checksums |
7c8cd02af817565d265a1ccbfe4b2a7e9e612ca6e0f0d9c7d1ed64afccb2726e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|