Skip to main content

agentforge_core/
shadow.rs

1use chrono::{DateTime, Utc};
2use serde::{Deserialize, Serialize};
3use uuid::Uuid;
4
5/// Status of a shadow (online eval) run.
6#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
7#[serde(rename_all = "snake_case")]
8pub enum ShadowRunStatus {
9    Pending,
10    Running,
11    Complete,
12    Error,
13}
14
15impl std::fmt::Display for ShadowRunStatus {
16    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
17        match self {
18            ShadowRunStatus::Pending => write!(f, "pending"),
19            ShadowRunStatus::Running => write!(f, "running"),
20            ShadowRunStatus::Complete => write!(f, "complete"),
21            ShadowRunStatus::Error => write!(f, "error"),
22        }
23    }
24}
25
26/// The outcome of comparing champion vs. candidate on a single dimension.
27#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
28#[serde(rename_all = "snake_case")]
29pub enum DimensionOutcome {
30    Win,
31    Loss,
32    Tie,
33}
34
35/// Per-dimension comparison of champion vs. candidate.
36#[derive(Debug, Clone, Serialize, Deserialize)]
37pub struct DimensionComparison {
38    pub dimension: String,
39    pub champion_score: f64,
40    pub candidate_score: f64,
41    pub outcome: DimensionOutcome,
42    pub delta: f64,
43}
44
45/// Aggregated shadow run comparison report.
46#[derive(Debug, Clone, Serialize, Deserialize)]
47pub struct ShadowComparison {
48    pub run_id: Uuid,
49    pub champion_agent_id: Uuid,
50    pub candidate_agent_id: Uuid,
51    /// Fraction of traffic routed to the candidate (0.0–1.0).
52    pub traffic_fraction: f64,
53    pub total_requests: u32,
54    pub champion_aggregate_score: f64,
55    pub candidate_aggregate_score: f64,
56    pub aggregate_delta: f64,
57    pub per_dimension: Vec<DimensionComparison>,
58    /// Whether the candidate won overall (positive delta + all regression gates pass).
59    pub candidate_wins: bool,
60    pub compared_at: DateTime<Utc>,
61}
62
63/// Persisted record of a shadow run configuration.
64#[derive(Debug, Clone, Serialize, Deserialize)]
65pub struct ShadowRun {
66    pub id: Uuid,
67    pub champion_agent_id: Uuid,
68    pub candidate_agent_id: Uuid,
69    /// Percentage of traffic sent to candidate (0–100).
70    pub traffic_percent: u8,
71    pub status: ShadowRunStatus,
72    pub comparison: Option<ShadowComparison>,
73    pub error_message: Option<String>,
74    pub created_at: DateTime<Utc>,
75    pub started_at: Option<DateTime<Utc>>,
76    pub completed_at: Option<DateTime<Utc>>,
77}
78
79#[cfg(test)]
80mod tests {
81    use super::*;
82
83    #[test]
84    fn status_display() {
85        assert_eq!(ShadowRunStatus::Pending.to_string(), "pending");
86        assert_eq!(ShadowRunStatus::Complete.to_string(), "complete");
87    }
88}