pi-mic0.4.0
Voice input for pi (CLI and pi-web), transcribed through an OpenAI-compatible gateway (e.g. OmniRoute to Deepgram)
Untested on omp
This is a pi package. No one has reported whether it works on omp yet. If you try it, add your result below.
Reports 0
No reports yet.
Readme
pi-mic
Voice input for pi, in the terminal (CLI) and in
pi-web. You speak, the audio goes to an
OpenAI-compatible /v1/audio/transcriptions endpoint (for example OmniRoute with Deepgram) and the
text lands in the message field without being sent.
Installation
pi install npm:pi-mic
Then, once, inside pi:
/mic setup # picks the provider (key and baseUrl come from models.json/auth.json) and the model
/mic check # confirms provider, key and endpoint with a silent audio clip
The configuration lives in ~/.pi/agent/pi-mic.json:
{ "provider": "omni-memory", "model": "deepgram/nova-3", "language": "pt", "device": "Microphone (HD Pro Webcam C920)" }
language and device are optional: without language the gateway detects the language; without
device the system default microphone is used.
CLI
| Action | How |
|---|---|
| Start / stop recording | Alt+M or /mic |
| Pick the microphone (Windows) | /mic device |
| Configure provider/model/language | /mic setup |
| Test the endpoint | /mic check |
The transcription shows up in the editor while you speak, in one of two ways:
- Real time (Deepgram): with
"realtime": { "provider": "deepgram", "apiKey": "…" }inpi-mic.json(orDEEPGRAM_API_KEY), the audio goes through Deepgram streaming and the words arrive as you speak./mic setupconfigures it. - Segment by segment (any provider): without
realtime, pi-mic splits speech on pauses and sends each segment to the provider's/audio/transcriptions, with a partial preview.
In both modes only audio with voice (loud and with pitch) goes through: keyboard, clicks and knocks do not become words. The audio keeps the microphone's native rate (48 kHz in the browser), without resampling.
In real time, when you stop, pi-mic records 500 ms more (the last syllable often comes out together
with the key press), sends Finalize and waits for the final streaming results, then sends the
whole recording to Deepgram's pre-recorded endpoint, which sees the full sentence; that text
replaces the streaming one.
Extra options in pi-mic.json: "keyterms": ["pi-mic", "OmniRoute"] (terms Deepgram should
prioritize) and "debug": true (the pi-web host saves the audio and the events of each session in
~/.pi/agent/pi-mic-debug).
While recording, the editor is locked (only Alt+M to stop and Esc to cancel and restore the
previous text) and the footer shows 🔴 with the microphone level.
Capture: Windows uses the native waveIn API (nothing to install); macOS/Linux use rec (SoX) or
arecord. For tests, PI_MIC_FAKE_INPUT=<file.wav> (16 kHz mono) replaces the microphone.
Set language (for example "pt-BR"): short segments hurt automatic language detection.
pi-web
pi-mic runs inside pi-web itself. Install it with pi install npm:pi-mic; when a session opens in
pi-web, the extension attaches to pi-web's own HTTP server, injects the microphone button into the
page and transcribes the audio on the server, so the provider key never reaches the browser. No
extra process or port is needed. Reload the page once after pi-web starts to get the button (pi-web
shows a notice when it is ready).
The microphone button sits on the message field row, right before Send; click it or press Alt+M
to record and again to stop, Esc cancels. As in the CLI, the text appears live and the field (and
Send) stay locked until the transcription finishes. In pi-web /mic setup and /mic check are
available; recording happens in the browser, never on the server machine.
The browser only grants the microphone in a secure context: localhost/127.0.0.1 or HTTPS. To
use it from another device, put pi-web behind HTTPS inside your private network (for example
tailscale serve --bg <pi-web port>). Do not expose pi-web publicly: it may have no password.
Architecture
All code is TypeScript under src/, split by layer:
| Folder | Responsibility |
|---|---|
src/core |
Browser-safe audio, text and transcription logic (no Node APIs) |
src/config |
Reading and writing pi-mic.json |
src/providers |
OpenAI-compatible gateway and Deepgram clients |
src/cli |
The pi extension: capture, dictation state machine, /mic commands |
src/web/host |
Attaches the mic routes and button injection to pi-web's HTTP server |
src/web/server |
Mic routes: transcription, realtime stream, config, browser assets |
src/web/client |
The browser script injected into pi-web |
pi loads the extension directly from src/cli/extension.ts. Only the web side is compiled
(npm run build writes dist/); the extension serves dist/core and dist/web/client to the browser
under /__pi-mic/assets/.
Development
Git-flow with PRs, everything in Conventional Commits (commits and PR titles; CI rejects what does not follow them, and PRs from branches outside the flow):
feature/<name>(orbugfix/<name>) → PR todev, titled likefeat: …/fix: ….- On every push to
dev, the pipeline computes the next version from the commits since the last tag (feat→ minor,fix/perf/refactor→ patch,!/BREAKING CHANGE→ major; before 1.0 a breaking change bumps the minor) and opens or updates therelease/<version>→mainPR on its own, titledchore(release): <version>. Nobody picks the version. - Merging into
mainpublishes to npm with trusted publishing, creates the tag and the GitHub release, mergesmainback intodevand deletes the release branch.
hotfix/<name> branches from main and goes back to main.
npm install
npm run check # biome + no-comments + tsc + build + tests
The code base has no comments by rule: names, small functions and types carry the explanation, and
npm run check fails if a comment sneaks in.