1use chrono::{DateTime, Utc};
2use serde::{Deserialize, Serialize};
3use uuid::Uuid;
4
5use super::artifact::Artifact;
6
7#[derive(Debug, Clone, Serialize, Deserialize)]
9pub struct AgentOutput {
10 pub run_id: Uuid,
12 pub agent_id: String,
14 pub phase: String,
16 pub exit_code: i64,
18 pub stdout: String,
20 pub stderr: String,
22 pub artifacts: Vec<Artifact>,
24 pub metrics: ExecutionMetrics,
26 pub confidence: f32,
28 pub quality_score: f32,
30 pub created_at: DateTime<Utc>,
32 #[serde(default, skip_serializing_if = "Vec::is_empty")]
34 pub build_errors: Vec<orchestrator_config::config::BuildError>,
35 #[serde(default, skip_serializing_if = "Vec::is_empty")]
37 pub test_failures: Vec<orchestrator_config::config::TestFailure>,
38}
39
40impl AgentOutput {
41 pub fn new(
43 run_id: Uuid,
44 agent_id: String,
45 phase: String,
46 exit_code: i64,
47 stdout: String,
48 stderr: String,
49 ) -> Self {
50 Self {
51 run_id,
52 agent_id,
53 phase,
54 exit_code,
55 stdout,
56 stderr,
57 artifacts: Vec::new(),
58 metrics: ExecutionMetrics::default(),
59 confidence: 1.0,
60 quality_score: 1.0,
61 created_at: Utc::now(),
62 build_errors: Vec::new(),
63 test_failures: Vec::new(),
64 }
65 }
66
67 pub fn with_artifacts(mut self, artifacts: Vec<Artifact>) -> Self {
69 self.artifacts = artifacts;
70 self
71 }
72
73 pub fn with_metrics(mut self, metrics: ExecutionMetrics) -> Self {
75 self.metrics = metrics;
76 self
77 }
78
79 pub fn with_confidence(mut self, confidence: f32) -> Self {
81 self.confidence = confidence.clamp(0.0, 1.0);
82 self
83 }
84
85 pub fn with_quality_score(mut self, score: f32) -> Self {
87 self.quality_score = score.clamp(0.0, 1.0);
88 self
89 }
90
91 pub fn is_success(&self) -> bool {
93 self.exit_code == 0
94 }
95}
96
97#[derive(Debug, Clone, Default, Serialize, Deserialize)]
99pub struct ExecutionMetrics {
100 pub duration_ms: u64,
102 pub tokens_consumed: Option<u64>,
104 pub api_calls: Option<u32>,
106 pub retry_count: u32,
108}
109
110#[cfg(test)]
111mod tests {
112 use super::*;
113 use crate::{ArtifactKind, ExecutionMetrics};
114
115 #[test]
116 fn test_agent_output_creation() {
117 let output = AgentOutput::new(
118 Uuid::new_v4(),
119 "qa_agent".to_string(),
120 "qa".to_string(),
121 0,
122 "test output".to_string(),
123 "".to_string(),
124 );
125
126 assert!(output.is_success());
127 assert_eq!(output.confidence, 1.0);
128 }
129
130 #[test]
131 fn test_agent_output_failure() {
132 let output = AgentOutput::new(
133 Uuid::new_v4(),
134 "impl_agent".to_string(),
135 "implement".to_string(),
136 1,
137 "".to_string(),
138 "error".to_string(),
139 );
140 assert!(!output.is_success());
141 }
142
143 #[test]
144 fn test_agent_output_builder_methods() {
145 let output = AgentOutput::new(
146 Uuid::new_v4(),
147 "agent".to_string(),
148 "qa".to_string(),
149 0,
150 "ok".to_string(),
151 "".to_string(),
152 )
153 .with_confidence(0.85)
154 .with_quality_score(0.9)
155 .with_metrics(ExecutionMetrics {
156 duration_ms: 1000,
157 tokens_consumed: Some(500),
158 api_calls: Some(3),
159 retry_count: 1,
160 })
161 .with_artifacts(vec![Artifact::new(ArtifactKind::Custom {
162 name: "test".to_string(),
163 })]);
164
165 assert_eq!(output.confidence, 0.85);
166 assert_eq!(output.quality_score, 0.9);
167 assert_eq!(output.metrics.duration_ms, 1000);
168 assert_eq!(output.artifacts.len(), 1);
169 }
170
171 #[test]
172 fn test_agent_output_confidence_clamped() {
173 let output = AgentOutput::new(
174 Uuid::new_v4(),
175 "a".to_string(),
176 "p".to_string(),
177 0,
178 "".to_string(),
179 "".to_string(),
180 )
181 .with_confidence(1.5)
182 .with_quality_score(-0.5);
183
184 assert_eq!(output.confidence, 1.0);
185 assert_eq!(output.quality_score, 0.0);
186 }
187}