feat(debug): encart temps = génération seule (sans la durée de lecture)

L'encart « dernier tour » ne compte plus que le temps de PRODUCTION de la réponse
(STT · Thinker · Speaker · TTS jusqu'au 1er son). La durée de lecture audio est
retirée du décompte (ce n'est pas de la génération, elle dépend de la longueur du
texte). Les lignes s'additionnent exactement au « TOTAL gén. ».

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Kazeia Team 2026-06-21 14:48:28 +02:00
parent 078804e2de
commit 2e6c55a068
1 changed files with 16 additions and 18 deletions

View File

@ -141,26 +141,24 @@ class PipelineMetrics(val tag: String = "Pipeline") {
// Bloc temps par étape (ne liste que les étapes réellement mesurées).
// Construit une fois, envoyé aux DEUX puits : summarySink (flot de logs) et
// timingsSink (encart persistant qui ne défile pas).
// DEUX BLOCS additifs : TRAITEMENT (latence avant le 1er son, ce qui compte
// pour la perf) + LECTURE (durée de la voix, dépend de la longueur du texte).
// Traitement + Lecture == TOTAL (exact). Le TTFT est un REPÈRE inclus dans
// Speaker, pas un poste à additionner.
// GÉNÉRATION seule : on ne compte que le temps pour PRODUIRE la réponse
// (STT · Thinker · Speaker · TTS jusqu'au 1er son). La durée de LECTURE
// audio est exclue (ce n'est pas de la génération, elle dépend de la
// longueur du texte). Les lignes s'additionnent exactement au TOTAL gén.
// Le TTFT est un REPÈRE inclus dans Speaker, pas un poste à additionner.
run {
val b = StringBuilder("⏱─── TEMPS DU TOUR ($status) ───\n")
val b = StringBuilder("⏱─── GÉNÉRATION ($status) ───\n")
fun row(name: String, value: String) { b.append(" ${name.padEnd(11)} $value\n") }
// first_audio = frontière traitement / lecture ; à défaut (pas de TTS,
// ex. mode texte) tout est « traitement ».
val processingMs = firstAudio ?: total
val readingMs = (total - processingMs).coerceAtLeast(0)
b.append(" TRAITEMENT (avant le 1er son) : $processingMs ms\n")
if (sttDur != null) row("· STT", "$sttDur ms")
if (thinkerDur != null) row("· Thinker", "$thinkerDur ms")
if (speakerGen != null) row("· Speaker", "$speakerGen ms" +
(speakerTtft?.let { " (dont 1er token $it)" } ?: ""))
if (ttsSynth != null) row("· TTS→son", "$ttsSynth ms")
if (firstAudio != null) b.append(" LECTURE (durée parlée) : $readingMs ms\n")
b.append(" ─────────────────────────────\n")
b.append(" TOTAL : $total ms")
var gen = 0L
if (sttDur != null) { row("STT", "$sttDur ms"); gen += sttDur }
if (thinkerDur != null) { row("Thinker", "$thinkerDur ms"); gen += thinkerDur }
if (speakerGen != null) {
row("Speaker", "$speakerGen ms" + (speakerTtft?.let { " (dont 1er token $it)" } ?: ""))
gen += speakerGen
}
if (ttsSynth != null) { row("TTS→son", "$ttsSynth ms"); gen += ttsSynth }
b.append(" ──────────\n")
b.append(" ${"TOTAL gén.".padEnd(11)} $gen ms")
val block = b.toString()
runCatching { timingsSink?.invoke(block) }
summarySink?.let { sink -> block.lineSequence().forEach { runCatching { sink(it) } } }