Skip to main content

agent_sdk_eval/
run_report.rs

1//! Run-level report helpers.
2
3use serde::{Deserialize, Serialize};
4
5use agent_sdk_core::{AgentError, RunId, RunTrace};
6
7use crate::{CostPolicy, CostReport, UsageReport};
8
9#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
10/// Limitations attached to a run report.
11pub struct RunReportLimitations {
12    /// Bounded limitations safe for logs and report views.
13    pub items: Vec<String>,
14}
15
16impl RunReportLimitations {
17    /// Creates limitations from report parts.
18    pub fn from_parts(usage: &UsageReport, cost: Option<&CostReport>) -> Self {
19        let mut items = usage.limitations.clone();
20        if let Some(cost) = cost {
21            items.extend(cost.limitations.clone());
22        } else {
23            items.push("cost report was not requested".to_string());
24        }
25        items.sort();
26        items.dedup();
27        Self { items }
28    }
29}
30
31#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
32/// Run report combining usage, optional cost, and limitations.
33pub struct RunReport {
34    /// Run id this report describes.
35    pub run_id: RunId,
36    /// Usage evidence derived from durable trace records.
37    pub usage: UsageReport,
38    /// Optional cost estimate.
39    pub cost: Option<CostReport>,
40    /// Report limitations and caveats.
41    pub limitations: RunReportLimitations,
42}
43
44impl RunReport {
45    /// Builds a run report from a run trace and optional cost policy.
46    pub fn from_run_trace(
47        trace: &RunTrace,
48        cost_policy: Option<&dyn CostPolicy>,
49    ) -> Result<Self, AgentError> {
50        let run_id = trace.run_id.clone().ok_or_else(|| {
51            AgentError::contract_violation("run report requires a run trace with run_id")
52        })?;
53        let usage = UsageReport::from_run_trace(trace)?;
54        let cost = cost_policy.map(|policy| policy.estimate_cost(&usage));
55        let limitations = RunReportLimitations::from_parts(&usage, cost.as_ref());
56        Ok(Self {
57            run_id,
58            usage,
59            cost,
60            limitations,
61        })
62    }
63}