aboutsummaryrefslogtreecommitdiffstats
path: root/crates/asist-core/src/telemetry.rs
diff options
context:
space:
mode:
authorelvis <elvis@claros.ar>2026-09-26 20:20:19 -0300
committerelvis <elvis@claros.ar>2026-09-26 20:20:19 -0300
commit8518a63f55153e7f45fd49ad6caff5555f4e374f (patch)
tree636684eea3fa6f35d78282ab95eb687ef49154af /crates/asist-core/src/telemetry.rs
parent69de76dc9cbedc6092d1e5ce84094a8030de1470 (diff)
downloadasist-p-8518a63f55153e7f45fd49ad6caff5555f4e374f.tar.gz
asist-p-8518a63f55153e7f45fd49ad6caff5555f4e374f.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.rs88
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));