teamsbot anonymous bot working

This commit is contained in:
ValueOn AG 2026-05-12 19:16:21 +02:00
parent 737441fe00
commit fbcfa6ee2b
2 changed files with 12 additions and 1 deletions

View file

@ -1,6 +1,6 @@
<!-- status: canonical -->
<!-- lastReviewed: 2026-05-12 -->
<!-- verifiedAgainst: service-teams-browser-bot (statisches Avatar-Video + anonymer Join via Chrome-Channel + WebRTC-Wrapper-Gating 2026-05-12); service-teams-browser-bot (documentation review 2026-02-18); gateway/modules/features/teamsbot/{service,routeFeatureTeamsbot,interfaceFeatureTeamsbot,datamodelTeamsbot}.py + `GET /api/teamsbot/{instanceId}/dashboard/stream` (2026-05-11); gateway/tests/unit/teamsbot/test_directorPrompts.py (Director Prompts 2026-04-24); gateway Voice STT batch + linear16 (2026-05-10); frontend_nyla TeamsbotDashboardView / TeamsbotModulesView / TeamsbotSessionView (IA + SSE 2026-05-11) -->
<!-- verifiedAgainst: service-teams-browser-bot (statisches Avatar-Video + anonymer Join via Chrome-Channel + WebRTC-Wrapper-Gating 2026-05-12); service-teams-browser-bot (documentation review 2026-02-18); gateway/modules/features/teamsbot/{service,routeFeatureTeamsbot,interfaceFeatureTeamsbot,datamodelTeamsbot}.py + STT single-language (2026-05-12) + `GET /api/teamsbot/{instanceId}/dashboard/stream` (2026-05-11); gateway/tests/unit/teamsbot/test_directorPrompts.py (Director Prompts 2026-04-24); gateway Voice STT batch + linear16 (2026-05-10); frontend_nyla TeamsbotDashboardView / TeamsbotModulesView / TeamsbotSessionView (IA + SSE 2026-05-11) -->
# Teams Meeting Bot -- Architektur
@ -84,6 +84,8 @@ Der Gateway (Feature `teamsbot`) verwaltet Sessions und stellt die AI-Pipeline b
**STT auf dem Gateway:** Meeting-Audio-Chunks (WebSocket `audioChunk`, PCM) werden pro Chunk mit `VoiceObjects.speechToText` transkribiert (Batch `recognize`, gemeinsamer Connector mit CommCoach). Konfiguration u. a. `audioFormat=linear16`, `skipFallbacks=True`; Details und Modell-Defaults: [voice-google.md](../gateway/voice-google.md).
**Sprache ist single-language.** Die STT-Sprache ist die Sessionsprache (`TeamsbotUserSettings.language` überschreibt `TeamsbotConfig.language`, Schema-Default `de-DE`). Es gibt **keine** hardcodierten Alternativ-Sprachen — frühere `alternative_language_codes=["en-US"]`-Kombi liess Google STT bei verrauschter Audio (z.B. nach langem Bot-TTS-Playback mit minimalem akustischem Loopback) auf en-US springen und englisches Kauderwelsch zurückgeben (Confidences 0.30.5), während saubere Sprache korrekt als de-DE mit ~0.9+ erkannt wurde. Falls mehrsprachige Meetings gebraucht werden: `connectorVoiceGoogle.speechToText` akzeptiert `alternativeLanguages: list[str]`; eine entsprechende Konfig-Spalte (z.B. `TeamsbotConfig.alternativeLanguages`) müsste explizit eingeführt werden.
### Audio-Capture: WebRTC-Wrapper-Gating
Der Bot installiert einen `RTCPeerConnection`-Wrapper per `addInitScript` (Browser-Start, vor jeder Teams-Navigation), damit später keine PC unbeobachtet bleibt. **Während Pre-Join, Lobby und SDP-Renegotiation darf der Wrapper aber NICHTS am Audio-Stream anfassen** — kein `clone()`, kein `createMediaStreamSource()`, kein `AudioContext`. Jeder Eingriff in dieser Phase löst in Teams' aktuellem `light-meetings`-Bundle den Renderer-Crash `Cannot read properties of null (reading 'rejectMediaDescriptionsUpdateAsync')` aus und der anonyme Bot landet entweder dauerhaft in der Lobby oder wird wieder zur Pre-Join-Seite geworfen.
@ -175,6 +177,11 @@ Implementierung in `src/bot/mediaGetUserMediaPatch.ts`:
- Video-Track-`contentHint` von `'motion'` auf `'detail'` umgestellt — sagt dem WebRTC-Encoder "statisch", er reduziert Bitrate und behält Textschärfe.
- Farben werden vom `audioProcedure.ts` an den Init-Script-Payload durchgereicht und in `_startBotAvatarStream()` als `fillStyle` für Hintergrund + Label verwendet.
**Benutzerdefiniertes Avatar-Bild / Video:** Benutzer können in den Bot-Einstellungen (`TeamsbotConfig.avatarFileId`) oder pro Modul (`TeamsbotMeetingModule.defaultAvatarFileId`) eine Datei aus dem Dateisystem auswählen (Bilder: PNG, JPG, etc.; Videos: MP4, WebM, etc.). Modul-Einstellung überschreibt Instanz-Default. Beim Session-Start löst das Gateway die Datei auf, lädt sie als Base64 und sendet sie im Join-Payload (`avatarMediaData` + `avatarMediaType`) an den Bot. Der Bot rendert:
- **Bilder**: `drawImage()` auf den Canvas (statisch, 2 fps).
- **Videos**: Unsichtbares `<video>`-Element mit Loop, Frames werden per `drawImage()` auf den Canvas gezeichnet (15 fps, `contentHint='motion'`).
- **Kein File gesetzt**: Fallback auf statische Farbfläche + Bot-Name (bisheriges Verhalten).
Der Auth-Bot ist von der Änderung nur betroffen, wenn `BOT_USE_CANVAS_VIDEO=true` gesetzt ist; sonst sendet er weiterhin gar kein Video.
## Hybrid-Routing: SPEECH_TEAMS + Agent

View file

@ -14,6 +14,10 @@ Skip: reine Refactors, Formatting, Lint, Dep-Bumps, Test-only, Wiki-Tippfehler.
## 2026-05-12
- 2026-05-12 | feat | gateway, frontend-nyla, teams-bot | Avatar-Bild/Video für TeamsBot: Neues Feld `avatarFileId` in `TeamsbotConfig`/`TeamsbotUserSettings` und `defaultAvatarFileId` in `TeamsbotMeetingModule`. Benutzer können in den Bot-Einstellungen und pro Modul ein Bild oder Video aus den Systemdateien auswählen, das anstelle der statischen Farbfläche als Bot-Video im Meeting gerendert wird. Modul-Einstellung überschreibt Instanz-Default. Gateway löst die Datei beim Session-Start auf, konvertiert zu Base64, und sendet sie im Join-Payload an den Browser-Bot. Bot-seitig: `mediaGetUserMediaPatch.ts` rendert Bilder per `drawImage()` auf den Canvas, Videos per `<video>`-Element mit Loop. Für Videos wird FPS auf 15 erhöht und `contentHint='motion'` gesetzt. Fallback bleibt die statische Farbfläche mit Bot-Name.
- 2026-05-12 | fix | gateway, frontend-nyla | TeamsBot Module-Status `[null]`: `createModule`-Route schrieb keinen `status` in die DB, weil `CreateMeetingModuleRequest` kein `status`-Feld hat und `recordCreate` bei Dict-Input keine Pydantic-Defaults anwendet. Frontend `t(null)` ergab `[null]`. Fix: (1) Route setzt `data.setdefault("status", "active")`, (2) `getModules` setzt fehlenden Status defensiv auf `"active"` für existierende Records, (3) Frontend-Fallback `mod.status || 'Aktiv'` als letzte Absicherung.
- 2026-05-12 | fix | frontend-nyla | TeamsBot Director-Panel Sichtbarkeit: gleiches Whitelist-Problem wie zuvor beim Stop-Button. `TeamsbotSessionView.tsx` Z.903 prüfte `['active','joining','pending'].includes(session.status)` — wenn der Status direkt nach Mount kurz leer / unbekannt war (SSE-Race vor `_loadSession`-Resolve, oder Backend schickt einen Übergangsstatus den die Liste nicht kennt), verschwand das ganze "Regieanweisungen"-Panel und kam erst nach Page-Reload wieder. Auf Blacklist umgestellt: `!['ended','error','leaving'].includes(session.status)`. Andere Whitelist-Stellen in derselben Datei (Session-Auswahl Z.136, SSE-Reconnect Z.186/373/439, Listen-Label Z.792) sind defensive Filter mit `status &&`-Guard und bleiben korrekt; Dashboard-Stop-Button war bereits auf Blacklist (Vorgänger-Fix).
- 2026-05-12 | fix | teams-bot, gateway | STT-Sprache: hardcodierten `alternativeLanguages=["en-US"]`-Fallback in `service._processAudioChunk` entfernt. Symptom: nach längerem Bot-TTS-Playback (z.B. 30s Web-Recherche-Antwort) sprang Google STT bei verrauschter Audio auf en-US (`language_code: "en-us"` im Response, Confidence 0.30.5) und lieferte englisches Kauderwelsch ("The transition trade constitute the Range Line."), während saubere Sprache derselben Session korrekt als de-DE mit 0.97 transkribiert wurde. Root Cause: der hardcodierte Englisch-Alternative gab Google die Erlaubnis, bei niedriger DE-Konfidenz nach EN umzuschalten. Fix: nur noch die Sessionsprache (`self.config.language`, Default `de-DE`) wird übergeben — `TeamsbotUserSettings.language` überschreibt `TeamsbotConfig.language` wie gehabt. Settings-Audit bestätigt: alle 12 UI-Felder (`botName`, `aiSystemPrompt`, `responseMode`, `responseChannel`, `transferMode`, `language`, `voiceId`, `triggerIntervalSeconds`, `triggerCooldownSeconds`, `contextWindowSegments`, `debugMode`, `browserBotUrl`) sind im Code aktiv genutzt — keine Karteileichen. (b-ref: teams-bot/architecture.md → "STT auf dem Gateway")
- 2026-05-12 | refactor | teams-bot | Chat-Panel-Detection: tote Fallback-Schichten entfernt. `_isChatPanelOpen()` reduziert auf den nachweislich funktionierenden Pfad: `[data-tid="calling-right-side-panel"]` Existenz+Visibility + chat-spezifischer Child-`data-tid` darin. Schicht 2 (`aria-pressed`-Toggle für "ältere Auth-Layouts") und Schicht 3 (Compose-Box-Sichtbarkeit als Last-Resort) waren toter Code — anon und auth nutzen beide denselben `calling-right-side-panel`-Container, der `aria-pressed`-Pfad war zwei Iterationen vorher selbst als unzuverlässig verworfen worden. Konform mit "Do not add fallback code, if not necessary". (b-ref: teams-bot/architecture.md → "Chat-Panel-Detection")
- 2026-05-12 | feat | teams-bot | Statisches Avatar-Video für anonymen Bot ersetzt Teams' grünen "no video"-Spinner. Neuer Toggle `BOT_USE_CANVAS_VIDEO=true` (Default true im `.env`) plus `BOT_AVATAR_BG_COLOR=#a8d4f0` / `BOT_AVATAR_TEXT_COLOR=#1a3552` in `src/config.ts`, durchgereicht via `audioProcedure.ts` an den Init-Script-Payload `mediaGetUserMediaPatch.ts`. `_startBotAvatarStream()` zeichnet jetzt eine ruhige einfarbige Fläche + zentrierter Anzeigename (kein Pulse, kein Gradient, keine "PORTA"-Marke), Tick-Rate von 15 fps auf 2 fps gesenkt (genug um `captureStream()` in headless Chromium aktiv zu halten, ohne dass der Encoder Frame-Diffs sieht), `contentHint='motion'``'detail'` damit WebRTC für statisches Bild Bitrate spart und Text scharf bleibt. Auth-Bot ist nur betroffen wenn der Toggle aktiv ist. (b-ref: teams-bot/architecture.md → "Statisches Avatar-Video (ersetzt grünen Lade-Spinner)")
- 2026-05-12 | fix | frontend-nyla | TeamsBot Stop-Button Sichtbarkeit: Logik von Whitelist (`['active','joining','pending']`) auf Blacklist (`!['ended','error','leaving']`) umgestellt in `TeamsbotSessionView.tsx` Z.841 + `TeamsbotDashboardView.tsx` Z.264. Verhindert Verschwinden des Buttons bei kurzfristig leerem/unbekanntem Status (z.B. SSE-Race kurz nach Mount, neuer Backend-Status). Button verschwindet nur noch nach explizit terminalen States. Hinweis zur i18n: Source-Keys `t('Sitzung beenden')` / `t('Stoppen')` waren bereits korrekt verpackt — die User-sichtbare englische Anzeige "End Session" stammt aus der Übersetzungs-DB und ist die intendierte i18n-Auflösung des deutschen Source-Keys.