11 KiB
5bis. Lunettes Ray-Ban Meta — état réel du code (relevé le 2026-08-09)
← Section §5bis du tableau de bord : STATUS.md
Cette section corrige une erreur de ce fichier : la roadmap classait l'AR lunettes en « pas commencé, pilote subventionné quand le SDK sort de preview ». Un POC fonctionnel existe depuis mai-juin 2026, ~2 700 lignes de Dart dans
mymuseum-visitapp, sur la brancheMeta-Rayban-Test(23 commits d'avance surmaster, jamais mergée — c'est la branche de travail courante du repo).
| Brique | Fichier | État |
|---|---|---|
| SDK Meta | pubspec.yaml:84 — meta_wearables_dat: ^0.1.3 |
Dépendance active, pas commentée |
| Connexion / photo / stream vidéo | lib/Services/meta_glasses_service.dart (241 l.) |
Cycle de vie complet, routage audio HFP via canal natif |
| Pipeline mains-libres | lib/Services/Glasses/voice_orchestrator.dart (408 l.) |
wake word → STT → dispatch → LLM ou scan QR → TTS, sons d'état |
| Moteurs interchangeables | lib/Services/Glasses/engines/ + impl/ (~1 040 l.) |
4 interfaces, 11 implémentations : openWakeWord, Porcupine, speech_to_text, Whisper, Home Assistant STT, Gemini TTS, ElevenLabs, flutter_tts, client LLM MyInfoMate |
| Service de fond | lib/Services/Glasses/glasses_background_service.dart |
Téléphone en poche, écran verrouillé |
| UI | VoiceModeSheet, GlassesStatusWidget, GlassesDebugPanel (450 l., via AdminPopup) |
Monté dans main.dart:109-112 |
Commit c652604 : « Working flow also in background ! With custom wakeword working (hey visit et hey viva). flow llm working + take photo + scan qr code working ».
Ce qui bloque réellement la commercialisation
Pas « le SDK est en preview » — la liste est plus concrète :
- Android uniquement (
if (!Platform.isAndroid) return;dansmeta_glasses_service.dart). Le modèle BYOD tombe pour tous les visiteurs iPhone. Seul le modèle « kit prêté par le lieu » tient aujourd'hui. - Pas de distribution store tant que le SDK est en developer preview : chaque installation passe par les credentials développeur.
- ✅
TTS = ElevenLabs, facturé au caractère — basculer sur Gemini avant de vendre l'add-onDéjà fait, constaté le 2026-08-13.constants.dart:20dit « ElevenLabs retiré du pipeline (trop cher) » etVoiceControllerne construit queGeminiTtsEngineou la voix système. Le risque de marge qui motivait cette ligne est écarté.impl/elevenlabs_tts_engine.dartest conservé comme option — à rouvrir seulement si des clients jugent la voix Gemini insuffisante, en sachant que c'est beaucoup plus cher. - ⚠️ Mais deux défauts ont vécu derrière cette ligne, corrigés le 2026-08-13.
- Le choix de voix du client n'était pas honoré. Le sélecteur Viva (
Sulafat) / Marco (Umbriel) existe dans manager-app depuis le 07/08 et écritInstance.GuideVoiceId;mymuseum-visitappne lisait jamais ce champ et utilisait une constante de build, dont le défaut étaitAlgieba— une voix que personne n'a écoutée. Même forme que W1 : un réglage offert par le CMS que l'app visiteur ignore. La voix vient désormais de l'instance, la constante n'étant plus qu'un repli (passé àSulafat). - Aucun APK ne parlait avec la voix du produit.
GeminiTtsEnginen'est choisi que siGEMINI_API_KEYest injectée au build — elle ne l'était nulle part, donc tous les builds retombaient surFlutterTtsEngine, la voix système d'Android, en silence. Le repli s'annonce maintenant dans les logs,constants.dartporte la commande, etlaunch.jsona une configuration « Dev + voix Gemini ». ⚠️ Le repli reste le défaut, et c'est voulu : les tests de terrain ne consomment ni jetons ni argent.
- Le choix de voix du client n'était pas honoré. Le sélecteur Viva (
- ⚠️ Le STT n'est pas Google non plus :
WhisperSttEnginepointe surapi.openai.comsiWHISPER_API_KEYest définie, sinon repli surspeech_to_text, le moteur du device. La clé est vide aujourd'hui, donc c'est le device qui transcrit. À trancher au lot J : activer Whisper ajouterait OpenAI comme sous-traitant, que le §8.5 des CGU ne mentionne pas — il ne parle que de Google. - Wake word de prod non réglé :
porcupine_flutterest commenté danspubspec.yaml(payant), l'implémentation courante s'appuie surspeech_to_text/ openWakeWord. - Branche jamais mergée dans
master. - Le POC ne compile plus en l'état — relevé le 2026-08-11.
flutter analyze libsurmymuseum-visitappremonte 4 erreurs, toutes le même manque :kElevenLabsApiKeyetkElevenLabsVoiceIdsont absents deconstants.dartalors queglasses_qr_scanner_service.dart:110-111etwake_word_service.dart:133-134les utilisent et importent bienconstants.dart. La doc deglasses_tts_service.dart:31-32confirme l'intention (« typiquementkElevenLabsApiKeydeconstants.dart»). Ces deux constantes sont des secrets qui n'ont jamais été committés — le POC tournait avec unconstants.dartlocal. Conséquence pratique : « démontrable » suppose de les remettre, ce n'est pas ungit checkoutqui suffit. À traiter avec la bascule TTS → Gemini ci-dessus, qui les rend inutiles. - ✅
— levé le 2026-08-12 par K5. Les trois flavors construisent ; le NDK est passé à 28.2.13676358 et Kotlin à 2.3.10.flutter build apkne passe plus non plus - ⛔ Correction du 2026-08-13 : « l'APK se construit sans le POC dedans » est faux, et la conclusion qu'on en tirait aussi. Les 4 erreurs ne sont pas dans le POC vivant, elles sont dans ses ancêtres :
wake_word_service.dartn'est importé par personne, etglasses_qr_scanner_service.dartseulement par lui — un îlot de deux fichiers hors du graphe demain.dart, la génération d'avant l'orchestrateur. Le POC actuel, lui, est bien dans l'APK :Services/Glasses/est importé parVoiceController, etGlassesStatusWidgetest monté sur l'accueil.
⚠️ Conséquence pratique, inverse de celle qui était écrite ici : « démontrable » ne suppose pas de remettre les deux constantes ElevenLabs. Le chemin vivant choisitGeminiTtsEnginedès quekGeminiApiKeyest renseignée (voice_controller.dart:94-100) et ne touche jamaiskElevenLabs*. La bascule TTS → Gemini réclamée ci-dessus est déjà faite dans le code ; ce qui reste est de supprimer les deux ancêtres morts. - ✅
Branche jamais mergée / à trancher avant K6— tranché le 2026-08-13 par le propriétaire du projet :Meta-Rayban-Testest la branche de travail à jour, pas un POC de côté. Le nom est trompeur, il date de la première expérimentation ; tout le travail V1 demymuseum-visitappy vit et les lunettes n'en sont qu'une partie — invisibles, d'ailleurs, si l'instance n'a pas l'assistant. On développe et on publie depuis elle. Idemtablet-appsurAI-Assistant-test. Rien à clarifier avant K6 ; c'est ce paragraphe qui affirmait le contraire et fabriquait l'alerte.
Latence, langues et accusés de réception — relevé le 2026-08-13
Plan complet : voice-latency-plan.md, découpé en « maintenant / après les tests / V2 ». Ce qui suit n'en garde que les constats vérifiés dans le code.
- ⛔ L'assistant vocal ne parle réellement que FR/NL/EN/DE, et personne ne le disait.
_toLangCode(voice_orchestrator.dart:399-407) ne mappe que ces quatre langues et renvoiefr-FRpar défaut ;constants.dart:59-70en déclare 10. Un visiteur en italien se fait répondre en français, sans erreur ni trace. Décidé le 2026-08-13 : la limite est assumée et documentée, le reste de l'app garde ses 10 langues. Le coût d'en rajouter une est faible et c'est vérifié — les traductionsvoice.*existent déjà pour les 10 langues danstranslations.dart, Whisper prend le code générique et est multilingue, le wake word est phonétique. - ⛔ Et les quatre langues annoncées ne le sont pas non plus.
_isStopCommand,_isRepeatCommand,_isQrScanCommand,_isPhotoCommand(:375-397) cherchent des mots français en dur — « répète », « arrête », « prends », « regarde ». Elles avaient été écrites en français pour tester et n'ont jamais été reprises. Un néerlandophone qui dit « herhaal » n'est pas compris, et_isQrScanCommandmatche surcode: « what's the code of this painting » déclenche un scan QR. À corriger avant le lot H — sinon les tests multilingues valident autre chose que ce qu'on croit. - ⚠️
done.mp3retarde la réponse de toute sa durée.:212faitawait _playDoneSound()juste avantttsEngine.speak(), et_playSoundattendplay(), dont le future ne se résout qu'à la fin de la lecture. En prime c'est un doublon : la parole est le signal de fin. - ⚠️ Le time-to-first-audio est la somme de tout.
LlmClient.chat()retourne un future de réponse complète (pas de flux) etGeminiTtsEngine._synthesize()fait ungenerateContentunaire qui attend tout le PCM avant d'écrire le WAV. D'où le son de réflexion en boucle, qui bouche ce trou. Le remède qui ne dépend d'aucune API nouvelle : découper la réponse en phrases et synthétiser la première pendant que les suivantes se préparent — contenu dansGeminiTtsEngine, sans toucher au backend. - 💡 Idée retenue mais repoussée : remplacer le bip du wake word par une phrase parlée (« Oui, je vous écoute ») dans la langue et la voix du visiteur, 2-3 variantes. Repoussée après les tests parce qu'elle demande de générer et valider à l'oreille ~40 fichiers (2 voix × 4 langues × 5 phrases), et qu'une partie du besoin qu'elle compense disparaît si la latence baisse. ⚠️ Règle de cohérence si on la fait : les acks ne s'activent que si le moteur runtime est Gemini et que la voix correspond à
guideVoiceId— des acks en Sulafat suivis d'une réponse en voix système Android seraient pires que le bip. - 🔭 Piste V2 : le Live API de Gemini (WebSocket, audio natif bidirectionnel) supprimerait les trois maillons Whisper → LLM → TTS et débloquerait le barge-in et le VAD serveur. Faisable, mais le tool calling devrait passer par un proxy WebSocket dans
manager-service(option retenue sur le papier), le modèle de coût passe à la session ouverte avec des jetons audio bien plus chers, et les modèles sont en preview. À ouvrir par un spike chiffré d'une journée, pas par une décision.
Ce que ça change côté commercial
Le POC est démontrable. Face à un concurrent mono-usage type Musa Guide, une démo qui tourne pèse plus qu'une ligne « bientôt » sur la landing. À condition d'assumer le kit Android prêté, et de ne pas vendre l'add-on avant la bascule TTS.