This commit is contained in:
ValueOn AG 2026-03-17 19:19:39 +01:00
parent 7f5f31db30
commit ebaaf77942
2 changed files with 327 additions and 0 deletions

View file

@ -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 (
<div>
<button onClick={startMic}>Mic Start</button>
<button onClick={stopMic}>Mic Stop</button>
<button onClick={() => setIsMuted(v => !v)}>{isMuted ? 'Unmute' : 'Mute'}</button>
<div>{speech.liveTranscript || 'Mikrofon bereit'}</div>
</div>
);
}
```
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.

Binary file not shown.