use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ThreadGoalStatus {
Active,
Paused,
BudgetLimited,
Complete,
}
impl ThreadGoalStatus {
pub fn as_str(&self) -> &'static str {
match self {
Self::Active => "active",
Self::Paused => "paused",
Self::BudgetLimited => "budget_limited",
Self::Complete => "complete",
}
}
pub fn is_active(&self) -> bool {
matches!(self, Self::Active)
}
pub fn is_terminal(&self) -> bool {
matches!(self, Self::Complete | Self::BudgetLimited)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ThreadGoal {
pub thread_id: String,
pub goal_id: String,
pub objective: String,
pub status: ThreadGoalStatus,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub token_budget: Option<u64>,
#[serde(default)]
pub tokens_used: u64,
#[serde(default)]
pub time_used_seconds: u64,
pub created_at_ms: u64,
pub updated_at_ms: u64,
#[serde(default)]
pub continuation_suppressed: bool,
}
impl ThreadGoal {
pub fn budget_remaining(&self) -> Option<u64> {
self.token_budget
.map(|b| b.saturating_sub(self.tokens_used))
}
pub fn over_budget(&self) -> bool {
matches!(self.token_budget, Some(b) if self.tokens_used >= b)
}
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct GoalProgress {
pub tokens_used: u64,
pub elapsed_secs: u64,
pub made_progress: bool,
}
pub type TurnOutcome = GoalProgress;