Skip to content

Fin de parole : fermer quand on dit merci - #24

Open
NeuTroNBZh wants to merge 4 commits into
lfpoulain:betafrom
NeuTroNBZh:fin-de-conversation-vocale
Open

NeuTroNBZh wants to merge 4 commits into
lfpoulain:betafrom
NeuTroNBZh:fin-de-conversation-vocale

Conversation

@NeuTroNBZh

@NeuTroNBZh NeuTroNBZh commented Nov 8, 2026 •

Copy link
Copy Markdown
Contributor

Ticket : #23

But

Pouvoir clore une conversation à la voix (« merci », « c'est tout », « au revoir »). Sur un satellite, seul le silence la ferme, et la voix d'une autre personne dans la pièce relance le délai sans fin.

Ce qui change pour l'utilisateur

  • Nouvelle case Quand il écoute → Fin de parole → Fermer quand on dit merci, désactivée par défaut : le comportement existant ne change pas.
  • Une fois cochée : quand la personne clôt l'échange sans nouvelle demande, Cornelius répond d'une formule courte (« avec plaisir »), puis la conversation se ferme 2 s après la fin de sa réponse, au plus tard 10 s après le mot de fin si Cornelius ne parle plus. Une réponse audible en cours n'est jamais coupée.
  • Une hésitation entendue ensuite (« euh », « oui ») ne retient pas la conversation. Une vraie demande transcrite après le mot de fin (« merci… ah, et la météo ? ») l'annule : la conversation continue et se termine ensuite comme d'habitude.
  • Champ Autres formules de fin (sous la case cochée) : formules du foyer, une par ligne, 20 au plus, 60 caractères au plus (« c'est bon Jarvis ») ; la phrase entière doit correspondre, à la ponctuation et à la casse près.
  • Le réglage vaut à partir de la conversation suivante (les outils sont fixés à l'ouverture).

Comment

Trois commits, plus un de corrections après relecture.

  1. Outil conversation__end (server/live/conversation-end.ts), sans argument, proposé à la délégation seulement si le réglage est actif ; consignes dans liveConfiguration(). LiveTransport appelle SessionController.requestEnd(scope) (refusé pour une conversation périmée ou non prête). endWordFinished() ferme quand Cornelius se tait depuis 2 s, ou 10 s après la demande. Migration end_word_preferences, route PATCH /api/admin/end-word, champ endWord de l'état, case dans SilenceCloseSettings.tsx.
  2. Détection côté serveur : le modèle ne déléguait pas toujours l'outil. isClosingPhrase() reconnaît une phrase faite seulement d'une formule de fin (vocabulaire fermé, sans « oui » ni « non ») ; LiveTransport.endOnClosingPhrase() l'examine dans la transcription de la personne (journal : source transcription).
  3. Formules du foyer : table end_word_preferences complétée par end_word_phrases (seconde migration, en fin de liste, rejouable), route PATCH /api/admin/end-word/phrases, champ endPhrases de l'état, cleanEndPhrases(), formules données au modèle.
  4. Après relecture :
    • SessionController.cancelEnd() : une phrase transcrite qui n'est pas une formule de fin et qui est une vraie demande (isNewRequest()) annule la fin demandée (journal « Fin de conversation annulée »).
    • Tampon userTurn : en plus du début de la réponse, la phrase est examinée 700 ms après la dernière transcription ; une transcription arrivée après le début de la réponse est donc détectée, et ne préfixe plus la phrase suivante.
    • Le serveur laisse décider le modèle (qui a le contexte) quand la dernière réponse de Cornelius était une question (« ça va bien, merci »), quand une alarme sonne (AlarmsToolBridge.ringing() ; « c'est bon », « arrête » vont à alarms__respond) et quand la phrase contient « ça va » (sauf « ça va aller »). La description de conversation__end le dit aussi.
    • Satellite : noteAssistantOutput(scope, playedUntil) reçoit la fin de lecture estimée (PCM 16 kHz envoyé plus vite qu'il n'est joué). Le plafond de 10 s ne s'applique plus pendant une réponse audible.
  • Documentation (ouverture et présence, administration, miroir, prompts) et CHANGELOG.md.

Vérifications

  • Rebasée sur beta (0.3.0), avec le volume du satellite (Satellites : régler le volume à la voix #38).
  • npm run check : OK ; prettier sur les fichiers modifiés.
  • conversation-end, controller, live, storage, satellite-conversation, alarms* : 199/199. Tests ajoutés après relecture : annulation par une vraie demande (et pas par une hésitation), ordre inversé des événements, réponse à une question, alarme qui sonne, lecture au-delà de 10 s, isNewRequest, « ça va bien, merci ».
  • Essayé sur le satellite Waveshare avant relecture (fermeture 3,7 s après « merci », source transcription). Les corrections après relecture ne sont pas encore essayées sur le matériel.

Copilot AI balanced review requested due to automatic review settings November 8, 2026 21:17

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has insufficient remaining quota. Try requesting a review at a lower effort level.

@NeuTroNBZh

Copy link
Copy Markdown
Contributor Author

Essai sur le matériel (satellite Waveshare, firmware 0.4.0) : après « merci », le modèle vocal n'a pas délégué conversation__end (la consigne « ne pas déléguer une salutation » l'emportait), et seul le silence a fermé la conversation.

Correctif ajouté (303265f) : le serveur relit la transcription de la phrase quand Cornelius commence à répondre. Si elle n'est faite que d'une formule de fin (« merci », « merci beaucoup, c'est tout », « au revoir »), la fermeture est demandée sans attendre l'outil. « Oui merci » ou « merci, et la météo ? » ne ferment pas. L'outil reste proposé, et la consigne de délégation le cite en exception.

Vérifié : npm run check OK, 4 nouveaux tests (tests/conversation-end.test.ts et tests/live.test.ts), npm test à 740/742. Les deux échecs sont sans lien : les licences (environnement de test) et retrait groupé de screensaver.test.ts, qui passe 3 fois sur 3 relancé seul. Nouvel essai sur le satellite à venir.

@NeuTroNBZh

Copy link
Copy Markdown
Contributor Author

Essai sur le matériel validé avec 303265f (satellite Waveshare, firmware 0.4.0) : « merci » reconnu dans la transcription (Fin de conversation demandée, source transcription), conversation fermée 3,7 s plus tard avec la raison « Fin de conversation demandée. ». Au même endroit, avant le correctif, seul le silence fermait la conversation, environ 16 s plus tard.

@NeuTroNBZh

Copy link
Copy Markdown
Contributor Author

Ajout (commit suivant) : Autres formules de fin, propres au foyer (« c'est bon Jarvis », « ciao »). Une par ligne sous la case, 20 au plus et 60 caractères. Elles ferment quand elles forment toute la phrase, et elles sont données au modèle en données JSON. Route PATCH /api/admin/end-word/phrases, champ endPhrases, table end_word_phrases (nouvelle migration). npm run check OK ; conversation-end, live, controller, storage : 167/167, dont 2 nouveaux tests.

@lfpoulain

Copy link
Copy Markdown
Owner

Merci Louis, la détection est bien pensée : « Merci de me rappeler demain » ne ferme pas la conversation (liste fermée de mots de clôture), je l'ai vérifié avec la vraie fonction. Avant de fusionner, il y a des points à reprendre.

À corriger

  1. Un « merci » suivi d'une vraie demande ferme quand même. endRequestedAt n'est jamais annulé (server/controller.ts, requestEnd et endWordFinished). Scénario : « Merci. », puis 1 s plus tard « ah, et la météo de demain ? ». La conversation se ferme au plus tard 10 s après le merci, en pleine réponse. La doc et SilenceCloseSettings.tsx affirment le contraire. Soit on annule la demande de fin dès qu'une nouvelle parole significative est transcrite, soit on corrige la doc.
  2. Tampon userTurn (server/live/transport.ts) : il n'est vidé que sur session.delegation.created ou session.output_transcript.delta. Si la transcription d'entrée arrive après le début de la réponse, le « merci » n'est pas détecté et il préfixe la phrase suivante. Un test avec l'ordre des événements inversé serait utile.
  3. Deux cas à trancher : « ça va bien merci » ferme la conversation (gênant si Cornelius vient de demander « comment ça va ? »), et « tu peux arrêter » ferme alors que « arrête » doit passer par alarms__respond quand une alarme sonne.
  4. Description de la PR périmée. Les commits 2 et 3 ajoutent la détection côté serveur (isClosingPhrase, endOnClosingPhrase), les formules de fin du foyer (table end_word_phrases, route PATCH /api/admin/end-word/phrases, champ endPhrases) et une seconde migration. Le corps ne parle que du premier commit.
  5. Doc : « Autres formules de fin » et la route end-word/phrases ne sont pas documentées dans l'administration.
  6. À confirmer : la fermeture 2 s après assistantOutputUntil et le plafond de 10 s peuvent couper une réponse longue si le serveur reçoit l'audio plus vite que le satellite ne le joue. Est-ce que close() vide la file audio ?

Rebase

beta a avancé (#34, #36, #38 et #26 sont mergées, ainsi que la 0.3.0). Conflits attendus sur CHANGELOG.md, server/storage.ts, server/app.ts, server/live/configuration.ts, server/live/transport.ts et tests/live.test.ts. Peux-tu rebaser sur beta ?

@lfpoulain lfpoulain added voix Conversation vocale, fin de parole, fournisseurs modifications demandées Relue : des corrections sont attendues avant la fusion à rebaser À mettre à jour depuis la branche beta labels Nov 9, 2026
NeuTroNBZh and others added 4 commits November 9, 2026 16:17
Nouvelle case dans « Fin de parole », désactivée par défaut : quand la
personne clôt l’échange sans nouvelle demande (« merci », « c’est tout »,
« au revoir »), Cornelius répond d’une formule courte et délègue l’outil
conversation__end. La conversation se ferme 2 s après la fin de sa
réponse, au plus tard 10 s après, sans tenir compte de la parole
entendue dans la pièce : sur un satellite, on peut parler ensuite à
quelqu’un d’autre sans que Cornelius continue d’écouter.

Réglage conservé dans end_word_preferences, route
PATCH /api/admin/end-word et champ endWord de l’état.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Essai sur un satellite : après « merci », le modèle vocal n'a pas délégué
conversation__end (la consigne « ne pas déléguer une salutation »
l'emportait) et seul le silence a fermé la conversation.

Le serveur relit maintenant la phrase de la personne quand Cornelius
commence à répondre : si elle n'est faite que d'une formule de fin
(« merci », « merci beaucoup, c'est tout », « au revoir »), la fermeture
est demandée sans attendre l'outil. « Oui merci » ou « merci, et la
météo ? » ne ferment pas. L'outil reste proposé, et la consigne de
délégation le cite désormais en exception.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sous « Fermer quand on dit merci », le champ « Autres formules de fin »
ajoute celles du foyer (« c'est bon Jarvis », « ciao »), une par ligne,
20 au plus et 60 caractères. Elles ferment quand elles forment toute la
phrase, à la ponctuation et à la casse près, et sont données au modèle
avec l'outil conversation__end (en données JSON, jamais en consignes).

Route PATCH /api/admin/end-word/phrases, champ endPhrases de l'état,
table end_word_phrases.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Une vraie demande transcrite après le mot de fin (« merci… ah, et la
  météo ? ») l'annule (cancelEnd) ; une hésitation (« euh », « oui »)
  ne compte pas.
- Transcription arrivée après le début de la réponse : la phrase est
  examinée après 700 ms sans nouvelle transcription, en plus du début
  de réponse ; « merci » ne préfixe plus la phrase suivante.
- Le serveur laisse décider le modèle quand la phrase répond à une
  question de Cornelius, quand une alarme sonne (« c'est bon » ou
  « arrête » passent par alarms__respond) ou quand elle contient
  « ça va » (sauf « ça va aller »). Description de conversation__end
  complétée.
- Satellite : la fin de réponse est la fin de lecture estimée (audio
  16 kHz reçu plus vite qu'il n'est joué), car la fermeture vide la
  file du satellite ; le plafond de 10 s ne coupe plus une réponse
  audible en cours.
- Doc : annulation, cas laissés au modèle, fin de lecture, « Autres
  formules de fin » dans l'administration, champs endWord/endPhrases et
  routes PATCH /api/admin/end-word et /end-word/phrases.
- Tests : annulation, ordre inversé des événements, question, alarme,
  lecture au-delà de 10 s, isNewRequest, « ça va bien, merci ».

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@NeuTroNBZh
NeuTroNBZh force-pushed the fin-de-conversation-vocale branch from 76cbb40 to 265e05d Compare November 9, 2026 15:54
@NeuTroNBZh

Copy link
Copy Markdown
Contributor Author

Merci pour la relecture détaillée. Corrigé dans 265e05d, branche rebasée sur beta (0.3.0) :

  1. « Merci » puis une vraie demande : SessionController.cancelEnd(). Une phrase transcrite qui n'est pas une formule de fin et qui est une vraie demande (isNewRequest() : deux mots, ou un mot de 4 lettres, hors « euh », « oui », « ok »…) annule la fin. La conversation continue puis se termine comme d'habitude. Une phrase dite à quelqu'un d'autre l'annule aussi ; c'est alors le silence réglé qui ferme. Doc et texte de SilenceCloseSettings.tsx corrigés.
  2. Tampon userTurn : la phrase est aussi examinée 700 ms après la dernière transcription d'entrée. Une transcription arrivée après le début de la réponse est donc détectée et ne préfixe plus la phrase suivante. Test avec l'ordre inversé ajouté.
  3. Cas tranchés : le serveur s'abstient (le modèle, qui a le contexte, garde conversation__end) quand la dernière réponse de Cornelius finit par une question, quand une alarme sonne (AlarmsToolBridge.ringing() : « c'est bon », « arrête » vont à alarms__respond), et quand la phrase contient « ça va » (sauf « ça va aller »). La description de conversation__end le dit aussi.
  4. Description de PR réécrite : les trois commits, plus celui-ci.
  5. Doc : « Autres formules de fin » dans l'administration ; champs endWord/endPhrases et routes PATCH /api/admin/end-word et /end-word/phrases dans « Ouverture et présence ».
  6. Bonne remarque, oui : à la fermeture, SatelliteHub.transition() envoie {"type":"session","state":"closed"} et le firmware appelle audio_play_flush() (main/link.c, set_conversation). L'audio en file était donc perdu. Le transport estime maintenant la fin de lecture du satellite (PCM 16 kHz, 32 octets/ms, cumulé depuis max(now, fin précédente)) et la passe à noteAssistantOutput(scope, playedUntil). Le délai de 2 s part de la fin réelle de lecture, et le plafond de 10 s ne coupe plus une réponse audible en cours : il ne vaut que si Cornelius ne parle plus (réponse qui tarde, outil bloqué).

npm run check OK ; conversation-end, controller, live, storage, satellite-conversation, alarms* : 199/199. Pas encore réessayé sur le satellite après ces corrections : je le ferai.

@lfpoulain

Copy link
Copy Markdown
Owner

Merci, les six points sont traités : « Merci » puis une vraie demande ne ferme plus, « ça va bien merci » non plus, rien ne se ferme pendant une alarme, et la fin de lecture du satellite est estimée. En sondant avec les vraies fonctions (vrai LiveTransport et vrai contrôleur), j'ai trouvé trois cas qui restent faux :

  1. Une confirmation annule la fin. « Merci », puis « non, c'est bon » (ou « d'accord », « parfait », « non merci », « super ») est compté comme une nouvelle demande par isNewRequest (server/live/conversation-end.ts), et la conversation reste ouverte. Seuls « euh », « oui », « ok » et « non » seuls sont ignorés.
  2. Ordre inversé. Cornelius demande « Autre chose ? », le modèle appelle conversation__end, puis la transcription « Non merci. » arrive en retard : la minuterie de 700 ms appelle cancelEnd sur la fin demandée par l'outil. Il faudrait n'annuler une fin demandée par l'outil que pour une phrase arrivée après la demande.
  3. La garde « question » rate en ordre inversé. « Comment vas-tu ? », puis la réponse suivante commence avant la transcription « Bien, merci. » : la conversation se ferme. askedQuestion est calculé au premier delta d'entrée, sur un assistantText qui contient déjà le début de la réponse suivante. Le figer au début de la réponse réglerait le problème.

Le test tests/live.test.ts:984 utilise « Oui, super, merci. », qui n'est de toute façon pas une phrase de fin : il ne prouve pas la garde. « Bien, merci. » le prouverait.

Rebase : #40 est fusionnée. Conflits sur CHANGELOG.md, server/app.ts, server/storage.ts (tes deux migrations après voiceUsageMigration) et tests/controller.test.ts. En recollant ce dernier, rétablis le }); du test « une réponse encore jouée par le satellite… », sinon tsc échoue. Pense aussi à refaire un essai sur un satellite après ces corrections.

@lfpoulain

Copy link
Copy Markdown
Owner

Salut Louis, petite relance sur celle-ci : il reste les trois cas de ma dernière relecture (une confirmation comme « non, c'est bon » qui annule la fin, l'ordre inversé entre conversation__end et la transcription, et askedQuestion à figer au début de la réponse), plus le test avec « Bien, merci. ».

beta a encore avancé depuis (catalogue de modules, mise à jour depuis l'administration, suites d'audit) : le rebase touchera toujours CHANGELOG.md, server/app.ts, server/storage.ts et tests/controller.test.ts. Garde tes migrations en fin de liste.

Pas d'urgence : dis-nous simplement si tu comptes la reprendre, ou si tu préfères qu'on s'en charge.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

modifications demandées Relue : des corrections sont attendues avant la fusion à rebaser À mettre à jour depuis la branche beta voix Conversation vocale, fin de parole, fournisseurs

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants