use std::collections::BTreeMap;
use serde::{Deserialize, Serialize};
use serde_json::Value;
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct ParamDef {
#[serde(rename = "type")]
pub param_type: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub format: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub required: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub default: Option<Value>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct RetryPolicy {
pub max_attempts: u64,
pub backoff: String,
pub base_ms: u64,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub factor: Option<f64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub jitter: Option<bool>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct HumanGate {
pub stage: String,
pub prompt: Value,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub approvers: Option<Vec<String>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub deadline_ms: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub on_timeout: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct LoopTerminate {
pub node: String,
pub via: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct LoopOutputProjection {
pub from: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct NodeBase {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub depends: Option<Vec<String>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub human_gate: Option<HumanGate>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub retry_policy: Option<RetryPolicy>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub timeout_ms: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub max_output_bytes: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub output_schema: Option<Value>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub unsafe_allow_ungated: Option<bool>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct SubagentNode {
#[serde(flatten)]
pub base: NodeBase,
pub bot: String,
pub prompt: Value,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub working_dir: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub model_overrides: Option<Value>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub tool_policy: Option<Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct HostExecutorNode {
#[serde(flatten)]
pub base: NodeBase,
pub executor: String,
pub input: Value,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct LoopNode {
#[serde(flatten)]
pub base: NodeBase,
pub max_iterations: u64,
pub body: Vec<String>,
pub terminate: LoopTerminate,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub output: Option<LoopOutputProjection>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct DecisionNode {
#[serde(flatten)]
pub base: NodeBase,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(tag = "type", rename_all = "camelCase")]
pub enum WorkflowNode {
Subagent(SubagentNode),
HostExecutor(HostExecutorNode),
Loop(LoopNode),
Decision(DecisionNode),
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct WorkflowDefaults {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub retry_policy: Option<RetryPolicy>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub timeout_ms: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub max_output_bytes: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub max_concurrency: Option<u64>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct WorkflowDefinition {
pub workflow_id: String,
pub version: u64,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub params: Option<BTreeMap<String, ParamDef>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub defaults: Option<WorkflowDefaults>,
pub nodes: BTreeMap<String, WorkflowNode>,
}