1use std::collections::HashMap;
2use crate::types::{Value, Duration, TimeUnit};
3#[derive(Debug, Clone)]
4pub struct HelixAst {
5 pub declarations: Vec<Declaration>,
6}
7#[derive(Debug, Clone)]
8pub enum Declaration {
9 Project(ProjectDecl),
10 Agent(AgentDecl),
11 Workflow(WorkflowDecl),
12 Memory(MemoryDecl),
13 Context(ContextDecl),
14 Crew(CrewDecl),
15 Pipeline(PipelineDecl),
16 Plugin(PluginDecl),
17 Database(DatabaseDecl),
18 Load(LoadDecl),
19}
20#[derive(Debug, Clone)]
21pub struct ProjectDecl {
22 pub name: String,
23 pub properties: HashMap<String, Expression>,
24}
25#[derive(Debug, Clone)]
26pub struct AgentDecl {
27 pub name: String,
28 pub properties: HashMap<String, Expression>,
29 pub capabilities: Option<Vec<String>>,
30 pub backstory: Option<BackstoryBlock>,
31 pub tools: Option<Vec<String>>,
32}
33#[derive(Debug, Clone)]
34pub struct WorkflowDecl {
35 pub name: String,
36 pub trigger: Option<Expression>,
37 pub steps: Vec<StepDecl>,
38 pub pipeline: Option<PipelineDecl>,
39 pub properties: HashMap<String, Expression>,
40}
41#[derive(Debug, Clone)]
42pub struct StepDecl {
43 pub name: String,
44 pub agent: Option<String>,
45 pub crew: Option<Vec<String>>,
46 pub task: Option<String>,
47 pub properties: HashMap<String, Expression>,
48}
49#[derive(Debug, Clone)]
50pub struct PipelineDecl {
51 pub flow: Vec<PipelineNode>,
52}
53#[derive(Debug, Clone)]
54pub enum PipelineNode {
55 Step(String),
56 Parallel(Vec<PipelineNode>),
57 Conditional {
58 condition: Expression,
59 then_branch: Box<PipelineNode>,
60 else_branch: Option<Box<PipelineNode>>,
61 },
62}
63#[derive(Debug, Clone)]
64pub struct MemoryDecl {
65 pub provider: String,
66 pub connection: String,
67 pub embeddings: Option<EmbeddingsDecl>,
68 pub properties: HashMap<String, Expression>,
69}
70#[derive(Debug, Clone)]
71pub struct EmbeddingsDecl {
72 pub model: String,
73 pub dimensions: u32,
74 pub properties: HashMap<String, Expression>,
75}
76#[derive(Debug, Clone)]
77pub struct ContextDecl {
78 pub name: String,
79 pub environment: String,
80 pub secrets: Option<HashMap<String, SecretRef>>,
81 pub variables: Option<HashMap<String, Expression>>,
82 pub properties: HashMap<String, Expression>,
83}
84#[derive(Debug, Clone)]
85pub struct CrewDecl {
86 pub name: String,
87 pub agents: Vec<String>,
88 pub process_type: Option<String>,
89 pub properties: HashMap<String, Expression>,
90}
91#[derive(Debug, Clone)]
92pub struct PluginDecl {
93 pub name: String,
94 pub source: String,
95 pub version: Option<String>,
96 pub config: HashMap<String, Expression>,
97}
98#[derive(Debug, Clone)]
99pub struct DatabaseDecl {
100 pub name: String,
101 pub path: Option<String>,
102 pub shards: Option<i64>,
103 pub compression: Option<bool>,
104 pub cache_size: Option<i64>,
105 pub vector_index: Option<VectorIndexConfig>,
106 pub properties: HashMap<String, Expression>,
107}
108#[derive(Debug, Clone)]
109pub struct VectorIndexConfig {
110 pub index_type: String,
111 pub dimensions: i64,
112 pub m: Option<i64>,
113 pub ef_construction: Option<i64>,
114 pub distance_metric: Option<String>,
115 pub properties: HashMap<String, Expression>,
116}
117#[derive(Debug, Clone)]
118pub struct LoadDecl {
119 pub file_name: String,
120 pub properties: HashMap<String, Expression>,
121}
122#[derive(Debug, Clone)]
123pub struct BackstoryBlock {
124 pub lines: Vec<String>,
125}
126#[derive(Debug, Clone)]
127pub enum SecretRef {
128 Environment(String),
129 Vault(String),
130 File(String),
131}
132#[derive(Debug, Clone)]
133pub enum Expression {
134 String(String),
135 Number(f64),
136 Bool(bool),
137 Duration(Duration),
138 Array(Vec<Expression>),
139 Object(HashMap<String, Expression>),
140 Variable(String),
141 Reference(String),
142 IndexedReference(String, String),
143 Identifier(String),
144 Pipeline(Vec<String>),
145 Block(Vec<Statement>),
146 TextBlock(Vec<String>),
147}
148#[derive(Debug, Clone)]
149pub enum Statement {
150 Assignment(String, Expression),
151 Declaration(Declaration),
152 Expression(Expression),
153}
154impl HelixAst {
155 pub fn new() -> Self {
156 HelixAst {
157 declarations: Vec::new(),
158 }
159 }
160 pub fn add_declaration(&mut self, decl: Declaration) {
161 self.declarations.push(decl);
162 }
163 pub fn get_projects(&self) -> Vec<&ProjectDecl> {
164 self.declarations
165 .iter()
166 .filter_map(|d| {
167 if let Declaration::Project(p) = d { Some(p) } else { None }
168 })
169 .collect()
170 }
171 pub fn get_agents(&self) -> Vec<&AgentDecl> {
172 self.declarations
173 .iter()
174 .filter_map(|d| {
175 if let Declaration::Agent(a) = d { Some(a) } else { None }
176 })
177 .collect()
178 }
179 pub fn get_workflows(&self) -> Vec<&WorkflowDecl> {
180 self.declarations
181 .iter()
182 .filter_map(|d| {
183 if let Declaration::Workflow(w) = d { Some(w) } else { None }
184 })
185 .collect()
186 }
187 pub fn get_contexts(&self) -> Vec<&ContextDecl> {
188 self.declarations
189 .iter()
190 .filter_map(|d| {
191 if let Declaration::Context(c) = d { Some(c) } else { None }
192 })
193 .collect()
194 }
195}
196#[allow(dead_code)]
197pub trait AstVisitor {
198 type Result;
199 fn visit_ast(&mut self, ast: &HelixAst) -> Self::Result;
200 fn visit_declaration(&mut self, decl: &Declaration) -> Self::Result;
201 fn visit_project(&mut self, project: &ProjectDecl) -> Self::Result;
202 fn visit_agent(&mut self, agent: &AgentDecl) -> Self::Result;
203 fn visit_workflow(&mut self, workflow: &WorkflowDecl) -> Self::Result;
204 fn visit_memory(&mut self, memory: &MemoryDecl) -> Self::Result;
205 fn visit_context(&mut self, context: &ContextDecl) -> Self::Result;
206 fn visit_crew(&mut self, crew: &CrewDecl) -> Self::Result;
207 fn visit_expression(&mut self, expr: &Expression) -> Self::Result;
208}
209pub struct AstPrettyPrinter {
210 indent: usize,
211 indent_str: String,
212}
213impl AstPrettyPrinter {
214 pub fn new() -> Self {
215 AstPrettyPrinter {
216 indent: 0,
217 indent_str: " ".to_string(),
218 }
219 }
220 fn write_indent(&self) -> String {
221 self.indent_str.repeat(self.indent)
222 }
223 pub fn print(&mut self, ast: &HelixAst) -> String {
224 let mut result = String::new();
225 result.push_str("# HELIX Language AST\n\n");
226 for decl in &ast.declarations {
227 result.push_str(&self.print_declaration(decl));
228 result.push_str("\n");
229 }
230 result
231 }
232 fn print_declaration(&mut self, decl: &Declaration) -> String {
233 match decl {
234 Declaration::Project(p) => self.print_project(p),
235 Declaration::Agent(a) => self.print_agent(a),
236 Declaration::Workflow(w) => self.print_workflow(w),
237 Declaration::Memory(m) => self.print_memory(m),
238 Declaration::Context(c) => self.print_context(c),
239 Declaration::Crew(cr) => self.print_crew(cr),
240 Declaration::Pipeline(p) => self.print_pipeline(p),
241 Declaration::Plugin(p) => self.print_plugin(p),
242 Declaration::Database(d) => self.print_database(d),
243 Declaration::Load(l) => self.print_load(l),
244 }
245 }
246 fn print_project(&mut self, project: &ProjectDecl) -> String {
247 let mut result = format!(
248 "{}project \"{}\" {{\n", self.write_indent(), project.name
249 );
250 self.indent += 1;
251 for (key, value) in &project.properties {
252 result
253 .push_str(
254 &format!(
255 "{}{} = {}\n", self.write_indent(), key, self
256 .print_expression(value)
257 ),
258 );
259 }
260 self.indent -= 1;
261 result.push_str(&format!("{}}}\n", self.write_indent()));
262 result
263 }
264 fn print_agent(&mut self, agent: &AgentDecl) -> String {
265 let mut result = format!("{}agent \"{}\" {{\n", self.write_indent(), agent.name);
266 self.indent += 1;
267 for (key, value) in &agent.properties {
268 result
269 .push_str(
270 &format!(
271 "{}{} = {}\n", self.write_indent(), key, self
272 .print_expression(value)
273 ),
274 );
275 }
276 if let Some(capabilities) = &agent.capabilities {
277 result.push_str(&format!("{}capabilities [\n", self.write_indent()));
278 self.indent += 1;
279 for cap in capabilities {
280 result.push_str(&format!("{}\"{}\"\n", self.write_indent(), cap));
281 }
282 self.indent -= 1;
283 result.push_str(&format!("{}]\n", self.write_indent()));
284 }
285 if let Some(backstory) = &agent.backstory {
286 result.push_str(&format!("{}backstory {{\n", self.write_indent()));
287 self.indent += 1;
288 for line in &backstory.lines {
289 result.push_str(&format!("{}{}\n", self.write_indent(), line));
290 }
291 self.indent -= 1;
292 result.push_str(&format!("{}}}\n", self.write_indent()));
293 }
294 self.indent -= 1;
295 result.push_str(&format!("{}}}\n", self.write_indent()));
296 result
297 }
298 fn print_workflow(&mut self, workflow: &WorkflowDecl) -> String {
299 let mut result = format!(
300 "{}workflow \"{}\" {{\n", self.write_indent(), workflow.name
301 );
302 self.indent += 1;
303 if let Some(trigger) = &workflow.trigger {
304 result
305 .push_str(
306 &format!(
307 "{}trigger = {}\n", self.write_indent(), self
308 .print_expression(trigger)
309 ),
310 );
311 }
312 for step in &workflow.steps {
313 result.push_str(&self.print_step(step));
314 }
315 if let Some(pipeline) = &workflow.pipeline {
316 result.push_str(&self.print_pipeline(pipeline));
317 }
318 for (key, value) in &workflow.properties {
319 result
320 .push_str(
321 &format!(
322 "{}{} = {}\n", self.write_indent(), key, self
323 .print_expression(value)
324 ),
325 );
326 }
327 self.indent -= 1;
328 result.push_str(&format!("{}}}\n", self.write_indent()));
329 result
330 }
331 fn print_step(&mut self, step: &StepDecl) -> String {
332 let mut result = format!("{}step \"{}\" {{\n", self.write_indent(), step.name);
333 self.indent += 1;
334 if let Some(agent) = &step.agent {
335 result.push_str(&format!("{}agent = \"{}\"\n", self.write_indent(), agent));
336 }
337 if let Some(crew) = &step.crew {
338 result.push_str(&format!("{}crew = [", self.write_indent()));
339 result
340 .push_str(
341 &crew
342 .iter()
343 .map(|c| format!("\"{}\"", c))
344 .collect::<Vec<_>>()
345 .join(", "),
346 );
347 result.push_str("]\n");
348 }
349 if let Some(task) = &step.task {
350 result.push_str(&format!("{}task = \"{}\"\n", self.write_indent(), task));
351 }
352 for (key, value) in &step.properties {
353 result
354 .push_str(
355 &format!(
356 "{}{} = {}\n", self.write_indent(), key, self
357 .print_expression(value)
358 ),
359 );
360 }
361 self.indent -= 1;
362 result.push_str(&format!("{}}}\n", self.write_indent()));
363 result
364 }
365 fn print_memory(&mut self, memory: &MemoryDecl) -> String {
366 let mut result = format!("{}memory {{\n", self.write_indent());
367 self.indent += 1;
368 result
369 .push_str(
370 &format!("{}provider = \"{}\"\n", self.write_indent(), memory.provider),
371 );
372 result
373 .push_str(
374 &format!(
375 "{}connection = \"{}\"\n", self.write_indent(), memory.connection
376 ),
377 );
378 if let Some(embeddings) = &memory.embeddings {
379 result.push_str(&self.print_embeddings(embeddings));
380 }
381 for (key, value) in &memory.properties {
382 result
383 .push_str(
384 &format!(
385 "{}{} = {}\n", self.write_indent(), key, self
386 .print_expression(value)
387 ),
388 );
389 }
390 self.indent -= 1;
391 result.push_str(&format!("{}}}\n", self.write_indent()));
392 result
393 }
394 fn print_embeddings(&mut self, embeddings: &EmbeddingsDecl) -> String {
395 let mut result = format!("{}embeddings {{\n", self.write_indent());
396 self.indent += 1;
397 result
398 .push_str(
399 &format!("{}model = \"{}\"\n", self.write_indent(), embeddings.model),
400 );
401 result
402 .push_str(
403 &format!(
404 "{}dimensions = {}\n", self.write_indent(), embeddings.dimensions
405 ),
406 );
407 for (key, value) in &embeddings.properties {
408 result
409 .push_str(
410 &format!(
411 "{}{} = {}\n", self.write_indent(), key, self
412 .print_expression(value)
413 ),
414 );
415 }
416 self.indent -= 1;
417 result.push_str(&format!("{}}}\n", self.write_indent()));
418 result
419 }
420 fn print_context(&mut self, context: &ContextDecl) -> String {
421 let mut result = format!(
422 "{}context \"{}\" {{\n", self.write_indent(), context.name
423 );
424 self.indent += 1;
425 result
426 .push_str(
427 &format!(
428 "{}environment = \"{}\"\n", self.write_indent(), context.environment
429 ),
430 );
431 if let Some(secrets) = &context.secrets {
432 result.push_str(&format!("{}secrets {{\n", self.write_indent()));
433 self.indent += 1;
434 for (key, secret_ref) in secrets {
435 result
436 .push_str(
437 &format!(
438 "{}{} = {}\n", self.write_indent(), key, self
439 .print_secret_ref(secret_ref)
440 ),
441 );
442 }
443 self.indent -= 1;
444 result.push_str(&format!("{}}}\n", self.write_indent()));
445 }
446 if let Some(variables) = &context.variables {
447 result.push_str(&format!("{}variables {{\n", self.write_indent()));
448 self.indent += 1;
449 for (key, value) in variables {
450 result
451 .push_str(
452 &format!(
453 "{}{} = {}\n", self.write_indent(), key, self
454 .print_expression(value)
455 ),
456 );
457 }
458 self.indent -= 1;
459 result.push_str(&format!("{}}}\n", self.write_indent()));
460 }
461 for (key, value) in &context.properties {
462 result
463 .push_str(
464 &format!(
465 "{}{} = {}\n", self.write_indent(), key, self
466 .print_expression(value)
467 ),
468 );
469 }
470 self.indent -= 1;
471 result.push_str(&format!("{}}}\n", self.write_indent()));
472 result
473 }
474 fn print_crew(&mut self, crew: &CrewDecl) -> String {
475 let mut result = format!("{}crew \"{}\" {{\n", self.write_indent(), crew.name);
476 self.indent += 1;
477 result.push_str(&format!("{}agents [\n", self.write_indent()));
478 self.indent += 1;
479 for agent in &crew.agents {
480 result.push_str(&format!("{}\"{}\"\n", self.write_indent(), agent));
481 }
482 self.indent -= 1;
483 result.push_str(&format!("{}]\n", self.write_indent()));
484 if let Some(process_type) = &crew.process_type {
485 result
486 .push_str(
487 &format!("{}process = \"{}\"\n", self.write_indent(), process_type),
488 );
489 }
490 for (key, value) in &crew.properties {
491 result
492 .push_str(
493 &format!(
494 "{}{} = {}\n", self.write_indent(), key, self
495 .print_expression(value)
496 ),
497 );
498 }
499 self.indent -= 1;
500 result.push_str(&format!("{}}}\n", self.write_indent()));
501 result
502 }
503 fn print_plugin(&mut self, plugin: &PluginDecl) -> String {
504 let mut result = format!(
505 "{}plugin \"{}\" {{\n", self.write_indent(), plugin.name
506 );
507 self.indent += 1;
508 result
509 .push_str(
510 &format!("{}source = \"{}\"\n", self.write_indent(), plugin.source),
511 );
512 if let Some(version) = &plugin.version {
513 result
514 .push_str(
515 &format!("{}version = \"{}\"\n", self.write_indent(), version),
516 );
517 }
518 for (key, value) in &plugin.config {
519 result
520 .push_str(
521 &format!(
522 "{}{} = {}\n", self.write_indent(), key, self
523 .print_expression(value)
524 ),
525 );
526 }
527 self.indent -= 1;
528 result.push_str(&format!("{}}}\n", self.write_indent()));
529 result
530 }
531 fn print_database(&mut self, database: &DatabaseDecl) -> String {
532 let mut result = format!(
533 "{}database \"{}\" {{\n", self.write_indent(), database.name
534 );
535 self.indent += 1;
536 if let Some(path) = &database.path {
537 result.push_str(&format!("{}path = \"{}\"\n", self.write_indent(), path));
538 }
539 if let Some(shards) = database.shards {
540 result.push_str(&format!("{}shards = {}\n", self.write_indent(), shards));
541 }
542 if let Some(compression) = database.compression {
543 result
544 .push_str(
545 &format!("{}compression = {}\n", self.write_indent(), compression),
546 );
547 }
548 if let Some(cache_size) = database.cache_size {
549 result
550 .push_str(
551 &format!("{}cache_size = {}\n", self.write_indent(), cache_size),
552 );
553 }
554 if let Some(vector_index) = &database.vector_index {
555 result.push_str(&format!("{}vector_index {{\n", self.write_indent()));
556 self.indent += 1;
557 result
558 .push_str(
559 &format!(
560 "{}index_type = \"{}\"\n", self.write_indent(), vector_index
561 .index_type
562 ),
563 );
564 result
565 .push_str(
566 &format!(
567 "{}dimensions = {}\n", self.write_indent(), vector_index
568 .dimensions
569 ),
570 );
571 if let Some(m) = vector_index.m {
572 result.push_str(&format!("{}m = {}\n", self.write_indent(), m));
573 }
574 if let Some(ef_construction) = vector_index.ef_construction {
575 result
576 .push_str(
577 &format!(
578 "{}ef_construction = {}\n", self.write_indent(),
579 ef_construction
580 ),
581 );
582 }
583 if let Some(distance_metric) = &vector_index.distance_metric {
584 result
585 .push_str(
586 &format!(
587 "{}distance_metric = \"{}\"\n", self.write_indent(),
588 distance_metric
589 ),
590 );
591 }
592 self.indent -= 1;
593 result.push_str(&format!("{}}}\n", self.write_indent()));
594 }
595 for (key, value) in &database.properties {
596 result
597 .push_str(
598 &format!(
599 "{}{} = {}\n", self.write_indent(), key, self
600 .print_expression(value)
601 ),
602 );
603 }
604 self.indent -= 1;
605 result.push_str(&format!("{}}}\n", self.write_indent()));
606 result
607 }
608 fn print_load(&mut self, load: &LoadDecl) -> String {
609 let mut result = format!(
610 "{}load \"{}\" {{\n", self.write_indent(), load.file_name
611 );
612 self.indent += 1;
613 for (key, value) in &load.properties {
614 result
615 .push_str(
616 &format!(
617 "{}{} = {}\n", self.write_indent(), key, self
618 .print_expression(value)
619 ),
620 );
621 }
622 self.indent -= 1;
623 result.push_str(&format!("{}}}\n", self.write_indent()));
624 result
625 }
626 fn print_pipeline(&mut self, pipeline: &PipelineDecl) -> String {
627 let mut result = format!("{}pipeline {{\n", self.write_indent());
628 self.indent += 1;
629 let flow_str = pipeline
630 .flow
631 .iter()
632 .map(|node| match node {
633 PipelineNode::Step(s) => s.clone(),
634 _ => "...".to_string(),
635 })
636 .collect::<Vec<_>>()
637 .join(" -> ");
638 result.push_str(&format!("{}{}\n", self.write_indent(), flow_str));
639 self.indent -= 1;
640 result.push_str(&format!("{}}}\n", self.write_indent()));
641 result
642 }
643 fn print_secret_ref(&mut self, secret_ref: &SecretRef) -> String {
644 match secret_ref {
645 SecretRef::Environment(var) => format!("${}", var),
646 SecretRef::Vault(path) => format!("vault:\"{}\"", path),
647 SecretRef::File(path) => format!("file:\"{}\"", path),
648 }
649 }
650 fn print_expression(&mut self, expr: &Expression) -> String {
651 match expr {
652 Expression::String(s) => format!("\"{}\"", s),
653 Expression::Number(n) => format!("{}", n),
654 Expression::Bool(b) => format!("{}", b),
655 Expression::Duration(d) => {
656 format!(
657 "{}{}", d.value, match d.unit { TimeUnit::Seconds => "s",
658 TimeUnit::Minutes => "m", TimeUnit::Hours => "h", TimeUnit::Days =>
659 "d", }
660 )
661 }
662 Expression::Variable(v) => format!("${}", v),
663 Expression::Reference(r) => format!("@{}", r),
664 Expression::IndexedReference(file, key) => format!("@{}[{}]", file, key),
665 Expression::Identifier(i) => i.clone(),
666 Expression::Pipeline(stages) => stages.join(" -> "),
667 Expression::Array(items) => {
668 format!(
669 "[{}]", items.iter().map(| i | self.print_expression(i)).collect::<
670 Vec < _ >> ().join(", ")
671 )
672 }
673 Expression::Object(map) => {
674 let items = map
675 .iter()
676 .map(|(k, v)| format!("{} = {}", k, self.print_expression(v)))
677 .collect::<Vec<_>>()
678 .join(", ");
679 format!("{{ {} }}", items)
680 }
681 _ => "...".to_string(),
682 }
683 }
684}
685impl Expression {
686 pub fn as_string(&self) -> Option<String> {
687 match self {
688 Expression::String(s) => Some(s.clone()),
689 Expression::Identifier(s) => Some(s.clone()),
690 _ => None,
691 }
692 }
693 pub fn as_number(&self) -> Option<f64> {
694 match self {
695 Expression::Number(n) => Some(*n),
696 _ => None,
697 }
698 }
699 pub fn as_bool(&self) -> Option<bool> {
700 match self {
701 Expression::Bool(b) => Some(*b),
702 _ => None,
703 }
704 }
705 pub fn as_array(&self) -> Option<Vec<Expression>> {
706 match self {
707 Expression::Array(arr) => Some(arr.clone()),
708 _ => None,
709 }
710 }
711 pub fn as_object(&self) -> Option<&HashMap<String, Expression>> {
712 match self {
713 Expression::Object(map) => Some(map),
714 _ => None,
715 }
716 }
717 pub fn to_value(&self) -> Value {
718 match self {
719 Expression::String(s) => Value::String(s.clone()),
720 Expression::Number(n) => Value::Number(*n),
721 Expression::Bool(b) => Value::Bool(*b),
722 Expression::Duration(d) => Value::Duration(d.clone()),
723 Expression::Array(arr) => {
724 Value::Array(arr.iter().map(|e| e.to_value()).collect())
725 }
726 Expression::Object(map) => {
727 Value::Object(
728 map.iter().map(|(k, v)| (k.clone(), v.to_value())).collect(),
729 )
730 }
731 Expression::Variable(v) => Value::Reference(format!("${}", v)),
732 Expression::Reference(r) => Value::Reference(format!("@{}", r)),
733 Expression::IndexedReference(file, key) => {
734 Value::Reference(format!("@{}[{}]", file, key))
735 }
736 Expression::Identifier(i) => Value::String(i.clone()),
737 _ => Value::String("".to_string()),
738 }
739 }
740}
741#[allow(dead_code)]
742pub struct AstBuilder {
743 ast: HelixAst,
744}
745#[allow(dead_code)]
746impl AstBuilder {
747 pub fn new() -> Self {
748 AstBuilder { ast: HelixAst::new() }
749 }
750 pub fn add_project(
751 mut self,
752 name: String,
753 properties: HashMap<String, Expression>,
754 ) -> Self {
755 self.ast.add_declaration(Declaration::Project(ProjectDecl { name, properties }));
756 self
757 }
758 pub fn add_agent(mut self, agent: AgentDecl) -> Self {
759 self.ast.add_declaration(Declaration::Agent(agent));
760 self
761 }
762 pub fn add_workflow(mut self, workflow: WorkflowDecl) -> Self {
763 self.ast.add_declaration(Declaration::Workflow(workflow));
764 self
765 }
766 pub fn add_context(mut self, context: ContextDecl) -> Self {
767 self.ast.add_declaration(Declaration::Context(context));
768 self
769 }
770 pub fn build(self) -> HelixAst {
771 self.ast
772 }
773}