use crate::executors::SkillCall;
use async_trait::async_trait;
use serde_json::Value;
use std::collections::HashMap;
use std::fmt::Debug;
use std::sync::Arc;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum WorkflowMode {
ReAct,
Batch,
Chain,
PlanAndExecute,
}
impl Default for WorkflowMode {
fn default() -> Self {
WorkflowMode::ReAct
}
}
impl std::fmt::Display for WorkflowMode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
WorkflowMode::ReAct => write!(f, "ReAct"),
WorkflowMode::Batch => write!(f, "Batch"),
WorkflowMode::Chain => write!(f, "Chain"),
WorkflowMode::PlanAndExecute => write!(f, "PlanAndExecute"),
}
}
}
#[async_trait]
pub trait WorkflowCallback: Send + Sync + Debug {
async fn on_step_start(
&self,
task_id: &str,
step_name: &str,
step_index: usize,
parameters: Option<&HashMap<String, Value>>,
);
async fn on_step_success(
&self,
task_id: &str,
step_name: &str,
step_index: usize,
output: &str,
duration_ms: u64,
);
async fn on_step_failure(
&self,
task_id: &str,
step_name: &str,
step_index: usize,
error: &str,
duration_ms: u64,
);
async fn on_workflow_complete(
&self,
task_id: &str,
final_output: &str,
total_duration_ms: u64,
total_steps: usize,
);
async fn on_workflow_failed(
&self,
task_id: &str,
error: &str,
total_duration_ms: u64,
total_steps: usize,
);
}
pub fn truncate_output(output: &str, max_len: usize) -> String {
if output.len() <= max_len {
output.to_string()
} else {
format!("{}...", &output[..max_len])
}
}
#[derive(Debug, Clone)]
pub struct Workflow {
pub variables: HashMap<String, Value>,
pub step_results: Vec<WorkflowStepResult>,
}
impl Workflow {
pub fn new() -> Self {
Self {
variables: HashMap::new(),
step_results: Vec::new(),
}
}
pub fn set_variable(&mut self, name: &str, value: Value) {
self.variables.insert(name.to_string(), value);
}
pub fn get_variable(&self, name: &str) -> Option<&Value> {
self.variables.get(name)
}
pub fn add_step_result(&mut self, result: WorkflowStepResult) {
self.step_results.push(result);
}
pub fn get_step_results(&self) -> &[WorkflowStepResult] {
&self.step_results
}
}
impl Default for Workflow {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct WorkflowStepResult {
pub step_id: String,
pub skill: String,
pub input: HashMap<String, Value>,
pub output: String,
pub success: bool,
pub error: Option<String>,
}
#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
pub struct WorkflowStep {
pub id: String,
pub action: String,
pub parameters: HashMap<String, ValueRef>,
#[serde(default)]
pub condition: Option<Condition>,
#[serde(default)]
pub output_as: Option<String>,
#[serde(default)]
pub on_error: Option<ErrorHandler>,
}
#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
#[serde(untagged)]
pub enum ValueRef {
Literal(Value),
Reference(Reference),
Expression(Expression),
}
#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
pub struct Reference {
#[serde(rename = "$ref")]
pub path: String,
}
#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
pub struct Expression {
#[serde(rename = "$expr")]
pub expr: String,
}
#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
pub struct Condition {
pub op: String,
pub left: ValueRef,
pub right: ValueRef,
}
#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
pub struct ErrorHandler {
pub action: String,
#[serde(default)]
pub fallback: Option<ValueRef>,
#[serde(default)]
pub max_retries: Option<u32>,
}
#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
pub struct WorkflowPlan {
pub name: Option<String>,
pub steps: Vec<WorkflowStep>,
#[serde(default)]
pub parameters: HashMap<String, Value>,
}
#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
pub struct PlanInstruction {
pub mode: String,
pub plan: Option<WorkflowPlan>,
pub message: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ExecutionStatus {
Success,
Failure,
}
#[derive(Debug, Clone)]
pub struct StepResult {
pub skill: String,
pub parameters: HashMap<String, Value>,
pub output: String,
pub status: ExecutionStatus,
}
impl StepResult {
pub fn to_string(&self) -> String {
let status_str = match self.status {
ExecutionStatus::Success => "SUCCESS",
ExecutionStatus::Failure => "FAILURE",
};
format!(
"{} Executed skill '{}' with parameters {:?}\nResult: {}",
status_str, self.skill, self.parameters, self.output
)
}
}
#[derive(Debug)]
pub enum ReactInstruction {
Done(String),
Single(SkillCall),
Batch(Vec<SkillCall>),
}
#[derive(Debug)]
pub struct ChainPlan {
pub steps: Vec<ChainStepDef>,
}
#[derive(Debug)]
pub struct ChainStepDef {
pub action: String,
pub parameters: HashMap<String, Value>,
pub output_as: Option<String>,
}