diff options
| author | elvis <elvis@claros.ar> | 2026-09-26 20:20:19 -0300 |
|---|---|---|
| committer | elvis <elvis@claros.ar> | 2026-09-26 20:20:19 -0300 |
| commit | 8518a63f55153e7f45fd49ad6caff5555f4e374f (patch) | |
| tree | 636684eea3fa6f35d78282ab95eb687ef49154af /crates/asist-core/src/telemetry.rs | |
| parent | 69de76dc9cbedc6092d1e5ce84094a8030de1470 (diff) | |
| download | asist-p-main.tar.gz asist-p-main.zip | |
Translate code, comments, logs and terminal UI to English; add English README; rename scriptsHEADmain
Diffstat (limited to 'crates/asist-core/src/telemetry.rs')
| -rw-r--r-- | crates/asist-core/src/telemetry.rs | 88 |
1 files changed, 44 insertions, 44 deletions
diff --git a/crates/asist-core/src/telemetry.rs b/crates/asist-core/src/telemetry.rs index 964aaa6..43a1de0 100644 --- a/crates/asist-core/src/telemetry.rs +++ b/crates/asist-core/src/telemetry.rs @@ -1,9 +1,9 @@ -//! Medición de latencia por turno. +//! Per-turn latency measurement. //! -//! El objetivo no es un histograma bonito sino contestar a una pregunta -//! concreta cada vez que el asistente responde: *¿quién se ha comido el -//! tiempo?* Por eso se marcan los cinco instantes que separan las etapas y se -//! imprime el desglose, en lugar de un único total que no dice dónde mirar. +//! The goal is not a pretty histogram but answering one concrete question every +//! time the assistant replies: *who ate the time?* That is why the five +//! instants separating the stages are marked and the breakdown is printed, +//! instead of a single total that does not say where to look. use std::collections::BTreeMap; use std::sync::Mutex; @@ -11,22 +11,22 @@ use std::time::{Duration, Instant}; use crate::event::TurnId; -/// Etapas del pipeline, en el orden en que ocurren. +/// Pipeline stages, in the order they happen. #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] pub enum Stage { - /// Del final del habla a la transcripción definitiva. + /// From end of speech to the final transcription. Asr, - /// De la transcripción al primer fragmento de texto del modelo. + /// From the transcription to the model's first text fragment. LlmFirstToken, - /// Del primer fragmento a la respuesta completa. + /// From the first fragment to the complete answer. LlmRest, - /// Ejecución de herramientas. + /// Tool execution. Tools, - /// De la primera frase al primer audio recibido. + /// From the first sentence to the first audio received. TtsFirstAudio, - /// Síntesis del resto del turno. + /// Synthesis of the rest of the turn. TtsRest, - /// Del final del habla al primer audio: lo que de verdad se percibe. + /// From end of speech to first audio: what is really perceived. PerceivedLatency, } @@ -34,26 +34,26 @@ impl Stage { pub fn label(self) -> &'static str { match self { Stage::Asr => "asr.final", - Stage::LlmFirstToken => "llm.primer_token", - Stage::LlmRest => "llm.resto", - Stage::Tools => "herramientas", - Stage::TtsFirstAudio => "tts.primer_audio", - Stage::TtsRest => "tts.resto", - Stage::PerceivedLatency => "LATENCIA PERCIBIDA", + Stage::LlmFirstToken => "llm.first_token", + Stage::LlmRest => "llm.rest", + Stage::Tools => "tools", + Stage::TtsFirstAudio => "tts.first_audio", + Stage::TtsRest => "tts.rest", + Stage::PerceivedLatency => "PERCEIVED LATENCY", } } } -/// Cronómetro de un turno. Se rellena a medida que el turno avanza y se -/// vuelca de una vez al terminar. +/// Stopwatch for one turn. Filled in as the turn progresses and dumped +/// in one go at the end. #[derive(Debug)] pub struct TurnTimer { pub turn: TurnId, started: Instant, marks: BTreeMap<Stage, Duration>, - /// Segundos de audio hablados por el usuario, para calcular el RTF del ASR. + /// Seconds of audio spoken by the user, for the ASR RTF. pub input_secs: f32, - /// Segundos de audio sintetizados, para el RTF del TTS. + /// Seconds of audio synthesized, for the TTS RTF. pub output_secs: f32, pub sentences: usize, pub tool_calls: usize, @@ -72,15 +72,15 @@ impl TurnTimer { } } - /// Instante de referencia del turno: el fin del habla del usuario. + /// Reference instant of the turn: the end of the user's speech. pub fn started(&self) -> Instant { self.started } pub fn record(&mut self, stage: Stage, took: Duration) { - // Etapas que se repiten (una síntesis por frase) se acumulan; las que - // marcan un hito (primer audio) se quedan con la primera medida, que - // es la que describe la latencia de arranque. + // Stages that repeat (one synthesis per sentence) accumulate; the ones + // that mark a milestone (first audio) keep the first measurement, which + // is the one describing startup latency. match stage { Stage::TtsFirstAudio | Stage::LlmFirstToken | Stage::PerceivedLatency => { self.marks.entry(stage).or_insert(took); @@ -89,7 +89,7 @@ impl TurnTimer { } } - /// Marca una etapa con el tiempo transcurrido desde el inicio del turno. + /// Marks a stage with the time elapsed since the start of the turn. pub fn mark_since_start(&mut self, stage: Stage) { let took = self.started.elapsed(); self.record(stage, took); @@ -103,7 +103,7 @@ impl TurnTimer { self.started.elapsed() } - /// Etapa que más ha tardado, excluyendo el total percibido (que las agrega). + /// Slowest stage, excluding the perceived total (which aggregates them). pub fn bottleneck(&self) -> Option<(Stage, Duration)> { self.marks .iter() @@ -112,13 +112,13 @@ impl TurnTimer { .map(|(stage, took)| (*stage, *took)) } - /// Informe de una línea por etapa, con el cuello de botella señalado. + /// One-line-per-stage report, with the bottleneck flagged. pub fn report(&self) -> String { - let mut out = format!("turno {} — desglose de latencia\n", self.turn); + let mut out = format!("turn {} — latency breakdown\n", self.turn); let worst = self.bottleneck().map(|(stage, _)| stage); for (stage, took) in &self.marks { let flag = if Some(*stage) == worst { - " <== cuello de botella" + " <== bottleneck" } else { "" }; @@ -132,7 +132,7 @@ impl TurnTimer { if self.input_secs > 0.0 { if let Some(asr) = self.get(Stage::Asr) { out.push_str(&format!( - " {:<20} {:>8.2} x tiempo real ({:.1} s de voz)\n", + " {:<20} {:>8.2} x real time ({:.1} s of speech)\n", "asr.rtf", asr.as_secs_f32() / self.input_secs, self.input_secs @@ -143,7 +143,7 @@ impl TurnTimer { let tts: Duration = self.get(Stage::TtsFirstAudio).unwrap_or_default() + self.get(Stage::TtsRest).unwrap_or_default(); out.push_str(&format!( - " {:<20} {:>8.2} x tiempo real ({:.1} s de audio, {} frases)\n", + " {:<20} {:>8.2} x real time ({:.1} s of audio, {} sentences)\n", "tts.rtf", tts.as_secs_f32() / self.output_secs, self.output_secs, @@ -152,14 +152,14 @@ impl TurnTimer { } out.push_str(&format!( " {:<20} {:>8.0} ms\n", - "total del turno", + "turn total", self.total().as_secs_f64() * 1000.0 )); out } } -/// Agregado de todos los turnos de la sesión, para el resumen del cierre. +/// Aggregate of every turn in the session, for the closing summary. #[derive(Debug, Default)] pub struct Metrics { inner: Mutex<MetricsInner>, @@ -186,13 +186,13 @@ impl Metrics { self.inner.lock().unwrap_or_else(|e| e.into_inner()).turns } - /// Media por etapa sobre toda la sesión. + /// Per-stage average over the whole session. pub fn summary(&self) -> String { let inner = self.inner.lock().unwrap_or_else(|e| e.into_inner()); if inner.turns == 0 { - return "sin turnos completados\n".into(); + return "no completed turns\n".into(); } - let mut out = format!("resumen de {} turno(s) — media por etapa\n", inner.turns); + let mut out = format!("summary of {} turn(s) — average per stage\n", inner.turns); for (stage, (total, count)) in &inner.totals { out.push_str(&format!( " {:<20} {:>8.0} ms\n", @@ -209,7 +209,7 @@ mod tests { use super::*; #[test] - fn el_cuello_de_botella_es_la_etapa_mas_lenta() { + fn bottleneck_is_the_slowest_stage() { let mut timer = TurnTimer::new(TurnId(1)); timer.record(Stage::Asr, Duration::from_millis(300)); timer.record(Stage::LlmFirstToken, Duration::from_millis(500)); @@ -218,7 +218,7 @@ mod tests { } #[test] - fn la_latencia_percibida_no_compite_como_cuello_de_botella() { + fn perceived_latency_is_not_a_bottleneck_candidate() { let mut timer = TurnTimer::new(TurnId(1)); timer.record(Stage::Asr, Duration::from_millis(300)); timer.record(Stage::PerceivedLatency, Duration::from_secs(9)); @@ -226,7 +226,7 @@ mod tests { } #[test] - fn las_etapas_repetidas_se_acumulan_y_los_hitos_no() { + fn repeated_stages_accumulate_and_milestones_do_not() { let mut timer = TurnTimer::new(TurnId(1)); timer.record(Stage::TtsRest, Duration::from_millis(100)); timer.record(Stage::TtsRest, Duration::from_millis(150)); @@ -237,12 +237,12 @@ mod tests { assert_eq!( timer.get(Stage::TtsFirstAudio), Some(Duration::from_millis(600)), - "el primer audio describe el arranque; una frase posterior no debe rebajarlo" + "first audio describes startup; a later sentence must not lower it" ); } #[test] - fn el_resumen_promedia_entre_turnos() { + fn summary_averages_across_turns() { let metrics = Metrics::default(); for ms in [100, 300] { let mut timer = TurnTimer::new(TurnId(1)); |