aboutsummaryrefslogtreecommitdiffstats
path: root/crates/asist-core/src/event.rs
blob: bf9a6e6edd79c527665cb3fabd70a51e7bf76f0f (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};

/// Identifies a conversation turn (one user utterance and the answer it
/// triggers). Everything travelling on the bus carries it, so a result that
/// arrives late is not mistaken for the turn in progress, the typical case
/// being the user interrupting the assistant.
#[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)
    }
}

/// Why the current playback is cut.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum InterruptReason {
    /// The user started talking over the assistant (barge-in).
    UserSpoke,
    /// Explicit request: key, signal or shutdown.
    Requested,
}

/// Everything the stages tell each other. A single enum keeps the bus
/// observable: the renderer and the telemetry see exactly the same events as
/// the orchestrator, with no parallel channels that fall out of sync.
#[derive(Debug, Clone)]
pub enum Event {
    /// The VAD detected the start of an utterance.
    SpeechStarted { turn: TurnId, at: Instant },

    /// Partial transcription of the sliding window: `committed` is already
    /// stable, `volatile` may still change.
    Partial {
        turn: TurnId,
        committed: String,
        volatile: String,
    },

    /// Final transcription of the whole utterance.
    Transcript {
        turn: TurnId,
        text: String,
        audio_secs: f32,
        decode: Duration,
    },

    /// The utterance had nothing transcribable.
    Discarded { turn: TurnId },

    /// First text fragment returned by the model.
    ReplyStarted { turn: TurnId, ttft: Duration },

    /// A piece of the answer as it arrives from the model.
    ReplyDelta { turn: TurnId, text: String },

    /// A complete sentence ready to be synthesized.
    Sentence {
        turn: TurnId,
        index: usize,
        text: String,
    },

    /// Complete model answer for this turn.
    ReplyDone { turn: TurnId, text: String },

    /// The model asked to run a tool.
    ToolRequested {
        turn: TurnId,
        name: String,
        arguments: String,
    },

    /// Result of that run.
    ToolFinished {
        turn: TurnId,
        name: String,
        ok: bool,
        output: String,
        took: Duration,
    },

    /// First audible audio of the turn: the metric the speaker really perceives.
    AudioStarted { turn: TurnId, latency: Duration },

    /// Everything of the turn has finished playing.
    AudioFinished { turn: TurnId },

    /// Playback was cut.
    Interrupted {
        turn: TurnId,
        reason: InterruptReason,
    },

    /// Non-fatal warning; the turn continues.
    Warning { turn: TurnId, message: String },

    /// Failure that aborts the turn.
    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,
        }
    }
}