TNT 🧨
Terminal voice-to-text. Tap Space, speak, tap Space — your words land in the transcript and on the clipboard.
Qwen3-ASR-1.7B runs in-process on the Apple GPU via mlx-speech as an 8-bit (int8) quantized checkpoint — ~2.5 GB resident: the model loads once, stays resident, and transcribes a short take in a fraction of a second. Fully local — no cloud, no runtime network calls. The microphone is captured natively through AVFoundation by a small Swift helper process, so a misbehaving audio stack can never trap the mic: TNT just kills the helper and macOS releases it.
[!NOTE] Using Termux on Android? Use the preserved
legacy/android-termux-qwen0.6bbranch instead ofmaster. It is a legacy proot setup and may need device-specific fixes; validate it locally and adapt it with your own tools or agentic AI workflow.git fetch origin git switch --track origin/legacy/android-termux-qwen0.6b
Features
- In-process GPU inference — pure MLX, no PyTorch
- 8-bit quantized — int8 weights (~2.5 GB), about half the memory of BF16 with a faster decode
- Resident model — loads once in the background at startup; every take is warm
- Native mic capture — AVFoundation via an isolated Swift helper process; the mic can always be reclaimed
- English, Chinese, and mixed speech — language auto-detected, or forced via env var
- Live braille oscilloscope — real audio levels while you record
- Clipboard-first — new transcriptions auto-copy; click any past entry to copy it again
- Responsive TUI — side-rail layout on wide terminals, stacked on narrow ones
Setup
[!IMPORTANT] Requires an Apple Silicon Mac (M1 or later), Python 3.13+, uv, and the Xcode command line tools (
xcode-select --install) — the mic capture helper is compiled from Swift on first launch and cached.
git clone https://github.com/appautomaton/tnt-asr.git
cd tnt-asr
uv sync
./bootstrap-mlx-asr.sh # downloads + links the int8 checkpoint (~2.5 GB, cached by Hugging Face)
uv run tnt
Or install from PyPI (automaton-tnt):
uv tool install automaton-tnt
TNT_MLX_MODEL=/path/to/qwen3-asr-1.7b-int8-mlx tnt
(Instead of exporting TNT_MLX_MODEL, you can symlink the checkpoint at
~/.local/share/tnt/qwen3-asr-mlx.)
Model checkpoint
TNT expects a converted Qwen3-ASR-1.7B MLX checkpoint. A ready-to-use int8 build (~2.5 GB) is published at appautomaton/qwen3-asr-1.7b-int8-mlx. The bootstrap script takes three forms:
./bootstrap-mlx-asr.sh # download the int8 build from Hugging Face, then link it
./bootstrap-mlx-asr.sh <hf-repo-id> # download a specific Hugging Face repo
./bootstrap-mlx-asr.sh /path/to/checkpoint # link a checkpoint you already have (no download)
Downloads use huggingface_hub (already installed via mlx-speech) and land in
the shared Hugging Face cache (~/.cache/huggingface); the script symlinks
bin/qwen3-asr-mlx to the cached snapshot. It is idempotent — if the model is
already cached, or you pass a local path, nothing is re-downloaded, so you
never keep two copies of the 2.5 GB weights. BF16 and mxfp8 builds work too —
mlx-speech reads the quantization from the checkpoint's config.json, so
switching is just a relink. Alternatively, convert the upstream
Qwen/Qwen3-ASR-1.7B weights
yourself with mlx-speech's
scripts/convert/qwen3_asr.py.
Configuration
| Environment variable | Default | Description |
|---|---|---|
TNT_MLX_MODEL |
bin/qwen3-asr-mlx, else ~/.local/share/tnt/qwen3-asr-mlx |
Path to the converted MLX checkpoint |
TNT_MLX_LANGUAGE |
auto |
Chinese, English, or auto. Use Chinese to keep mixed Chinese/English speech from being translated to English |
TNT_INPUT_DEVICE |
system default | Microphone, by index or name |
TNT_CAPTURE_BACKEND |
auto |
macOS always uses native AVFoundation (needs the Xcode command line tools: xcode-select --install); other platforms use PortAudio. portaudio is rejected on macOS |
Keybindings
| Key | Action |
|---|---|
| Space | Start / stop recording, or hold to record until release; cancels during transcription |
| c | Copy the last transcript entry |
| mouse click | Copy the clicked transcript entry |
| x | Clear the transcript |
| q | Quit |
Project structure
src/tnt/
├── app.py # Textual TUI, state machine, keybindings
├── audio.py # Recorder protocol, backend selection, PortAudio (non-macOS)
├── avf_audio.py # Native AVFoundation capture via helper process (macOS)
├── mic_helper.swift # AVFoundation helper source, compiled on demand
├── async_threads.py # Daemon-thread helpers for blocking work
├── transcriber.py # In-process MLX Qwen3-ASR transcription
└── widgets/
├── transcript.py # Scrollable transcript log
└── status.py # Braille oscilloscope + state rail
bin/
└── qwen3-asr-mlx # Symlink to converted MLX checkpoint (gitignored)
[!TIP] The inference path expects 16 kHz mono PCM WAV; the recorder produces exactly that. Cancelling a transcription abandons its result — the in-process generation cannot be killed mid-flight and quietly finishes in the background.
Related projects
- mlx-speech — our MLX-native speech runtime that powers TNT (PyPI)
- qwen3-asr-1.7b-int8-mlx — our int8 MLX checkpoint that TNT runs (converted from Qwen3-ASR-1.7B); more on Hugging Face
License
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
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file automaton_tnt-0.1.2.tar.gz.
File metadata
- Download URL: automaton_tnt-0.1.2.tar.gz
- Upload date:
- Size: 56.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d03f72107f8f44c76466c7b42f04f5260d423fd209ae3238f999966b466c785b
|
|
| MD5 |
b1045e15935561e1d4bdfface7e869d6
|
|
| BLAKE2b-256 |
92192bc44f84587d72bec9ca29c9dba971da17723462b0efee5fc4cff1f2ec2b
|
Provenance
The following attestation bundles were made for automaton_tnt-0.1.2.tar.gz:
Publisher:
workflow.yaml on appautomaton/tnt-asr
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
automaton_tnt-0.1.2.tar.gz -
Subject digest:
d03f72107f8f44c76466c7b42f04f5260d423fd209ae3238f999966b466c785b - Sigstore transparency entry: 1914834755
- Sigstore integration time:
-
Permalink:
appautomaton/tnt-asr@d59a2f07d667e7a17b65214abf5ad45a8d82d0c8 -
Branch / Tag:
refs/tags/v0.1.2 - Owner: https://github.com/appautomaton
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
workflow.yaml@d59a2f07d667e7a17b65214abf5ad45a8d82d0c8 -
Trigger Event:
push
-
Statement type:
File details
Details for the file automaton_tnt-0.1.2-py3-none-any.whl.
File metadata
- Download URL: automaton_tnt-0.1.2-py3-none-any.whl
- Upload date:
- Size: 29.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d6471ad747e78e8190a801b6e216225a600f14276c9f8172b5002613363a2c3e
|
|
| MD5 |
81123cd5ebb3000e229fc0802b2cfe07
|
|
| BLAKE2b-256 |
8b95162e95837e277cd30e71758153dfa7e92976734eff91f549e72a896b86ce
|
Provenance
The following attestation bundles were made for automaton_tnt-0.1.2-py3-none-any.whl:
Publisher:
workflow.yaml on appautomaton/tnt-asr
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
automaton_tnt-0.1.2-py3-none-any.whl -
Subject digest:
d6471ad747e78e8190a801b6e216225a600f14276c9f8172b5002613363a2c3e - Sigstore transparency entry: 1914834901
- Sigstore integration time:
-
Permalink:
appautomaton/tnt-asr@d59a2f07d667e7a17b65214abf5ad45a8d82d0c8 -
Branch / Tag:
refs/tags/v0.1.2 - Owner: https://github.com/appautomaton
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
workflow.yaml@d59a2f07d667e7a17b65214abf5ad45a8d82d0c8 -
Trigger Event:
push
-
Statement type: