git_internal/internal/object/
run.rs1use std::{collections::HashMap, fmt};
20
21use serde::{Deserialize, Serialize};
22use uuid::Uuid;
23
24use crate::{
25 errors::GitError,
26 hash::ObjectHash,
27 internal::object::{
28 ObjectTrait,
29 integrity::IntegrityHash,
30 types::{ActorRef, Header, ObjectType},
31 },
32};
33
34#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
36#[serde(rename_all = "snake_case")]
37pub enum RunStatus {
38 Created,
40 Patching,
42 Validating,
44 Completed,
46 Failed,
48}
49
50impl RunStatus {
51 pub fn as_str(&self) -> &'static str {
52 match self {
53 RunStatus::Created => "created",
54 RunStatus::Patching => "patching",
55 RunStatus::Validating => "validating",
56 RunStatus::Completed => "completed",
57 RunStatus::Failed => "failed",
58 }
59 }
60}
61
62impl fmt::Display for RunStatus {
63 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
64 write!(f, "{}", self.as_str())
65 }
66}
67
68#[derive(Debug, Clone, Serialize, Deserialize)]
71pub struct Environment {
72 pub os: String, pub arch: String, pub cwd: String, #[serde(flatten)]
76 pub extra: HashMap<String, serde_json::Value>,
77}
78
79impl Environment {
80 pub fn capture() -> Self {
82 Self {
83 os: std::env::consts::OS.to_string(),
84 arch: std::env::consts::ARCH.to_string(),
85 cwd: std::env::current_dir()
86 .map(|p| p.to_string_lossy().to_string())
87 .unwrap_or_else(|e| {
88 tracing::warn!("Failed to get current directory: {}", e);
89 "unknown".to_string()
90 }),
91 extra: HashMap::new(),
92 }
93 }
94}
95
96#[derive(Debug, Clone, Serialize, Deserialize)]
98pub struct AgentInstance {
99 pub role: String,
100 pub provider_route: Option<String>,
101}
102
103#[derive(Debug, Clone, Serialize, Deserialize)]
106pub struct Run {
107 #[serde(flatten)]
108 header: Header,
109 task_id: Uuid,
110 orchestrator_version: String,
111 base_commit_sha: IntegrityHash,
112 status: RunStatus,
113 context_snapshot_id: Option<Uuid>,
114 #[serde(default)]
115 agent_instances: Vec<AgentInstance>,
116 metrics: Option<serde_json::Value>,
117 error: Option<String>,
118 environment: Option<Environment>,
119}
120
121impl Run {
122 pub fn new(
130 repo_id: Uuid,
131 created_by: ActorRef,
132 task_id: Uuid,
133 base_commit_sha: impl AsRef<str>,
134 ) -> Result<Self, String> {
135 let base_commit_sha = base_commit_sha.as_ref().parse()?;
136 Ok(Self {
137 header: Header::new(ObjectType::Run, repo_id, created_by)?,
138 task_id,
139 orchestrator_version: "libra-builtin".to_string(),
140 base_commit_sha,
141 status: RunStatus::Created,
142 context_snapshot_id: None,
143 agent_instances: Vec::new(),
144 metrics: None,
145 error: None,
146 environment: Some(Environment::capture()),
147 })
148 }
149
150 pub fn header(&self) -> &Header {
151 &self.header
152 }
153
154 pub fn task_id(&self) -> Uuid {
155 self.task_id
156 }
157
158 pub fn orchestrator_version(&self) -> &str {
159 &self.orchestrator_version
160 }
161
162 pub fn base_commit_sha(&self) -> &IntegrityHash {
163 &self.base_commit_sha
164 }
165
166 pub fn status(&self) -> &RunStatus {
167 &self.status
168 }
169
170 pub fn context_snapshot_id(&self) -> Option<Uuid> {
171 self.context_snapshot_id
172 }
173
174 pub fn agent_instances(&self) -> &[AgentInstance] {
175 &self.agent_instances
176 }
177
178 pub fn metrics(&self) -> Option<&serde_json::Value> {
179 self.metrics.as_ref()
180 }
181
182 pub fn error(&self) -> Option<&str> {
183 self.error.as_deref()
184 }
185
186 pub fn environment(&self) -> Option<&Environment> {
187 self.environment.as_ref()
188 }
189
190 pub fn set_status(&mut self, status: RunStatus) {
191 self.status = status;
192 }
193
194 pub fn set_context_snapshot_id(&mut self, context_snapshot_id: Option<Uuid>) {
195 self.context_snapshot_id = context_snapshot_id;
196 }
197
198 pub fn add_agent_instance(&mut self, instance: AgentInstance) {
199 self.agent_instances.push(instance);
200 }
201
202 pub fn set_metrics(&mut self, metrics: Option<serde_json::Value>) {
203 self.metrics = metrics;
204 }
205
206 pub fn set_error(&mut self, error: Option<String>) {
207 self.error = error;
208 }
209}
210
211impl fmt::Display for Run {
212 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
213 write!(f, "Run: {}", self.header.object_id())
214 }
215}
216
217impl ObjectTrait for Run {
218 fn from_bytes(data: &[u8], _hash: ObjectHash) -> Result<Self, GitError>
219 where
220 Self: Sized,
221 {
222 serde_json::from_slice(data).map_err(|e| GitError::InvalidObjectInfo(e.to_string()))
223 }
224
225 fn get_type(&self) -> ObjectType {
226 ObjectType::Run
227 }
228
229 fn get_size(&self) -> usize {
230 serde_json::to_vec(self).map(|v| v.len()).unwrap_or(0)
231 }
232
233 fn to_data(&self) -> Result<Vec<u8>, GitError> {
234 serde_json::to_vec(self).map_err(|e| GitError::InvalidObjectInfo(e.to_string()))
235 }
236}
237
238#[cfg(test)]
239mod tests {
240 use super::*;
241
242 fn test_hash_hex() -> String {
243 IntegrityHash::compute(b"ai-process-test").to_hex()
244 }
245
246 #[test]
247 fn test_new_objects_creation() {
248 let repo_id = Uuid::from_u128(0x0123456789abcdef0123456789abcdef);
249 let actor = ActorRef::agent("test-agent").expect("actor");
250 let base_hash = test_hash_hex();
251
252 let run = Run::new(repo_id, actor.clone(), Uuid::from_u128(0x1), &base_hash).expect("run");
254
255 let env = run.environment().unwrap();
256 assert!(!env.os.is_empty());
258 assert!(!env.arch.is_empty());
259 assert!(!env.cwd.is_empty());
260 }
261}