1use chrono::{DateTime, Utc};
4use klieo_runlog::types::{Cost, RunStatus, Usage};
5use serde::Serialize;
6
7#[non_exhaustive]
11#[derive(Debug, Clone, Serialize)]
12pub struct RunLogSummary {
13 pub run_id: String,
15 #[allow(missing_docs)]
16 pub agent: String,
17 #[allow(missing_docs)]
18 pub status: RunStatus,
19 pub started_at: DateTime<Utc>,
21 pub finished_at: Option<DateTime<Utc>>,
23 pub tokens: Usage,
25 pub cost_estimate: Option<Cost>,
27}
28
29impl RunLogSummary {
30 pub fn from_run_log(log: &klieo_runlog::RunLog) -> Self {
33 Self {
34 run_id: log.run_id.to_string(),
35 agent: log.agent.clone(),
36 status: log.status,
37 started_at: log.started_at,
38 finished_at: log.finished_at,
39 tokens: log.tokens,
40 cost_estimate: log.cost_estimate,
41 }
42 }
43}
44
45#[non_exhaustive]
49#[derive(Debug, Clone, Serialize)]
50pub struct AgentSummary {
51 pub agent: String,
53 pub last_seen: DateTime<Utc>,
55 #[allow(missing_docs)]
56 pub runs_running: u32,
57 #[allow(missing_docs)]
58 pub runs_completed: u32,
59 #[allow(missing_docs)]
60 pub runs_failed: u32,
61 #[allow(missing_docs)]
62 pub runs_cancelled: u32,
63}
64
65#[non_exhaustive]
67#[derive(Debug, Clone, Serialize)]
68pub struct StreamSummary {
69 pub stream_id: String,
71 pub is_terminal: bool,
73 pub next_id: u64,
75 pub last_write_unix_ms: u64,
77}
78
79#[cfg(test)]
80mod tests {
81 use super::*;
82 use chrono::Utc;
83 use klieo_core::ids::RunId;
84 use klieo_runlog::types::RunStatus;
85
86 #[test]
87 fn run_log_summary_serialises_run_id_as_string() {
88 let run_id = RunId::new();
89 let summary = RunLogSummary {
90 run_id: run_id.to_string(),
91 agent: "test-agent".into(),
92 status: RunStatus::Running,
93 started_at: Utc::now(),
94 finished_at: None,
95 tokens: Usage::default(),
96 cost_estimate: None,
97 };
98 let json = serde_json::to_string(&summary).unwrap();
99 assert!(
100 json.contains(&format!("\"run_id\":\"{}\"", run_id)),
101 "run_id not serialised as string, got: {json}"
102 );
103 assert!(json.contains("\"status\":\"running\""), "got: {json}");
104 assert!(json.contains("\"agent\":\"test-agent\""), "got: {json}");
105 }
106
107 #[test]
108 fn agent_summary_serialises() {
109 let summary = AgentSummary {
110 agent: "writer".into(),
111 last_seen: Utc::now(),
112 runs_running: 1,
113 runs_completed: 5,
114 runs_failed: 0,
115 runs_cancelled: 0,
116 };
117 let json = serde_json::to_string(&summary).unwrap();
118 assert!(json.contains("\"agent\":\"writer\""), "got: {json}");
119 assert!(json.contains("\"runs_completed\":5"), "got: {json}");
120 }
121
122 #[test]
123 fn stream_summary_serialises() {
124 let summary = StreamSummary {
125 stream_id: "s-001".into(),
126 is_terminal: false,
127 next_id: 7,
128 last_write_unix_ms: 1_000_000,
129 };
130 let json = serde_json::to_string(&summary).unwrap();
131 assert!(json.contains("\"stream_id\":\"s-001\""), "got: {json}");
132 assert!(json.contains("\"next_id\":7"), "got: {json}");
133 }
134}