hippox 0.7.30

πŸ¦›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.
docs.rs failed to build hippox-0.7.30
Please check the build logs for more information.
See Builds for ideas on how to fix a failed build, or Metadata for how to configure docs.rs builds.
If you believe this is docs.rs' fault, open an issue.
Visit the last successful build: hippox-0.3.9

πŸ”— Quick Links

Resource Link
🌐 Website https://hippox.vercel.app/
πŸ“– Documentation https://hippox-docs-en.vercel.app/
πŸ“¦ Crates.io https://crates.io/crates/hippox
πŸ’» GitHub https://github.com/0xhappyboy/hippo

Basic Usage

Instantiate

// =================== Method 1 ===================
let hippox = Hippox::builder(ModelProvider::OpenAI)
    .api_key("sk-xxx")
    .lang("zh")
    .identity(|id| {
        id.name = Some("agent".to_string());
        id.role = Some("assistant".to_string());
        id.personality = Some("friendly".to_string());
    })
    .build()
    .await?;

// =================== Method 2 ===================
let mut config = HippoxConfig::default();
config.lang = "zh".to_string();
config.identity_information = IdentityInformation {
    name: Some("agent".to_string()),
    role: Some("assistant".to_string()),
    personality: Some("friendly".to_string()),
    ..Default::default()
};
let hippox = Hippox::new(
    ModelProvider::OpenAI,
    Some("sk-xxx".to_string()),
    None,
    Some(config),
).await?;

// =================== Simple Method ===================
let hippox = Hippox::new(
    ModelProvider::OpenAI,
    Some("sk-xxx".to_string()),
    None,
    Some(HippoxConfig::default()),
).await?;

// builder
let hippox = Hippox::builder(ModelProvider::OpenAI)
    .api_key("sk-xxx")
    .build()
    .await?;

Task Execution

Submit

1. Execution mode
  • Asynchronous non-blocking submission. Task goes to background pool, returns task_id immediately. Result must be obtained via polling.
2. How it works
  • Call submit() method
  • NaturalLanguageTask is created and pushed to global TASK_POOL
  • Background execution engine processes tasks automatically
  • Method returns task_id immediately (does NOT wait for completion)
  • Caller repeatedly queries get_task(task_id) to check status
  • When task.status == TaskStatus::Completed, extract result from task.final_output
3. Use when
  • You don't need immediate results, or want to run multiple tasks concurrently.
use hippox::{Hippox, TaskStatus};
use langhub::types::ModelProvider;
use std::time::Duration;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let hippox = Hippox::builder(ModelProvider::OpenAI)
        .api_key("sk-xxx")
        .build()
        .await?;
    // Submit task, returns task_id immediately
    let task_id = hippox.submit("Calculate 15 * 3", None);
    let result = hippox.wait_task(&task_id).await?;
    println!("Result: {}", result);
    Ok(())
}

Execute - Direct execution

1. Execution mode
  • Synchronous blocking call. The function waits until the task completes and returns the result directly.
2. How it works
  • Call execute() method
  • Task starts immediately in the current thread
  • Code pauses and waits for completion
  • Returns HippoxStringResult result directly
3. Use when
  • You need the result immediately and don't want to manage task state.
use hippox::Hippox;
use langhub::types::ModelProvider;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let hippox = Hippox::builder(ModelProvider::OpenAI)
        .api_key("sk-xxx")
        .build()
        .await?;
    // Execute and wait for result
    let result = hippox.execute("Calculate 15 * 3", None).await;
    println!("Result: {}", result);
    Ok(())
}

Image / Video / Audio Task

Hippox also exposes async submit/poll/download helpers for media generation.

Image Task

use hippox::Hippox;
use langhub::image::ImageModelProvider;
use langhub::types::ModelProvider;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let hippox = Hippox::builder(ModelProvider::OpenAI)
        .api_key("sk-xxx")
        .build()
        .await?;

    // 1. Submit image task (non-blocking, returns task info)
    let info = hippox
        .submit_image_task_info(
            ImageModelProvider::DallE,
            "sk-xxx".to_string(),
            "a cat sitting on a windowsill".to_string(),
            None,
            None,
            None,
        )
        .await?;
    let task_id = info.task_id.clone();
    let provider_task_id = info.provider_task_id.clone().unwrap_or_default();
    let created_at = info.created_at;

    // 2. Poll until terminal
    let mut current = info;
    while !current.state.is_terminal() {
        tokio::time::sleep(std::time::Duration::from_secs(2)).await;
        current = hippox
            .poll_image_task_info(
                ImageModelProvider::DallE,
                "sk-xxx".to_string(),
                provider_task_id.clone(),
                None,
                task_id.clone(),
                "a cat sitting on a windowsill".to_string(),
                created_at,
            )
            .await?;
    }

    // 3. Download produced image(s)
    if !current.download_urls.is_empty() {
        let downloaded = hippox
            .download_image_task(
                current.download_urls.clone(),
                "./output".to_string(),
                None,
                task_id,
                current.provider.clone(),
                current.prompt.clone(),
                created_at,
            )
            .await?;
        println!("Image saved to: {:?}", downloaded.local_paths);
    }

    Ok(())
}

Video Task

use hippox::Hippox;
use langhub::video::VideoModelProvider;
use langhub::types::ModelProvider;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let hippox = Hippox::builder(ModelProvider::OpenAI)
        .api_key("sk-xxx")
        .build()
        .await?;

    // 1. Submit video task
    let info = hippox
        .submit_video_task_info(
            VideoModelProvider::Seedance,
            "ark-key".to_string(),
            "a cat walking on the beach".to_string(),
            None,
            None,
            None,
        )
        .await?;
    let task_id = info.task_id.clone();
    let provider_task_id = info.provider_task_id.clone().unwrap_or_default();
    let created_at = info.created_at;

    // 2. Poll until terminal
    let mut current = info;
    while !current.state.is_terminal() {
        tokio::time::sleep(std::time::Duration::from_secs(2)).await;
        current = hippox
            .poll_video_task_info(
                VideoModelProvider::Seedance,
                "ark-key".to_string(),
                provider_task_id.clone(),
                None,
                task_id.clone(),
                "a cat walking on the beach".to_string(),
                created_at,
            )
            .await?;
    }

    // 3. Download produced video
    if let Some(url) = current.download_url.clone() {
        let downloaded = hippox
            .download_video_task(
                url,
                "./output".to_string(),
                None,
                task_id,
                current.provider.clone(),
                current.prompt.clone(),
                created_at,
            )
            .await?;
        println!("Video saved to: {:?}", downloaded.local_paths);
    }

    Ok(())
}

Audio Task

use hippox::Hippox;
use langhub::audio::AudioModelProvider;
use langhub::types::ModelProvider;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let hippox = Hippox::builder(ModelProvider::OpenAI)
        .api_key("sk-xxx")
        .build()
        .await?;

    // 1. Submit audio task
    let info = hippox
        .submit_audio_task_info(
            AudioModelProvider::QwenTts,
            "dashscope-key".to_string(),
            "Hello, world!".to_string(),
            None,
            None,
            None,
        )
        .await?;
    let task_id = info.task_id.clone();
    let provider_task_id = info.provider_task_id.clone().unwrap_or_default();
    let created_at = info.created_at;

    // 2. Poll until terminal
    let mut current = info;
    while !current.state.is_terminal() {
        tokio::time::sleep(std::time::Duration::from_secs(2)).await;
        current = hippox
            .poll_audio_task_info(
                AudioModelProvider::QwenTts,
                "dashscope-key".to_string(),
                provider_task_id.clone(),
                None,
                task_id.clone(),
                "Hello, world!".to_string(),
                created_at,
            )
            .await?;
    }

    // 3. Download produced audio (URL or base64)
    let downloaded = hippox
        .download_audio_task(
            current.download_url.clone(),
            None,
            current.format.clone(),
            "./output".to_string(),
            None,
            task_id,
            current.provider.clone(),
            current.prompt.clone(),
            created_at,
        )
        .await?;
    println!("Audio saved to: {:?}", downloaded.local_paths);

    Ok(())
}

Custom Drivers and Driver Classification

use hippox_drivers::{
    Driver, DriverCallback, DriverCategory, DriverContext, DriverError, DriverResult,
    DriverParameter, register_driver,
};
use serde_json::{json, Value};
use std::collections::HashMap;
use std::sync::Arc;

const CATEGORY_WEATHER: DriverCategory = DriverCategory::Custom("weather_ops");

#[derive(Debug)]
pub struct WeatherDriver;

#[async_trait::async_trait]
impl Driver for WeatherDriver {
    fn name(&self) -> &str { "weather_query" }
    fn description(&self) -> &str { "Query weather for a city" }
    fn category(&self) -> DriverCategory { CATEGORY_WEATHER }
    fn parameters(&self) -> Vec<DriverParameter> {
        vec![DriverParameter {
            name: "city".to_string(),
            param_type: "string".to_string(),
            description: "City name".to_string(),
            required: true,
            default: None,
            example: Some(Value::String("Beijing".to_string())),
            enum_values: None,
        }]
    }
    fn example_call(&self) -> DriverResult<Value> {
        Ok(json!({"action": "weather_query", "parameters": {"city": "Beijing"}}))
    }
    fn example_output(&self) -> String {
        "Weather for Beijing: 25Β°C, Sunny".to_string()
    }
    async fn execute(
        &self,
        parameters: &HashMap<String, Value>,
        _callback: Option<&dyn DriverCallback>,
        _context: Option<&DriverContext>,
    ) -> DriverResult<String> {
        let city = parameters.get("city").and_then(|v| v.as_str())
            .ok_or_else(|| DriverError::missing_parameter("city"))?;
        Ok(format!("Weather for {}: 25Β°C, Sunny", city))
    }
}

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    // Register custom driver
    register_driver(
        CATEGORY_WEATHER,
        "weather_query".to_string(),
        Arc::new(WeatherDriver),
    );
    // Verify: get driver and execute
    use hippox_drivers::get_driver_by_name;
    let driver = get_driver_by_name("weather_query").unwrap();
    let mut params = HashMap::new();
    params.insert("city".to_string(), json!("Shanghai"));
    let result = driver.execute(&params, None, None).await?;
    println!("{}", result); // Weather for Shanghai: 25Β°C, Sunny
    // Verify custom category is registered
    use hippox_drivers::get_all_categorys;
    println!("All categories: {:?}", get_all_categorys()); // includes "weather_ops"
    Ok(())
}

Configuration

1. HippoxConfig

/// Hippox global configuration
#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
pub struct HippoxConfig {
    /// Language setting: "en" or "zh"
    pub lang: String,
    /// AI identity information (name, role, personality, etc.)
    pub identity_information: IdentityInformation,
}

2. IdentityInformation

/// AI identity configuration
#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
pub struct IdentityInformation {
    /// AI name, e.g., "Assistant", "Hippox"
    pub name: Option<String>,
    /// Gender, e.g., "male", "female", "neutral"
    pub sex: Option<String>,
    /// Age, e.g., "25", "young"
    pub age: Option<String>,
    /// Species, e.g., "AI", "human", "robot"
    pub species: Option<String>,
    /// Role, e.g., "assistant", "teacher", "life coach"
    pub role: Option<String>,
    /// Personality, e.g., "friendly", "humorous", "professional"
    pub personality: Option<String>,
    /// Tone style, e.g., "casual", "formal", "poetic"
    pub tone_style: Option<String>,
    /// Knowledge scope, e.g., "general", "medical", "programming"
    pub knowledge_scope: Option<String>,
    /// Catchphrase, e.g., "Haha", "I see", "Let's go"
    pub catchphrase: Option<String>,
    /// Prohibited topics, e.g., "no politics", "no medical advice"
    pub taboos: Option<String>,
}

Hippox Core Working Principle

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                        Hippox Core Working Principle                        β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                                                             β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚  β”‚ 1. Task Submission (Non-blocking)                                     β”‚ β”‚
β”‚  β”‚    hippox.submit(input) β†’ NaturalLanguageTask β†’ TASK_POOL            β”‚ β”‚
β”‚  β”‚    β†’ TASK_NOTIFIER.notify_one() β†’ return task_id                     β”‚ β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β”‚                                      β”‚                                      β”‚
β”‚                                      β–Ό                                      β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚  β”‚ 2. Intent Analysis (Step 1)                                           β”‚ β”‚
β”‚  β”‚    build_intent_parser_prompt() β†’ LLM.generate() β†’ parse              β”‚ β”‚
β”‚  β”‚    Output: clean_intent, skill_categories                             β”‚ β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β”‚                                      β”‚                                      β”‚
β”‚                                      β–Ό                                      β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚  β”‚ 3. Workflow Execution (Step 2) using clean_intent                     β”‚ β”‚
β”‚  β”‚    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”        β”‚ β”‚
β”‚  β”‚    β”‚  ReAct   β”‚ β”‚  Batch   β”‚ β”‚  Chain   β”‚ β”‚ PlanAndExecute  β”‚        β”‚ β”‚
β”‚  β”‚    β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜        β”‚ β”‚
β”‚  β”‚         β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                   β”‚ β”‚
β”‚  β”‚    LLM generates SkillCall β†’ Executor.execute() β†’ raw_json           β”‚ β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β”‚                                      β”‚                                      β”‚
β”‚                                      β–Ό                                      β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚  β”‚ 4. Response Formatting (Step 3)                                       β”‚ β”‚
β”‚  β”‚    needs_format_conversion(original_input)?                           β”‚ β”‚
β”‚  β”‚    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”      β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚ β”‚
β”‚  β”‚    β”‚  false  β”‚ ──▢ β”‚  return raw_json directly                    β”‚  β”‚ β”‚
β”‚  β”‚    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜      β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚ β”‚
β”‚  β”‚    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”      β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚ β”‚
β”‚  β”‚    β”‚  true   β”‚ ──▢ β”‚  build_format_conversion_prompt()            β”‚  β”‚ β”‚
β”‚  β”‚    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜      β”‚  β†’ LLM.generate() β†’ formatted output        β”‚  β”‚ β”‚
β”‚  β”‚                     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚ β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β”‚                                      β”‚                                      β”‚
β”‚                                      β–Ό                                      β”‚
β”‚                              final_output                                   β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Pipe Line

User Input
    β”‚
    β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚   Step 1    β”‚ β†’ Intent Analysis
β”‚   Analysis  β”‚   build_intent_parser_prompt() β†’ LLM
β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜   Output: clean_intent, skill_categories
       β”‚
       β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚   Step 2    β”‚ β†’ Workflow Execution
β”‚  Execution  β”‚   Execute skills using clean_intent
β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜   Output: raw_json
       β”‚
       β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”      β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Need Format β”‚ ──Yes──▢│   Step 3    β”‚ β†’ Response Formatting
β”‚ Conversion? β”‚      β”‚ Formatting  β”‚   build_format_conversion_prompt() β†’ LLM
β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜      β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜
       β”‚                    β”‚
       β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                  β–Ό
            Final Output

Task Pool

State Machine

Pending ──► Running ──► Completed
    β”‚           β”‚
    β”‚           β”œβ”€β”€β–Ί Paused ──► Running (resume)
    β”‚           β”‚
    β”‚           └──► Cancelled
    β”‚
    └──► Cancelled
              β”‚
              └──► Failed ──► Pending (retry)

Architecture

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                      Global Static (Auto-Start)                 β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
β”‚  β”‚                    GLOBAL_TASK_POOL                        β”‚  β”‚
β”‚  β”‚              (Initialized at program load)                β”‚  β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                              β”‚
                              β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                        Hippox Instance                          β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
β”‚  β”‚                    TaskPool (Global)                       β”‚  β”‚
β”‚  β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”                   β”‚  β”‚
β”‚  β”‚  β”‚Task A   β”‚  β”‚Task B   β”‚  β”‚Task C   β”‚                   β”‚  β”‚
β”‚  β”‚  β”‚Pending  β”‚  β”‚Running  β”‚  β”‚Pending  β”‚                   β”‚  β”‚
β”‚  β”‚  β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”˜                   β”‚  β”‚
β”‚  β”‚       β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                         β”‚  β”‚
β”‚  β”‚                    β–Ό                                      β”‚  β”‚
β”‚  β”‚         β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                          β”‚  β”‚
β”‚  β”‚         β”‚    Priority Queue   β”‚                          β”‚  β”‚
β”‚  β”‚         β”‚  [Task A, Task C]   β”‚                          β”‚  β”‚
β”‚  β”‚         β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                          β”‚  β”‚
β”‚  β”‚                    β”‚                                      β”‚  β”‚
β”‚  β”‚                    β–Ό                                      β”‚  β”‚
β”‚  β”‚         β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                          β”‚  β”‚
β”‚  β”‚         β”‚  Execution Engine   β”‚  (max: 10 workers)      β”‚  β”‚
β”‚  β”‚         β”‚  β”Œβ”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”                         β”‚  β”‚
β”‚  β”‚         β”‚  β”‚ W1 β”‚ β”‚ W2 β”‚ β”‚ W3 β”‚  ...                    β”‚  β”‚
β”‚  β”‚         β”‚  β””β”€β”€β”¬β”€β”˜ β””β”€β”€β”¬β”€β”˜ β””β”€β”€β”¬β”€β”˜                         β”‚  β”‚
β”‚  β”‚         β”‚     β””β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”˜                           β”‚  β”‚
β”‚  β”‚         β”‚           β–Ό                                   β”‚  β”‚
β”‚  β”‚         β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                          β”‚  β”‚
β”‚  β”‚         β”‚  β”‚ ExecutableTask  β”‚                          β”‚  β”‚
β”‚  β”‚         β”‚  β”‚   .execute()    β”‚                          β”‚  β”‚
β”‚  β”‚         β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                          β”‚  β”‚
β”‚  β”‚         β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                          β”‚  β”‚
β”‚  β”‚                    β–²                                      β”‚  β”‚
β”‚  β”‚         β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                          β”‚  β”‚
β”‚  β”‚         β”‚  Notifier (wakeup)  β”‚                          β”‚  β”‚
β”‚  β”‚         β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                          β”‚  β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
β”‚                                                                  β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
β”‚  β”‚              ExecutableTask Implementation                β”‚  β”‚
β”‚  β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚  β”‚
β”‚  β”‚  β”‚                  NaturalLanguageTask                β”‚  β”‚  β”‚
β”‚  β”‚  β”‚  β€’ input: String                                    β”‚  β”‚  β”‚
β”‚  β”‚  β”‚  β€’ workflow_executor: WorkflowExecutor              β”‚  β”‚  β”‚
β”‚  β”‚  β”‚  β€’ scheduler: SkillScheduler                        β”‚  β”‚  β”‚
β”‚  β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚  β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                              β”‚
                              β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                         External APIs                           β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚  handle_natural_language()  β†’ task_id  (non-blocking)          β”‚
β”‚  get_task_status() / cancel() / pause() / resume() / retry()   β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Workflow Model

Mode Enum Value Core Features LLM Calls Use Cases
ReAct WorkflowMode::ReAct Think β†’ Act β†’ Observe loop, LLM decides next step after each execution 1 per skill + 1 final response Open-ended tasks, dynamic decision making, error recovery
Batch WorkflowMode::Batch Execute multiple independent skills in parallel 1 (generates batch plan) Independent operations, bulk processing
Chain WorkflowMode::Chain Sequential execution with variable passing ({{variable}} syntax) 1 (generates chain) Linear pipelines, data transformation chains
PlanAndExecute WorkflowMode::PlanAndExecute One-time planning with conditional branching, variable references ({"$ref":"var"}), error handling (retry/skip/fail) 1 plan + optional dynamic decisions Complex workflows, conditional logic, deterministic tasks

Workflow Atomic Skill Retry Strategy

Atomic Skill Registry

πŸ’‘ Hint:In Hippox, an atomic skill represents a smallest indivisible unit of execution, This is a different concept from "Skill" in user business.

Working Principle

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                      SKILL REGISTRY                        β”‚
β”‚                                                           β”‚
β”‚  SkillRegistryMap = HashMap<SkillCategory,               β”‚
β”‚                      HashMap<String, Arc<dyn Skill>>>    β”‚
β”‚                                                           β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”              β”‚
β”‚  β”‚ File     β”‚  β”‚ Math     β”‚  β”‚ Net      β”‚              β”‚
β”‚  β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€  β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€  β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€              β”‚
β”‚  β”‚ read     β”‚  β”‚ calc     β”‚  β”‚ http     β”‚              β”‚
β”‚  β”‚ write    β”‚  β”‚ power    β”‚  β”‚ ping     β”‚              β”‚
β”‚  β”‚ delete   β”‚  β”‚ stats    β”‚  β”‚ dns      β”‚              β”‚
β”‚  β”‚ ...      β”‚  β”‚ ...      β”‚  β”‚ ...      β”‚              β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜              β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Registration:

  Compile-time: file_register() / math_register() / net_register()
  Runtime: register_skill(category, name, skill)

Query:

  get_skill_by_name("read") β†’ Skill impl β†’ execute()

Core Type

pub type SkillRegistryMap = HashMap<SkillCategory, HashMap<String, Arc<dyn Skill>>>;

Main Functions

Function Description
get_registry() Get read lock on the registry
get_registry_mut() Get write lock on the registry
register_skill(category, name, skill) Dynamically register a skill
get_all_skills() Get all registered skills
get_skill_by_name(name) Find a skill by name
get_skill_by_name_and_category(name, category) Find a skill by name and category
has_skill(name) Check if a skill exists
list_skills_names() List all skill names
list_skills_name_by_category(category) List skill names in a category
get_skills_by_category(category) Get skills by category string
get_skills_by_category_list(categories) Get skills by multiple categories
list_skills_name_by_category_list(categories) List skill names by multiple categories
get_all_categorys() Get all category names
get_skill_category() Get categories with skill counts
get_skill_category_names() Get all category names
get_skill_category_name_and_describe() Get category names with descriptions
generate_skill_registry_table_json_str() Generate registry JSON string

SkillCategory Methods

Method Description
from_str(s) Convert string to enum
name() Convert enum to string (lowercase)
display_name() Get human-readable display name
description() Get category description
icon() Get category icon/emoji
priority() Get display priority (lower = first)
metadata() Get complete category metadata
all_categories() Get metadata for all categories