DOCS/status/rayban-meta.md
2026-09-03 14:00:51 +02:00

11 KiB
Raw Permalink Blame History

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 branche Meta-Rayban-Test (23 commits d'avance sur master, jamais mergée — c'est la branche de travail courante du repo).

Brique Fichier État
SDK Meta pubspec.yaml:84meta_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; dans meta_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-on Déjà fait, constaté le 2026-08-13. constants.dart:20 dit « ElevenLabs retiré du pipeline (trop cher) » et VoiceController ne construit que GeminiTtsEngine ou la voix système. Le risque de marge qui motivait cette ligne est écarté. impl/elevenlabs_tts_engine.dart est 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.
    1. 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 écrit Instance.GuideVoiceId ; mymuseum-visitapp ne lisait jamais ce champ et utilisait une constante de build, dont le défaut était Algieba — 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).
    2. Aucun APK ne parlait avec la voix du produit. GeminiTtsEngine n'est choisi que si GEMINI_API_KEY est injectée au build — elle ne l'était nulle part, donc tous les builds retombaient sur FlutterTtsEngine, la voix système d'Android, en silence. Le repli s'annonce maintenant dans les logs, constants.dart porte la commande, et launch.json a 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 STT n'est pas Google non plus : WhisperSttEngine pointe sur api.openai.com si WHISPER_API_KEY est définie, sinon repli sur speech_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_flutter est commenté dans pubspec.yaml (payant), l'implémentation courante s'appuie sur speech_to_text / openWakeWord.
  • Branche jamais mergée dans master.
  • Le POC ne compile plus en l'état — relevé le 2026-08-11. flutter analyze lib sur mymuseum-visitapp remonte 4 erreurs, toutes le même manque : kElevenLabsApiKey et kElevenLabsVoiceId sont absents de constants.dart alors que glasses_qr_scanner_service.dart:110-111 et wake_word_service.dart:133-134 les utilisent et importent bien constants.dart. La doc de glasses_tts_service.dart:31-32 confirme l'intention (« typiquement kElevenLabsApiKey de constants.dart »). Ces deux constantes sont des secrets qui n'ont jamais été committés — le POC tournait avec un constants.dart local. Conséquence pratique : « démontrable » suppose de les remettre, ce n'est pas un git checkout qui suffit. À traiter avec la bascule TTS → Gemini ci-dessus, qui les rend inutiles.
  • flutter build apk ne passe plus non pluslevé le 2026-08-12 par K5. Les trois flavors construisent ; le NDK est passé à 28.2.13676358 et Kotlin à 2.3.10.
  • 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.dart n'est importé par personne, et glasses_qr_scanner_service.dart seulement par lui — un îlot de deux fichiers hors du graphe de main.dart, la génération d'avant l'orchestrateur. Le POC actuel, lui, est bien dans l'APK : Services/Glasses/ est importé par VoiceController, et GlassesStatusWidget est 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 choisit GeminiTtsEngine dès que kGeminiApiKey est renseignée (voice_controller.dart:94-100) et ne touche jamais kElevenLabs*. 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 K6tranché le 2026-08-13 par le propriétaire du projet : Meta-Rayban-Test est 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 de mymuseum-visitapp y 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. Idem tablet-app sur AI-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 renvoie fr-FR par défaut ; constants.dart:59-70 en 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 traductions voice.* existent déjà pour les 10 langues dans translations.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 _isQrScanCommand matche sur code : « 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.mp3 retarde la réponse de toute sa durée. :212 fait await _playDoneSound() juste avant ttsEngine.speak(), et _playSound attend play(), 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) et GeminiTtsEngine._synthesize() fait un generateContent unaire 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 dans GeminiTtsEngine, 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.