Skip to content

Latest commit

 

History

221 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

English | 简体中文 | 日本語 | 한국어 | Español | Français | Português | हिन्दी

pi-voicekit

Community continuation of codexstar69/pi-listen (upstream, MIT — dormant since v7.2.2 in May 2026). Not affiliated with the original author. Old name: pi-listen.

pi-voicekit — Voice input and output for the Pi coding agent

Voice in and voice out for Pi. Hold-to-talk STT — Deepgram streaming (cloud) or 21 offline models — plus TTS that speaks the agent's replies (Kitten, Kokoro, Piper, or Deepgram Aura).

npm version license original author

v0.1.3 — current release — audio capture prefers ffmpeg when PULSE_SERVER is set (SSH audio tunnel / remote PulseAudio), so remote microphones record reliably. Voice in and voice out: 21 offline STT models, 20 local TTS voices plus Deepgram Aura, driven by one /voice-settings panel with 6 tabs. The 0.1.x line is documented in the changelog.


See How It Works

Watch demo video
Click to watch the demo video


Setup (2 minutes)

1. Install the extension

# In a regular terminal (not inside Pi)
pi install npm:pi-voicekit

2. Choose your backend

pi-voicekit supports two transcription backends:

Deepgram (cloud) Local models (offline recognition)
How it works Live streaming — text appears as you speak Batch mode — transcribes after you finish recording
Setup API key required No API key, models auto-download on first use
Internet Required Not required after model download for recognition; the polish step may use the network
Latency Real-time interim results 2–10 seconds after recording stops
Languages 56+ with live streaming Depends on model (1–57 languages)
Cost $200 free credit (lasts 6–12 months for most developers) Recognition is free; the polish step may cost money

Run /voice-settings inside Pi to choose your backend and configure everything from one panel.

Option A: Deepgram (recommended for live streaming)

Sign up at dpgr.am/pi-voice — $200 free credit, no card needed.

export DEEPGRAM_API_KEY="your-key-here"    # add to ~/.zshrc or ~/.bashrc

Option B: Local models (offline recognition)

No setup needed — run /voice-settings, switch backend to Local, and select a model. It downloads automatically.

Note: Local models use batch mode — they transcribe after you finish recording, not while you speak. For live streaming as you speak, use Deepgram.

3. Open Pi

On first launch, pi-voicekit checks your setup and tells you what's ready:

  • Backend configured (Deepgram key or local model)
  • Audio capture tool detected (sox, ffmpeg, or arecord)
  • If everything checks out, voice activates immediately

Audio capture

pi-voicekit auto-detects your audio tool. No manual install needed if you already have sox or ffmpeg.

Priority Tool Platforms Install
1 SoX (rec) macOS, Linux, Windows brew install sox / apt install sox / choco install sox
2 ffmpeg macOS, Linux, Windows brew install ffmpeg / apt install ffmpeg
3 arecord Linux only Pre-installed (ALSA)

When PULSE_SERVER is set (SSH audio tunnel or remote PulseAudio) the order becomes ffmpeg → sox → arecord — network Pulse sources need ffmpeg.


Settings Panel

All configuration lives in one place: /voice-settings. Six tabs cover everything you need.

General — backend, language, scope

General settings — backend, model, language, scope, voice toggle

Toggle between Deepgram (cloud, live streaming) and Local (offline, batch mode). Change language, scope, and enable/disable voice — all with keyboard shortcuts.

Models — browse, search, install

Models tab — browse 21 models with accuracy/speed ratings

Browse 21 models from Parakeet, Whisper, Moonshine, SenseVoice, GigaAM, Paraformer, and Qwen3. Each model shows accuracy and speed ratings (●●●●○/●●●●○), fitness badges, and download status. Fuzzy search to find models fast. Press Enter to activate and download.

Downloaded — manage installed models

Downloaded tab — manage installed models, activate or delete

See what's installed, total disk usage, and which model is active. Press Enter to activate, x to delete. Models from Handy are auto-detected and can be imported without re-downloading.

Speak — TTS models and voices

Pick a TTS backend (local sherpa-onnx or Deepgram Aura), browse 20 local voices from ~13 MB, download on selection, and choose a voice per backend. Auto-speak of agent replies is toggled here.

Device — hardware profile and dependencies

Device tab — hardware profile, dependencies, disk space

See your hardware profile (RAM, CPU, GPU), dependency status (sherpa-onnx runtime), available disk space, and total downloaded models. Model recommendations are based on this profile.

Polish — transcript cleanup

Optional post-ASR cleanup, on by default. Toggle it, pick the model, set how many recent conversation turns accompany the transcript (0–10), and cap how long one pass may take (1000–30000 ms). The last row shows the most recent polished dictation as a RAW / POLISHED pair. /voice-polish last prints the newest dictation a pass ran on — including one whose result was discarded — with its STATUS, RAW, and WRITTEN text, or says that nothing was written.

/voice-polish takes on, off, model, turns <0-10>, last and restore; run it with no argument for the current status.


Usage

Keybindings

Action Key Notes
Record to editor Hold SPACE (≥0.7s) Release to finalize. Pre-records during warmup so you don't miss words.
Toggle recording Ctrl+Shift+V Works in all terminals — press to start, press again to stop.
Clear editor Escape × 2 Double-tap within 500ms to clear all text.

How recording works

  1. Hold SPACE — warmup countdown appears, audio capture starts immediately (pre-recording)
  2. Keep holding — live transcription streams into the editor (Deepgram) or audio buffers (local)
  3. Release SPACE — recording continues for 1.5s (tail recording) to catch your last word, then finalizes
  4. Text appears in the editor, ready to send

Commands

Command Description
/voice-settings Settings panel — backend, models, language, scope, device
/voice-models Settings panel (Models tab)
/voice-setup Run the first-run setup wizard
/voice-language Open the settings panel to change language
/voice-speak <text> Speak text out loud (TTS)
/voice-speak-test Speak a sample sentence
/voice-speak-toggle Enable / disable TTS
/voice-stream Toggle Deepgram streaming TTS (cloud)
/voice-speak-stop Stop in-flight TTS playback
/voice-autosubmit Toggle: STT text auto-sent to the agent (on/off)
/voice-polish [sub] Transcript polish: on, off, model, turns, last, restore
/voice-hold-delay Set hold-to-talk delay (200-3000 ms, default 700)
/voice-speak-models Browse / install TTS voice models
/voice-speak-info Diagnose TTS state
/voice-help Keyboard + command reference (or press F1)
/voice test Full diagnostics — audio tool, mic, API key
/voice on / off Enable or disable voice
/voice dictate Continuous dictation (no key hold)
/voice stop Stop active recording or dictation
/voice history Recent transcriptions
/voice Toggle on/off

v7.1 keyboard

While in the settings panel:

Key Action
← → switch tab
↑ ↓ navigate row (skips group headings)
↵ select / activate
esc back to main / close panel
type filter (search)
bksp clear last search char

While an install widget or playback indicator is mounted (no overlay in front):

Key Action
esc cancel active install (most-recent first), then stop playback
F1 open help overlay (always available)

Local Models

21 models across 7 families. Sorted by quality — best models first.

Top picks

Model Accuracy Speed Size Languages Notes
Parakeet TDT v3 ●●●●○ ●●●●○ 671 MB 25 (auto-detect) Best overall. WER 6.3%.
Parakeet TDT v2 ●●●●● ●●●●○ 661 MB English Best English. WER 6.0%.
Whisper Turbo ●●●●○ ●●○○○ 1.0 GB 57 Broadest language support.

Fast and lightweight

Model Accuracy Speed Size Languages Notes
Moonshine v2 Tiny ●●○○○ ●●●●● 43 MB English 34ms latency. Raspberry Pi friendly.
Moonshine Base ●●●○○ ●●●●● 287 MB English Handles accents well.
SenseVoice Small ●●●○○ ●●●●● 228 MB zh/en/ja/ko/yue Best for CJK languages.

Specialist

Model Accuracy Speed Size Languages Notes
GigaAM v3 ●●●●○ ●●●●○ 225 MB Russian 50% lower WER than Whisper on Russian.
Whisper Medium ●●●●○ ●●●○○ 946 MB 57 Good accuracy, medium speed.
Whisper Large v3 ●●●●○ ●○○○○ 1.8 GB 57 Highest Whisper accuracy. Slow on CPU.

Plus 8 language-specialized Moonshine v2 variants for Japanese, Korean, Arabic, Chinese, Ukrainian, Vietnamese, and Spanish.

How local models work

Hold SPACE → audio captured to memory buffer
                ↓
Release SPACE → buffer sent to sherpa-onnx (in-process)
                ↓
         ONNX inference on CPU (2–10 seconds)
                ↓
         Final transcript inserted into editor

Models download automatically on first use. Downloads are resumable, verified after completion, and deduplicated (no double-downloads). The settings panel shows real-time download progress with speed and ETA.

Models from Handy (~/Library/Application Support/com.pais.handy/models/) are auto-detected and can be imported via symlink (zero disk duplication).


Performance

Measured on the maintainer's machine, local CPU, no network, over 70 published utterances (28 Chinese, 28 English, 14 mixed Chinese–English). RTF is processing time divided by audio duration — lower is better, and below 1.0 is faster than real time.

Recogniser Chinese RTF English RTF Mixed RTF Characters per second
paraformer-zh 0.014 0.013 0.016 247–983
sensevoice-small 0.028 0.029 0.040 134–451
whisper-turbo 0.372 0.369 0.395 7–27

End to end — local recognition plus the remote polish call — four real dictations of 8.1–28.0 s came back in an estimated 0.56–2.98 s, an RTF of 0.07–0.15 (recognition time is derived from the measured recognition RTF, not timed per dictation: see docs/BENCHMARKS.md). The segmented polish pass is what keeps that together: the 0.2.x line polished in one call over the whole transcript, so one slow call returned the dictation unpolished, while against a degraded endpoint the segmented pipeline polished 35 of 35 segments where the old path fell back on 100% of the run. whisper-turbo is the slowest of the three by an order of magnitude and the least accurate on this corpus — measured for comparison, not recommended for CPU-only use.

Protocol, all result tables, reproduction commands and the honest limits live in docs/BENCHMARKS.md — GitHub only, because npm ships the extension and this README.


Features

Feature Description
Dual backend Deepgram (cloud, live streaming) or local models (offline, batch) — switch in settings
21 local models Parakeet, Whisper, Moonshine, SenseVoice, GigaAM, Paraformer, Qwen3 — with accuracy/speed ratings
Unified settings panel One overlay panel for all configuration — /voice-settings
Device-aware recommendations Scores models against your hardware. Only best-in-class models get [recommended].
Enterprise download pipeline Pre-checks (disk, network, permissions), live progress with speed/ETA, post-verification
Handy integration Auto-detects models from Handy app, imports via symlink
Audio fallback chain Tries sox → ffmpeg → arecord in order — ffmpeg first when PULSE_SERVER is set
Pre-recording Audio capture starts during warmup — you never miss the first word
Tail recording Keeps recording 1.5s after release so your last word isn't clipped
Live streaming Deepgram Nova 3 WebSocket (Nova 2 for Chinese locales) — live interim transcripts
Transcript polish Optional post-ASR cleanup — the local backend polishes each recogniser segment as it is decoded (up to three calls in flight); the last N conversation turns (default 2) are sent with the first segment, and no conversation context at all when the turn count is zero. Disable with /voice-polish off
56+ languages Deepgram: 56+ with live streaming. Local: up to 57 depending on model.
Continuous dictation /voice dictate for long-form input without holding keys
Typing cooldown Space holds within 400ms of typing are ignored
Sound feedback macOS system sounds for start, stop, and error events
Cross-platform macOS, Windows, Linux — Kitty protocol + non-Kitty fallback

Architecture

# core
extensions/voice.ts                         Main extension — state machine, recording, UI, command surface
extensions/voice/config.ts                  Config loading, saving, migration
extensions/voice/onboarding.ts              First-run wizard, language picker
extensions/voice/audio-tool.ts              Capture tool detection (sox / ffmpeg / arecord)
extensions/voice/hold-to-talk.ts            Hold detection, Kitty and non-Kitty terminals
extensions/voice/release-controller.ts      Recording lifecycle, release handling

# speech-to-text
extensions/voice/deepgram.ts                Deepgram URL builder, API key resolver
extensions/voice/local.ts                   Model catalog (21 models), in-process transcription
extensions/voice/sherpa-engine.ts           sherpa-onnx bindings — recognizer lifecycle, inference
extensions/voice/sherpa-loader.ts           Lazy native module loading
extensions/voice/model-download.ts          Download manager — resume, progress, verification, Handy import
extensions/voice/device.ts                  Device profiling — RAM, GPU, CPU, container detection

# transcript post-processing
extensions/voice/post-process.ts            Polish pass — fail-open guardrails, model resolution, bounded call
extensions/voice/post-process-context.ts    Context assembly — recent turns and character caps
extensions/voice/post-process-prompt.ts     Fixed polish prompt and request shape

# text-to-speech
extensions/voice/speak.ts                   Speak entry point, auto-speak wiring
extensions/voice/tts-engine.ts              sherpa-onnx TTS synthesis
extensions/voice/tts-deepgram.ts            Deepgram Aura voices (cloud)
extensions/voice/tts-local-models.ts        Local TTS catalog — 20 voices (Kitten, Kokoro, Piper)
extensions/voice/tts-playback.ts            Playback, buffering, player detection
extensions/voice/tts-text-filter.ts         Code-block stripping, sentence prep
extensions/voice/tts-onboarding.ts          TTS onboarding flow
extensions/voice/tts-onboarding-overlay.ts  TTS onboarding overlay
extensions/voice/tts-install-progress.ts    Model install progress widget
extensions/voice/tts-playback-indicator.ts  Speaking indicator widget

# settings and UI
extensions/voice/settings-panel.ts          Settings panel — overlay, 6 tabs
extensions/voice/ui-picker.ts               Generic list picker
extensions/voice/ui-help-overlay.ts         Keyboard and command reference
extensions/voice/ui-aura.ts                 Visual primitives (Liquid Braille, Aurora)
extensions/voice/ui-widget-base.ts          Widget registry and base class
extensions/voice/ui-render-ticker.ts        Shared render ticker
extensions/voice/ui-icons.ts                Glyph and icon set
extensions/voice/ui-width.ts                CJK-aware visual width helpers
extensions/voice/ui-locale-labels.ts        Native language and voice labels

# types
extensions/voice/sherpa-onnx-node.d.ts      Type declarations for the optional native module

Configuration

Settings stored in Pi's settings files under the voice key:

Scope Path
Global ~/.pi/agent/settings.json
Project <project>/.pi/settings.json
{
	"voice": {
		"version": 3,
		"enabled": true,
		"language": "en",
		"backend": "local",
		"localModel": "parakeet-v3",
		"scope": "global",
		"onboarding": { "completed": true, "schemaVersion": 3 }
	}
}

DEEPGRAM_API_KEY from your shell is used at runtime and is not copied back into ~/.pi/agent/settings.json. If you paste a key during onboarding, that is an explicit save and it still goes to ~/.env.secrets or ~/.zshrc.

Hold-to-talk delay defaults to 700 ms (/voice-hold-delay accepts 200–3000 ms).

Transcript polish

Transcript polish is on by default: every dictation runs an extra model pass. When the selected model is a cloud provider, the text that leaves your machine is:

  • the transcript of the dictation;
  • the last N conversation turns of user and assistant text, where N is postProcessContextTurns (default 2; 0 sends no conversation context);
  • nothing else. The compaction summary is deliberately not sent: it is a digest built from earlier messages, so it can carry residues of thinking and tool output, and it measured no gain over the turns alone.

One measured behaviour is worth knowing: a model that thinks before it answers may normalise a spoken operator into its symbol — select star comes back as select *. The information is unchanged, there is no setting for it, and /voice-polish off is the way to keep the words verbatim.

A reasoning model used to spend its whole token budget thinking about a long dictation, so the answer was truncated and the pass kept the raw transcript — which looked like polish quietly doing nothing past roughly half a minute of speech. Dictations longer than 200 characters now turn thinking off (the same 309-character input went from 10.2 s to 1.2 s with the same punctuation), while shorter ones keep it, because there it costs almost nothing and corrects terms and self-corrections better. The field only reaches OpenAI-compatible providers; one that ignores it behaves exactly as before.

On the local backend, polish no longer waits for the whole transcript: each recogniser segment — roughly 10 s of speech — is polished as it is decoded, with up to three segment calls in flight. A long dictation may come back partly polished, and that is deliberate: a segment that times out is retried once with thinking disabled for that retry, and if it still fails, that segment keeps its raw text while its neighbours keep their polished text — so one slow call no longer costs the rest of the dictation. The conversation context is attached to the first segment only; a later segment sees just the previous segment's raw text.

The gain is measurable: on 79.6 s of corpus audio (35 segments) against a degraded endpoint, the old single-call path fell back on 100% of the run, while the segmented path polished all 35 segments. Real dictations after the change: 4 of 4 applied, with polish taking 0.4–2.3 s for 8–28 s of audio — roughly 5–13% of the audio duration.

Assistant text can contain anything the conversation contained — file paths, identifiers, values the agent echoed. The character limits bound how much is sent, not how sensitive it is. With the local backend, nothing else leaves your machine, and audio never does: recognition runs on this machine with no API key. Turn the feature off with /voice-polish off or the Polish tab's Enabled row.

Every dictation also writes one voice-polish entry into the session file: the raw transcript, what reached the editor and why the pass decided that. The model never sees these entries — they are not part of the conversation context — so they are there for analysis, and they do keep the raw text on disk for as long as the session file exists. Each entry also records how the pass was configured: the transcript length on its own (separate from any text already in the editor), whether thinking was turned off for it, and the output-token cap it carried, plus the audio seconds it covered and which recogniser produced it — which is what makes polish time readable as a speedup. A segmented pass adds a segments summary to the entry: how many segments were polished, how many kept their raw text, and how many were retried.

Setting Scope Default Notes
postProcessEnabled global only true Master switch. A project voice block cannot flip it.
postProcessModel global only "session" Reuses the session model, or provider/modelId.
postProcessContextTurns global and project 2 Conversation turns sent with the transcript, 0–10.
postProcessTimeoutMs global and project 12000 Per-pass timeout in milliseconds, 1000–30000.

The global-only fields resolve from ~/.pi/agent/settings.json even when a repository provides its own voice block, so a cloned repo can neither turn the feature on nor redirect where dictated text goes. The model is chosen from a picker (/voice-polish model), never typed: a hand-typed reference is refused, and an unavailable or malformed model keeps the raw transcript instead of switching provider. postProcessNoticeShown is machine-local bookkeeping for the one-time notice, not a user setting.


Troubleshooting

Run /voice test inside Pi for full diagnostics.

Problem Solution
"DEEPGRAM_API_KEY not set" Get a key → export DEEPGRAM_API_KEY="..." in ~/.zshrc
"No audio capture tool found" brew install sox or brew install ffmpeg
Remote microphone records silence Audio over PulseAudio/SSH — install ffmpeg on the Pi side (capture then prefers ffmpeg)
Space doesn't activate voice Run /voice-settings — voice may be disabled
Local model not transcribing Check /voice-settings → Device tab for sherpa-onnx status
Download failed Partial downloads auto-resume on retry. Check disk space in Device tab.
dyld: Library not loaded: libsimdjson on macOS Homebrew Node ABI mismatch — run brew reinstall node or switch to version-managed Node (mise, fnm, nvm)

Security

  • Cloud STT — audio is sent to Deepgram for transcription (Deepgram backend only)
  • Local STT — audio never leaves your machine (local backend)
  • No telemetry — pi-voicekit does not collect or transmit usage data
  • API key — stored in env var or Pi settings, never logged

See SECURITY.md for vulnerability reporting.


License

MIT — original by @baanditeagle, maintained by CyFeng16


Continuation of pi-listen by @baanditeagle, maintained by CyFeng16

Website · 𝕏 Twitter · GitHub · npm · Report a Bug · Pi CLI

About

Voice in + voice out for the Pi coding agent — hold-to-talk STT (Deepgram streaming or offline models) plus TTS (Kitten, Piper, Kokoro, Deepgram Aura). Successor to pi-listen-ng.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages