I fit a complete offline voice agent into 1.2 GB on Android

I fit a complete offline voice agent into 1.2 GB on Android

I maintain the Soniqo speech stack, and I built this prototype to test whether a useful mobile voice agent really needs a large general-purpose model.

The complete voice-to-action pipeline runs on a Galaxy S23 Ultra:

Silero VAD → Parakeet-EOU 120M STT → FunctionGemma 270M → Android action → Pocket TTS

After the initial model download, speech recognition, action selection and speech synthesis all run locally. No recorded audio or transcript is sent to a server. The complete pipeline uses approximately 1.2 GB of memory on my S23 Ultra.

It currently supports:

  • Looking up a contact or phone number
  • Opening the Android dialer for a contact
  • Dialling a number spoken by the user
  • Listing or searching music stored on the device
  • Playing and stopping local music
  • Setting the media volume
  • Explaining which actions are currently available

Calls are not placed silently—the agent opens Android’s dialer so the user remains in control.

FunctionGemma is paired with a 9.5 MB adapter trained on a compact command-to-action format. It produces one structured tool call rather than an open-ended conversational response.

One useful design choice was filtering the available tools according to the current device state. For example, stop_music is only presented to the model while music is playing. This prevents some invalid actions before generation instead of asking the model to reason around them.

The model is also not trusted to invent outcomes. Contact and music searches are checked against real device data. The spoken confirmation comes from the result returned by Android rather than what the model predicted would happen.

This is still a deliberately narrow prototype, not a replacement for Google Assistant. The experiment is about whether small local models can provide fast, inspectable device control without routing private speech through a cloud service.

Demo: https://www.youtube.com/watch?v=7L7_Uvvxtv0 Source and signed APK: https://github.com/soniqo/speech-android

I’m curious where others would draw the boundary: keep extending a constrained action router, or introduce a larger planner once commands require multiple steps?

u/ivan_digital — 7 days ago

Our agent's LLM is 270M params and runs on a phone — LoRA on the tool schema + state-gated tools made it reliable (open source, 1.2 GB RAM)

I maintain speech-android (Apache-2.0). We built a voice agent that runs entirely on an Android phone — VAD → streaming STT → tool-calling LLM → device action → TTS reply — in 1.2 GB of RAM, answering 0.9 s after you stop talking (measured on a Galaxy S23 Ultra).

The router is FunctionGemma 270M. Two things made a model that small reliable as an agent:

  1. We LoRA-tuned a 9.5 MB adapter on the exact tool schema and a compact serialization of device state — not a generic function-calling tune.
  2. At runtime it's only offered tools valid in the current state: no stop_music when nothing is playing. Shrinking the action space did more than any prompt engineering.

There's no keyword/regex fallback — routing is entirely the model's decision. The tool call itself takes 294 ms (12-run mean).

Anyone else running sub-1B routers in a real agent loop? Where did yours start breaking?

reddit.com
u/ivan_digital — 1 month ago
▲ 13 r/androiddev+2 crossposts

Fully offline voice assistant — FOSS (Apache-2.0), no Google services, works in airplane mode

I maintain speech-android (Apache-2.0). We published a demo voice assistant that runs the whole loop on the device: local VAD, local streaming speech recognition, a 270M function-calling model that picks the action, and local TTS for the answer. After the one-time model download (~560 MB) it works in airplane mode — there is no server side, no Google services, and no trackers.

Runs on arm64 phones, Android 8+, using about 1.2 GB of RAM while active (measured on a Galaxy S23).

90-second demo: https://youtu.be/7L7_Uvvxtv0 APK (GitHub releases) + source: https://github.com/soniqo/speech-android

u/ivan_digital — 1 month ago
▲ 0 r/Kotlin

A full offline voice agent (VAD → STT → 270M LLM tool calls → TTS) driven from Kotlin through one Flow

I maintain speech-android. We just published a demo where you speak a command and the phone executes it and answers out loud, fully offline — here's what it looks like:

https://preview.redd.it/q3j9e57hz0eh1.jpg?width=1280&format=pjpg&auto=webp&s=52fac524cfc8844165c33f5962f0fcb019ba4c46

90-second video: https://youtu.be/7L7_Uvvxtv0

The interesting Kotlin part is how thin the API stayed: the whole C++ pipeline (VAD, streaming STT, tool-calling LLM, streaming TTS) surfaces as one Flow of sealed events over a ~250-line JNI bridge.

val pipeline = SpeechPipeline(SpeechConfig(modelDir = modelDir))

pipeline.events.collect { event ->
    when (event) {
        is SpeechEvent.TranscriptionCompleted -> route(event.text)
        is SpeechEvent.ResponseDone -> pipeline.resumeListening()
        else -> {}
    }
}

For the LLM stage we deliberately did not depend on the runtime: the SDK ships the prompt formatter and tool-call parser, and you adapt your LiteRT-LM engine to a one-method FunctionGemma.Runtime interface.

On a Galaxy S23 Ultra: 908 ms from end of speech to the first TTS sample, 1,116 MB resident for the entire app (measured 2026-07-17). How the memory budget breaks down per model — and how the same pipeline fits on an iPhone and a desktop — is in our write-up: https://www.soniqo.audio/blog/on-device-voice-agents

Source, demo APK, and the SDK (Apache-2.0): https://github.com/soniqo/speech-android

reddit.com
u/ivan_digital — 1 month ago

speech-core v0.0.10: packaged local speech-to-text and TTS CLI for Linux, amd64 and arm64

I maintain speech-core. v0.0.10 ships prebuilt Linux packages, so the speech-to-text and TTS tools no longer need to be compiled from the examples.

Install on Debian or Ubuntu:

VERSION=0.0.10
ARCH="$(dpkg --print-architecture)"
curl -fLO "https://github.com/soniqo/speech-core/releases/download/v${VERSION}/speech_${VERSION}_${ARCH}.deb"
sudo apt install "./speech_${VERSION}_${ARCH}.deb"

Then:

speech download-models
speech transcribe recording.wav
speech speak "Hello world" hello.wav
speech phonemize "Bonjour le monde" fr

The package bundles the ONNX Runtime/LiteRT libraries but not the model files. Model downloads are explicit and inference runs locally.

Source and packages: https://github.com/soniqo/speech-core

I would particularly appreciate installation reports from Debian 12 and arm64 boards.

reddit.com
u/ivan_digital — 1 month ago
▲ 5 r/foss

speech-core v0.0.10: local STT, TTS, diarization and voice-agent pipelines in C++17

I maintain speech-core, an Apache-2.0 C++17 engine for local speech processing on Linux, Windows and Android.

It includes voice activity detection, batch and streaming speech-to-text, speaker diarization, text-to-speech and the pipeline needed to connect these components into a voice agent. Native model implementations use ONNX Runtime or LiteRT.

v0.0.10 includes Parakeet-EOU streaming ASR, Whisper ONNX, beam search with contextual phrase biasing, Kokoro TTS through ONNX and LiteRT, and Linux packages for amd64 and arm64.

Install on Debian or Ubuntu:

VERSION=0.0.10
ARCH="$(dpkg --print-architecture)"
curl -fLO "https://github.com/soniqo/speech-core/releases/download/v${VERSION}/speech_${VERSION}_${ARCH}.deb"
sudo apt install "./speech_${VERSION}_${ARCH}.deb"

speech download-models
speech transcribe recording.wav

Source and packages: https://github.com/soniqo/speech-core

Runtime libraries are bundled, while model downloads remain explicit.

reddit.com
u/ivan_digital — 1 month ago
▲ 2 r/foss

Speech Studio: Apache 2.0 local voice cloning desktop app

I built Speech Studio, an Apache 2.0 desktop app for local voice cloning and multi-speaker script rendering.

Source: https://github.com/soniqo/speech-studio

It is built on the speech-swift library/CLI: https://github.com/soniqo/speech-swift

I also published a benchmark for several local voice-cloning models across English, German, Arabic, Spanish, and Chinese:

https://www.soniqo.audio/blog/voice-cloning-benchmarks

The benchmark includes reference audio, generated audio, speaker similarity, WER/CER, generated audio length, and RTF.

u/ivan_digital — 2 months ago
▲ 20 r/AudioAI+5 crossposts

I benchmarked local voice cloning models across English, German, Arabic, Spanish, and Chinese

I put together a dataset-backed benchmark for local voice cloning models:


- OmniVoice int8
- Chatterbox Multilingual fp16
- VoxCPM2 bf16
- Fish Audio S2 Pro fp16


It uses Google FLEURS test clips as references, then reports speaker similarity, WER/CER, generated audio length, and RTF. I also included the reference audio and generated clips for every row, so the table is not just numbers.


Post:
https://www.soniqo.audio/blog/voice-cloning-benchmarks


The result that surprised me most: OmniVoice was the best all-around row set in this run, but the language-by-language behavior is more interesting than the aggregate. VoxCPM2 was strong on Arabic speaker match; Fish Audio had strong German/Arabic similarity but slower RTF; Chatterbox looked good on Arabic/Spanish but I am not benchmarking Chinese until the Swift tokenizer path is ready.


I maintain the Soniqo speech stack, so this is self-promo, but the benchmark is meant to be useful/reproducible rather than a launch post.


Speech Studio is the open-source desktop app built on the same stack:


https://www.soniqo.audio/speech-studio
https://github.com/soniqo/speech-studio


What model/language should I add next?
u/ivan_digital — 1 month ago

Open source Swift library for on-device speech AI — ASR that beats Whisper Large v3, full-duplex speech-to-speech, native async/await

We just published speech-swift — an open-source Swift library for on-device speech AI on Apple Silicon.

The library ships ASR, TTS, VAD, speaker diarization, and full-duplex speech-to-speech. Everything runs locally via MLX (GPU) or CoreML (Neural Engine). Native async/await API throughout.


let model = try await Qwen3ASRModel.fromPretrained()

let text = model.transcribe(audio: samples, sampleRate: 16000)

One command build, models auto-download, no Python runtime, no C++ bridge.

The ASR models outperform Whisper Large v3 on LibriSpeech — including a 634 MB CoreML model running entirely on the Neural Engine, leaving CPU and GPU completely free. 20 seconds of audio transcribed in under 0.5 seconds.

We also just shipped PersonaPlex 7B — full-duplex speech-to-speech (audio in, audio out, one model, no ASR→LLM→TTS pipeline) running faster than real-time on M2 Max.

Full benchmark breakdown + architecture deep-dive: https://blog.ivan.digital/we-beat-whisper-large-v3-with-a-600m-model-running-entirely-on-your-mac-20e6ce191174

Library: github.com/soniqo/speech-swift

Would love feedback from anyone building speech features in Swift — especially around CoreML KV cache patterns and MLX threading.

reddit.com
u/ivan_digital — 2 months ago
▲ 9 r/nvidia

NVIDIA Nemotron-3.5 ASR streaming, ported to Apple Silicon — runs on the Neural Engine, full WER parity vs fp32

NVIDIA released Nemotron-3.5-ASR-Streaming-0.6B last month. Same FastConformer + RNN-T family as their earlier English-only streaming model, but trained on 40 language-locales and conditioned by a prompt kernel that injects a one-hot language slot into every encoded frame — so the same 600M weights serve every language. Ported it to Apple Silicon end-to-end:

  • CoreML INT8 bundle that routes through the Apple Neural Engine (encoder + RNN-T decoder + joint network)
  • MLX bf16 / 8-bit / 4-bit bundles for GPU-resident inference

WER (FLEURS test, 50 samples/language, M5 Pro):

lang fp32 NeMo source CoreML INT8 (ANE) MLX bf16 MLX 4-bit
en_us 9.33 9.59 10.36 15.98
de_de 10.22 10.41 10.87 14.96
fr_fr 11.13 12.18 11.62 15.85
ar_eg 13.27 13.37 13.76 20.88
hi_in 5.26 4.42 5.36 8.13
ja_jp 16.97 * 17.66 * 17.33 * 19.56 *

*char-level scoring, matching NVIDIA's CJK methodology.

INT8 palletization, MLX bf16, and MLX 8-bit all stay within ±0.3 pp WER of the fp32 PyTorch reference. MLX 4-bit costs ~6 pp on average in exchange for the smallest disk (473 MB) and streaming RSS (747 MB) — useful when disk or RAM is the binding constraint.

Architecture preserved — exported through EncDecRNNTBPEModelWithPrompt (restored from EncDecHybridRNNTCTCBPEModelWithPrompt since the prompt-RNNT target class hasn't been released yet):

audio → mel (NeMo FilterbankFeatures-equivalent) → 24-layer cache-aware FastConformer encoder (1024 hidden) → prompt kernel: Linear(1152→2048) → ReLU → Linear(2048→1024) (folds one-hot language_mask into every encoded frame) → RNN-T: 2-layer LSTM predictor (640 hidden) + joint over 13 087 BPE

Streaming caches — attention KV [24, 1, 56, 1024], depthwise conv [24, 1, 1024, 8], mel pre_cache — flow chunk-to-chunk so context survives the 320 ms boundaries. Streaming RTF on M5 Pro is 0.068 with the CoreML INT8 bundle (p50 chunk latency 18.6 ms, p99 23.4 ms).

Bit-identical Swift↔Python WER on 5 of 6 languages — to validate the Apple-side numbers I ported Whisper's BasicTextNormalizer + EnglishTextNormalizer + the English number-words state machine to Swift. The lone 0.04 pp Hindi delta traces to a single ANE non-determinism sample, not a port bug.

Bundles (Apache 2.0 SDK; bundles carry NVIDIA's eval license, linked on each model card):

Repo: https://github.com/soniqo/speech-swift Guide: https://soniqo.audio/guides/nemotron

u/ivan_digital — 3 months ago

Ported NVIDIA Nemotron-3.5 multilingual streaming ASR to Apple Silicon — 40 languages, runs on the Neural Engine, open source

NVIDIA released Nemotron-3.5-ASR-Streaming-0.6B last month — a cache-aware FastConformer + RNN-T trained on 40 language-locales, native punctuation and capitalization (no post-processor), 320 ms streaming chunks. I ported it to Apple Silicon and shipped four open bundles plus a Swift SDK.

Bundles (M5 Pro numbers):

Variant On-disk Streaming peak Encoder
CoreML INT8 612 MB 1238 MB ANE
MLX bf16 1217 MB 1474 MB GPU
MLX 8-bit 732 MB 997 MB GPU
MLX 4-bit 473 MB 747 MB GPU

WER (FLEURS test, vs fp32 NeMo source, Whisper EnglishTextNormalizer for en, BasicTextNormalizer split_letters=True for hi/ja):

lang CoreML INT8 MLX bf16 MLX 4-bit fp32 source
en_us 9.59 10.36 15.98 9.33
de_de 10.41 10.87 14.96 10.22
fr_fr 12.18 11.62 15.85 11.13
hi_in 4.42 5.36 8.13 5.26
ja_jp 17.66 * 17.33 * 19.56 * 16.97 *
  • char-level (NVIDIA methodology for CJK)

CoreML INT8, MLX bf16, MLX 8-bit are within ±0.3 pp WER of fp32. MLX 4-bit costs ~6 pp on average for the smallest disk + streaming RSS.

Swift SDK:

import NemotronStreamingASR
let model = try await NemotronStreamingASRModel.fromPretrained()
for await partial in model.transcribeStream(audio: samples, sampleRate: 16000, language: "ja-JP") {
     print(partial.text, partial.isFinal)
}

CLI:

brew install soniqo/tap/speech
speech transcribe meeting.wav --engine nemotron --language de-DE

Bit-identical Swift↔Python WER on 5 of 6 languages — to verify Apple-side ports of HF model cards' WER claims, I ported Whisper's BasicTextNormalizer and EnglishTextNormalizer + the English number-words state machine to Swift.

Repo: https://github.com/soniqo/speech-swift HF: https://huggingface.co/aufklarer Guide: https://soniqo.audio/guides/nemotron

Apache 2.0 SDK; the model bundles carry NVIDIA's eval license (linked on each HF model card).

reddit.com
u/ivan_digital — 3 months ago

[C++] speech-core — on-device voice-agent runtime: VAD + STT + diarization + TTS, Apache 2.0

C++17 runtime for real-time voice agents: VAD-driven turn detection, interruption handling, speech queue with cancel/resume, plus reference model wrappers behind abstract STT / TTS / VAD / LLM interfaces (bring your own backend if you prefer).

Models wired up, all on-device CPU:

- VAD: Silero v5

- STT: Parakeet TDT v3 (batch) · Nemotron Speech Streaming 0.6B (true streaming RNN-T, ~80 ms partials) · Omnilingual ASR CTC-300M (multilingual)

- Diarization: Pyannote Segmentation 3.0 + WeSpeaker ResNet34-LM, composed in pure C++

- TTS: VoxCPM2 (2B, 48 kHz, zero-shot voice cloning) · Kokoro 82M

- Enhancement: DeepFilterNet3

Two interchangeable backends: ONNX Runtime and LiteRT (Google's ai-edge-litert). Both CPU today; CUDA / TensorRT EP just landed on the ONNX path (gated, default off). Runs on Linux x86_64 + aarch64, Windows x86_64, Android. Stable C ABI for FFI (Swift, Kotlin, Python, …). The orchestration core has zero ML dependencies.

https://github.com/soniqo/speech-core

u/ivan_digital — 3 months ago

speech-core — open-source C++17 runtime for on-device VAD + streaming STT + diarization + TTS

C++17 runtime that composes several open speech models behind a small interface layer:

  • Silero VAD → StreamingVAD (4-state hysteresis: silence / pendingSpeech / speech / pendingSilence)
  • Parakeet TDT v3 (FastConformer encoder INT8 + decoder-joint FP32 RNN-T state; CTC fallback)
  • Nemotron Speech Streaming 0.6B (cache-aware FastConformer + RNN-T, true streaming)
  • Omnilingual ASR CTC-300M (Wav2Vec2 + CTC, SentencePiece decode)
  • Pyannote Segmentation 3.0 + WeSpeaker ResNet34-LM → constrained agglomerative clustering in pure C++ (no ML-runtime dep)
  • VoxCPM2 (2B AR LM + AudioVAE, 48 kHz, zero-shot voice cloning, 4-graph pipeline: text_prefill → token_step ×N → audio_decoder)
  • Kokoro 82M, DeepFilterNet3

Two interchangeable backends — ONNX Runtime and LiteRT (libLiteRt from Google's ai-edge-litert wheel) — both CPU today; CUDA / TensorRT EP just landed on the ONNX path (build-flag gated, env-resolved, runtime-probed, CPU fallback). Build the orchestration core alone (zero ML deps) or with either / both backends.

C++17, Apache 2.0, Linux + Windows + Android, stable C ABI for FFI.

https://github.com/soniqo/speech-core

u/ivan_digital — 18 days ago
▲ 16 r/iOSProgramming+1 crossposts

Speech Studio — I open-sourced a local voice cloning Mac app (free, no API keys)

Just open-sourced Speech Studio — a Mac app for voice cloning and multi-speaker dialog generation, runs entirely on-device on Apple Silicon. No API keys, no cloud, no per-character billing.

Drop a reference clip → cloned voice. Drop another → second voice. Write a multi-speaker scene with inline emotion markers like (whispering) or (intense). Hit Synthesize.

30-sec A/B/C blind test vs ElevenLabs: https://youtu.be/EuIU8tOWyzg

(Same voice, three takes — real, Speech Studio local, ElevenLabs cloud.)

In the comments — feedback welcome, especially on the cloning quality.

u/ivan_digital — 2 months ago
▲ 0 r/swift

Demo Video: Local Speech AI in Swift on Apple Silicon

Hey r/swift,

I made a short demo video of speech-swift, an open-source Swift library for local speech AI on Apple Silicon.

It shows realtime transcription, speech-to-speech, voice cloning, and local TTS running without a cloud speech API.

The voiceover in the video was generated with the library’s voice cloning / TTS capabilities, using my own voice reference.

Video: https://www.youtube.com/watch?v=x9zgcaW0gUk
Repo: https://github.com/soniqo/speech-swift

Would love feedback from Swift developers.

u/ivan_digital — 3 months ago
▲ 77 r/voicemod+12 crossposts

I created a RecognitionService that handles system-wide voice input fully on-device (no Google, no network)

Most voice input on Android - SpeechRecognizer.createSpeechRecognizer(context) calls — gets routed to Google's network-backed recognizer. I wanted that path to run locally, so I wrote one.

The service hooks the framework's SpeechRecognizer API. Once it's set as the default, any app calling createSpeechRecognizer(context) (no ComponentName) ends up in our pipeline and gets back transcription that never left the device. Pipeline is Silero VAD + Parakeet TDT v3 (114 languages, ~890 MB INT8) on ONNX Runtime with NNAPI.

Honest caveat: Gboard, Samsung Keyboard, and Google Assistant ship their own recognizers and skip the system default. So the default-IME voice button on most phones won't go through this. What does: accessibility tools, custom dictation UIs, and anything calling the framework API directly.

Models download on first use (~1.2 GB) via a foreground WorkManager job so it survives backgrounding. After that, fully offline.

Setup + demo APK: github.com/soniqo/speech-android

audio.soniqo:speech:0.0.9 on Maven Central

Library:

Happy to answer questions about the binder lifecycle, the foreground worker setup, or why SpeechRecognizer is such a tarpit of edge cases.

u/ivan_digital — 6 days ago