diff --git a/concepts/Commcoach-Voice-Recording-Streaming-Konzept.md b/concepts/Commcoach-Voice-Recording-Streaming-Konzept.md index df939f7..f1424c8 100644 --- a/concepts/Commcoach-Voice-Recording-Streaming-Konzept.md +++ b/concepts/Commcoach-Voice-Recording-Streaming-Konzept.md @@ -332,3 +332,330 @@ Ersetzt wird: Der Umbau ist vollständig in der eigenen Plattform machbar und löst das Mobile-5-Sekunden-Problem an der Wurzel. Für Wartbarkeit und konsistentes Verhalten ist ein **genereller Umbau (Variante B)** sinnvoll. Ein stufenweiser Rollout mit Feature-Flag minimiert Risiko. + +--- + +## Implementierungsfreigabe (Ready for Build) + +Dieser Abschnitt ist die verbindliche Umsetzungsvorlage. + +### Verbindliche Entscheidungen + +1. **Scope** + - Umsetzung als **Variante B** (ein Stack fuer Mobile + Desktop). + +2. **Transport** + - STT-Eingang erfolgt ueber **WebSocket**. + - Text-/Assistant-Antworten bleiben vorerst auf bestehendem **SSE**-Pfad. + +3. **STT-Engine** + - Self-hosted Whisper-Pfad (`faster-whisper`) wird als neue Connector-Linie eingefuehrt. + - Bestehender Google-One-Shot-Pfad bleibt nur als Legacy fuer `audio/stream`. + +4. **Fehlerbehandlung** + - Keine stillen Fallbacks. + - Bei aktivem `streamedStt` wird ein STT-Fehler sichtbar ins UI gemeldet. + +5. **Feature Flag** + - `commcoachVoiceProvider = browserSpeech | streamedStt`. + - Default initial: `browserSpeech`, Pilot: `streamedStt`. + +### Verbindliche API Spezifikation + +#### WS Endpoint + +- `GET ws /api/commcoach/{instanceId}/sessions/{sessionId}/stt/stream` + +#### Client -> Server Nachrichten + +1. `open` +```json +{ + "type": "open", + "sessionId": "string", + "language": "de-DE", + "codec": "pcm16", + "sampleRate": 16000, + "channels": 1 +} +``` + +2. `audio` +```json +{ + "type": "audio", + "seq": 1, + "chunk": "base64-encoded-audio-bytes", + "durationMs": 200 +} +``` + +3. `commit` +```json +{ + "type": "commit", + "reason": "silence" +} +``` + +4. `close` +```json +{ + "type": "close" +} +``` + +#### Server -> Client Nachrichten + +1. `status` +```json +{ + "type": "status", + "label": "Sprache wird erkannt..." +} +``` + +2. `ack` +```json +{ + "type": "ack", + "seq": 1 +} +``` + +3. `interim` +```json +{ + "type": "interim", + "segmentId": "seg-uuid", + "text": "teiltranskript" +} +``` + +4. `final` +```json +{ + "type": "final", + "segmentId": "seg-uuid", + "text": "finales segment", + "confidence": 0.91 +} +``` + +5. `error` +```json +{ + "type": "error", + "code": "stt_failed", + "message": "Transkription fehlgeschlagen" +} +``` + +6. `closed` +```json +{ + "type": "closed", + "reason": "server" +} +``` + +### Orchestrierungsregeln (verbindlich) + +1. `useVoiceController` bleibt Owner der Zustandsmaschine. +2. `streamedStt` liefert nur Transkript-Ereignisse, steuert keine State-Transitions direkt. +3. Nur `final` Texte duerfen `onMessage(...)` triggern. +4. `interim` aktualisiert nur `liveTranscript`. +5. Bei `ttsPlaying` wird STT-Stream pausiert/geschlossen. +6. Bei `ttsEnded` wird STT-Stream wieder geoeffnet. +7. `muted=true` blockiert Senden von `audio` Chunks. + +### Konkrete Datei- und Funktionsaenderungen + +#### Frontend `frontend_nyla` + +1. `src/api/commcoachApi.ts` + - Neue Funktion `openSttStreamApi(instanceId, sessionId, handlers, options)`. + - Rueckgabeobjekt mit `sendAudioChunk`, `sendCommit`, `close`. + +2. `src/hooks/useAudioStreamTranscription.ts` (neu) + - Intern: + - `_startMicCapture()` + - `_stopMicCapture()` + - `_encodePcm16Chunk()` + - `_pushChunkToWs()` + - Extern: + - `startStream()` + - `stopStream()` + - `commitSegment(reason)` + +3. `src/pages/views/commcoach/useVoiceController.ts` + - Provider-Layer einfuegen: + - `browserSpeechProvider` + - `streamedSttProvider` + - `streamedSttProvider` auf neues Hook mappen. + - Bestehende State-Transitionen unveraendert lassen. + +4. `src/pages/views/commcoach/CommcoachDossierView.tsx` + - Debugpanel um STT-WS Events erweitern (`STT-OPEN`, `STT-INTERIM`, `STT-FINAL`, `STT-ERR`, `STT-CLOSE`). + +#### Gateway `gateway` + +1. `modules/features/commcoach/routeFeatureCommcoach.py` + - Neue WS-Route `.../stt/stream`. + - Auth/Ownership-Pruefung identisch zu bestehenden Session-Endpunkten. + +2. `modules/features/commcoach/serviceCommcoachSttStream.py` (neu) + - Session-Stream-Manager: + - `_openSessionStream()` + - `_handleAudioChunk()` + - `_flushSegment()` + - `_closeSessionStream()` + - Final-Segmente an `CommcoachService.processMessage(...)` uebergeben. + +3. `modules/interfaces/interfaceVoiceObjects.py` + - Streaming API erweitern: + - `startSttStream(...)` + - `pushSttAudioChunk(...)` + - `finalizeSttSegment(...)` + - `stopSttStream(...)` + +4. `modules/connectors/connectorVoiceWhisper.py` (neu) + - Whisper-basierte Implementierung fuer Streaming-Segmente. + - Config fuer Modellgroesse, Sprache, VAD. + +### Reihenfolge fuer die Umsetzung + +1. Gateway WS Route + Dummy-Events (ohne echte STT). +2. Frontend WS Client + Mikrofondaten senden. +3. Connector/Whisper Integration im Gateway. +4. Segmentierung und `final -> processMessage`. +5. Feature-Flag Integration und Pilot-Rollout. +6. Legacy-Bereinigung. + +### Abnahmekriterien (Definition of Done) + +1. **Mobile Stabilitaet** + - 60 Sekunden durchgehendes Sprechen ohne erzwungenen 5-Sekunden-Reset. + +2. **Textintegritaet** + - Keine abgeschnittenen Saetze zwischen Segmenten. + - Keine Duplikate bei finalen Segmenten. + +3. **State Machine Integritaet** + - Keine User-Transkripte waehrend `botSpeaking`. + - `muted` blockiert Audio-Chunks sofort. + +4. **Fehlertransparenz** + - STT- oder WS-Fehler werden im UI sichtbar angezeigt. + - Kein stiller Fallback auf Browser-STT im `streamedStt` Modus. + +5. **Performance** + - Time to first interim <= 1200 ms (WLAN Referenz). + - Time to first final <= 2500 ms fuer kurze Saetze. + +### Testplan (verbindlich) + +1. Unit Tests + - Segment-Assembler und Seq-Handling. + - State Transition Tests fuer `ttsPlaying`, `ttsEnded`, `muted`. + +2. Integration Tests + - WS Open -> Audio -> Interim -> Final -> Close. + - Reconnect nach Netzunterbruch. + +3. Manuelle Geraetetests + - iOS Safari (aktuell + 1 Vorversion). + - Android Chrome (aktuell + 1 Vorversion). + - Desktop Chrome/Edge. + +4. Regression + - Bestehende Text-SSE Flows unveraendert funktionsfaehig. + - TTS Wiedergabe und Stop/Resume weiterhin stabil. + +### Rollout und Backout + +1. Rollout + - Feature-Flag pilotweise pro Instanz aktivieren. + - Metriken mindestens 3 Tage beobachten. + +2. Backout + - Bei kritischen Fehlern Flag auf `browserSpeech` zuruecksetzen. + - Keine DB-Migrationen erforderlich. + +### Aufwandsschaetzung + +- Gateway WS + Stream Service: 2-3 Tage +- Whisper Connector + Tuning: 2-4 Tage +- Frontend Hook + Provider Refactor: 2-3 Tage +- Tests + Pilot-Hardening: 2-3 Tage + +Gesamt: **8-13 Arbeitstage** fuer produktionsreife Erstversion. + +## Reuse Snippet (AI Workspace und weitere Views) + +Der Hook `useSpeechAudioCapture` ist generisch und kann ausserhalb von CommCoach wiederverwendet werden. +Er kapselt Mikrofonzugriff, VAD, Segmentierung und Chunk-Emission. + +Beispielintegration in einer anderen View: + +```tsx +import React, { useState } from 'react'; +import { useSpeechAudioCapture } from '../../hooks/useSpeechAudioCapture'; + +export default function WorkspaceVoiceInput() { + const [isMicActive, setIsMicActive] = useState(false); + const [isMuted, setIsMuted] = useState(false); + + const speech = useSpeechAudioCapture( + { + // Controller-Bedingung: nur aufnehmen wenn aktiv und nicht stumm + isCaptureAllowed: () => isMicActive && !isMuted, + + // Laufende Audio-Chunks an den eigenen Stream senden + onChunk: async (audioChunk) => { + await wsSendChunk(audioChunk); // eigene WS/API Implementierung + }, + + // Segmentabschluss bei Stille oder manuellem Stop + onSegment: async ({ reason }) => { + wsSendCommit(reason); // z. B. 'silence' oder 'manual' + }, + + // Optionales Debugging + onDebug: (tag, info) => console.log(tag, info), + onError: (error) => console.error('Speech capture error', error), + }, + { + silenceTimeoutMs: 1200, + vadRmsThreshold: 0.03, + vadIntervalMs: 120, + minSegmentDurationMs: 450, + recordingChunkMs: 250, + }, + ); + + const startMic = async () => { + setIsMicActive(true); + await speech.startCapture(); + }; + + const stopMic = () => { + setIsMicActive(false); + speech.stopCapture('manual', true); + }; + + return ( +
+ + + +
{speech.liveTranscript || 'Mikrofon bereit'}
+
+ ); +} +``` + +Minimalanforderungen fuer Reuse: +- `isCaptureAllowed` muss den lokalen UI-State korrekt abbilden. +- `onChunk` und `onSegment` muessen auf den Ziel-Transport (WS/SSE/API) gemappt werden. +- `stopCapture(..., true)` soll bei View-Wechsel oder Unmount aufgerufen werden, um Mic sauber freizugeben. diff --git a/deployment/poweron_sec.kdbx b/deployment/poweron_sec.kdbx index ea65af5..9e0f6af 100644 Binary files a/deployment/poweron_sec.kdbx and b/deployment/poweron_sec.kdbx differ