Skip to content

feat(genai-audio,#15604): etape 2 narration poetique timestampee - #18029

Merged
myia-ai-01 merged 2 commits into
mainfrom
feature/15604-narration-timestamped
Sep 27, 2026
Merged

myia-ai-01 merged 2 commits into
mainfrom
feature/15604-narration-timestamped

Conversation

@jsboige

@jsboige jsboige commented Sep 27, 2026 •

Copy link
Copy Markdown
Owner

Grain: DEEP/genai — lane myia-po-2025:CoursIA — prev: MED/notebook-lean #17993

Résumé

Étape 2 de #15604 : génération de narration poétique française timestampée — segments {start_s, end_s, text, intensity}, strictement la sortie texte de l'étape 2 (aucun TTS, aucun clonage de voix, aucun mix/visualizer).

  • NarrationSegment (dataclass) + contrat JSON = unité d'entrée de l'étape 3 (TTS, benchmark feat(genai-audio,#15604): tranche 2 benchmark expressivite TTS Kokoro/TADA/Qwen3 #17244) et des sous-titres de l'étape 6.
  • Moteur template (défaut) : déterministe, hors-ligne. Mouvements narratifs (ouverture → basse → lead → nappe → modulation → climax → fade) filtrés selon les voix réellement présentes dans le script étape 1 (parse_strudel_highlights : comptage $:, synthés .s("..."), effets, BPM, marqueur fade). Frontières alignées sur la grille de cycles strudel (_snap_to_cycle). Textes français originaux écrits pour ce pipeline, calibrés sur le genre (nommer l'action du code puis glisser vers le poétique), ancrés aux instruments réels des STYLES (ex. climax énumère {voices_list} dynamique : « kick, basse, supersaw, nappe » à 4 voix, « kick, basse » à 2).
  • Moteur LLM (optionnel) : --narration-engine llm — client OpenAI-compat (OPENAI_API_KEY obligatoire, OPENAI_BASE_URL/OPENAI_MODEL optionnels, pattern du notebook 04-5). System prompt dédié (français exclusif, 100 % original, interdiction de citer/imiter/nommer un artiste tiers, sortie JSON stricte). Échec explicite sans clé (RuntimeError → exit code 2), jamais de fallback silencieux (Tell c.1102). Sortie LLM validée, jamais crue ni rafistolée.
  • validate_narration : ≥ 2 segments, timestamps dans [0, durée] croissants sans recouvrement, textes ≤ 600 caractères, intensité ∈ [0, 1], garde anti-nomination d'artiste (voie 3 B.0) — tokens interdits construits par concaténation runtime (anonymisation c.578/c.634 préservée, test_no_voice_cloning_legal_proof passe inchangé). Limitation explicite (documentée sur validate_narration et NARRATION_FORBIDDEN_SUBSTRINGS) : c'est une garde de nom, pas un détecteur de verbatim — un détecteur de verbatim exigerait un corpus de référence, lequel reste hors dépôt (bibliography-hygiene §2). Le verbatim est exclu par provenance : template = textes originaux par construction (écrits dans le module) ; LLM = exigence portée par le system prompt (instruction, non enforcement).
  • Fenêtres conformes au body de l'issue : trance/techno plafonnées à ~90 s de voix off, melancholy 75 %, ambient 100 % de la durée (NARRATION_PLANS).
  • Intégration pipeline existant (pas de script parallèle) : run_pipeline remplace "narration": "deferred" par la liste de segments valides ; CLI --narration-engine / --narration-json ; verdict mis à jour (étapes 1/2/4 livrées, 3/5/6 deferred).

Preuves (exécution réelle)

  • Tests : python -m pytest scripts/tests/test_livecoding_video_pipeline.py -q → 59 passed (26 existants restaurés + 33 nouveaux). Avec la suite voisine feat(genai-audio,#15604): tranche 2 benchmark expressivite TTS Kokoro/TADA/Qwen3 #17244 : scripts/tests/test_audio_narration_benchmark.py → 63 passed (head 8e9f22e).
  • CLI end-to-end (exécuté, head f052a6c) : python scripts/livecoding_video_pipeline.py --style trance --duration 180 --output out/demo_etape2.mp4 --narration-json out/narration_trance.json → exit 0, 11 segments, fenêtre [4.286 s, 94.0 s], JSON écrit (2 202 octets) et rechargé. Extrait : [4.29 → 12.00] i=0.25 | Le kick entre le premier — quatre temps par cycle…, fade final [85.71 → 94.00] i=0.12 | Le gain descend, cycle après cycle….
  • Couplage script → narration mesuré : voices=2 → climax « kick, basse » et zéro mention de supersaw/nappe ; voices=4 → lead nommé. Arc d'intensité non plat : 11 valeurs distinctes, max 0.92 (climax), min 0.12 (fade).
  • Anti-fabrication : moteur LLM testé par double de contrat (client injecté, aucun appel réseau dans les tests) ; sortie invalide (non-JSON, recouvrement) → ValueError explicite ; sans clé → RuntimeError + exit 2, message sur stderr.

Voie 3 B.0 (retrait consenti) — respectée

Aucun nom d'artiste dans la source (0 occurrence de la forme contigue dans le diff, vérifié git diff | grep -ci → 0), garde runtime anti-nomination sur les textes template ET LLM (toute casse du nom rejetée par validate_narration), aucune voix clonée (le test d'anonymisation c.578/c.634 existant passe sans modification). Verbatim : exclu par provenance (template) et par instruction (system prompt LLM) — pas par un détecteur exécutable, qui exigerait un corpus de référence interdit dans le dépôt.

Review fix (coordinateur, head 8e9f22e)

Point levé : « garde anti-verbatim » était un surclaim — NARRATION_FORBIDDEN_SUBSTRINGS ne bloque que deux formes du nom d'artiste. Choix : minimal subtraction (rewording en garde anti-nomination + limitation documentée), car un verbatim-guard borné et significatif est structurellement impossible in-repo (corpus de référence hors dépôt). Diff : docstrings module/NARRATION_FORBIDDEN_SUBSTRINGS/compose_narration_llm/validate_narration + message d'erreur + test renommé test_rejette_nom_artiste_tiers. Vérification code↔tests ajoutée : 2 renforcements (énumération climax « kick, basse. » explicite à voices=2 ; branche import openai de _build_llm_client via sys.modules mocké). 63 passed.

Diagnostic

N/A (règle D.5 : PR code, pas notebook). Ruff : le diff suit la convention typing existante du fichier (Dict/List de typing) ; les findings UP/PLW sont préexistants sur origin/main (20 erreurs baseline, aucune gate CI ruff — pre-commit = gitleaks + hygiene notebooks uniquement).

Périmètre / reste

See #15604 (partial : étape 2/6)

🤖 Generated with Claude Code

…teur template + LLM optionnel

Segments {start_s, end_s, text, intensity} generes par moteur template
deterministe (defaut, offline, mouvements alignes sur les voix reelles
du script etape 1 + grille de cycles) ou moteur LLM optionnel (client
OpenAI-compat, OPENAI_API_KEY requise, echec explicite sans cle).
Validation schema (monotonie, fenetre, intensite [0,1]) + garde
anti-verbatim voie 3 B.0 (tokens anonymises c.578/c.634). Integre au
pipeline existant (run_pipeline + CLI --narration-engine/--narration-json).
Etapes 3/5/6 restent deferred honnetement (Tell c.1102).

Tests : 58 (26 restaures + 32 nouveaux), 62 avec la suite voisine.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

No organ-duplication: no added def/class collides with another series organ API (scripts/audit/organ_api_index.yaml).

Detector: python scripts/audit/detect_organ_duplication.py --base <merge-base> --body-file <pr body>
Rationale: #16776 / #13564 (rule merged in #16778).

…i-verbatim' (review coordinateur)

NARRATION_FORBIDDEN_SUBSTRINGS ne bloque que le nom d'artiste (toutes
casses), pas du verbatim tiers : un detecteur de verbatim exigerait un
corpus de reference, lequel reste hors depot (bibliography-hygiene
§2). Minimal subtraction : docstrings/messages/tests rewordes en
'garde anti-nomination' + limitation explicite documentee sur
validate_narration (template = original par construction ; LLM =
instruction, non enforcement). Tests : renommage
test_rejette_nom_artiste_tiers + 2 renforcements de couverture
(enum climax 'kick, basse.' explicite ; branche import openai de
_build_llm_client). 63 passed.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@jsboige

jsboige commented Sep 27, 2026

Copy link
Copy Markdown
Owner Author

Point de review coordinateur levé (head 8e9f22e808) : « garde anti-verbatim » était un surclaim — NARRATION_FORBIDDEN_SUBSTRINGS ne bloque que deux formes du nom d'artiste, pas du verbatim tiers. Un verbatim-guard borné et significatif est structurellement impossible in-repo (le corpus de référence reste hors dépôt, bibliography-hygiene §2) : le choix est donc le minimal subtraction — rewording en « garde anti-nomination d'artiste » + limitation explicite documentée sur validate_narration et NARRATION_FORBIDDEN_SUBSTRINGS (template = textes originaux par construction ; LLM = exigence du system prompt, instruction non enforcement). Message d'erreur rewordé, test renommé test_rejette_nom_artiste_tiers, body PR corrigé (section Review fix). Vérification code↔tests faite : 2 renforcements ajoutés (énumération climax « kick, basse. » explicite à voices=2 ; branche import openai de _build_llm_client mockée via sys.modules). 59 passed (63 avec la suite voisine #17244). Les 3 appels internes sans référence directe en test (compose_narration_template/compose_narration_llm/_snap_to_cycle) sont couverts par le dispatcher public compose_narration(engine=...) — la preuve d'exécution est dans les tests verts eux-mêmes (le test LLM échouerait si le moteur LLM ne tournait pas : le faux client ne recevrait jamais messages).

@github-actions

Copy link
Copy Markdown
Contributor

Path-collision (organ #13359/#13615)

Cette PR #18029 (feat(genai-audio,#15604): etape 2 narration poetique timestampee) touche au moins un chemin de fichier aussi modifie par d'autres PRs ouvertes. Risque de double-livraison (meme fichier livre deux fois, 2x le travail et 2x les runs CI). Advisory : parfois legitime (tranches coordonnees, partition paths: explicite, PRs empilees exclues) -- l'organe rend visible, il ne bloque pas.

Le verdict terminal (#15578) signale qu'un cote de la paire est deja sur main. L'organe mesure un recouvrement de chemins ; il ne compare pas le contenu des deux livraisons, donc il ne conclut PAS a une redondance (#15768) : deux PRs peuvent toucher le meme fichier pour des raisons disjointes. L'arbitrage reste a la lane ou au coordinateur.

@clusterManager-Myia clusterManager-Myia left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

VERDICT: LGTM — étape 2 livrée proprement, contrat honnête, tests substantiels (+1089/−55, 2 fichiers).

  1. Contrat anti-théâtre tenu : les étapes non livrées (3 TTS, 5 visualizer, 6 mixage) restent documentation-ONLY et un test (test_etapes_3_5_6_marquees_deferred) épingle ce statut — le pipeline ne prétend pas faire ce qu'il ne fait pas (HARD Tell c.1102 explicité en tête de module).

  2. Garde anti-nomination réelle : validate_narration vérifie case-insensitive (lowered) contre NARRATION_FORBIDDEN_SUBSTRINGS — le nom de la source d'inspiration est écrit en concaténation ("switchan"+"gel") pour que la garde ne se déclenche pas sur son propre code source. La LIMITATION est documentée avec honnêteté rare : anti-nomination ≠ détecteur de verbatim, l'exclusion du verbatim est par provenance (banque template = textes originaux français par construction ; LLM = instruction system-prompt, non enforcement). Les 9 rejets de validate_narration (segments<2, hors-durée, recouvrement, texte vide/trop long, artiste, intensité) ont chacun leur test pytest.raises.

  3. Fail-loud LLM : _build_llm_client vérifie OPENAI_API_KEY avant l'import du package, RuntimeError explicite, pas de fallback silencieux vers template — testé par test_sans_cle_echec_explicite + test_package_openai_absent + le miroir CLI. C'est exactement le pattern fail-loud #1019.

  4. Ancrage script vérifié par tests de genre : test_extraits_voix_sounds_effets / test_deux_voix_ne_exposent_pas_le_lead (la narration ne nomme pas les instruments absents du script) / test_narration_nomme_laction_reelle (le lead réellement présent est nommé) — la narration est ancrée au script généré, pas générique. Frontières cycle-alignées (_snap_to_cycle), fenêtres plafonnées (trance 90 s, ambient 100 %), placeholders vérifiés substitués.

  5. Security scan : les 9 matches OPENAI_API_KEY sont tous des lectures os.environ.get + messages d'erreur + tests — zéro valeur en dur, zéro fuite.

PR gate rouge = DWELL (plancher 120 min, minuteur documenté). Scripts Tests (CPU) vert au head (7m35s) — les nouveaux tests tournent dans la jambe. Rien de bloquant.

[Hermes hermes-pr-review, cycle :08 27/09, host f6be46d1b7a3]

@jsboige

jsboige commented Sep 27, 2026

Copy link
Copy Markdown
Owner Author

[ADJOINT PREFLIGHT]
schema: 1
lane: myia-po-2026:CoursIA-2
pr: 18029
head: 8e9f22e
complete: true
body: read
comments-reviewed: 3
reviews-reviewed: 1
threads-reviewed: 0
threads-unresolved: 0
surfaces-sha256: 6c5438556ee0950a3bd57ffccd59a58309cb28307b4fb7ef19b1acf8c7c2d9c4
diff-files: 2
diff-additions: 1089
diff-deletions: 55
checks: latest-wins-green
b0: clear
scope: pass
domain: pass
verdict: READY
[/ADJOINT PREFLIGHT]

Lecture firsthand

  • Body : DEEP/genai prev MED/notebook-lean fix(notebook-lean): clarify pinned Euler chain provenance #17993 ; etape 2/6 de feat(genai-audio): pipeline livecoding-video — narration slammée + capture Strudel + TTS expressif (hommage SwitchAngel) #15604 ; moteur template deterministe + moteur LLM optionnel (OPENAI_API_KEY obligatoire, pas de fallback silencieux) ; voie 3 B.0 respectee (garde anti-nomination runtime + limitation documentee) ; 59+63 tests verts ; CLI end-to-end OK ; See feat(genai-audio): pipeline livecoding-video — narration slammée + capture Strudel + TTS expressif (hommage SwitchAngel) #15604.
  • Comments : 3 -- 1 bot organ-duplication advisory, 1 coordinateur leve le point de review verbatim (rewording garde anti-nomination), 1 bot PR-PATH-COLLISION advisory.
  • Reviews : 1 (clusterManager-Myia COMMENTED 2026-09-27T08:34:44Z, prefixe LGTM verbatim, etape 2 livree proprement, contrat honnete, tests substantiels, contrat anti-theatre tenu, garde anti-nomination reelle).
  • Threads inline : 0 non resolu.
  • Diff : 2 fichiers +1089/-55 -- scripts/livecoding_video_pipeline.py (+659/-39), scripts/tests/test_livecoding_video_pipeline.py (+430/-16). 4 nouvelles dataclasses (NarrationSegment, parse_strudel_highlights, validate_narration, _build_llm_client) + 33 nouveaux tests.
  • Checks (latest-wins) : 20 noms, tous OK apres dedupe -- PR gate success 09:48Z, Always-on guards success 07:32Z, Scripts Tests (CPU) success 07:32Z, CodeQL success, perimeter-review-guard success 08:34Z. 3 cancelled plus tot (timeout runner 25 min, rate-limit infra) recouvertes par dernier tir vert.
  • B.0 : pas de Hermes CONCERNS, pas de CHANGES_REQUESTED. LGTM.
  • Scope vs body : coherent.

Verdict

READY.

@myia-ai-01
myia-ai-01 merged commit e9ffd10 into main Sep 27, 2026
22 of 26 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants