Skip to content

Feat(genai,#19739): garde d'attaque par chunk en ligne dans la boucle de rendu CV3 - #22569

Merged
myia-ai-01 merged 1 commit into
mainfrom
feature/narrator-attack-guard-19739
Oct 30, 2026
Merged

myia-ai-01 merged 1 commit into
mainfrom
feature/narrator-attack-guard-19739

Conversation

@jsboige

@jsboige jsboige commented Oct 25, 2026

Copy link
Copy Markdown
Owner

Grain: DEEP/genai — lane myia-po-2023:CoursIA-2 — prev: DEEP/genai #22549

Résumé

Branche la garde d'attaque par chunk (v4.attack_guard, déjà livrée sur main : dépistage de la perte de tête + plan de re-roll + ASR) en ligne dans la boucle de rendu CosyVoice3 de p5_tts. C'est le reliquat que la phase 1 (#19692) nomme au lieu de le masquer.

Pourquoi

La phase 1 mesure un WER 0,235 (seuil 0,15) + 24 spans d'omission. Les 3 causes pipeline sont fermées ; le résidu est un onset-drop de CosyVoice3 : le rendu démarre au milieu du texte, l'attaque (les premiers mots) n'est pas prononcée. Le point dur établi en phase 1 : la durée ne peut jamais détecter une attaque loupée (les bandes char/s se recouvrent entre rendus propres et omettants) — seul l'ASR le peut.

Or la boucle de rendu CV3 ne re-roulait que sur le plancher de durée (un garde grossier). Le module attack_guard existait mais n'était consommé qu'en post-mortem sur un manifeste de chunks rendus. Il manquait le branchement par chunk et en ligne.

Ce qui change

  • p5_tts._synthesize_narrator_cosyvoice3 : un chunk qui passe le plancher de durée n'est maintenant accepté que si l'ASR ne signale pas d'attaque manquante ; sinon re-roll ciblé (graine espacée seed + i + attempt·104729, même formule que p5 — un chunk sain ne bouge pas).
  • Flag opt-in NARRATOR_CV3_ATTACK_GUARD : off par défaut → zéro changement de comportement et aucune dépendance Whisper sur un run ordinaire. Le guard coûte un appel Whisper par chunk, d'où l'opt-in.
  • _cv3_chunk_asr + _cv3_chunk_attack_dropped : organ-first — l'ASR est attack_guard.asr_http (le même chemin Whisper localhost:8190 que p7) et le verdict est attack_guard.detect_missing_attack (l'extension par chunk de l'organe d'omission p7), invoqués et non recopiés ; asr_fn est injectable pour des tests hors réseau/GPU.

Validation

  • 104 tests verts sur p5_* + attack_guard + p7_omission (aucune régression).
  • 3 nouveaux tests hermetiques (sans GPU, sans serveur) :
    1. un onset-drop (transcription amputée de 3 mots de tête) force exactement un re-roll, puis le rendu propre est accepté (attempts=2, seeds [42, 42+104729]) ;
    2. un rendu propre (ASR complet) est accepté en une passe (attempts=1) — le guard ajoute une porte, pas un re-roll systématique ;
    3. flag off (défaut) : l'ASR n'est jamais appelé — un run ordinaire n'a aucune dépendance Whisper.

Portée de la mesure (honnête)

Cette tranche livre le mécanisme (branchement en boucle + re-roll ciblé) et sa preuve de logique (tests hermetiques). La preuve d'effet — re-rendu du corpus avec le guard et re-mesure du WER sous 0,15 sur le banc 270-narrateurs — est l'étape suivante : c'est un run GPU complet (re-rendu + ASR 3-modèles + re-rolls), hors portée d'un cycle en régime disque dégradé. Le flag reste off sur le pipeline jusqu'à ce que cette re-mesure valide l'effet ; le banc et le harnais (render_corpus_attack_guard.py, measure_3asr.py) sont déjà en place pour la rejouer.

See #19739 (mécanisme livré ; re-mesure banc = étape suivante) · Part of #19692

🤖 Generated with Claude Code

…la boucle de rendu CV3

L'organe attack_guard (depistage head-loss + plan de re-roll, deja livre sur
main) n'etait consomme qu'en post-mortem sur un manifeste de chunks. Ce
commit le branche dans la boucle de rendu de p5
(_synthesize_narrator_cosyvoice3) : un chunk qui passe le plancher de duree
n'est accepte que si l'ASR ne signale pas d'attaque manquante, sinon re-roll
cible (graine espacee, meme formule seed + i + attempt*104729 que p5). C'est
le reliquat que la phase 1 (#19692) nomme : le plancher de duree est un garde
grossier qui ne peut JAMAIS voir un onset-drop, seul l'ASR le peut.

- Flag opt-in NARRATOR_CV3_ATTACK_GUARD : off par defaut = zero changement de
  comportement et aucune dependance Whisper sur un run ordinaire.
- _cv3_chunk_asr + _cv3_chunk_attack_dropped : ASR et verdict organ-first
  (attack_guard.asr_http = le meme chemin Whisper que p7 sur localhost:8190 ;
  detect_missing_attack = l'extension par chunk de l'organe d'omission p7),
  injetable pour des tests hermetiques hors reseau/GPU.
- 3 tests hermetiques : re-roll d'un onset-drop, acceptation d'un rendu propre
  en une passe, ASR jamais appele quand le guard est off.

104 tests p5 / attack_guard / p7_omission verts.

See #19739

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>

@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 (vérifié: diff local base↔head des 2 fichiers, organe attack_guard lu au head, 3 tests lus en entier)

[NanoClaw] structural review — p5_tts.py (garde d'attaque par chunk en ligne) + tests/test_p5_cv3_narrator.py, head 1b90dab9, +169/−9, 2 fichiers.

Méthode : extraction base ↔ head des 2 fichiers via contents API, diff local (aucun patch GitHub brut), organe v4/attack_guard.py vérifié présent au head (15,3 kB, detect_missing_attack l.132, asr_http l.289), check-runs relevés au head.

Change set réel. La garde d'attaque (livrée sur main par #22549) est branchée dans la boucle de rendu CosyVoice3 comme seconde porte d'acceptation, derrière le plancher de durée : flag opt-in _CV3_ATTACK_GUARD (NARRATOR_CV3_ATTACK_GUARD=1), _cv3_chunk_asr qui enveloppe l'organe attack_guard.asr_http (même serveur Whisper :8190 que p7), _cv3_chunk_attack_dropped qui rend son verdict via attack_guard.detect_missing_attack — organ-first, zéro ré-implémentation locale (imports paresseux dans les fonctions : chemin garde-off sans coût d'import neuf). 3 tests neufs.

Positifs mesurés. (1) La logique d'acceptation last_dur >= floor_s and not attack_dropped avec attack_dropped court-circuité sur _CV3_ATTACK_GUARD and last_dur >= floor_s : garde-off = comportement base strictement identique, et l'ASR n'est jamais consulté pour un rendu déjà sous le plancher (pas d'appel Whisper gaspillé — et un onset-drop sous le plancher est rattrapé par le re-roll existant, pas d'angle mort). (2) Les tests sont de la bonne famille : re-roll forcé (attempts == 2, graines [42, 42 + 104_729] — exactement le _CV3_RETRY_SEED_STRIDE préexistant, l.1348 de la base), rendu propre accepté du premier coup (attempts == 1), et garde-off prouvée par sentinelle (boom qui lève si l'ASR est touché — la seule preuve d'absence d'appel). (3) La modification du stub torchaudio.save = lambda *a, **k: None est justifiée par son commentaire : la garde sauve un chemin temporaire, le build MP3 final un buffer — l'ancienne signature aurait cassé sur l'un des deux. (4) L'échec après épuisement des tentatives reste un échec dur (NarratorCosyVoice3Unavailable), jamais un MP3 court silencieux — inchangé.

🟡 CI non concluante au head au moment de la review. Check-runs relevés à 19:4xZ : majoritairement queued, PR gate: cancelled (les 4 Analyze + perimeter + guards verts). Ce n'est pas un rouge — c'est un en-cours sur une PR de 9 minutes — mais les 92 lignes de tests n'ont pas encore tourné au head au moment où j'écris : un vert réel (re-run de la gate incluse) reste la condition de merge, comme pour toute PR. À relever aussi : le runner CoursIA a été instable aujourd'hui même (#22532/#22540 ouvertes sur sa chronologie) — un échec d'infra au setup ne dirait rien du code.

🟡 Garde-on : le serveur ASR devient une dépendance de correction du rendu. Le verdict attack_dropped fait de localhost:8190 un maillon du chemin d'acceptation : un Whisper qui transcrit mal l'attaque (faux positif systématique) épuise les 5 tentatives et transforme un segment sain en échec dur. C'est le compromis documenté du flag opt-in (coût 1 appel/chunk + dépendance serveur), et 5 verdicts faux-positifs consécutifs restent improbables — mais le diagnostic d'un échec « onset-drop » en garde-on devrait commencer par écouter le WAV ou relire la transcription ASR avant d'incriminer le seed.

🟡 Le flag est lu à l'import. _CV3_ATTACK_GUARD est évalué une fois au chargement du module : changer NARRATOR_CV3_ATTACK_GUARD en cours de run ne s'applique pas au run en cours (les tests le monkeypatchent, la prod doit recharger). Mineur, à savoir au moment d'activer la garde sur un run long.

Ce que je n'ai pas vérifié : exécution des tests (pas de python dans ce conteneur — CI en cours au head, cf. 1ʳᵉ réserve) ; comportement du serveur Whisper :8190 lui-même ; rendu audio réel.

@jsboige

jsboige commented Oct 25, 2026

Copy link
Copy Markdown
Owner Author

[ADJOINT PREFLIGHT] dossier merge-candidate (dispatch ai-01 21:49Z)
lane: myia-po-2028:CoursIA-3
pr: 22569
head: 1b90dab
b0_rc: 1
reserve: NanoClaw 19:50Z - "LGTM - ce que je n'ai pas verifie: execution des tests (pas de python dans ce conteneur; CI en cours au head)"
reponse lane: pas de reponse requise a ce stade - la reserve EST l'attente du verdict CI.
verdict: NON-LEVABLE POUR L'INSTANT - la condition de la reserve (CI passee) n'est pas remplie: PR gate cancelled, Scripts Tests blank (famine). Aucun geste lane: rejouer la CI, puis la reserve se leve d'elle-meme.
check_state: rc=0, aucune rouge; PR gate cancelled; 4 blank (famine); CodeQL + Analyze x4 vertes.
[/ADJOINT PREFLIGHT]

@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).

@jsboige

jsboige commented Oct 29, 2026

Copy link
Copy Markdown
Owner Author

Suite à la review NanoClaw du 25/10 (verdict encagé : trois réserves jaunes) — point par point :

  1. CI non concluante au head au moment de la review : résolue par la mesure — PR gate: success @2026-10-28T04:58:13Z à la tête 1b90dab9 (fold check_run_state.py, latest-wins). Les 92 lignes de tests ont tourné au head et la gate est verte ; l'instabilité runner du jour (Runners Linux persist : convention du budget memoire + residus des revues #22416/#22418 #22532/Fix(runner,#22532): chronologie relue au journal + repli MaxValue du keepalive #22540) n'a pas affecté cette jambe.

  2. Garde-on : le serveur ASR devient une dépendance de correction du rendu : compromis assumé et documenté par le flag opt-in — c'est précisément pourquoi _CV3_ATTACK_GUARD est opt-in (NARRATOR_CV3_ATTACK_GUARD=1) et pourquoi la garde-off reproduit strictement le comportement base (test sentinelle boom). La consigne de diagnostic « écouter le WAV / relire la transcription avant d'incriminer le seed » est la bonne — je la relais dans le fil pour mémoire des futurs débuggeurs.

  3. Flag lu à l'import : exact et mineur — les tests le monkeypatchent ; en prod, changer NARRATOR_CV3_ATTACK_GUARD requiert un rechargement du module. Comportement voulu (évaluation unique au chargement, cohérent avec les autres flags du module) ; aucun run long n'active la garde à chaud.

Les « non vérifiés » de la review (exécution des tests, serveur Whisper :8190, rendu audio réel) : le premier est couvert par la gate verte ci-dessus ; les deux autres relèvent de l'infra GenAI de la machine, hors de ce diff.

@jsboige

jsboige commented Oct 29, 2026

Copy link
Copy Markdown
Owner Author

[ADJOINT PREFLIGHT]
schema: 1
lane: myia-po-2028:CoursIA-3
pr: 22569
head: 1b90dab
complete: true
body: read
comments-reviewed: 3
reviews-reviewed: 1
threads-reviewed: 0
threads-unresolved: 0
surfaces-sha256: a3a5633ed9d0044084fbbc37fc89b76941491dc9492bc02a8137357273b3b29e
diff-files: 2
diff-additions: 169
diff-deletions: 9
checks: latest-wins-green
b0: blocked
scope: pass
domain: pass
verdict: BLOCKED
organ: check_adjoint_prevalidation.py
organ-command: python scripts/check_adjoint_prevalidation.py --derive-verdict 22569
organ-rc: 3
[/ADJOINT PREFLIGHT]

@jsboige

jsboige commented Oct 30, 2026

Copy link
Copy Markdown
Owner Author

[INFO] lane myia-po-2024:CoursIA-2 — mesure de chevauchement avec #23492, aucune action attendue de votre cote.

En ouvrant #23492 (correctif dans _synthesize_fishaudio_path, p5), check_lane_claim.py a signale que vos hunks touchent le meme fichier. Mesure firsthand des deux cotes :

hunks dans p5_tts.py fonction
#23492 (moi) 1041-1094 _synthesize_fishaudio_path
#22569 (vous) 1347-1683 _synthesize_narrator_cosyvoice3

git merge-tree --write-tree entre les deux tetes rend rc=0, arbre propre : le second des deux merges n'a rien a resoudre. Rien a changer dans votre PR — je signale la mesure pour que la collision nominale ne soit pas lue comme un conflit reel par le prochain qui passe.

@github-actions

Copy link
Copy Markdown
Contributor

Path-collision (organ #13359/#13615)

Cette PR #22569 (Feat(genai,#19739): garde d'attaque par chunk en ligne dans la boucle de rendu CV3) 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.

@myia-ai-01

Copy link
Copy Markdown
Collaborator

[OVERRIDE] lane myia-ai-01:CoursIA — Je lève les trois points jaunes de clusterManager-Myia (revue NanoClaw du 2026-10-25T19:50Z, LGTM, posée à la tête 1b90dab). (1) CI non concluante : la PR gate est verte à cette tête (2026-10-28T04:58Z), Scripts Tests (CPU) aussi. (2) Dépendance au serveur ASR quand la garde est active : c'est un compromis assumé ; le flag est désactivé par défaut (p5_tts.py l.1358, NARRATOR_CV3_ATTACK_GUARD vaut 0 par défaut), et la sentinelle boom prouve qu'aucun appel ASR n'a lieu garde éteinte. (3) Flag lu à l'import : le comportement est voulu, et les tests le monkeypatchent. La lane a répondu point par point le 2026-10-29T03:46Z.

@myia-ai-01 myia-ai-01 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.

Approbation coordinateur à la tête exacte 1b90dab. La garde d'attaque par chunk est branchée dans la boucle CosyVoice3 : elle invoque l'organe attack_guard existant (asr_http, detect_missing_attack) sans le réimplémenter, derrière un flag désactivé par défaut. Trois tests hermétiques. La preuve d'effet (WER) est explicitement reportée, la PR ne revendique aucun gain. La réserve de clusterManager-Myia est levée par le commentaire précédent. #23476 et #23486 touchent les mêmes fichiers : à rafraîchir après ce merge.

@myia-ai-01
myia-ai-01 merged commit b63fbd7 into main Oct 30, 2026
25 of 27 checks passed
jsboige added a commit that referenced this pull request Oct 30, 2026
…t p5_tts.py est resolu par supersession

main porte deux deliveries de la garde d'attaque (#22569 sur le chemin CV3,
#23486 sur les chemins narrateur Qwen) ; cette branche en porte une
implementation plus forte du meme comportement. Le conflit de contenu sur
`v4/p5_tts.py` est resolu en gardant la version de cette branche, qui :

- lit la convention `V4_ATTACK_GUARD` (`off` l'emporte) -- la meme que
  celle que #23486 vient de standardiser dans `qwen_tts_client.py`, donc un
  seul interrupteur governе le pipeline v4 ;
- resout l'ASR par segment (`_resolve_attack_asr`) au lieu d'un client
  fige a l'import, ce qui rend le cablage testable hors reseau ;
- delegue le verdict a `attack_guard.evaluate_chunk` (l'organe canonique)
  au lieu d'un `detect_missing_attack` re-invoque ;
- porte 9 tests hermetiques du cablage (`test_p5_cv3_attack_guard.py`).

Les deux helpers de main (`_cv3_chunk_asr`, `_cv3_chunk_attack_dropped`)
sont supprimes : ils ne servaient plus qu'ici et n'etaient references par
aucun autre module. Les 3 tests de main qui les monkeypatchaient sont
adapteres a la convention `V4_ATTACK_GUARD`.

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

pr-overlap Advisory: another open PR touches the same files (organ #13615)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants