📝 Note

pro/translatert/decisions

translatertadr

Decisions — TranslateRT

ADR légers · ordre antéchronologique Liens : Architecture · Journal


2026-06-01 — Deltas gpt-realtime-translate sont cumulatifs, pas incrémentaux

Contexte : La doc OpenAI Realtime indique textContent += event.delta (incrémental). Backend utilisait +=. Résultat : concaténation de tous les états intermédiaires dans e.full → duplication visible. Décision : Utiliser = (assign) au lieu de += dans handleInputTranscript et handleOutputTranscript. Raison : L’API gpt-realtime-translate (endpoint /v1/realtime/translations) envoie dans chaque delta le texte complet depuis le début de l’utterance (rolling cumulative). Le dernier delta reçu = texte courant complet. Alternatives écartées : Garder += (doc officielle) — cassé en pratique. Utiliser les events done — se déclenchent par mot, pas par phrase, rendant l’approche inutilisable.

Voir aussi : Journal

2026-06-01 — Debounce display pour éviter affichage de mots partiels

Contexte : Avec =, chaque delta rapide montre un texte partiel (ex: “Mill” avant “Millenium”) avant que le delta suivant arrive. Affichage mot-par-mot non désirable. Décision : Debounce 400ms dans EventListener.tsx, 200ms par langue dans DisplayView.tsx via useRef timers. Raison : OpenAI envoie ~1 delta / 50-100ms pendant la parole. Un debounce > temps inter-déltas assure que l’affichage ne se met à jour qu’aux pauses naturelles. Alternatives écartées : Debounce 200ms (trop court, visible encore). done events (trop fréquents). Affichage live sans debounce (flashs visibles).

Voir aussi : Journal


2026-06-01 — Transcript WS émet e.full au lieu de e.delta

Contexte : Backend émettait chaque delta OpenAI brut via WS. Frontend accumulait avec " " fixe → espaces parasites dans les mots sur tokens sub-mots. Décision : pipeline.ts envoie text: e.full (texte cumulé complet) plutôt que text: e.delta. Raison : Backend accumule déjà dans this.metrics.outputTranscripte.full toujours cohérent. Frontend simplifié sans accumulation manuelle. Alternatives écartées : Concaténation sans espace (prev.join("") + event.text) — fragile, dépend du formatage OpenAI.


2026-06-01 — webAudioMix: false dans LiveKit Room

Contexte : Son métallique/flanging côté spectateur. Décision : webAudioMix: false dans constructeur Room (EventListener.tsx). Raison : webAudioMix: true + audioTrack.attach() = deux chemins lecture simultanés → comb filter. Alternatives écartées : webAudioMix: true sans attach() — joue tous les tracks, impossible sélectionner une seule langue.


2026-05-xx — Packaging Tauri V2

Contexte : Déploiement Mac mini = Homebrew + launchd multi-process, fragile. Décision : App Tauri V2 avec Node.js (SEA), livekit-server, caddy comme sidecars. Raison : Un seul .app, SIGTERM-stoppable, config externalisée TRANSLATERT_HOME. Alternatives écartées : Docker (incompatible CoreAudio/DVS), Electron (trop lourd).


MVP — Décisions fondatrices

DécisionChoixRaison
Capture audioffmpeg avfoundation / dshowCoreAudio direct
IAOpenAI Realtime S2SLatence < 3s
SFULiveKitWebRTC natif
FrontendNext.js export statiquePas SSR
BackendMonolithe FastifyMVP simple
DBAucuneÉtat mémoire
TLSCaddy DNS-01LAN HTTPS
Auth adminDésactivéeLAN privé
Format audioPCM16 24kHz monoOpus compatible
Frame LiveKit480 samples (20ms)Standard VoIP