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
use std::time::{Duration, Instant};
/// Tracks one reasoning stretch: open window + timer for Thought for … summaries.
///
/// Display policy (full text vs Thinking... vs hidden) lives on
/// [`super::ReasoningChrome`], not here.
#[derive(Clone, Debug, Default)]
pub(super) struct ReasoningPhase {
/// True between [`Self::begin_step`] and [`Self::finalize`] / [`Self::reset`].
open: bool,
started_at: Option<Instant>,
}
impl ReasoningPhase {
/// Open a new reasoning stretch for the current provider step.
pub(super) fn begin_step(&mut self) {
*self = Self {
open: true,
started_at: None,
};
}
pub(super) fn reset(&mut self) {
*self = Self::default();
}
pub(super) fn on_reasoning_delta(&mut self) {
if self.started_at.is_none() {
self.started_at = Some(Instant::now());
}
}
/// Closes the stretch. Returns elapsed when reasoning deltas were seen.
pub(super) fn finalize(&mut self) -> Option<Duration> {
self.open = false;
self.started_at
.take()
.map(|started_at| started_at.elapsed())
}
/// Whether the current step's reasoning stretch is still open.
pub(super) fn is_open(&self) -> bool {
self.open
}
}
#[cfg(test)]
#[path = "reasoning_phase_tests.rs"]
mod tests;