Dokumentation aktualisiert, CHANGELOG ergänzt, Version 0.2.0

- CHANGELOG.md neu (0.1.0, 0.2.0)
- Bedienungsanleitung: LLM-Korrektur in Überblick, Voraussetzungen, Installation, Deinstallation
- Entwicklerdoku: Output-Klasse, STT_LLM_*-Konfiguration, Nebenläufigkeitsregel für Ausgaben,
  Erweitern (LLM/Prompt), Grenzen und nächste Schritte
- README, CLAUDE.md: Verweise und Befehle ergänzt
- stt_core.__version__ = 0.2.0

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Dieter Schlüter 2026-09-14 01:02:21 +02:00
commit cb7022a298
7 changed files with 76 additions and 12 deletions

View file

@ -29,12 +29,14 @@ SpeechToText wandelt **gesprochene Sprache in Text** um — vollständig **lokal
ohne Cloud. Grundlage ist das Spracherkennungsmodell *Whisper* in der schnellen Umsetzung
*faster-whisper*.
Es besteht aus zwei Teilen:
Es besteht aus zwei Teilen und einem optionalen Zusatz:
- **Dem Dienst** (`stt_service.py`): läuft im Hintergrund, hält das Sprachmodell im Speicher und nimmt
Aufträge entgegen — von Home Assistant, von Mikrocontrollern, von Skripten oder von der CLI.
- **Der Kommandozeile** (`stt_cli.py`): schickt Audiodateien oder das Mikrofon an den Dienst und gibt den
erkannten Text aus.
- **Optional der LLM-Korrektur** (`stt_llm.py`): lässt den erkannten Text auf Wunsch von einem lokalen
Sprachmodell (Ollama) auf Erkennungsfehler prüfen — nur in der Kommandozeile mit `--llm-correct`.
```mermaid
flowchart LR
@ -44,6 +46,7 @@ flowchart LR
ESP[ESP32 / Skripte / Apps] -- HTTP / WebSocket :9998 --> SVC
CLI -- HTTP / WebSocket :9998 --> SVC[STT-Dienst<br/>Whisper-Modell auf GPU]
SVC --> TXT[Text]
CLI -. "optional: llm-correct" .-> LLM[Ollama<br/>gemma4:12b] -.-> TXT
```
**Wie schnell?** Auf einer RTX 3090 mit dem Modell `large-v3-turbo`: ein Satz von 6 Sekunden ist in
@ -65,6 +68,7 @@ nach Sprache). Ein Stoppwort ist nicht nötig.
| Arbeitsspeicher | 4 GB frei | 8 GB frei |
| Festplatte | 0,5 GB (Modell `small`) | 2 GB (Modell `large-v3-turbo`) |
| Mikrofon | beliebig (nur für Live-Betrieb) | ReSpeaker XVF3800 oder gutes USB-Mikrofon |
| Nur für LLM-Korrektur | Ollama + Modell `gemma4:12b` (7,6 GB Festplatte) | ~10 GB freier Grafikspeicher |
Systemprogramme: `ffmpeg` ist hilfreich (Formatumwandlung), `pactl` (PipeWire/PulseAudio) für die
Mikrofonwahl, `espeak-ng` nur für die automatischen Tests.
@ -92,6 +96,13 @@ mit. Ohne vorhandene Installation lädt `pip` alles herunter.
# … 49 passed
```
**Optional für die LLM-Korrektur** (`--llm-correct`): [Ollama](https://ollama.com) installieren und das
Modell holen:
```bash
ollama pull gemma4:12b
```
> Beim **ersten Start des Dienstes** wird das Whisper-Modell aus dem Internet geladen
> (`large-v3-turbo` ≈ 1,6 GB). Danach arbeitet alles offline.
@ -502,4 +513,5 @@ rm -rf ~/Python_Programs/SpeechToText
Heruntergeladene Modelle liegen im Hugging-Face-Cache (`$HF_HOME/hub` bzw. `~/.cache/huggingface/hub`,
Ordner `models--*faster-whisper*`) und werden eventuell auch von anderen Programmen genutzt — nur löschen,
wenn sicher ist, dass sie nicht mehr gebraucht werden.
wenn sicher ist, dass sie nicht mehr gebraucht werden. Dasselbe gilt für das LLM der optionalen Korrektur
(`ollama rm gemma4:12b`).

34
CHANGELOG.md Normal file
View file

@ -0,0 +1,34 @@
# Änderungsprotokoll
Alle nennenswerten Änderungen an SpeechToText. Versionsnummer: `stt_core.__version__`
(erscheint in `/health`, in der FastAPI-Doku und im Wyoming-`info`).
## 0.2.0 — 2026-09-14
### Neu
- **CLI `--until-enter`:** Aufnahme mit beliebig langen Pausen; Enter beendet, der gesamte Text wird als
ein Ergebnis ausgegeben. Funktioniert mit dem Dienst und mit `--local`.
- **Optionale LLM-Nachkorrektur** (`stt_llm.py`, CLI `--llm-correct`, `--llm-model`, `--llm-url`):
korrigiert offensichtliche Erkennungsfehler mit einem lokalen LLM (Standard `gemma4:12b` über Ollama,
OpenAI-kompatible Server möglich). Strenger Prompt, Ähnlichkeitsprüfung und Fehler-Fallback auf den
Rohtext; `--json` liefert `raw_text` und Korrekturdetails. Standardmäßig aus.
- Umgebungsvariablen `STT_LLM_*` für die LLM-Korrektur.
- Umfassende Dokumentation: `BEDIENUNGSANLEITUNG.md`, `docs/API.md`, `docs/ENTWICKLER.md`, dieses Änderungsprotokoll.
### Geändert
- Ungültige Kombinationen von CLI-Optionen enden mit Exit-Code 1 (Fehler) statt 2.
- Die alten Einzelskripte `stt.py` und `stt_respeaker.py` liegen jetzt in `legacy/`.
## 0.1.0 — 2026-09-13
Erste Version als Dienst — Neuentwurf des alten Einzelskripts `stt.py`.
- `stt_core.py`: Whisper-Engine (faster-whisper) mit einmaligem Laden, GPU→CPU-Fallback,
Dekodierung beliebiger Audioformate, Halluzinationsfilter, Äußerungserkennung per Silero-VAD.
- `stt_service.py`: FastAPI-Dienst mit `/health`, `/v1/audio/transcriptions` (OpenAI-kompatibel),
`/transcribe` (Rohdaten) und `WS /ws/stream` (Live-Streaming); Inferenz in einem Worker-Thread,
begrenzte Warteschlange (HTTP 503).
- `stt_wyoming.py`: Wyoming-Protokoll für Home Assistant (TCP 10300).
- `stt_cli.py`: Dateien und Mikrofon über den Dienst oder lokal; Standardmikrofon über PipeWire,
ReSpeaker XVF3800 automatisch mit ASR-Kanal.
- Tests (Unit und Integration), systemd-User-Unit `deploy/stt-service.service`.

View file

@ -17,17 +17,17 @@ Git-Remote auf kitux.de (Forgejo) geplant, noch nicht eingerichtet.
`README.md` (Überblick) · `BEDIENUNGSANLEITUNG.md` (Anwender, vollständige Tabelle der `STT_*`-Variablen) ·
`docs/API.md` (HTTP/WebSocket/Wyoming-Protokoll) · `docs/ENTWICKLER.md` (Architektur, Nebenläufigkeitsregeln,
Tests, Erweitern). Bei Änderungen an Schnittstellen, Optionen oder Umgebungsvariablen die betroffenen Dokumente
mitpflegen.
mitpflegen; bei neuer Version `stt_core.__version__` erhöhen und `CHANGELOG.md` ergänzen.
## Befehle
```bash
.venv/bin/python -m pytest # Unit-Tests, ohne Modell/LLM (~9 s)
.venv/bin/python -m pytest -m integration # echtes Whisper small/CPU + Silero, espeak-ng-Sprache (~20 s)
.venv/bin/python -m pytest -m integration # echtes Whisper small/CPU + Silero (~20 s), LLM über Ollama (bis ~75 s)
.venv/bin/python -m pytest tests/test_service.py::test_ws_stream_with_vad_emits_transcripts_per_utterance
CUDA_VISIBLE_DEVICES=GPU-83ba6d1f-ebbe-f8c8-1433-153e0ebc3866 .venv/bin/python stt_service.py # Port 9998, Wyoming 10300
.venv/bin/python stt_cli.py datei.wav | --once | --local --model small datei.wav
.venv/bin/python stt_cli.py datei.wav | --once | --until-enter [--llm-correct] | --local --model small datei.wav
```
`.venv` ist mit `--system-site-packages` auf miniforge-Base angelegt (faster-whisper, fastapi, onnxruntime kommen

View file

@ -21,7 +21,8 @@ Gedacht als Baustein für Sprachassistenten und Smarthome: **Home Assistant** (W
|---|---|---|
| **[BEDIENUNGSANLEITUNG.md](BEDIENUNGSANLEITUNG.md)** | Anwender | Installation, Starten, CLI, Mikrofon, Home Assistant, Einstellungen, Fehlerbehebung |
| **[docs/API.md](docs/API.md)** | Client-Entwickler | HTTP, WebSocket-Protokoll, Wyoming, Fehlercodes, Beispiele (Python, curl, ESP32) |
| **[docs/ENTWICKLER.md](docs/ENTWICKLER.md)** | Programmierer am Projekt | Architektur, Module, Nebenläufigkeit, Tests, Erweitern, Designentscheidungen |
| **[docs/ENTWICKLER.md](docs/ENTWICKLER.md)** | Programmierer am Projekt | Architektur, Module, Nebenläufigkeit, LLM-Korrektur, Tests, Erweitern, Designentscheidungen |
| **[CHANGELOG.md](CHANGELOG.md)** | alle | Änderungen je Version (aktuell 0.2.0) |
## Schnellstart
@ -38,6 +39,7 @@ curl http://127.0.0.1:9998/health # "status":"ok" = bereit
.venv/bin/python stt_cli.py aufnahme.wav # Datei → Text
.venv/bin/python stt_cli.py -v # Mikrofon → Text (Strg+C beendet)
.venv/bin/python stt_cli.py --until-enter # Pausen erlaubt, Enter beendet → ein Text
.venv/bin/python stt_cli.py --until-enter --llm-correct # zusätzlich per lokalem LLM korrigieren (Ollama)
```
Beim ersten Start wird das Modell geladen (`large-v3-turbo` ≈ 1,6 GB). Schnittstellen zum Ausprobieren
@ -86,6 +88,7 @@ curl -F file=@aufnahme.mp3 http://127.0.0.1:9998/v1/audio/transcriptions
(bei natürlicher Sprache selten).
- Keine Zwischenergebnisse während des Sprechens — Text kommt pro Äußerung.
- Keine Authentifizierung und kein TLS.
- LLM-Korrektur nur in der CLI; kostet ~1 s pro Text, das erste Laden des LLM rund eine Minute.
## Lizenz und Autor

View file

@ -103,7 +103,7 @@ curl http://127.0.0.1:9998/health
"load_seconds": 2.34,
"wyoming_port": 10300,
"error": null,
"version": "0.1.0"
"version": "0.2.0"
}
```

View file

@ -32,6 +32,7 @@ Für Programmierer, die den Code verstehen, ändern oder erweitern wollen. Anwen
| **Zuverlässig** | GPU→CPU-Fallback beim Laden; Halluzinationsfilter; robustes Dekodieren beliebiger Formate; Fehler einzelner Anfragen beenden keine Verbindung |
| **Vielseitig anbindbar** | eine Engine, mehrere Schnittstellen: OpenAI-HTTP, Roh-HTTP, WebSocket, Wyoming, CLI |
| **Testbar** | reine Logik ohne I/O im Kern; App-Fabrik mit injizierbarer Engine und VAD |
| **Zusätze getrennt halten** | LLM-Korrektur nur optional in der CLI; Kern, Dienst und Wyoming bleiben davon unberührt |
| **Gegenstück zu chatterbox-tts-cli** | gleiche Bauart: Kernmodul + FastAPI-Dienst + CLI + systemd-User-Unit |
Das Projekt ersetzt das alte Einzelskript `legacy/stt.py`. Dessen Schwächen — Stoppwort-Erkennung in
@ -345,6 +346,7 @@ Port `0` ist in Tests erlaubt (freien Port über `server._server.sockets[0]` erm
| Funktion | Aufgabe |
|---|---|
| `main` | Argumente, Modus-Auswahl, Fehler → Exit-Code, Hinweis bei nicht erreichbarem Dienst |
| `Output` | einzige Ausgabestelle: `put()` gibt direkt aus oder reicht an die LLM-Korrektur (eigener Thread, Reihenfolge bleibt); `warmup()` lädt das LLM vorab; `close()` wartet auf ausstehende Korrekturen |
| `service_file` | `POST /transcribe` mit Datei als Body (Streaming-Upload über `requests`) |
| `service_mic` | Mikrofon → `/ws/stream` (synchroner `websockets`-Client, Empfänger-Thread) |
| `local_files` / `local_mic` | wie oben, aber `SttEngine` + `UtteranceSegmenter` im Prozess; Inferenz im `ThreadPoolExecutor(1)` |
@ -364,8 +366,6 @@ ReSpeaker-Erkennung (→ Kanal 1).
**Ausgabevertrag:** stdout nur Ergebnisse (Text oder JSON-Zeile), stderr alles andere.
Exit-Codes `0/1/2/130` (siehe Bedienungsanleitung).
---
### LLM-Nachkorrektur (`stt_llm.py`)
Optionaler Zusatz, bewusst **außerhalb** von Kern und Dienst: Smarthome-, ESP32- und Wyoming-Pfade bleiben
@ -414,6 +414,8 @@ Prompt mit Tags und Beispielen):
Das erste Laden eines Modells dauert 35–75 s; `Output.warmup()` stößt es im Mikrofon-Modus sofort an.
---
## 8. Nebenläufigkeit — die wichtigsten Regeln
1. **Nie** `engine.transcribe` (oder anderes Blockierendes) direkt in einer `async`-Funktion aufrufen —
@ -423,7 +425,9 @@ Das erste Laden eines Modells dauert 35–75 s; `Output.warmup()` stößt es im
4. `SttEngine` ist thread-sicher (`_load_lock`, `_run_lock`), aber der Dienst verlässt sich zusätzlich
auf den Ein-Thread-Worker für die Warteschlangenlogik.
5. `UtteranceSegmenter` und `StreamResampler` sind **nicht** thread-sicher — eine Instanz pro Stream.
6. Silero-VAD läuft im Event-Loop (gemessen ≈ 0,4 ms Rechenzeit pro 100 ms Audio). Bei sehr vielen gleichzeitigen
6. In der CLI nur über `Output.put` ausgeben — nie `print` aus Empfänger- oder Callback-Threads, sonst
geraten LLM-korrigierte und direkte Ausgaben durcheinander.
7. Silero-VAD läuft im Event-Loop (gemessen ≈ 0,4 ms Rechenzeit pro 100 ms Audio). Bei sehr vielen gleichzeitigen
Streams wäre das der erste Kandidat für einen eigenen Thread.
---
@ -439,6 +443,7 @@ Stellen:
| `stt_core.EngineConfig.from_env` | `STT_MODEL`, `STT_DEVICE`, `STT_COMPUTE_TYPE`, `STT_DEVICE_INDEX`, `STT_CPU_MODEL`, `STT_CPU_THREADS`, `STT_LANGUAGE`, `STT_BEAM_SIZE`, `STT_INITIAL_PROMPT`, `STT_DOWNLOAD_ROOT` |
| `stt_core.VadConfig.from_env` | `STT_VAD_THRESHOLD`, `STT_VAD_MIN_SPEECH_MS`, `STT_VAD_SILENCE_MS`, `STT_VAD_PRE_ROLL_MS`, `STT_VAD_MAX_UTTERANCE_S` |
| `stt_service.ServiceSettings.from_env` | `STT_MAX_QUEUE`, `STT_MAX_UPLOAD_MB`, `STT_PRELOAD`, `STT_WYOMING_HOST`, `STT_WYOMING_PORT` |
| `stt_llm.LlmConfig.from_env` | `STT_LLM_URL`, `STT_LLM_MODEL`, `STT_LLM_API`, `STT_LLM_TIMEOUT`, `STT_LLM_KEEP_ALIVE`, `STT_LLM_NUM_CTX`, `STT_LLM_MIN_SIMILARITY` |
Außerdem direkt: `STT_LOG_LEVEL`, `STT_HOST`, `STT_PORT` (`stt_service.py`), `STT_URL` (`stt_cli.py`).
Hilfsfunktion `stt_core._env(name, default, cast)`: leere Werte gelten als „nicht gesetzt“,
@ -546,10 +551,17 @@ Parametrisierung in `test_hallucinations_are_detected` **und** ein Gegenbeispiel
**Andere Engine** (z. B. anderes STT-Backend) — Unterklasse von `SttEngine`, die `load` und `transcribe`
überschreibt und ein `Transcript` liefert (`FakeEngine` zeigt das minimale Muster).
**Anderes LLM oder Prompt anpassen** — Modell/Server nur per `STT_LLM_MODEL`/`STT_LLM_URL` wechseln. Vor
einem neuen Standardmodell den Vergleich wiederholen (Fälle wie in §7 „LLM-Nachkorrektur“: echte
Whisper-Fehler, korrekte Sätze inkl. Befehle mit Verneinung/Zahlen, eine „Anweisung“ im Diktat). Prompt-
Änderungen in `SYSTEM_PROMPT`/`FEW_SHOT`, danach `pytest -m integration tests/test_llm.py`. Neue
Ablehnungsregeln gehören in `_accept` und brauchen einen Test mit `ScriptedCorrector`.
**Neuer Client** — gegen [docs/API.md](API.md) programmieren; `stt_cli.py` ist die Referenzimplementierung
für Datei-Upload und WebSocket-Streaming.
**Version erhöhen** — `stt_core.__version__` (wird von `/health`, FastAPI und Wyoming übernommen).
**Version erhöhen** — `stt_core.__version__` (wird von `/health`, FastAPI und Wyoming übernommen),
Beispiel in `docs/API.md` (`/health`) anpassen und Eintrag in `CHANGELOG.md` ergänzen.
---
@ -582,6 +594,8 @@ für Datei-Upload und WebSocket-Streaming.
- Keine Authentifizierung, kein TLS.
- Ein Modell pro Dienstinstanz; Modellwechsel = Neustart.
- Wyoming protokolliert Transkripttexte auf `INFO`.
- LLM-Korrektur: ~1 s pro Text, erstes Laden 35–75 s, ~10 GB Grafikspeicher; stark verstümmelte Sätze
werden oft nicht sinnvoll repariert. Nur in der CLI verfügbar.
- Die Mikrofonpfade der CLI sind mit synthetischer Sprache und Pegelmessung getestet, der WebSocket-Pfad
mit echter Sprache in Echtzeit-Simulation — nicht automatisiert mit echtem Mikrofon.
@ -590,6 +604,7 @@ für Datei-Upload und WebSocket-Streaming.
- Optionales Bearer-Token (`STT_API_TOKEN`) für HTTP/WebSocket.
- Metriken (`/metrics` im Prometheus-Format: Latenzen, Queue, Fehler).
- Zwischenergebnisse (partielle Transkripte) während langer Äußerungen.
- LLM-Korrektur optional auch im Dienst anbieten (z. B. `/transcribe?correct=true`), weiterhin standardmäßig aus.
- MCP-Adapter analog zu chatterbox-tts-cli (`mcp_adapter.py`).
- Remote-Repository auf kitux.de (Forgejo).
- Anbindung als STT-Provider im Voice-Assistant v3 (OpenAI-kompatibel bereits möglich).

View file

@ -28,7 +28,7 @@ import numpy as np
log = logging.getLogger("stt")
__version__ = "0.1.0"
__version__ = "0.2.0"
SAMPLE_RATE = 16000 # Whisper arbeitet intern mit 16 kHz mono float32