Private local STT on your Mac. Speech never leaves the machine. No cloud. No $20/month subscription.
- Option+Space — speak, text pastes at the cursor
- $0/month — not SuperWhisper or cloud dictation
- Native menu bar app + one local model (Qwen 1.7B 8-bit by default; Whisper from the menu)
- Same API for agents — full transcript with timecodes, language per request, loopback only
curl -fsS http://127.0.0.1:8765/healthz
curl -fsS -F file=@sample.wav http://127.0.0.1:8765/v1/audio/transcriptions
curl -fsS -F file=@meeting.wav -F response_format=verbose_json -F language=auto \
http://127.0.0.1:8765/v1/audio/transcriptionsDictation stays Russian (qwen3_asr.language: ru). Agents override the language on a single request.
Apple Silicon Mac, macOS 13+, uv.
git clone https://github.com/FUYOH666/VoiceToText.git
cd VoiceToText
uv sync --extra qwen
macos/scripts/build.sh
uv run python src/vtt2/main.py --installThat starts two LaunchAgents: ai.vtt2.stt (model + HTTP on 127.0.0.1:8765) and ai.vtt2 (Swift VoiceToText.app).
Grant Microphone, Accessibility, and Input Monitoring to VoiceToText — not Terminal, not python.
After an ad-hoc rebuild, macOS treats the binary as a new app: remove the old VoiceToText row in Privacy, add macos/dist/VoiceToText.app again, turn the toggles on.
uv run python src/vtt2/main.py --status
curl -fsS http://127.0.0.1:8765/healthzOpen source — run it yourself.
Or I can deploy, customize, and integrate it for your team in 2 weeks.
Free consultation: private@scanovich.ai · Telegram @ScanovichAI
- Option+Space starts recording
- Speak
- Option+Space stops
- The menu bar POSTs audio to local STT and pastes the text
The orange microphone pill is macOS (privacy). The waveform is VoiceToText.
Agents use the same POST /v1/audio/transcriptions — docs/STT_API.md. verbose_json returns the full text plus segment start/end. language=auto or a code such as language=en applies to that file only.
Default profile loads Qwen3-ASR 8-bit on demand and unloads after 15 minutes idle. Always-on, same checkpoint: config.resident.yaml. Whisper is the menu item, not a second process.
stt_server:
host: "127.0.0.1"
port: 8765
engine: qwen3_asr
preload_on_start: false
idle_unload_seconds: 900
transcription:
engine: local_stt
qwen3_asr:
model_name: "moona3k/mlx-qwen3-asr-1.7b-8bit"
quantization_bits: null
language: "ru"The checkpoint is pre-quantized 8-bit, group 64, about 2 GB. quantization_bits: null so it is not quantized again. Install it with the server: uv sync --extra qwen. A later plain uv sync removes that extra and the default server exits instead of falling back to Whisper.
uv sync --extra qwen
launchctl kickstart -k "gui/$(id -u)/ai.vtt2.stt"On this Mac the first request downloaded 2.0 GB and answered a 3-second clip in 43 seconds. The same clip with the model already loaded took 0.22 seconds. After 15 minutes idle the weights unload; the next request loads that snapshot again and does not quantize it.
Whisper stays in the menu. That item rewrites stt_server.engine and restarts only the STT process. On 8 GB RAM the Whisper checkpoint to pick is whisper-medium-mlx. Remote GPU ASR stays under transcription.engine: remote_asr (host in gitignored .env.vtt2). model= on the POST does not switch checkpoints. Details: docs/STT_API.md.
Paste does nothing: Privacy → Accessibility and Input Monitoring → VoiceToText. Text is also on the clipboard (Cmd+V). Menu: «Разрешения…».
Daily builds sign with the login-keychain identity vtt-local-codesign, so those two grants survive the next rebuild. Create it once: macos/scripts/setup-local-signing.sh. The first rebuild after an ad-hoc signature still needs one grant. Without that identity, build.sh signs ad-hoc and macOS drops the grants every time.
STT slow / readyz 503: idle-unload. healthz is enough; the next POST loads the model. Always-on: cp config.resident.yaml config.yaml && uv run python src/vtt2/main.py --install.
After a git pull of STT: restart the model process so agents see the new contract — launchctl kickstart -k gui/$(id -u)/ai.vtt2.stt. Menubar can stay running.
Two menu bar icons: uv run python src/vtt2/main.py --install kills leftover UI (not --serve-stt).
Logs: ~/Library/Logs/vtt2/ (voicetotext.log, stt.stdout.log).
uv run pytest
macos/scripts/test.shNotarized .app needs Apple Developer + macos/Signing.xcconfig (not in git) and macos/scripts/release.sh. No signed public zip is attached until a Developer ID exists.
MIT. Built with MLX.
