11 KiB
Plan: Add Local whisper.cpp Transcription
Summary
Add whisper-cpp as a new transcription provider using CLI subprocess, with integrated model download in hyprvoice configure and standalone hyprvoice model commands.
Tasks
1. Model Management Package
File: internal/whisper/models.go (NEW)
package whisper
const DefaultModelsDir = "~/.local/share/hyprvoice/models"
type ModelInfo struct {
Name string
Size string
Desc string
URL string
Filename string
}
var AvailableModels = []ModelInfo{
// English-only (faster)
{Name: "tiny.en", Size: "75MB", Desc: "Fastest, English only", ...},
{Name: "base.en", Size: "142MB", Desc: "Fast, good accuracy (recommended)", ...},
{Name: "small.en", Size: "466MB", Desc: "Better accuracy, slower", ...},
// Multilingual
{Name: "tiny", Size: "75MB", Desc: "Fastest, 99 languages", ...},
{Name: "base", Size: "142MB", Desc: "Fast, 99 languages", ...},
{Name: "small", Size: "466MB", Desc: "Better accuracy, 99 languages", ...},
}
func GetModelsDir() string
func DownloadModel(name string, onProgress func(downloaded, total int64)) error
func ListInstalledModels() ([]string, error)
func GetModelPath(name string) string
func RemoveModel(name string) error
func IsModelInstalled(name string) bool
Download URL pattern: https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-{name}.bin
2. Whisper.cpp Adapter
File: internal/transcriber/adapter_whisper_cpp.go (NEW)
package transcriber
type WhisperCppAdapter struct {
modelPath string
language string
threads int
}
func NewWhisperCppAdapter(config Config) *WhisperCppAdapter
func (a *WhisperCppAdapter) Transcribe(ctx context.Context, audioData []byte) (string, error)
Implementation:
- Write audioData to temp WAV file (reuse
convertToWAV) - Build command:
whisper-cli -m <model> -l <lang> -t <threads> --no-timestamps -f <temp.wav> - Execute with context timeout
- Parse stdout - whisper-cli outputs transcription to stdout
- Cleanup temp file
- Return text
Error handling:
- whisper-cli not found → clear error message with install instructions
- Model file not found → suggest
hyprvoice model download - Transcription timeout → configurable via context
3. Config Updates
File: internal/config/config.go (MODIFY)
Add to TranscriptionConfig:
ModelPath string `toml:"model_path"` // path to .bin model file
Threads int `toml:"threads"` // CPU threads (default: 4)
Add validation for whisper-cpp:
case "whisper-cpp":
if config.ModelPath == "" {
return fmt.Errorf("model_path required for whisper-cpp provider")
}
if _, err := os.Stat(expandPath(config.ModelPath)); os.IsNotExist(err) {
return fmt.Errorf("model file not found: %s (run 'hyprvoice model download')", config.ModelPath)
}
// No API key required
Default threads to 4 if not set.
4. Transcriber Factory Update
File: internal/transcriber/transcriber.go (MODIFY)
Add case:
case "whisper-cpp":
adapter = NewWhisperCppAdapter(config)
Note: No API key check for whisper-cpp.
5. CLI Model Commands
File: cmd/hyprvoice/main.go (MODIFY)
Add commands:
rootCmd.AddCommand(modelCmd())
func modelCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "model",
Short: "Manage whisper.cpp models",
}
cmd.AddCommand(
modelListCmd(),
modelDownloadCmd(),
modelRemoveCmd(),
)
return cmd
}
hyprvoice model list
Available models:
NAME SIZE DESCRIPTION
tiny.en 75MB Fastest, English only
base.en 142MB Fast, good accuracy (recommended)
small.en 466MB Better accuracy, slower
tiny 75MB Fastest, 99 languages
base 142MB Fast, 99 languages
small 466MB Better accuracy, 99 languages
Installed:
✓ base.en (~/.local/share/hyprvoice/models/ggml-base.en.bin)
hyprvoice model download <name>
$ hyprvoice model download base.en
Downloading ggml-base.en.bin (142MB)...
[████████████████████████████████] 100% 142MB/142MB
✓ Model saved to ~/.local/share/hyprvoice/models/ggml-base.en.bin
To use this model, add to your config:
[transcription]
provider = "whisper-cpp"
model_path = "~/.local/share/hyprvoice/models/ggml-base.en.bin"
hyprvoice model remove <name>
$ hyprvoice model remove base.en
Remove model base.en? [y/N] y
✓ Removed ~/.local/share/hyprvoice/models/ggml-base.en.bin
6. Configure Wizard Updates
File: cmd/hyprvoice/main.go (MODIFY)
Add to provider selection:
Select transcription provider:
1. openai - OpenAI Whisper API (cloud-based)
2. groq-transcription - Groq Whisper API (fast transcription)
3. groq-translation - Groq Whisper API (translate to English)
4. mistral-transcription - Mistral Voxtral API
5. elevenlabs - ElevenLabs Scribe API
6. whisper-cpp - Local transcription (offline, private)
When whisper-cpp selected:
🔒 whisper.cpp - Local Transcription
Checking for whisper-cli... ✓ found
Checking for installed models...
No models found in ~/.local/share/hyprvoice/models/
Would you like to download a model now? [Y/n] y
Select model:
English-only (faster):
1. tiny.en (75MB) - Fastest
2. base.en (142MB) - Recommended for dictation
3. small.en (466MB) - Better accuracy
Multilingual (99 languages):
4. tiny (75MB) - Fastest
5. base (142MB) - Good balance
6. small (466MB) - Better accuracy
Model [1-6] (default: 2): 2
Downloading ggml-base.en.bin...
[████████████████████████████████] 100%
✓ Model downloaded!
Note: You can adjust threads in config.toml (default: 4)
If whisper-cli not found:
⚠ whisper-cli not found!
Install whisper.cpp first:
Arch Linux: yay -S whisper.cpp
Other: see https://github.com/ggerganov/whisper.cpp
Continue anyway? [y/N]
7. README Updates
File: README.md (MODIFY)
Update provider list in Features section:
- **Multiple transcription backends**: OpenAI, Groq, Mistral, Eleven Labs, and **whisper.cpp (local/offline)**
Add new section after ElevenLabs:
#### whisper.cpp Local (Privacy-First)
**100% offline transcription** - your voice never leaves your machine. No API keys, no cloud, no data collection.
```toml
[transcription]
provider = "whisper-cpp"
model_path = "~/.local/share/hyprvoice/models/ggml-base.en.bin"
language = "en" # or empty for auto-detect
threads = 4 # CPU threads (adjust based on your CPU)
Quick setup:
# 1. Install whisper.cpp
yay -S whisper.cpp # Arch Linux
# or build from source: https://github.com/ggerganov/whisper.cpp
# 2. Download a model and configure
hyprvoice configure # interactive setup with model download
# or manually:
hyprvoice model download base.en
Available models:
| Model | Size | Speed | Languages | Best For |
|---|---|---|---|---|
| tiny.en | 75MB | Fastest | English | Quick notes, testing |
| base.en | 142MB | Fast | English | Daily dictation (recommended) |
| small.en | 466MB | Moderate | English | When accuracy matters |
| tiny | 75MB | Fastest | 99 | Multilingual, speed priority |
| base | 142MB | Fast | 99 | Multilingual, balanced |
| small | 466MB | Moderate | 99 | Multilingual, accuracy |
Tips:
.enmodels are faster and more accurate for English- Use multilingual models only if you need other languages
- Adjust
threadsbased on your CPU (4-8 is usually good) - First transcription may be slower (model loading)
Features:
- 🔒 100% offline - complete privacy
- ⚡ Fast inference on modern CPUs
- 🎯 Optimized quantized models
- 🌍 99 language support (multilingual models)
#### Update Development Status table:
```markdown
| whisper.cpp support | ✅ | Local offline transcription |
Remove the "⏳ Planned" entries for whisper.cpp.
Update default config example:
Add whisper-cpp to provider comment:
provider = "openai" # "openai", "groq-transcription", "groq-translation", "mistral-transcription", "elevenlabs", or "whisper-cpp"
8. Default Config Template
File: internal/config/config.go (MODIFY)
Update SaveDefaultConfig() to include whisper-cpp options in comments:
# Speech Transcription Configuration
[transcription]
provider = "openai" # Options: openai, groq-transcription, groq-translation, mistral-transcription, elevenlabs, whisper-cpp
api_key = "" # API key (not needed for whisper-cpp)
language = "" # Language code (empty for auto-detect)
model = "whisper-1" # Model name (ignored for whisper-cpp)
# model_path = "" # For whisper-cpp: path to .bin model file
# threads = 4 # For whisper-cpp: CPU threads to use
9. AUR Package Update
File: packaging/PKGBUILD (MODIFY)
Add optional dependency:
optdepends=(
'whisper.cpp: local offline transcription'
)
File Summary
| File | Action | Description |
|---|---|---|
internal/whisper/models.go |
NEW | Model download/management |
internal/transcriber/adapter_whisper_cpp.go |
NEW | CLI subprocess adapter |
internal/config/config.go |
MODIFY | Add model_path, threads fields + validation |
internal/transcriber/transcriber.go |
MODIFY | Add whisper-cpp case to factory |
cmd/hyprvoice/main.go |
MODIFY | Add model commands + configure wizard |
README.md |
MODIFY | Documentation for local transcription |
packaging/PKGBUILD |
MODIFY | Add optdepends |
Implementation Order
internal/whisper/models.go- model management (foundation)internal/transcriber/adapter_whisper_cpp.go- the adapterinternal/config/config.go- config fields + validationinternal/transcriber/transcriber.go- factory updatecmd/hyprvoice/main.go- model commands + configure wizardREADME.md- documentationpackaging/PKGBUILD- AUR update- Test end-to-end
Testing Checklist
hyprvoice model listshows available/installed modelshyprvoice model download base.endownloads with progresshyprvoice model remove base.enremoves modelhyprvoice configurewith whisper-cpp offers model download- Configure wizard handles missing whisper-cli gracefully
- Transcription works with downloaded model
- Config validation catches missing model file
- Threads setting respected
- Language setting works (en vs auto-detect)
- Context cancellation stops transcription
- Error messages are clear and actionable