use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum TerminalStatus {
Completed,
Refused,
ExhaustedSteps,
ExhaustedTokens,
Deadline,
Stalled,
LoopDetected,
Cancelled,
Crashed,
}
impl TerminalStatus {
pub fn as_str(self) -> &'static str {
use TerminalStatus::*;
match self {
Completed => "completed",
Refused => "refused",
ExhaustedSteps => "exhausted_steps",
ExhaustedTokens => "exhausted_tokens",
Deadline => "deadline",
Stalled => "stalled",
LoopDetected => "loop_detected",
Cancelled => "cancelled",
Crashed => "crashed",
}
}
pub fn is_success(self) -> bool {
matches!(self, TerminalStatus::Completed)
}
pub fn is_budget(self) -> bool {
matches!(
self,
TerminalStatus::ExhaustedSteps
| TerminalStatus::ExhaustedTokens
| TerminalStatus::Deadline
)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ScheduleRequest {
pub after_ms: u64,
pub instruction: String,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct SubscriptionRequest {
pub uri: String,
pub action: SubscriptionAction,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub condition: Option<serde_json::Value>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum SubscriptionAction {
Subscribe,
Unsubscribe,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Outcome {
pub status: TerminalStatus,
pub partial: bool,
pub result: serde_json::Value,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub scheduled: Vec<ScheduleRequest>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub subscriptions: Vec<SubscriptionRequest>,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn status_serializes_snake_case() {
let s = serde_json::to_string(&TerminalStatus::ExhaustedSteps).unwrap();
assert_eq!(s, "\"exhausted_steps\"");
}
#[test]
fn budget_classification() {
assert!(TerminalStatus::Deadline.is_budget());
assert!(!TerminalStatus::Completed.is_budget());
assert!(TerminalStatus::Completed.is_success());
}
}