hippox 0.7.3

🦛A reliable, autonomous LLM runtime and skill orchestration engine, Capable of processing natural language and automatically executing OS-native atomic skills, fundamentally enabling the LLM to truly take over the computer.
//! Type definitions for workflow execution
use async_trait::async_trait;
use hippox_drivers::DriverCall;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::collections::HashMap;
use std::fmt::Debug;
use std::sync::Arc;
/// Workflow execution mode enumeration
///
/// Defines the strategy for processing user requests and executing drivers.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum WorkflowMode {
    /// ReAct mode: Think → Act → Observe loop
    ///
    /// Each driver execution is followed by LLM decision for next step.
    /// Best for: Open-ended tasks, dynamic decision making, error recovery
    /// LLM calls: 1 per driver + 1 for final response
    ReAct,
    /// Batch mode: Execute multiple independent drivers in parallel
    ///
    /// Drivers must have no dependencies on each other's results.
    /// Best for: Independent operations, bulk processing
    /// LLM calls: 1 (generates batch plan)
    Batch,
    /// Chain mode: Sequential execution with variable passing
    ///
    /// Each driver's output can be passed as input to the next driver.
    /// Best for: Linear pipelines, data transformation chains
    /// LLM calls: 1 (generates chain)
    Chain,
    /// Plan-and-Execute mode: One-time planning with full workflow
    ///
    /// Supports conditionals, variable references, and error handling.
    /// Best for: Complex workflows, conditional logic, deterministic tasks
    /// LLM calls: 1 (generates plan) + optional for dynamic decisions
    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"),
        }
    }
}
/// Workflow execution result
/// Workflow execution result
#[derive(Debug, Clone)]
pub enum WorkflowExecutionResult {
    Completed(String),
    /// Completed with separate raw JSON data for stage two conversion
    CompletedWithRaw {
        display: String,
        raw_json: String,
    },
    Paused {
        checkpoint: Option<String>,
        completed_steps: usize,
        partial_output: String,
    },
    Cancelled {
        completed_steps: usize,
    },
    Failed {
        error: String,
        completed_steps: usize,
    },
}
impl WorkflowExecutionResult {
    pub fn is_completed(&self) -> bool {
        matches!(self, WorkflowExecutionResult::Completed(_) | WorkflowExecutionResult::CompletedWithRaw { .. })
    }
    pub fn is_paused(&self) -> bool {
        matches!(self, WorkflowExecutionResult::Paused { .. })
    }
    pub fn is_cancelled(&self) -> bool {
        matches!(self, WorkflowExecutionResult::Cancelled { .. })
    }
    /// Get the display output (for callbacks)
    pub fn display_output(&self) -> Option<&str> {
        match self {
            WorkflowExecutionResult::Completed(output) => Some(output),
            WorkflowExecutionResult::CompletedWithRaw { display, .. } => Some(display),
            WorkflowExecutionResult::Paused { partial_output, .. } => Some(partial_output),
            _ => None,
        }
    }
    /// Get the raw JSON output (for stage two conversion)
    pub fn raw_json(&self) -> Option<&str> {
        match self {
            WorkflowExecutionResult::CompletedWithRaw { raw_json, .. } => Some(raw_json),
            WorkflowExecutionResult::Completed(output) => Some(output),
            _ => None,
        }
    }
    pub fn final_output(&self) -> Option<&str> {
        self.raw_json()
    }
}
/// Step interruption info for callback
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StepInterruptionInfo {
    pub interrupted: bool,
    pub reason: String,
    pub step_index: usize,
    pub step_name: String,
    pub checkpoint: Option<String>,
}
/// Workflow execution callback trait
#[async_trait]
pub trait WorkflowCallback: Send + Sync + Debug {
    async fn on_step_start(&self, task_id: &str, step_name: &str, step_index: usize, input: 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_step_timeout(&self, task_id: &str, step_name: &str, step_index: usize, error: &str, duration_ms: u64);
    async fn on_step_interrupted(&self, task_id: &str, info: &StepInterruptionInfo);
    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);
    async fn on_workflow_cancelled(&self, task_id: &str, total_duration_ms: u64, total_steps: usize);
    async fn on_workflow_paused(&self, task_id: &str, checkpoint: Option<&str>, total_duration_ms: u64, total_steps: usize);
    async fn on_workflow_resumed(&self, task_id: &str, total_duration_ms: u64, total_steps: usize) {
        let _ = (task_id, total_duration_ms, total_steps);
    }
}
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 driver: 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, Serialize, Deserialize)]
pub enum ExecutionStatus {
    Success,
    Failure,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StepResult {
    pub driver: 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 driver '{}' with parameters {:?}\nResult: {}", status_str, self.driver, self.parameters, self.output)
    }
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WorkflowCheckpoint {
    pub last_completed_step: usize,
    pub variables: HashMap<String, Value>,
    pub completed_results: Vec<StepResult>,
    pub mode: WorkflowMode,
    pub metadata: HashMap<String, String>,
}
#[derive(Debug)]
pub enum ReactInstruction {
    Done(String),
    Single(DriverCall),
    Batch(Vec<DriverCall>),
}
#[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>,
}