use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use uuid::Uuid;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SubTask {
pub id: String,
pub name: String,
pub instruction: String,
pub specialty: Option<String>,
pub dependencies: Vec<String>,
pub priority: i32,
pub max_steps: usize,
pub context: SubTaskContext,
pub status: SubTaskStatus,
pub assigned_agent: Option<String>,
pub created_at: DateTime<Utc>,
pub completed_at: Option<DateTime<Utc>>,
pub stage: usize,
}
impl SubTask {
pub fn new(name: impl Into<String>, instruction: impl Into<String>) -> Self {
Self {
id: Uuid::new_v4().to_string(),
name: name.into(),
instruction: instruction.into(),
specialty: None,
dependencies: Vec::new(),
priority: 0,
max_steps: 100,
context: SubTaskContext::default(),
status: SubTaskStatus::Pending,
assigned_agent: None,
created_at: Utc::now(),
completed_at: None,
stage: 0,
}
}
pub fn with_specialty(mut self, specialty: impl Into<String>) -> Self {
self.specialty = Some(specialty.into());
self
}
pub fn with_dependencies(mut self, deps: Vec<String>) -> Self {
self.dependencies = deps;
self
}
pub fn with_priority(mut self, priority: i32) -> Self {
self.priority = priority;
self
}
pub fn with_max_steps(mut self, max_steps: usize) -> Self {
self.max_steps = max_steps;
self
}
pub fn with_context(mut self, context: SubTaskContext) -> Self {
self.context = context;
self
}
pub fn can_run(&self, completed: &[String]) -> bool {
self.dependencies.iter().all(|dep| completed.contains(dep))
}
pub fn start(&mut self, agent_id: &str) {
self.status = SubTaskStatus::Running;
self.assigned_agent = Some(agent_id.to_string());
}
pub fn complete(&mut self, success: bool) {
self.status = if success {
SubTaskStatus::Completed
} else {
SubTaskStatus::Failed
};
self.completed_at = Some(Utc::now());
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct SubTaskContext {
pub parent_task: Option<String>,
pub dependency_results: HashMap<String, String>,
pub shared_resources: Vec<String>,
pub metadata: HashMap<String, serde_json::Value>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum SubTaskStatus {
Pending,
Blocked,
Running,
Completed,
Failed,
Cancelled,
TimedOut,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SubAgent {
pub id: String,
pub name: String,
pub specialty: String,
pub subtask_id: String,
pub status: SubAgentStatus,
pub steps: usize,
pub tool_calls: Vec<ToolCallRecord>,
pub output: String,
pub model: String,
pub provider: String,
pub created_at: DateTime<Utc>,
pub last_active: DateTime<Utc>,
}
impl SubAgent {
pub fn new(
name: impl Into<String>,
specialty: impl Into<String>,
subtask_id: impl Into<String>,
model: impl Into<String>,
provider: impl Into<String>,
) -> Self {
let now = Utc::now();
Self {
id: Uuid::new_v4().to_string(),
name: name.into(),
specialty: specialty.into(),
subtask_id: subtask_id.into(),
status: SubAgentStatus::Initializing,
steps: 0,
tool_calls: Vec::new(),
output: String::new(),
model: model.into(),
provider: provider.into(),
created_at: now,
last_active: now,
}
}
pub fn record_tool_call(&mut self, name: &str, success: bool) {
self.tool_calls.push(ToolCallRecord {
name: name.to_string(),
timestamp: Utc::now(),
success,
});
self.steps += 1;
self.last_active = Utc::now();
}
pub fn append_output(&mut self, text: &str) {
self.output.push_str(text);
self.last_active = Utc::now();
}
pub fn set_status(&mut self, status: SubAgentStatus) {
self.status = status;
self.last_active = Utc::now();
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum SubAgentStatus {
Initializing,
Working,
Waiting,
Completed,
Failed,
Terminated,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ToolCallRecord {
pub name: String,
pub timestamp: DateTime<Utc>,
pub success: bool,
}
pub fn is_transient_error(error: &str) -> bool {
let lower = error.to_ascii_lowercase();
if lower.contains("rate limit")
|| lower.contains("ratelimit")
|| lower.contains("too many requests")
|| lower.contains("429")
{
return true;
}
if lower.contains("timeout")
|| lower.contains("timed out")
|| lower.contains("connection reset")
|| lower.contains("connection refused")
|| lower.contains("broken pipe")
|| lower.contains("network")
|| lower.contains("socket")
|| lower.contains("io error")
{
return true;
}
if lower.contains("503")
|| lower.contains("502")
|| lower.contains("504")
|| lower.contains("service unavailable")
|| lower.contains("bad gateway")
|| lower.contains("gateway timeout")
|| lower.contains("overloaded")
|| lower.contains("temporarily unavailable")
{
return true;
}
false
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SubTaskResult {
pub subtask_id: String,
pub subagent_id: String,
pub success: bool,
pub result: String,
pub steps: usize,
pub tool_calls: usize,
pub execution_time_ms: u64,
pub error: Option<String>,
pub artifacts: Vec<String>,
#[serde(default)]
pub retry_count: u32,
}