Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,46 @@ async def websocket_endpoint(websocket: WebSocket):
**Frontend Implementation**: The package includes an HTML/JavaScript implementation [here](https://github.com/QuentinFuxa/WhisperLiveKit/blob/main/whisperlivekit/web/live_transcription.html). You can also import it using `from whisperlivekit import get_inline_ui_html` & `page = get_inline_ui_html()`


## Dictation (`wlk dictate`)

Push-to-talk dictation for Linux (X11): press F9, speak, press F9 again, and the text is pasted where your cursor is. Audio goes straight into the streaming pipeline while you speak, so the text is ready within about half a second of stopping. No server or port is opened. F8 pastes the last text again.

```bash
sudo apt install xdotool x11-utils libnotify-bin # paste, terminal detection, notifications
pip install -e ".[dictate]"
wlk dictate --model large-v3 --backend faster-whisper --backend-policy localagreement --lan en \
--min-chunk-size 1.0 --buffer_trimming_sec 8 --no-vac --log-level INFO
```

- `--hotkey` and `--repaste-hotkey` change the keys (default `f9` and `f8`). All other options are the same as `wlk serve`.
- `--min-chunk-size 1.0 --buffer_trimming_sec 8 --no-vac` keep transcription ahead of your speech. With the defaults it re-transcribes too often and falls behind, so you wait after stopping.
- Terminals get Ctrl+Shift+V instead of Ctrl+V. Your clipboard is restored after pasting.
- Recordings with no speech, and filler Whisper produces from silence ("Thank you for watching."), are not pasted.
- On RTX 50-series GPUs, `--compute-type int8_float16` needs a recent cuBLAS: `pip install nvidia-cublas-cu12==12.9.2.10` and set `LD_LIBRARY_PATH` to its `nvidia/cublas/lib` folder. With it, `large-v3` keeps up on a laptop GPU.

Run it as a systemd user service:

```ini
# ~/.config/systemd/user/wlk-dictate.service
[Unit]
Description=WhisperLiveKit dictation
After=graphical-session.target
PartOf=graphical-session.target

[Service]
ExecStart=/path/to/venv/bin/wlk dictate --model large-v3 --backend faster-whisper --backend-policy localagreement --lan en --min-chunk-size 1.0 --buffer_trimming_sec 8 --no-vac --log-level INFO
Restart=on-failure
RestartSec=5

[Install]
WantedBy=graphical-session.target
```

```bash
systemctl --user daemon-reload && systemctl --user enable --now wlk-dictate
```


## Parameters & Configuration


Expand All @@ -399,6 +439,7 @@ async def websocket_endpoint(websocket: WebSocket):
| `--diarization` | Enable speaker identification | `False` |
| `--backend-policy` | Streaming strategy: `1`/`simulstreaming` uses AlignAtt SimulStreaming, `2`/`localagreement` uses the LocalAgreement policy | `simulstreaming` |
| `--backend` | ASR backend selector. `auto` picks MLX on macOS (if installed), otherwise Faster-Whisper, otherwise vanilla Whisper. Options: `mlx-whisper`, `faster-whisper`, `whisper`, `openai-api` (LocalAgreement only), `funasr` (SenseVoiceSmall, LocalAgreement only), `voxtral-mlx` (Apple Silicon), `voxtral` (HuggingFace), `qwen3-vllm`, `qwen3-vllm-metal` (Apple Silicon), `qwen3-streaming` (HuggingFace, CUDA/MPS/CPU), `canary` (NeMo, CUDA/CPU, LocalAgreement only) | `auto` |
| `--compute-type` | CTranslate2 compute type for faster-whisper models, e.g. `float16`, `int8_float16`, `float32`. `auto` lets CTranslate2 choose | `auto` |
| `--pause-segmentation-seconds` | Create a stable transcript boundary when a VAD pause is longer than this many seconds. Use `0` to disable pause-based segmentation. | `5.0` |
| `--asr-coalesce-min-s` | Defer an ASR call until this much new audio has accrued. Trades update cadence for fewer encoder passes; use `0` to disable. | `0` |
| `--no-vac` | Disable Voice Activity Controller. NOT ADVISED | `False` |
Expand Down
1 change: 1 addition & 0 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ same settings as the CLI.
| `--model` | Model size or name for the selected backend | `base` |
| `--language` | Source language; coverage depends on the backend | `auto` |
| `--backend-policy` | Whisper streaming policy: `simulstreaming` or `localagreement` | `simulstreaming` |
| `--compute-type` | CTranslate2 compute type for faster-whisper models, e.g. `float16` or `int8_float16` | `auto` |
| `--target-language` | Enable translation to this language | disabled |
| `--translation-backend` | `nllb`, the [AlignAtt4LLM server](translation-alignatt.md), or [local MLX translation](translation-mlx.md) | `nllb` |
| `--diarization` | Attribute words to speakers | disabled |
Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ test = [
"matplotlib>=3.8",
]
translation = ["nllw"]
dictate = ["pynput", "sounddevice", "pyperclip"]
sentence_tokenizer = ["mosestokenizer", "wtpsplit"]
funasr = ["funasr~=1.4.1"]
mlx-whisper = [
Expand Down
86 changes: 86 additions & 0 deletions tests/test_compute_type.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
"""--compute-type reaches every faster-whisper model WhisperLiveKit creates."""

import sys

import faster_whisper

from tests.test_backends import _base_simul_kwargs, _patch_simul_loaders


def test_cli_accepts_compute_type(monkeypatch):
from whisperlivekit.parse_args import parse_args

monkeypatch.setattr(sys, "argv", ["whisperlivekit-server", "--compute-type", "float16"])

assert parse_args().compute_type == "float16"


def test_cli_compute_type_defaults_to_auto(monkeypatch):
from whisperlivekit.parse_args import parse_args

monkeypatch.setattr(sys, "argv", ["whisperlivekit-server"])

assert parse_args().compute_type == "auto"


def test_simulstreaming_encoder_uses_compute_type(monkeypatch):
backend, _ = _patch_simul_loaders(monkeypatch)

asr = backend.SimulStreamingASR(**_base_simul_kwargs(compute_type="float16"))

assert asr.fw_encoder.kwargs["compute_type"] == "float16"


def test_simulstreaming_encoder_compute_type_defaults_to_auto(monkeypatch):
backend, _ = _patch_simul_loaders(monkeypatch)

asr = backend.SimulStreamingASR(**_base_simul_kwargs())

assert asr.fw_encoder.kwargs["compute_type"] == "auto"


def test_faster_whisper_asr_uses_compute_type(monkeypatch):
from whisperlivekit.local_agreement.backends import FasterWhisperASR

created = {}

class FakeWhisperModel:
def __init__(self, model_size_or_path, **kwargs):
created.update(kwargs)

monkeypatch.setattr(faster_whisper, "WhisperModel", FakeWhisperModel)

FasterWhisperASR(lan="en", model_size="tiny", compute_type="float16")

assert created["compute_type"] == "float16"


def test_backend_factory_passes_compute_type_to_faster_whisper(monkeypatch):
import whisperlivekit.local_agreement.whisper_online as whisper_online

created = {}

class FakeFasterWhisperASR:
def __init__(self, **kwargs):
created.update(kwargs)
self.transcribe_kargs = {}

monkeypatch.setattr(whisper_online, "FasterWhisperASR", FakeFasterWhisperASR)

whisper_online.backend_factory(
backend="faster-whisper",
lan="en",
model_size="tiny",
model_cache_dir=None,
model_dir=None,
model_path=None,
lora_path=None,
direct_english_translation=False,
buffer_trimming="segment",
buffer_trimming_sec=15,
confidence_validation=False,
warmup_file="", # "" skips warmup; None downloads a sample file
compute_type="float16",
)

assert created["compute_type"] == "float16"
Loading