use serde::{Deserialize, Serialize};
use serde_json::Value as JsonValue;
use super::cancel::{CancelInfo, FailureInfo};
use super::pause::PauseInfo;
use crate::TokenUsage;
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "state", rename_all = "snake_case")]
pub enum ExecutionState {
Running,
Paused(PauseInfo),
Done(ExecutionResult),
Cancelled(CancelInfo),
Failed(FailureInfo),
}
impl ExecutionState {
pub fn tag(&self) -> ExecutionStateTag {
ExecutionStateTag::from(self)
}
pub fn is_terminal(&self) -> bool {
matches!(self, Self::Done(_) | Self::Cancelled(_) | Self::Failed(_))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ExecutionStateTag {
Running,
Paused,
Done,
Cancelled,
Failed,
}
impl From<&ExecutionState> for ExecutionStateTag {
fn from(state: &ExecutionState) -> Self {
match state {
ExecutionState::Running => Self::Running,
ExecutionState::Paused(_) => Self::Paused,
ExecutionState::Done(_) => Self::Done,
ExecutionState::Cancelled(_) => Self::Cancelled,
ExecutionState::Failed(_) => Self::Failed,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ExecutionResult {
pub value: JsonValue,
pub usage: Option<TokenUsage>,
pub finished_at: i64,
}
#[cfg(test)]
mod tests {
use super::*;
use crate::execution::cancel::{CancelCode, CancelInfo, CancelReason};
use crate::execution::pause::{PauseInfo, PauseKind};
fn make_pause_info() -> PauseInfo {
PauseInfo {
kind: PauseKind::Single,
prompts: vec![],
paused_at: 0,
}
}
fn make_cancel_info() -> CancelInfo {
CancelInfo {
reason: CancelReason {
code: CancelCode::User,
detail: None,
requested_at: 0,
},
observed_at: 0,
state_before: Box::new(ExecutionState::Running),
}
}
fn make_failure_info() -> FailureInfo {
crate::execution::cancel::FailureInfo {
message: "test".into(),
kind: crate::execution::cancel::FailureKind::Other,
occurred_at: 0,
}
}
#[test]
fn execution_state_tag_from_state() {
assert_eq!(
ExecutionStateTag::from(&ExecutionState::Running),
ExecutionStateTag::Running
);
assert_eq!(
ExecutionStateTag::from(&ExecutionState::Paused(make_pause_info())),
ExecutionStateTag::Paused
);
assert_eq!(
ExecutionStateTag::from(&ExecutionState::Done(ExecutionResult {
value: serde_json::Value::Null,
usage: None,
finished_at: 0,
})),
ExecutionStateTag::Done
);
assert_eq!(
ExecutionStateTag::from(&ExecutionState::Cancelled(make_cancel_info())),
ExecutionStateTag::Cancelled
);
assert_eq!(
ExecutionStateTag::from(&ExecutionState::Failed(make_failure_info())),
ExecutionStateTag::Failed
);
}
#[test]
fn execution_state_serde_roundtrip() {
let states: Vec<ExecutionState> = vec![
ExecutionState::Running,
ExecutionState::Paused(make_pause_info()),
ExecutionState::Done(ExecutionResult {
value: serde_json::json!({"ok": true}),
usage: None,
finished_at: 1_700_000_000_000,
}),
ExecutionState::Cancelled(make_cancel_info()),
ExecutionState::Failed(make_failure_info()),
];
for state in states {
let json = serde_json::to_string(&state).expect("serialize");
let _: ExecutionState = serde_json::from_str(&json).expect("deserialize");
}
}
}