-
Notifications
You must be signed in to change notification settings - Fork 4
How It Works
A contributor's tour of the YazSes internals. The goal here is orientation — enough to find the file you need and understand how a spoken phrase becomes typed text. For per-module depth, read the code; each module is small and focused.
flowchart LR
subgraph UI["Control surface"]
CLI["yazses CLI"]
TRAY["tray icon"]
end
CLI -- "JSON-RPC / IPC" --> D
TRAY -- "JSON-RPC / IPC" --> D
subgraph D["yazses-daemon · core/daemon.py (state machine)"]
direction LR
HK["1· Hotkey<br/>keyboard / EMG"] --> AUD["2· Audio<br/>capture · VAD · padding"]
AUD --> STT["3· Speech-to-text<br/>faster-whisper (CPU int8)"]
STT --> PP["4· Post-process<br/>cleanup · disfluency"]
PP --> CMD{"5· Command<br/>or dictation?"}
end
CMD -- "dictate" --> INJ["Inject into focused app<br/>xdotool / ydotool / wtype"]
CMD -- "command" --> KEYS["Send key sequence<br/>undo · save · go to line…"]
INJ -. "SSH tunnel" .-> REMOTE["remote host"]
subgraph PLAT["Platform abstraction · platform/ (Protocols)"]
LNX["linux"]
MAC["macos"]
WIN["windows"]
end
PLAT -. "implements" .-> D
CFG[("config.toml")] -. "reads" .-> D
classDef offline fill:#ede7f6,stroke:#5e35b1,color:#311b92;
class D,STT offline;
Everything above runs on your machine. There are no network calls in the dictation path — audio is transcribed locally and never uploaded. The full, always-current diagram (with the opt-in learning loop and v2 cognitive layer) is on the documentation site.
When you hold the hotkey and speak, this is the journey of your audio:
Hotkey held (keyboard hook, or a USB EMG squeeze sensor)
→ Audio captured (sounddevice → numpy buffer)
→ Voice-activity gate (calibrated RMS threshold) + pre-speech padding
→ faster-whisper transcribes locally ←── optional context prompt (vocabulary, editor)
→ Text cleanup (strip Whisper artefacts) + disfluency filter
→ Command grammar: is this dictation, or a command like "undo" / "new line"?
→ Dispatch:
• DICTATE → (optional offline LLM cleanup) → inject into the focused app
• COMMAND → translate to a key sequence and send it
→ Injection: type locally, or forward over SSH to a remote host
The whole thing is orchestrated by one long-lived process — the daemon — which talks to the CLI and the tray icon over a small JSON-RPC IPC channel.
src/yazses/core/daemon.py is the orchestrator. It moves through:
LOADING → IDLE ↔ RECORDING → TRANSCRIBING → INJECTING → IDLE
plus side-states for remote setup, enrollment, meeting capture, and errors. It owns the IPC server, signal handling, and all the pipeline wiring above. If you're changing behaviour, you'll almost always touch this file or one of the modules it wires together.
| Area | Where | What it does |
|---|---|---|
| Orchestration | core/daemon.py |
The state machine + all pipeline wiring. |
| OS abstraction | platform/ |
base.py declares Protocol interfaces (hotkey, injector, lifecycle, IPC, permissions, tray). linux/, macos/, windows/ each implement them. factory.py returns the right bundle. |
| Hotkey |
hotkeys/, platform/*/
|
"Key held ≥ N ms → start/stop recording." Linux uses evdev; an optional USB EMG squeeze sensor is an alternate hotkey backend. |
| Audio | audio/ |
Capture, voice-activity gating, pre-speech padding, and input-device resilience (auto-heal when the OS default mic changes). |
| Speech-to-text | stt/ |
Wraps faster_whisper.WhisperModel; optional streaming decode; a disfluency filter. |
| Post-processing | postprocess/ |
Text cleanup, continuation spacing, spoken-punctuation, and optional offline LLM cleanup. |
| Commands | commands/ |
A fast regex grammar classifies dictation vs. commands; an optional small-model router handles the ambiguous cases. |
| Injection | inject/ |
Types into the focused window (xdotool / ydotool / wtype / clipboard, auto-selected) — with a guard that avoids typing when no text field is focused. |
| IPC | ipc/ |
JSON-RPC 2.0 over a Unix socket (Linux/macOS) or named pipe (Windows) — how the CLI and tray talk to the daemon. |
| CLI | cli.py |
The yazses command (start/stop/status/doctor/features/…). |
| Tray |
tray/, platform/linux/tray.py
|
A top-bar status icon whose colour tracks the daemon state and whose menu picks/pins the mic and restarts the daemon. |
- On-device only. No network calls in the dictation path. If a feature seems to need the cloud, it's either off-by-default-and-clearly-labelled or it doesn't ship.
-
Protocols over
if platform ==. New OS support means implementing the Protocol interfaces inplatform/base.pyand registering the platform infactory.py— the daemon and CLI need no changes. See Contributing → adding a platform. -
Off by default. Advanced features are dormant until enabled (
yazses features enable <name>), so a fresh install is simple and safe. - Pure cores, injected backends. The heavy/OS-specific parts are injected so the logic can be unit-tested without hardware. That's why the test suite is large and fast.
- Want to fix dictation behaviour? →
core/daemon.py+ the relevantaudio/,stt/, orpostprocess/module. - Want to add a voice command? →
commands/grammar.py. - Want to add OS/injection support? →
platform/base.py+ a newplatform/<os>/orinject/backend. - Want a docs/UX fix (great first PR)? →
docs/,cli.py, orsystem/doctor.py.
Then head to Contributing — Start Here.
YazSes — offline, on-device voice dictation · Apache-2.0 · Docs · Discussions · Good first issues
Contribute
Use it