use crate::workflow::error::WorkflowError;
pub use potato_agent::agents::{
agent::Agent,
task::{Task, TaskStatus, WorkflowTask},
};
use potato_agent::AgentResponse;
use pyo3::prelude::*;
use serde::Deserialize;
use serde::Serialize;
use serde_json::Value;
use std::collections::{HashMap, HashSet};
use std::sync::Arc;
use std::sync::RwLock;
use tracing::instrument;
use tracing::{debug, warn};
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[pyclass(from_py_object)]
pub struct TaskList {
pub tasks: HashMap<String, Arc<RwLock<Task>>>,
pub execution_order: Vec<String>,
}
impl PartialEq for TaskList {
fn eq(&self, other: &Self) -> bool {
self.tasks.keys().eq(other.tasks.keys()) && self.execution_order == other.execution_order
}
}
#[pymethods]
impl TaskList {
#[getter]
pub fn items(&self) -> HashMap<String, Task> {
self.tasks()
}
pub fn tasks(&self) -> HashMap<String, Task> {
self.tasks
.iter()
.map(|(id, task)| {
let cloned_task = task.read().unwrap().clone();
(id.clone(), cloned_task)
})
.collect()
}
pub fn deep_clone(&self) -> Result<Self, WorkflowError> {
let mut new_task_list = TaskList::new();
for (task_id, task_arc) in &self.tasks {
let task = task_arc.read().unwrap();
let cloned_task = task.clone(); new_task_list
.tasks
.insert(task_id.clone(), Arc::new(RwLock::new(cloned_task)));
}
new_task_list.execution_order = self.execution_order.clone();
Ok(new_task_list)
}
}
impl TaskList {
pub fn new() -> Self {
Self {
tasks: HashMap::new(),
execution_order: Vec::new(),
}
}
pub fn len(&self) -> usize {
self.tasks.len()
}
pub fn is_empty(&self) -> bool {
self.tasks.is_empty()
}
pub fn rebuild_task_validators(&mut self) -> Result<(), WorkflowError> {
for task_arc in self.tasks.values_mut() {
let mut task = task_arc.write().unwrap();
task.rebuild_validator()?
}
Ok(())
}
pub fn is_complete(&self) -> bool {
self.tasks.values().all(|task| {
task.read().unwrap().status == TaskStatus::Completed
|| task.read().unwrap().status == TaskStatus::Failed
})
}
pub fn add_task(&mut self, task: Task) -> Result<(), WorkflowError> {
if self.tasks.contains_key(&task.id) {
return Err(WorkflowError::TaskAlreadyExists(task.id.clone()));
}
for dep_id in &task.dependencies {
if !self.tasks.contains_key(dep_id) {
return Err(WorkflowError::DependencyNotFound(dep_id.clone()));
}
if dep_id == &task.id {
return Err(WorkflowError::TaskDependsOnItself(task.id.clone()));
}
}
self.tasks
.insert(task.id.clone(), Arc::new(RwLock::new(task)));
self.rebuild_execution_order();
Ok(())
}
pub fn get_task(&self, task_id: &str) -> Option<Arc<RwLock<Task>>> {
self.tasks.get(task_id).cloned()
}
pub fn get_task_responses(&self) -> Result<HashMap<String, Value>, WorkflowError> {
let mut responses = HashMap::new();
for (task_id, task_arc) in &self.tasks {
let task = task_arc
.read()
.map_err(|_| WorkflowError::ReadLockAcquireError)?;
if let Some(result) = &task.result {
if let Some(value) = result.response_value() {
responses.insert(task_id.clone(), value);
} else {
responses.insert(task_id.clone(), Value::Null);
}
}
}
Ok(responses)
}
pub fn remove_task(&mut self, task_id: &str) {
self.tasks.remove(task_id);
}
pub fn pending_count(&self) -> usize {
self.tasks
.values()
.filter(|task| task.read().unwrap().status == TaskStatus::Pending)
.count()
}
#[instrument(skip_all)]
pub fn update_task_status(
&mut self,
task_id: &str,
status: TaskStatus,
result: Option<&AgentResponse>,
) {
debug!(status=?status, result=?result, "Updating task status");
if let Some(task) = self.tasks.get_mut(task_id) {
let mut task = task.write().unwrap();
task.status = status;
task.result = result.cloned();
}
}
fn topological_sort(
&self,
task_id: &str,
visited: &mut HashSet<String>,
temp_visited: &mut HashSet<String>,
order: &mut Vec<String>,
) {
if temp_visited.contains(task_id) {
return; }
if visited.contains(task_id) {
return;
}
temp_visited.insert(task_id.to_string());
if let Some(task) = self.tasks.get(task_id) {
for dep_id in &task.read().unwrap().dependencies {
self.topological_sort(dep_id, visited, temp_visited, order);
}
}
temp_visited.remove(task_id);
visited.insert(task_id.to_string());
order.push(task_id.to_string());
}
fn rebuild_execution_order(&mut self) {
let mut order = Vec::new();
let mut visited = HashSet::new();
let mut temp_visited = HashSet::new();
for task_id in self.tasks.keys() {
if !visited.contains(task_id) {
self.topological_sort(task_id, &mut visited, &mut temp_visited, &mut order);
}
}
self.execution_order = order;
}
pub fn get_ready_tasks(&self) -> Vec<Arc<RwLock<Task>>> {
self.tasks
.values()
.filter(|task_arc| {
let task = task_arc.read().unwrap();
task.status == TaskStatus::Pending
&& task.dependencies.iter().all(|dep_id| {
self.tasks
.get(dep_id)
.map(|dep| dep.read().unwrap().status == TaskStatus::Completed)
.unwrap_or(false)
})
})
.cloned()
.collect()
}
pub fn reset_failed_tasks(&mut self) -> Result<(), WorkflowError> {
for task in self.tasks.values_mut() {
let mut task = task.write().unwrap();
if task.status == TaskStatus::Failed {
task.status = TaskStatus::Pending;
task.increment_retry();
if task.retry_count > task.max_retries {
return Err(WorkflowError::MaxRetriesExceeded(task.id.clone()));
}
}
}
Ok(())
}
pub fn get_last_task_id(&self) -> Option<String> {
self.execution_order.last().cloned()
}
}