use tokio_util::sync::CancellationToken;
use zeph_llm::provider::MessagePart;
use crate::metrics::TurnTimings;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct TurnId(pub u64);
impl TurnId {
#[allow(dead_code)]
pub(crate) fn next(self) -> TurnId {
TurnId(self.0 + 1)
}
}
impl std::fmt::Display for TurnId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
pub struct TurnInput {
pub text: String,
pub image_parts: Vec<MessagePart>,
}
impl TurnInput {
#[must_use]
pub fn new(text: String, image_parts: Vec<MessagePart>) -> Self {
Self { text, image_parts }
}
}
#[derive(Debug, Default, Clone)]
pub struct TurnMetrics {
pub timings: TurnTimings,
}
#[allow(dead_code)]
pub(crate) struct ToolOutputRecord {
pub(crate) tool_name: String,
pub(crate) summary: String,
pub(crate) latency_ms: u64,
}
pub struct Turn {
pub id: TurnId,
pub input: TurnInput,
pub metrics: TurnMetrics,
pub cancel_token: CancellationToken,
}
impl Turn {
#[must_use]
pub fn new(id: TurnId, input: TurnInput) -> Self {
Self {
id,
input,
metrics: TurnMetrics::default(),
cancel_token: CancellationToken::new(),
}
}
#[must_use]
pub fn id(&self) -> TurnId {
self.id
}
#[must_use]
pub fn metrics_snapshot(&self) -> &TurnMetrics {
&self.metrics
}
pub fn metrics_mut(&mut self) -> &mut TurnMetrics {
&mut self.metrics
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn turn_new_sets_id() {
let input = TurnInput::new("hello".to_owned(), vec![]);
let turn = Turn::new(TurnId(7), input);
assert_eq!(turn.id, TurnId(7));
assert_eq!(turn.id(), TurnId(7));
}
#[test]
fn turn_id_display() {
assert_eq!(TurnId(42).to_string(), "42");
}
#[test]
fn turn_id_next() {
assert_eq!(TurnId(3).next(), TurnId(4));
}
#[test]
fn turn_input_fields() {
let input = TurnInput::new("hi".to_owned(), vec![]);
assert_eq!(input.text, "hi");
assert!(input.image_parts.is_empty());
}
#[test]
fn turn_metrics_default_timings_are_zero() {
let m = TurnMetrics::default();
assert_eq!(m.timings.prepare_context_ms, 0);
assert_eq!(m.timings.llm_chat_ms, 0);
}
#[test]
fn turn_cancel_token_not_cancelled_on_new() {
let input = TurnInput::new("x".to_owned(), vec![]);
let turn = Turn::new(TurnId(0), input);
assert!(!turn.cancel_token.is_cancelled());
}
#[test]
fn turn_cancel_token_cancel() {
let input = TurnInput::new("x".to_owned(), vec![]);
let turn = Turn::new(TurnId(0), input);
turn.cancel_token.cancel();
assert!(turn.cancel_token.is_cancelled());
}
#[test]
fn turn_metrics_mut_allows_write() {
let input = TurnInput::new("x".to_owned(), vec![]);
let mut turn = Turn::new(TurnId(0), input);
turn.metrics_mut().timings.prepare_context_ms = 99;
assert_eq!(turn.metrics_snapshot().timings.prepare_context_ms, 99);
}
}