Skip to main content

cruxx_script/
schema.rs

1/// YAML schema types for pipeline definitions.
2use serde::Deserialize;
3
4#[derive(Debug, Clone, Deserialize)]
5pub struct PipelineDef {
6    pub pipeline: String,
7    #[serde(default)]
8    pub budget: Option<BudgetDef>,
9    pub steps: Vec<StepDef>,
10}
11
12/// An arm or stage in a join_all or pipe — either a bare handler name string,
13/// or a full step object with `step`, optional `handler`, and optional `args`.
14#[derive(Debug, Clone, Deserialize)]
15#[serde(untagged)]
16pub enum ArmDef {
17    /// Bare string: the name is both the step label and the handler name.
18    Name(String),
19    /// Full step object: `step` is the label, `handler` overrides the name, `args` injected.
20    Step {
21        step: String,
22        #[serde(default)]
23        handler: Option<String>,
24        #[serde(default)]
25        args: Option<serde_json::Value>,
26    },
27}
28
29impl ArmDef {
30    /// The label used in traces (step name).
31    pub fn label(&self) -> &str {
32        match self {
33            ArmDef::Name(n) => n,
34            ArmDef::Step { step, .. } => step,
35        }
36    }
37
38    /// The handler name to look up in the registry.
39    pub fn handler_name(&self) -> &str {
40        match self {
41            ArmDef::Name(n) => n,
42            ArmDef::Step { step, handler, .. } => handler.as_deref().unwrap_or(step),
43        }
44    }
45
46    /// Optional static args to inject into the handler input.
47    pub fn args(&self) -> Option<&serde_json::Value> {
48        match self {
49            ArmDef::Name(_) => None,
50            ArmDef::Step { args, .. } => args.as_ref(),
51        }
52    }
53}
54
55#[derive(Debug, Clone, Deserialize)]
56#[serde(untagged)]
57pub enum BudgetDef {
58    Tokens { tokens: u64 },
59    Calls { calls: u64 },
60    Duration { duration_ms: u64 },
61    CostCents { cost_cents: u64 },
62}
63
64#[derive(Debug, Clone, Deserialize)]
65#[serde(untagged)]
66pub enum StepDef {
67    Step(StepNode),
68    Delegate(DelegateNode),
69    Pipe(PipeNode),
70    JoinAll(JoinAllNode),
71    RouteOnConfidence(RouteNode),
72    Speculate(SpeculateNode),
73}
74
75#[derive(Debug, Clone, Deserialize)]
76pub struct StepNode {
77    pub step: String,
78    #[serde(default)]
79    pub handler: Option<String>,
80    #[serde(default)]
81    pub args: Option<serde_json::Value>,
82}
83
84#[derive(Debug, Clone, Deserialize)]
85pub struct DelegateNode {
86    pub delegate: String,
87    #[serde(default)]
88    pub name: Option<String>,
89    #[serde(default)]
90    pub budget: Option<BudgetDef>,
91}
92
93#[derive(Debug, Clone, Deserialize)]
94pub struct PipeNode {
95    pub pipe: String,
96    pub stages: Vec<ArmDef>,
97}
98
99#[derive(Debug, Clone, Deserialize)]
100pub struct JoinAllNode {
101    pub join_all: String,
102    pub arms: Vec<ArmDef>,
103}
104
105#[derive(Debug, Clone, Deserialize)]
106pub struct RouteNode {
107    pub route_on_confidence: String,
108    pub value: String,
109    pub routes: Vec<RouteBranch>,
110}
111
112#[derive(Debug, Clone, Deserialize)]
113pub struct RouteBranch {
114    pub range: String,
115    pub label: String,
116    pub handler: String,
117    #[serde(default)]
118    pub args: Option<serde_json::Value>,
119}
120
121#[derive(Debug, Clone, Deserialize)]
122pub struct SpeculateNode {
123    pub speculate: String,
124    #[serde(default = "default_speculate_mode")]
125    pub mode: SpeculateMode,
126    pub arms: Vec<ArmDef>,
127}
128
129#[derive(Debug, Clone, Deserialize, Default)]
130#[serde(rename_all = "snake_case")]
131pub enum SpeculateMode {
132    #[default]
133    PickBest,
134    FirstOk,
135}
136
137fn default_speculate_mode() -> SpeculateMode {
138    SpeculateMode::PickBest
139}