1use std::collections::HashMap;
14
15use indexmap::IndexMap;
16
17use super::{
18 Component, ErrorAction, ExampleInput, Flow, FlowSchema, JsonPath, Step, TestConfig,
19 VariableSchema,
20};
21use crate::{ValueExpr, schema::SchemaRef};
22
23#[derive(Default)]
25pub struct FlowBuilder {
26 name: Option<String>,
27 description: Option<String>,
28 version: Option<String>,
29 input_schema: Option<SchemaRef>,
30 output_schema: Option<SchemaRef>,
31 steps: Vec<Step>,
32 output: Option<ValueExpr>,
33 variables: Option<VariableSchema>,
34 test: Option<TestConfig>,
35 examples: Option<Vec<ExampleInput>>,
36 metadata: HashMap<String, serde_json::Value>,
37}
38
39impl FlowBuilder {
40 pub fn new() -> Self {
42 Default::default()
43 }
44
45 pub fn name<S: Into<String>>(mut self, name: S) -> Self {
47 self.name = Some(name.into());
48 self
49 }
50
51 pub fn description<S: Into<String>>(mut self, desc: S) -> Self {
53 self.description = Some(desc.into());
54 self
55 }
56
57 pub fn version<S: Into<String>>(mut self, version: S) -> Self {
59 self.version = Some(version.into());
60 self
61 }
62
63 pub fn input_schema(mut self, schema: SchemaRef) -> Self {
65 self.input_schema = Some(schema);
66 self
67 }
68
69 pub fn output_schema(mut self, schema: SchemaRef) -> Self {
71 self.output_schema = Some(schema);
72 self
73 }
74
75 pub fn step(mut self, step: Step) -> Self {
77 self.steps.push(step);
78 self
79 }
80
81 pub fn steps<I: IntoIterator<Item = Step>>(mut self, steps: I) -> Self {
83 self.steps.extend(steps);
84 self
85 }
86
87 pub fn output(mut self, output: ValueExpr) -> Self {
89 self.output = Some(output);
90 self
91 }
92
93 pub fn variables(mut self, variables: VariableSchema) -> Self {
95 self.variables = Some(variables);
96 self
97 }
98
99 pub fn test_config(mut self, test: TestConfig) -> Self {
101 self.test = Some(test);
102 self
103 }
104
105 pub fn examples(mut self, examples: Vec<ExampleInput>) -> Self {
107 self.examples = Some(examples);
108 self
109 }
110
111 pub fn metadata<S: Into<String>>(mut self, key: S, value: serde_json::Value) -> Self {
113 self.metadata.insert(key.into(), value);
114 self
115 }
116
117 pub fn test_flow() -> Self {
119 Self::new().name("test_workflow")
120 }
121
122 pub fn mock_flow() -> Self {
124 Self::new()
125 .name("mock_flow")
126 .description("A test flow for mocking")
127 }
128
129 pub fn build(self) -> Flow {
131 Flow {
132 name: self.name,
133 description: self.description,
134 version: self.version,
135 schemas: FlowSchema {
136 defs: HashMap::new(),
137 input: self.input_schema,
138 output: self.output_schema,
139 variables: self.variables.map(SchemaRef::from),
140 steps: IndexMap::new(),
141 },
142 steps: self.steps,
143 output: self.output.unwrap_or_default(),
144 test: self.test,
145 examples: self.examples,
146 metadata: self.metadata,
147 }
148 }
149}
150
151pub struct StepBuilder {
153 id: Option<String>,
154 component: Option<Component>,
155 input: Option<ValueExpr>,
156 on_error: Option<ErrorAction>,
157 must_execute: Option<bool>,
158 metadata: HashMap<String, serde_json::Value>,
159}
160
161impl StepBuilder {
162 pub fn new<S: Into<String>>(id: S) -> Self {
164 Self {
165 id: Some(id.into()),
166 component: None,
167 input: None,
168 on_error: None,
169 must_execute: None,
170 metadata: HashMap::new(),
171 }
172 }
173
174 pub fn component<S: Into<String>>(mut self, component: S) -> Self {
176 self.component = Some(Component::from_string(component.into()));
177 self
178 }
179
180 pub fn input(mut self, input: ValueExpr) -> Self {
182 self.input = Some(input);
183 self
184 }
185
186 pub fn input_json(mut self, input: serde_json::Value) -> Self {
188 self.input = Some(serde_json::from_value(input).unwrap());
189 self
190 }
191
192 pub fn input_literal(mut self, input: serde_json::Value) -> Self {
194 self.input = Some(ValueExpr::literal(input));
195 self
196 }
197
198 pub fn on_error(mut self, action: ErrorAction) -> Self {
200 self.on_error = Some(action);
201 self
202 }
203
204 pub fn must_execute(mut self, must_execute: bool) -> Self {
206 self.must_execute = Some(must_execute);
207 self
208 }
209
210 pub fn metadata<S: Into<String>>(mut self, key: S, value: serde_json::Value) -> Self {
212 self.metadata.insert(key.into(), value);
213 self
214 }
215
216 pub fn mock_step<S: Into<String>>(id: S) -> Self {
218 Self::new(id).component("/mock/test")
219 }
220
221 pub fn builtin_step<S: Into<String>>(id: S, component: S) -> Self {
223 Self::new(id).component(format!("/builtin/{}", component.into()))
224 }
225
226 pub fn step_ref<S: Into<String>>(id: S, ref_step: S) -> Self {
228 Self::new(id)
229 .component("/mock/test")
230 .input(ValueExpr::step_output(ref_step))
231 }
232
233 pub fn workflow_input<S: Into<String>>(id: S) -> Self {
235 Self::new(id)
236 .component("/mock/test")
237 .input(ValueExpr::workflow_input(JsonPath::default()))
238 }
239
240 pub fn build(self) -> Step {
242 Step {
243 id: self.id.expect("Step ID is required"),
244 component: self
245 .component
246 .unwrap_or_else(|| Component::from_string("/mock/test")),
247 input: self.input.unwrap_or_else(ValueExpr::null),
248 on_error: self.on_error,
249 must_execute: self.must_execute,
250 metadata: self.metadata,
251 }
252 }
253}
254
255impl Flow {
256 pub fn builder() -> FlowBuilder {
258 FlowBuilder::new()
259 }
260}
261
262impl Step {
263 pub fn builder<S: Into<String>>(id: S) -> StepBuilder {
265 StepBuilder::new(id)
266 }
267}
268
269pub fn mock_flow() -> FlowBuilder {
271 FlowBuilder::mock_flow()
272}
273
274pub fn test_flow() -> FlowBuilder {
276 FlowBuilder::test_flow()
277}
278
279pub fn mock_step<S: Into<String>>(id: S) -> StepBuilder {
281 StepBuilder::mock_step(id)
282}
283
284pub fn builtin_step<S: Into<String>>(id: S, component: S) -> StepBuilder {
286 StepBuilder::builtin_step(id, component)
287}
288
289#[cfg(test)]
290mod tests {
291 use super::*;
292 use serde_json::json;
293
294 #[test]
295 fn test_flow_builder_basic() {
296 let flow = FlowBuilder::new()
297 .name("test")
298 .description("A test flow")
299 .build();
300
301 assert_eq!(flow.name, Some("test".to_string()));
302 assert_eq!(flow.description, Some("A test flow".to_string()));
303 assert!(flow.steps.is_empty());
304 }
305
306 #[test]
307 fn test_flow_builder_with_steps() {
308 let step1 = StepBuilder::mock_step("step1")
309 .input_literal(json!({"value": 42}))
310 .build();
311
312 let step2 = StepBuilder::step_ref("step2", "step1").build();
313
314 let flow = FlowBuilder::test_flow()
315 .description("Test with steps")
316 .step(step1)
317 .step(step2)
318 .output(ValueExpr::step_output("step2"))
319 .build();
320
321 assert_eq!(flow.name, Some("test_workflow".to_string()));
322 assert_eq!(flow.steps.len(), 2);
323 assert_eq!(flow.steps[0].id, "step1");
324 assert_eq!(flow.steps[1].id, "step2");
325 }
326
327 #[test]
328 fn test_step_builder_basic() {
329 let step = StepBuilder::new("test_step")
330 .component("/test/component")
331 .input_literal(json!({"key": "value"}))
332 .build();
333
334 assert_eq!(step.id, "test_step");
335 assert_eq!(step.component.path(), "/test/component");
336 assert_eq!(step.on_error, None);
337 assert_eq!(step.on_error_or_default(), ErrorAction::Fail);
338 }
339
340 #[test]
341 fn test_step_builder_convenience_methods() {
342 let mock_step = StepBuilder::mock_step("mock1").build();
343 assert_eq!(mock_step.component.path(), "/mock/test");
344
345 let builtin_step = StepBuilder::builtin_step("builtin1", "openai").build();
346 assert_eq!(builtin_step.component.path(), "/builtin/openai");
347
348 let workflow_input_step = StepBuilder::workflow_input("input1").build();
349 assert_eq!(workflow_input_step.component.path(), "/mock/test");
350
351 let step_ref = StepBuilder::step_ref("ref1", "step1").build();
352 assert_eq!(step_ref.component.path(), "/mock/test");
353 }
354
355 #[test]
356 fn test_convenience_functions() {
357 let flow = mock_flow().step(mock_step("step1").build()).build();
358
359 assert_eq!(flow.name, Some("mock_flow".to_string()));
360 assert_eq!(flow.steps.len(), 1);
361 }
362}