docs: runbook Dr Nexum Atlas Cloud i2v + ElevenLabs + mcp-vps/mcp-nas OAuth fix

This commit is contained in:
2026-07-22 13:43:02 +00:00
parent 6a3f3e5616
commit a780050951
+187
View File
@@ -0,0 +1,187 @@
# Dr Nexum — pipeline image→vidéo (Atlas Cloud, Seedance 2.0 Fast)
**Sphère** : Nyora / Dr Nexum
**Script** : `hermes-nabil` VPS, `/data/dr_nexum/atlas_i2v.py`
**Statut** : PIPELINE VALIDÉ — 3/3 clips confirmés harmonieux (480p/4s, generate_audio=False). Prêt pour production.
## Config gagnante
- **480p, 4s**, `generate_audio=False`
- Prompt **minimal et littéral** : décrire uniquement le mouvement physique observable dans l'image, ne jamais mentionner un objet ou un détail absent/ambigu
## Piège identifié : prompt trop détaillé → hallucination
Testé en **720p/5s** avec un prompt plus détaillé : le modèle a fait "surgir" des t-shirts qui n'étaient pas dans l'image d'origine. Cause probable : un prompt qui décrit des éléments non ancrés visuellement pousse Seedance à les générer plutôt qu'à les ignorer. Rester strictement descriptif du mouvement visible corrige le problème.
## Coûts réels confirmés (facturation Atlas Cloud)
| Config | Tokens | Coût |
|---|---|---|
| 720p / 5s | 108 900 | 0,79 $ |
| 480p / 4s | 40 594 | ~0,30 $ (ratio proportionnel) |
480p/4s est nettement plus économique pour un ratio qualité déjà jugé satisfaisant. Sur un budget PAYG de 25 $, largement de quoi itérer en 480p/4s (~80 clips possibles).
## Reste à faire
- ~~Tester `hq720.jpg` et `images__3_.jpeg`~~ FAIT le 21/07, validé
- Bascule en production sur hermes-nabil : le script y tourne déjà nativement, aucune étape supplémentaire nécessaire — utiliser mcp-vps + `docker exec` pour chaque nouvel épisode plutôt que le sandbox Claude
- Nettoyage : images/clips de test hébergés temporairement sur filebin.net (bin `nabil-dr-nexum-test`, expire automatiquement le 28/07) — contenu Dr Nexum non encore publié, accessible publiquement jusqu'à expiration. Aucune action requise si le contenu n'est pas sensible, sinon supprimer manuellement avant expiration.
## Contexte d'exécution
Faute de connecteur mcp-vps disponible lors des tests, l'appel à l'API Atlas Cloud a été fait en direct depuis un sandbox Claude plutôt que via SSH sur le VPS. Le script `atlas_i2v.py` reste néanmoins déployé et fonctionnel sur `hermes-nabil` pour la bascule en production.
## Piège : `/data` est un chemin container, pas host
En SSH direct sur le VPS (hors container), `/data/dr_nexum/...` n'existe pas — c'est le chemin vu depuis l'intérieur du container `hermes-nabil` (bind-mount host réel : `/home/claude-oversight/hermes-nabil/data`, appartenance root). Éditer avec `nano /data/...` sur l'hôte ouvre un buffer vide sans jamais pouvoir sauvegarder, et un `cat >>` direct échoue en `No such file or directory`.
Fix : toute lecture/écriture sous `/data` sur hermes-nabil passe par `docker exec` :
```bash
docker exec -i hermes-nabil bash -c "mkdir -p /data/dr_nexum && cat >> /data/dr_nexum/JOURNAL.md" << 'EOF'
...
EOF
```
Cohérent avec la règle déjà connue pour `SOUL.md` sur ce même container ("root, via docker exec").
---
# Incident résolu : connecteur mcp-vps déconnecté (21/07/2026)
**Sphère** : infra transverse (Contabo VPS, `linux-mcp-vps` + `cloudflared-mcp-vps`)
## Symptôme
Connecteur `mcp-vps` affiché "déconnecté" côté réglages Claude, tentative de connexion échouant systématiquement. Une alerte Telegram (`NewSessionAlertMiddleware`, déjà en place) confirmait qu'une tentative de handshake démarrait bien côté serveur, sans jamais se finaliser côté Claude.
## Diagnostic (dans l'ordre, fausses pistes incluses — utile pour ne pas les rejouer)
1. **Fausse piste : nom du container.** `docker logs mcp-vps``No such container`. Les vrais noms sont `linux-mcp-vps` (serveur MCP) et `cloudflared-mcp-vps` (tunnel).
2. **Fausse piste : piège lifespan façon mcp-nas.** Testé `/mcp` directement → 404, mais c'était un test au mauvais chemin (le endpoint réel est préfixé par `MCP_PATH_SECRET`, jamais `/mcp` nu). Les logs montraient déjà des `200 OK` sur le vrai chemin — pas de piège lifespan ici.
3. **Piste réelle mais insuffisante : tunnel Cloudflare instable.** `docker logs cloudflared-mcp-vps` montrait des échecs QUIC récurrents ("Application error 0x0", "no recent network activity") sur 9 jours d'uptime, version 2026.7.1 obsolète. Un `docker restart cloudflared-mcp-vps` a stabilisé le tunnel (4 connexions QUIC propres, aucune erreur) — nécessaire mais pas suffisant, la connexion échouait encore après.
4. **Cause racine : flux OAuth incomplet côté serveur MCP.** Les endpoints `/.well-known/oauth-authorization-server` et `/.well-known/oauth-protected-resource` étaient corrects (bon hostname public), mais :
- Pas de `registration_endpoint` déclaré → Claude ne peut pas faire de Dynamic Client Registration (RFC 7591)
- `/authorize` et `/token` n'existaient pas du tout comme routes (seul `/register` existait, en stub)
- Message d'erreur Claude explicite reçu à ce moment : *"L'enregistrement automatique du client n'est pas pris en charge par mcp-vps. Modifiez le connecteur et ajoutez un OAuth Client ID."*
## Fix appliqué
Client OAuth statique (`client_id = claude-vps-mcp`, `token_endpoint_auth_method: none`) plutôt que du DCR dynamique — cohérent avec la vraie protection du serveur qui est le chemin secret `MCP_PATH_SECRET` dans l'URL, pas un token vérifié en base. Ajout dans `/app/linux_mcp_server.py` :
- `/authorize` (GET) : auto-approuve (single-user), émet un code éphémère (TTL 5 min), redirige avec PKCE (S256) supporté
- `/token` (POST) : échange `authorization_code`/`refresh_token` contre un `access_token` (TTL 30j) — token jamais vérifié ailleurs dans le code, sert uniquement à faire aboutir le flux OAuth
- Métadonnées `oauth-authorization-server` complétées : `grant_types_supported`, `code_challenge_methods_supported`, `token_endpoint_auth_methods_supported`
- Client ID `claude-vps-mcp` renseigné dans le connecteur côté réglages Claude
**Patch appliqué en live via `docker exec` + script Python (remplacement de bloc par chaîne exacte)** — fonctionne, mais vit uniquement dans la couche writable du container.
## ⚠️ Action restante obligatoire
Le patch n'est PAS dans l'image Docker ni dans le repo source — un `docker restart` simple le garde (couche writable persistante), mais un **rebuild ou `--force-recreate` l'effacera**. À reporter dans le fichier source du repo (probablement `bolbol/linux-mcp-vps` ou équivalent sur Gitea) dès que le NAS est de retour, puis rebuild l'image proprement avec le patch inclus en dur.
## Piège méthodologique à retenir
Toute commande `sed`/`cat`/edition de fichier sur ce VPS doit systématiquement passer par `docker exec linux-mcp-vps ...` ou `docker exec hermes-nabil ...` — jamais directement sur l'hôte. Un `assert` de correspondance de texte exacte peut échouer sur un détail invisible (ligne vide manquante, `\r\n` vs `\n`) : toujours vérifier avec `cat -A` avant de retenter un patch par remplacement de chaîne.
---
# Piège : Atlas Cloud ne suit pas les redirections HTTP sur `--image` URL
Testé le 21/07 en passant une URL de partage temporaire (filebin.net) directement en `--image` : échec systématique en `400 InvalidParameter.FormatUnsupported`. Le script ne fait que transmettre l'URL telle quelle à l'API (`submit()` ne télécharge rien lui-même quand c'est déjà une URL http/https) — c'est donc **Atlas Cloud qui va chercher l'image**, et son fetcher ne suit pas les redirections 302. Un hébergeur qui redirige vers une URL S3 signée (filebin.net, et probablement d'autres) fait échouer la requête : Atlas Cloud lit la petite réponse de redirection au lieu du JPEG.
**Fix** : toujours résoudre la redirection en amont et passer l'URL finale (ex. l'URL S3 signée) directement en `--image`, jamais l'URL de la page d'hébergement. Vérifiable en amont avec `curl -o /dev/null -w '%{redirect_url}' <url>`.
**Limite à connaître** : une URL S3 signée expire vite (900s observé chez filebin.net) — à générer juste avant l'appel, pas à l'avance.
# 3 clips testés en 480p/4s — coûts identiques
`hq720.jpg` et `images__3_.jpeg` testés le 21/07 avec la config gagnante (via mcp-vps, sans upload manuel — images hébergées temporairement puis passées en URL). Coût confirmé identique au premier test : **40 594 tokens** par clip en 480p/4s, peu importe l'image source — cohérent avec une tarification basée sur la durée/résolution, pas sur le contenu.
**Validé par Nabil le 21/07** : les 2 clips confirmés harmonieux, cohérents avec le premier test. Les 3 tests (1 sandbox + 2 via mcp-vps) sont concluants. Pipeline prêt pour bascule en production sur hermes-nabil.
---
# Montage test final (21/07)
Les 2 clips validés (`clip_hq720.mp4`, `clip_images3.mp4`) partagent exactement les mêmes specs de sortie Seedance (h264, 864×496, 24fps) — concaténation directe possible via `ffmpeg -f concat -c copy`, sans ré-encodage, donc sans perte de qualité ni temps de calcul.
```bash
docker exec hermes-nabil bash -c "printf \"file '/data/dr_nexum/out/clip_hq720.mp4'\nfile '/data/dr_nexum/out/clip_images3.mp4'\n\" > /data/dr_nexum/out/concat_list.txt && ffmpeg -y -f concat -safe 0 -i /data/dr_nexum/out/concat_list.txt -c copy /data/dr_nexum/out/dr_nexum_test_final.mp4"
```
Résultat : `dr_nexum_test_final.mp4`, 8s, 2,6 Mo — validé comme premier montage test bout-en-bout du pipeline Dr Nexum.
**Note montage final réel** : ce concat brut (`-c copy`, cut sec) est suffisant pour un test. Le montage définitif (transitions, étalonnage, sous-titres, voix ElevenLabs) reste porté par `video-use` — encore à installer, cf. pipeline prévu à l'origine (étape 4, non démarrée).
---
# Voix Dr Nexum — ElevenLabs (21/07)
**Clé** : `ELEVENLABS_API_KEY` ajoutée à `/data/.env` sur hermes-nabil (déposée par Nabil via `/tmp/ELEVENLABS.apikey` sur l'hôte VPS, transférée dans le container sans jamais transiter en clair dans les logs/outputs). ⚠️ Fichier `/tmp/ELEVENLABS.apikey` toujours présent sur l'hôte (root-owned, `claude-oversight` n'a pas de sudo NOPASSWD sur ce host contrairement à une note antérieure — à corriger dans la mémoire). Nabil doit le supprimer manuellement (`rm /tmp/ELEVENLABS.apikey`).
**Voix retenue pour ce test** : `Michael` (voice_id `IbTlccXlWxGVwnbGUHEd`), parisienne, chaude/confiante, taggée "idéal pour assistants IA" dans la bibliothèque partagée ElevenLabs — pas encore validée par Nabil comme voix définitive Dr Nexum, juste un premier choix cohérent. Modèle utilisé : `eleven_multilingual_v2`.
**API** : `POST https://api.elevenlabs.io/v1/text-to-speech/{voice_id}`, header `xi-api-key`, body `{text, model_id, voice_settings:{stability, similarity_boost}}` → réponse = bytes MP3 direct (pas de polling comme Atlas Cloud).
## Piège : décalage audio/vidéo (attendu, pas encore résolu proprement)
Le script de test (~45 mots) dure 17,45s en voix, contre 12s de vidéo (3 clips × 4s). Pas de synchronisation réelle à ce stade — **fix temporaire** appliqué : étirer le dernier plan en image fixe via `ffmpeg -filter_complex tpad=stop_mode=clone:stop_duration=Ns` pour couvrir la durée audio, avec `-shortest` pour ne pas dépasser.
```bash
ffmpeg -y -i dr_nexum_test_final.mp4 -i voix_test.mp3 \
-filter_complex '[0:v]tpad=stop_mode=clone:stop_duration=6[v]' \
-map '[v]' -map 1:a -c:v libx264 -pix_fmt yuv420p -c:a aac -shortest \
dr_nexum_test_with_voice.mp4
```
**Ce n'est pas la solution finale.** Le plan d'origine (voir en-tête du pipeline) prévoit `video-use` + ElevenLabs Scribe pour un montage guidé par la transcription word-level (coupes calées sur le texte, pas un simple gel d'image). Toujours à installer — reste la vraie prochaine étape avant tout épisode réel.
## Leçon capitalisée : toujours persister un clip généré immédiatement
Le premier clip validé "harmonieux" (image `xiaomi-robotics-1-01-hero.jpg`, prompt *"The robotic arm continues its packing motion into the open suitcase, smooth mechanical movement, static camera, realistic robotics demo footage"*, 480p/4s) avait été généré dans le sandbox Claude d'une session précédente et jamais copié sur `/data/dr_nexum/out/` — perdu à la fin de cette session, récupéré seulement parce que Nabil l'avait sauvegardé localement. **Règle à suivre désormais** : tout clip Atlas Cloud validé doit être `docker cp`/transféré vers `/data/dr_nexum/out/` sur hermes-nabil immédiatement, jamais laissé uniquement dans un sandbox Claude éphémère.
---
# Piège capitalisé : accents manquants dans le texte envoyé à ElevenLabs
Le premier essai de voix (voir plus haut) a été généré avec un texte **sans accents français** ("Generer", "quantite", "entrepot"...) — erreur introduite par Claude en construisant le script d'appel API, pas une limite d'ElevenLabs. Résultat : articulation dégradée, syllabes qui sautent. Même famille de bug que celui déjà corrigé sur les SOUL.md Hermes (priming ASCII), mais réapparu ici dans un contexte différent (script ponctuel, pas un prompt système).
**Règle à appliquer systématiquement** : tout texte français destiné à un TTS (ElevenLabs ou autre) doit être écrit avec les accents corrects, transmis en UTF-8 de bout en bout, et vérifié avant l'appel API (`grep` ou `cat` du fichier script juste avant exécution, pas seulement après).
**Réglages `voice_settings` ajustés en même temps** (à valider sur écoute, pas encore confirmé si ça a un effet mesurable séparé de la correction des accents) : `stability` 0.5→0.65, `style` 0.0 explicite, `use_speaker_boost` True.
---
# Piste future (Mac M5 Pro, septembre) : avatar Dr Nexum open-source
Recherche du 21/07 suite à l'abandon de HeyGen (crédits limités + coût API automatisation imprévisible).
**Repo identifié** : `github.com/PunithVT/ai-avatar-system` — plateforme avatar IA 100% open-source et self-hosted. Pipeline : photo → clone de voix → Whisper (STT) → Claude/GPT/Ollama (LLM) → Chatterbox TTS (multilingue, 23 langues) → MuseTalk (lip-sync). Correspond exactement au besoin (identité fixe Dr Nexum + voix, zéro abonnement).
**Blocage confirmé** : MuseTalk nécessite un GPU Nvidia (benchmark officiel sur Tesla V100). Le VPS Contabo actuel (6 vCPU, 12 Go RAM, pas de GPU) ne peut pas le faire tourner en temps utile — même conclusion que le test Bonsai 27B (LLM local CPU-only inutilisable).
**Décision (21/07)** : attendre le MacBook M5 Pro (septembre) plutôt que de louer du GPU cloud ou repartir sur une solution SaaS. ⚠️ À vérifier à l'arrivée du Mac : ces outils ciblent CUDA/Nvidia en priorité, la compatibilité Apple Silicon (MPS) n'est pas confirmée — tester avant de construire dessus, ne pas supposer que ça marche.
---
# Approfondissement (21/07) : explication des briques + piste low-cost immédiate
## Rôle de chaque composant identifié
- **Whisper** (OpenAI) : speech-to-text. Utile pour transcription live ou timestamps mot-par-mot. Inutile pour Dr Nexum (script déjà écrit, ElevenLabs a sa propre fonction équivalente).
- **Claude/GPT/Ollama** (dans `ai-avatar-system`) : LLM conversationnel pour chat en direct. Hors-sujet pour Dr Nexum (script déjà généré via Bifrost/DeepSeek).
- **Chatterbox TTS** (Resemble AI) : TTS open-source, clonage de voix zero-shot, 23 langues. Alternative gratuite à ElevenLabs — besoins matériels non vérifiés, à tester avant d'en dépendre.
- **MuseTalk** (Tencent) : lip-sync temps réel, bouche uniquement, exige GPU Nvidia (V100 en benchmark officiel, fonctionne aussi RTX 20/30 séries).
- **InfiniteTalk** (MeiGen-AI, Apache 2.0) : génération vidéo parlante plus complète (tête, épaules, expressions, pas juste bouche), longueur illimitée sans dérive d'identité. Basé sur Wan2.1 (modèle vidéo 14B). Testé avec succès en conditions réelles sur **RTX 3090** (30s de génération pour un clip 3s).
## Correction architecturale importante
`ai-avatar-system` (présenté précédemment comme "le plus complet") est conçu pour la conversation en temps réel (streaming, interruption, WebRTC) — inadapté à un besoin "script écrit à l'avance → fichier vidéo". **Pour Dr Nexum, utiliser InfiniteTalk directement en mode batch** (image + audio → .mp4), sans la couche conversationnelle, est la bonne forme d'outil.
## Piste low-cost immédiate (pas besoin d'attendre le Mac)
Location GPU à l'usage, facturée à la seconde : **RunPod, RTX 3090 (24 Go) ≈ 0,46$/heure**, zéro coût GPU éteint. Estimation réaliste par épisode Dr Nexum (démarrage pod + chargement modèles 2-5 min + génération 1-3 min/clip + extinction) : **15-30 min de location, quelques centimes à <1$/épisode**. Nettement sous le coût HeyGen, sans risque d'abonnement.
**Coût réel = temps d'ingénierie, pas le GPU** : nécessite une image Docker (CUDA/PyTorch/flash_attn compatibles — piège connu signalé dans le README InfiniteTalk), un stockage réseau persistant RunPod pour les poids du modèle (éviter re-téléchargement à chaque run), et un script d'orchestration (probablement piloté depuis hermes-nabil via l'API RunPod) : spin up → génération → récupération → extinction immédiate.
**Décision (21/07)** : chantier identifié et documenté, mais pas démarré cette session — en attente de décision de Nabil (lancer maintenant vs garder pour le Mac de septembre).