use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use uuid::Uuid;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Recipe {
pub id: String,
pub name: String,
pub description: String,
pub version: String,
pub steps: Vec<Step>,
pub created_at: DateTime<Utc>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub metadata: Option<serde_json::Value>,
}
impl Recipe {
pub fn new(name: impl Into<String>, steps: Vec<Step>) -> Self {
Self {
id: Uuid::new_v4().to_string(),
name: name.into(),
description: String::new(),
version: "0.1.0".into(),
steps,
created_at: Utc::now(),
metadata: None,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Step {
pub id: String,
pub name: String,
pub kind: StepKind,
#[serde(skip_serializing_if = "Option::is_none")]
pub agent: Option<String>,
#[serde(default)]
pub parallel: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub timeout: Option<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub depends_on: Vec<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub retry: Option<RetryPolicy>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub notify: Vec<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub config: Option<serde_json::Value>,
}
impl Step {
pub fn agent(name: impl Into<String>, agent_ref: impl Into<String>) -> Self {
Self {
id: Uuid::new_v4().to_string(),
name: name.into(),
kind: StepKind::Agent,
agent: Some(agent_ref.into()),
parallel: false,
timeout: None,
depends_on: Vec::new(),
retry: None,
notify: Vec::new(),
config: None,
}
}
pub fn human_gate(name: impl Into<String>, notify: Vec<String>) -> Self {
Self {
id: Uuid::new_v4().to_string(),
name: name.into(),
kind: StepKind::HumanGate,
agent: None,
parallel: false,
timeout: None,
depends_on: Vec::new(),
retry: None,
notify,
config: None,
}
}
pub fn evaluator(name: impl Into<String>, agent_ref: impl Into<String>) -> Self {
Self {
id: Uuid::new_v4().to_string(),
name: name.into(),
kind: StepKind::Evaluator,
agent: Some(agent_ref.into()),
parallel: false,
timeout: None,
depends_on: Vec::new(),
retry: None,
notify: Vec::new(),
config: None,
}
}
pub fn with_parallel(mut self) -> Self {
self.parallel = true;
self
}
pub fn with_timeout(mut self, timeout: impl Into<String>) -> Self {
self.timeout = Some(timeout.into());
self
}
pub fn with_depends_on(mut self, deps: Vec<String>) -> Self {
self.depends_on = deps;
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "kebab-case")]
pub enum StepKind {
Agent,
HumanGate,
Evaluator,
Condition,
FanOut,
FanIn,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RetryPolicy {
pub max_retries: u32,
pub delay_seconds: u32,
#[serde(default)]
pub exponential_backoff: bool,
}