gemini_adk_rs/live/
soft_turn.rs1use std::time::{Duration, Instant};
9
10pub const DEFAULT_SOFT_TURN_TIMEOUT: Duration = Duration::from_secs(2);
12
13pub struct SoftTurnDetector {
15 vad_ended_at: Option<Instant>,
17 timeout: Duration,
19}
20
21impl SoftTurnDetector {
22 pub fn new(timeout: Duration) -> Self {
24 Self {
25 vad_ended_at: None,
26 timeout,
27 }
28 }
29
30 pub fn on_vad_end(&mut self) {
32 self.vad_ended_at = Some(Instant::now());
33 }
34
35 pub fn on_model_response(&mut self) {
38 self.vad_ended_at = None;
39 }
40
41 pub fn check(&self, now: Instant) -> bool {
43 self.vad_ended_at
44 .map(|t| now.duration_since(t) >= self.timeout)
45 .unwrap_or(false)
46 }
47
48 pub fn reset(&mut self) {
50 self.vad_ended_at = None;
51 }
52}
53
54#[cfg(test)]
55mod tests {
56 use super::*;
57
58 #[test]
59 fn no_soft_turn_without_vad_end() {
60 let d = SoftTurnDetector::new(Duration::from_millis(100));
61 assert!(!d.check(Instant::now()));
62 }
63
64 #[test]
65 fn soft_turn_after_timeout() {
66 let mut d = SoftTurnDetector::new(Duration::from_millis(50));
67 d.on_vad_end();
68 assert!(!d.check(Instant::now()));
70 std::thread::sleep(Duration::from_millis(60));
72 assert!(d.check(Instant::now()));
73 }
74
75 #[test]
76 fn model_response_cancels_soft_turn() {
77 let mut d = SoftTurnDetector::new(Duration::from_millis(50));
78 d.on_vad_end();
79 d.on_model_response();
80 std::thread::sleep(Duration::from_millis(60));
81 assert!(!d.check(Instant::now()));
82 }
83
84 #[test]
85 fn reset_clears_state() {
86 let mut d = SoftTurnDetector::new(Duration::from_millis(50));
87 d.on_vad_end();
88 std::thread::sleep(Duration::from_millis(60));
89 assert!(d.check(Instant::now()));
90 d.reset();
91 assert!(!d.check(Instant::now()));
92 }
93}