aboutsummaryrefslogtreecommitdiffstats
path: root/crates/asist-core/src/event.rs
blob: 549bec0a8ad3e10b9be33f84e2e54168e59f0ee9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
use std::time::{Duration, Instant};

/// Identifica un turno de conversación (una intervención del usuario y la
/// respuesta que provoca). Todo lo que viaja por el bus lo lleva, para que un
/// resultado que llega tarde no se confunda con el turno que ya está en curso
/// —el caso típico cuando el usuario interrumpe al asistente.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub struct TurnId(pub u64);

impl std::fmt::Display for TurnId {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "#{}", self.0)
    }
}

/// Motivo por el que se corta la reproducción en curso.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum InterruptReason {
    /// El usuario ha empezado a hablar encima del asistente (barge-in).
    UserSpoke,
    /// Petición explícita: tecla, señal o cierre.
    Requested,
}

/// Todo lo que las etapas se cuentan entre sí. Un único enum mantiene el bus
/// observable: el renderizador y la telemetría ven exactamente los mismos
/// eventos que el orquestador, sin canales paralelos que se desincronicen.
#[derive(Debug, Clone)]
pub enum Event {
    /// El VAD ha detectado el arranque de una intervención.
    SpeechStarted { turn: TurnId, at: Instant },

    /// Transcripción provisional de la ventana deslizante: `committed` ya es
    /// estable, `volatile` todavía puede cambiar.
    Partial {
        turn: TurnId,
        committed: String,
        volatile: String,
    },

    /// Transcripción definitiva de la intervención completa.
    Transcript {
        turn: TurnId,
        text: String,
        audio_secs: f32,
        decode: Duration,
    },

    /// La intervención no contenía nada transcribible.
    Discarded { turn: TurnId },

    /// Primer fragmento de texto que devuelve el modelo.
    ReplyStarted { turn: TurnId, ttft: Duration },

    /// Un trozo de respuesta según llega del modelo.
    ReplyDelta { turn: TurnId, text: String },

    /// Una frase completa lista para sintetizar.
    Sentence {
        turn: TurnId,
        index: usize,
        text: String,
    },

    /// Respuesta completa del modelo para este turno.
    ReplyDone { turn: TurnId, text: String },

    /// El modelo ha pedido ejecutar una herramienta.
    ToolRequested {
        turn: TurnId,
        name: String,
        arguments: String,
    },

    /// Resultado de esa ejecución.
    ToolFinished {
        turn: TurnId,
        name: String,
        ok: bool,
        output: String,
        took: Duration,
    },

    /// Primer audio audible del turno: la métrica que de verdad percibe quien habla.
    AudioStarted { turn: TurnId, latency: Duration },

    /// Se ha terminado de reproducir todo lo del turno.
    AudioFinished { turn: TurnId },

    /// Se ha cortado la reproducción.
    Interrupted {
        turn: TurnId,
        reason: InterruptReason,
    },

    /// Aviso no fatal; el turno continúa.
    Warning { turn: TurnId, message: String },

    /// Fallo que aborta el turno.
    Failed { turn: TurnId, message: String },

    /// Cierre ordenado.
    Shutdown,
}

impl Event {
    pub fn turn(&self) -> Option<TurnId> {
        match self {
            Event::SpeechStarted { turn, .. }
            | Event::Partial { turn, .. }
            | Event::Transcript { turn, .. }
            | Event::Discarded { turn }
            | Event::ReplyStarted { turn, .. }
            | Event::ReplyDelta { turn, .. }
            | Event::Sentence { turn, .. }
            | Event::ReplyDone { turn, .. }
            | Event::ToolRequested { turn, .. }
            | Event::ToolFinished { turn, .. }
            | Event::AudioStarted { turn, .. }
            | Event::AudioFinished { turn }
            | Event::Interrupted { turn, .. }
            | Event::Warning { turn, .. }
            | Event::Failed { turn, .. } => Some(*turn),
            Event::Shutdown => None,
        }
    }
}