helix/dna/atp/
types.rs

1use std::collections::HashMap;
2use std::fs;
3use std::path::Path;
4use serde::{Deserialize, Serialize};
5use crate::atp::ast::*;
6use crate::atp::value::Value as AstValue;
7
8fn extract_string_value(
9    expr: &Option<&Expression>,
10) -> Result<String, String> {
11    match expr {
12        Some(Expression::String(s)) => Ok(s.clone()),
13        Some(Expression::Identifier(s)) => Ok(s.clone()),
14        _ => Ok(String::new()),
15    }
16}
17fn extract_float_value(expr: &Option<&Expression>) -> Result<f64, String> {
18    match expr {
19        Some(Expression::Number(n)) => Ok(*n),
20        _ => Ok(0.0),
21    }
22}
23fn extract_int_value(expr: &Option<&Expression>) -> Result<i64, String> {
24    match expr {
25        Some(Expression::Number(n)) => Ok(*n as i64),
26        _ => Ok(0),
27    }
28}
29fn extract_bool_value(expr: &Option<&Expression>) -> Result<bool, String> {
30    match expr {
31        Some(Expression::Bool(b)) => Ok(*b),
32        _ => Ok(false),
33    }
34}
35fn extract_duration_value(
36    expr: &Option<&Expression>,
37) -> Result<Duration, String> {
38    match expr {
39        Some(Expression::Duration(duration)) => {
40            Ok(Duration {
41                value: duration.value as u64,
42                unit: duration.unit.clone(),
43            })
44        }
45        _ => {
46            Ok(Duration {
47                value: 0,
48                unit: TimeUnit::Seconds,
49            })
50        }
51    }
52}
53fn extract_array_values(
54    expr: &Option<&Expression>,
55) -> Result<Vec<String>, String> {
56    match expr {
57        Some(Expression::Array(items)) => {
58            items
59                .iter()
60                .map(|e| match e {
61                    Expression::String(s) => Ok(s.clone()),
62                    Expression::Identifier(s) => Ok(s.clone()),
63                    _ => Err("Array items must be strings".to_string()),
64                })
65                .collect()
66        }
67        _ => Ok(Vec::new()),
68    }
69}
70fn extract_map_values(
71    expr: &Option<&Expression>,
72) -> Result<std::collections::HashMap<String, String>, String> {
73    match expr {
74        Some(Expression::Object(map)) => {
75            let mut result = std::collections::HashMap::new();
76            for (k, v) in map {
77                let value = match v {
78                    Expression::String(s) => s.clone(),
79                    Expression::Identifier(s) => s.clone(),
80                    Expression::Number(n) => n.to_string(),
81                    Expression::Bool(b) => b.to_string(),
82                    _ => String::new(),
83                };
84                result.insert(k.clone(), value);
85            }
86            Ok(result)
87        }
88        _ => Ok(std::collections::HashMap::new()),
89    }
90}
91#[derive(Debug, Clone, Serialize, Deserialize)]
92pub struct HelixConfig {
93    pub projects: HashMap<String, ProjectConfig>,
94    pub agents: HashMap<String, AgentConfig>,
95    pub workflows: HashMap<String, WorkflowConfig>,
96    pub memory: Option<MemoryConfig>,
97    pub contexts: HashMap<String, ContextConfig>,
98    pub crews: HashMap<String, CrewConfig>,
99    pub pipelines: HashMap<String, PipelineConfig>,
100    pub plugins: Vec<PluginConfig>,
101    pub databases: HashMap<String, DatabaseConfig>,
102    pub sections: HashMap<String, HashMap<String, Value>>,
103}
104impl Default for HelixConfig {
105    fn default() -> Self {
106        Self {
107            projects: HashMap::new(),
108            agents: HashMap::new(),
109            workflows: HashMap::new(),
110            memory: None,
111            contexts: HashMap::new(),
112            crews: HashMap::new(),
113            pipelines: HashMap::new(),
114            plugins: Vec::new(),
115            databases: HashMap::new(),
116            sections: HashMap::new(),
117        }
118    }
119}
120#[derive(Debug, Clone, Serialize, Deserialize)]
121pub struct ProjectConfig {
122    pub name: String,
123    pub version: String,
124    pub author: String,
125    pub description: Option<String>,
126    pub metadata: HashMap<String, Value>,
127}
128#[derive(Debug, Clone, Serialize, Deserialize)]
129pub struct AgentConfig {
130    pub name: String,
131    pub model: String,
132    pub role: String,
133    pub temperature: Option<f32>,
134    pub max_tokens: Option<u32>,
135    pub capabilities: Vec<String>,
136    pub backstory: Option<String>,
137    pub tools: Vec<String>,
138    pub constraints: Vec<String>,
139}
140#[derive(Debug, Clone, Serialize, Deserialize)]
141pub struct WorkflowConfig {
142    pub name: String,
143    pub trigger: TriggerConfig,
144    pub steps: Vec<StepConfig>,
145    pub pipeline: Option<PipelineConfig>,
146    pub outputs: Vec<String>,
147    pub on_error: Option<String>,
148}
149#[derive(Debug, Clone, Serialize, Deserialize)]
150pub struct StepConfig {
151    pub name: String,
152    pub agent: Option<String>,
153    pub crew: Option<Vec<String>>,
154    pub task: String,
155    pub timeout: Option<Duration>,
156    pub parallel: bool,
157    pub depends_on: Vec<String>,
158    pub retry: Option<RetryConfig>,
159}
160#[derive(Debug, Clone, Serialize, Deserialize)]
161pub struct MemoryConfig {
162    pub provider: String,
163    pub connection: String,
164    pub embeddings: EmbeddingConfig,
165    pub cache_size: Option<usize>,
166    pub persistence: bool,
167}
168#[derive(Debug, Clone, Serialize, Deserialize)]
169pub struct EmbeddingConfig {
170    pub model: String,
171    pub dimensions: u32,
172    pub batch_size: Option<u32>,
173}
174#[derive(Debug, Clone, Serialize, Deserialize)]
175pub struct ContextConfig {
176    pub name: String,
177    pub environment: String,
178    pub debug: bool,
179    pub max_tokens: Option<u64>,
180    pub secrets: HashMap<String, SecretRef>,
181    pub variables: HashMap<String, Value>,
182}
183#[derive(Debug, Clone, Serialize, Deserialize)]
184pub struct CrewConfig {
185    pub name: String,
186    pub agents: Vec<String>,
187    pub process_type: ProcessType,
188    pub manager: Option<String>,
189    pub max_iterations: Option<u32>,
190    pub verbose: bool,
191}
192#[derive(Debug, Clone, Serialize, Deserialize)]
193pub struct PluginConfig {
194    pub name: String,
195    pub source: String,
196    pub version: String,
197    pub config: HashMap<String, Value>,
198}
199#[derive(Debug, Clone, Serialize, Deserialize)]
200pub struct DatabaseConfig {
201    pub name: String,
202    pub path: Option<String>,
203    pub shards: Option<i64>,
204    pub compression: Option<bool>,
205    pub cache_size: Option<i64>,
206    pub vector_index: Option<VectorIndexConfig>,
207    pub properties: HashMap<String, Value>,
208}
209#[derive(Debug, Clone, Serialize, Deserialize)]
210pub struct VectorIndexConfig {
211    pub index_type: String,
212    pub dimensions: i64,
213    pub m: Option<i64>,
214    pub ef_construction: Option<i64>,
215    pub distance_metric: Option<String>,
216}
217#[derive(Debug, Clone, Serialize, Deserialize)]
218pub enum Value {
219    String(String),
220    Number(f64),
221    Bool(bool),
222    Null,
223    Array(Vec<Value>),
224    Object(HashMap<String, Value>),
225    Duration(Duration),
226    Reference(String),
227    Identifier(String),
228}
229impl Value {
230    pub fn as_string(&self) -> Option<&str> {
231        match self {
232            Value::String(s) => Some(s),
233            _ => None,
234        }
235    }
236    pub fn as_str(&self) -> Option<&str> {
237        self.as_string()
238    }
239    pub fn as_number(&self) -> Option<f64> {
240        match self {
241            Value::Number(n) => Some(*n),
242            _ => None,
243        }
244    }
245    pub fn as_bool(&self) -> Option<bool> {
246        match self {
247            Value::Bool(b) => Some(*b),
248            _ => None,
249        }
250    }
251}
252
253#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
254pub struct Duration {
255    pub value: u64,
256    pub unit: TimeUnit,
257}
258#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
259pub enum TimeUnit {
260    Seconds,
261    Minutes,
262    Hours,
263    Days,
264}
265#[derive(Debug, Clone, Serialize, Deserialize)]
266pub enum TriggerConfig {
267    Manual,
268    Schedule(String),
269    Webhook(String),
270    Event(String),
271    FileWatch(String),
272}
273#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
274pub enum ProcessType {
275    Sequential,
276    Hierarchical,
277    Parallel,
278    Consensus,
279}
280#[derive(Debug, Clone, Serialize, Deserialize)]
281pub struct RetryConfig {
282    pub max_attempts: u32,
283    pub delay: Duration,
284    pub backoff: BackoffStrategy,
285}
286#[derive(Debug, Clone, Serialize, Deserialize)]
287pub enum BackoffStrategy {
288    Fixed,
289    Linear,
290    Exponential,
291}
292#[derive(Debug, Clone, Serialize, Deserialize)]
293pub struct PipelineConfig {
294    pub name: String,
295    pub stages: Vec<String>,
296    pub flow: String,
297}
298#[derive(Debug, Clone, Serialize, Deserialize)]
299pub enum SecretRef {
300    Environment(String),
301    Vault(String),
302    File(String),
303}
304pub struct HelixLoader {
305    configs: HashMap<String, HelixConfig>,
306    current_context: Option<String>,
307}
308impl HelixLoader {
309    pub fn new() -> Self {
310        HelixLoader {
311            configs: HashMap::new(),
312            current_context: None,
313        }
314    }
315    pub fn load_file<P: AsRef<Path>>(
316        &mut self,
317        path: P,
318    ) -> Result<HelixConfig, HlxError> {
319        let content = fs::read_to_string(path)?;
320        self.parse(&content)
321    }
322    pub fn parse(&mut self, content: &str) -> Result<HelixConfig, HlxError> {
323        let tokens = crate::atp::lexer::tokenize(content)?;
324        let ast = crate::atp::parser::parse(tokens)?;
325        let config = self.ast_to_config(ast)?;
326        Ok(config)
327    }
328    pub fn ast_to_config(
329        &self,
330        ast: HelixAst,
331    ) -> Result<HelixConfig, HlxError> {
332        let mut config = HelixConfig {
333            projects: HashMap::new(),
334            agents: HashMap::new(),
335            workflows: HashMap::new(),
336            memory: None,
337            contexts: HashMap::new(),
338            crews: HashMap::new(),
339            pipelines: HashMap::new(),
340            plugins: Vec::new(),
341            databases: HashMap::new(),
342            sections: HashMap::new(),
343        };
344        for decl in ast.declarations {
345            match decl {
346                Declaration::Project(p) => {
347                    let project = self.convert_project(p)?;
348                    config.projects.insert(project.name.clone(), project);
349                }
350                Declaration::Agent(a) => {
351                    let agent = self.convert_agent(a)?;
352                    config.agents.insert(agent.name.clone(), agent);
353                }
354                Declaration::Workflow(w) => {
355                    let workflow = self.convert_workflow(w)?;
356                    config.workflows.insert(workflow.name.clone(), workflow);
357                }
358                Declaration::Memory(m) => {
359                    config.memory = Some(self.convert_memory(m)?);
360                }
361                Declaration::Context(c) => {
362                    let context = self.convert_context(c)?;
363                    config.contexts.insert(context.name.clone(), context);
364                }
365                Declaration::Crew(cr) => {
366                    let crew = self.convert_crew(cr)?;
367                    config.crews.insert(crew.name.clone(), crew);
368                }
369                Declaration::Plugin(p) => {
370                    config.plugins.push(self.convert_plugin(p)?);
371                }
372                Declaration::Database(d) => {
373                    let database = self.convert_database(d)?;
374                    config.databases.insert(database.name.clone(), database);
375                }
376                Declaration::Task(t) => {
377                    let task_data: HashMap<String, Value> = t
378                        .properties
379                        .iter()
380                        .map(|(k, v)| (k.clone(), v.to_value()))
381                        .collect();
382                    config.sections.insert(format!("task.{}", t.name), task_data);
383                }
384                Declaration::Pipeline(p) => {
385                    let pipeline = self.convert_pipeline(p)?;
386                    config.pipelines.insert(pipeline.name.clone(), pipeline);
387                }
388                Declaration::Load(_l) => {}
389                Declaration::Section(s) => {
390                    let section_data: HashMap<String, Value> = s
391                        .properties
392                        .iter()
393                        .map(|(k, v)| (k.clone(), v.to_value()))
394                        .collect();
395                    config.sections.insert(s.name.clone(), section_data);
396                }
397            }
398        }
399        Ok(config)
400    }
401    fn convert_project(
402        &self,
403        project: ProjectDecl,
404    ) -> Result<ProjectConfig, HlxError> {
405        let mut metadata = HashMap::new();
406        let mut version = String::new();
407        let mut author = String::new();
408        let mut description = None;
409        for (key, expr) in project.properties {
410            let expr_opt = Some(&expr);
411            match key.as_str() {
412                "version" => {
413                    version = extract_string_value(&expr_opt).unwrap_or_default();
414                }
415                "author" => {
416                    author = extract_string_value(&expr_opt).unwrap_or_default();
417                }
418                "description" => {
419                    let desc = extract_string_value(&expr_opt).unwrap_or_default();
420                    description = if desc.is_empty() { None } else { Some(desc) };
421                }
422                _ => {
423                    metadata.insert(key, self.expression_to_value(expr));
424                }
425            }
426        }
427        Ok(ProjectConfig {
428            name: project.name,
429            version,
430            author,
431            description,
432            metadata,
433        })
434    }
435    fn convert_agent(
436        &self,
437        agent: AgentDecl,
438    ) -> Result<AgentConfig, HlxError> {
439        let mut config = AgentConfig {
440            name: agent.name.clone(),
441            model: String::new(),
442            role: String::new(),
443            temperature: None,
444            max_tokens: None,
445            capabilities: agent.capabilities.unwrap_or_default(),
446            backstory: agent.backstory.map(|b| b.lines.join("\n")),
447            tools: agent.tools.unwrap_or_default(),
448            constraints: Vec::new(),
449        };
450        for (key, expr) in agent.properties {
451            let expr_opt = Some(&expr);
452            match key.as_str() {
453                "model" => {
454                    config.model = extract_string_value(&expr_opt).unwrap_or_default();
455                }
456                "role" => {
457                    config.role = extract_string_value(&expr_opt).unwrap_or_default();
458                }
459                "temperature" => {
460                    config.temperature = extract_float_value(&expr_opt)
461                        .ok()
462                        .map(|f| f as f32);
463                }
464                "max_tokens" => {
465                    config.max_tokens = extract_int_value(&expr_opt)
466                        .ok()
467                        .map(|i| i as u32);
468                }
469                "custom" | "properties" | "config" => {
470                    if let Ok(custom_map) = extract_map_values(&expr_opt) {
471                        for (key, value) in custom_map {
472                            println!("Agent custom property: {} = {}", key, value);
473                        }
474                    }
475                }
476                _ => {}
477            }
478        }
479        Ok(config)
480    }
481    fn convert_workflow(
482        &self,
483        workflow: WorkflowDecl,
484    ) -> Result<WorkflowConfig, HlxError> {
485        let trigger = if let Some(t) = workflow.trigger {
486            self.convert_trigger(t)?
487        } else {
488            TriggerConfig::Manual
489        };
490        let mut steps = Vec::new();
491        for step in workflow.steps {
492            steps.push(self.convert_step(step)?);
493        }
494        let pipeline = if let Some(p) = workflow.pipeline {
495            Some(self.convert_pipeline(p)?)
496        } else {
497            None
498        };
499        Ok(WorkflowConfig {
500            name: workflow.name,
501            trigger,
502            steps,
503            pipeline,
504            outputs: Vec::new(),
505            on_error: None,
506        })
507    }
508    fn convert_trigger(
509        &self,
510        expr: Expression,
511    ) -> Result<TriggerConfig, HlxError> {
512        match expr {
513            Expression::String(s) | Expression::Identifier(s) => {
514                match s.as_str() {
515                    "manual" => Ok(TriggerConfig::Manual),
516                    s if s.starts_with("schedule:") => {
517                        Ok(
518                            TriggerConfig::Schedule(
519                                s.trim_start_matches("schedule:").to_string(),
520                            ),
521                        )
522                    }
523                    s if s.starts_with("webhook:") => {
524                        Ok(
525                            TriggerConfig::Webhook(
526                                s.trim_start_matches("webhook:").to_string(),
527                            ),
528                        )
529                    }
530                    s if s.starts_with("event:") => {
531                        Ok(
532                            TriggerConfig::Event(
533                                s.trim_start_matches("event:").to_string(),
534                            ),
535                        )
536                    }
537                    s if s.starts_with("file:") => {
538                        Ok(
539                            TriggerConfig::FileWatch(
540                                s.trim_start_matches("file:").to_string(),
541                            ),
542                        )
543                    }
544                    _ => Ok(TriggerConfig::Manual),
545                }
546            }
547            _ => Ok(TriggerConfig::Manual),
548        }
549    }
550    fn convert_step(&self, step: StepDecl) -> Result<StepConfig, HlxError> {
551        let mut config = StepConfig {
552            name: step.name,
553            agent: step.agent,
554            crew: step.crew,
555            task: step.task.unwrap_or_default(),
556            timeout: None,
557            parallel: false,
558            depends_on: Vec::new(),
559            retry: None,
560        };
561        for (key, expr) in step.properties {
562            let expr_opt = Some(&expr);
563            match key.as_str() {
564                "timeout" => {
565                    config.timeout = extract_duration_value(&expr_opt).ok();
566                }
567                "parallel" => {
568                    config.parallel = extract_bool_value(&expr_opt).unwrap_or(false);
569                }
570                "depends_on" => {
571                    config.depends_on = extract_array_values(&expr_opt)
572                        .unwrap_or_default();
573                }
574                "retry" => {
575                    if let Some(obj) = expr.as_object() {
576                        config.retry = self.convert_retry_config(obj);
577                    }
578                }
579                _ => {}
580            }
581        }
582        Ok(config)
583    }
584    fn convert_retry_config(
585        &self,
586        obj: &HashMap<String, Expression>,
587    ) -> Option<RetryConfig> {
588        let max_attempts = obj.get("max_attempts")?.as_number()? as u32;
589        let delay = obj
590            .get("delay")
591            .and_then(|e| self.expression_to_duration(e.clone()))?;
592        let backoff = obj
593            .get("backoff")
594            .and_then(|e| e.as_string())
595            .and_then(|s| match s.as_str() {
596                "fixed" => Some(BackoffStrategy::Fixed),
597                "linear" => Some(BackoffStrategy::Linear),
598                "exponential" => Some(BackoffStrategy::Exponential),
599                _ => None,
600            })
601            .unwrap_or(BackoffStrategy::Fixed);
602        Some(RetryConfig {
603            max_attempts,
604            delay,
605            backoff,
606        })
607    }
608    fn convert_pipeline(
609        &self,
610        pipeline: PipelineDecl,
611    ) -> Result<PipelineConfig, HlxError> {
612        let stages = pipeline
613            .flow
614            .iter()
615            .filter_map(|node| {
616                if let PipelineNode::Step(name) = node {
617                    Some(name.clone())
618                } else {
619                    None
620                }
621            })
622            .collect();
623        let flow = pipeline
624            .flow
625            .iter()
626            .filter_map(|node| {
627                if let PipelineNode::Step(name) = node {
628                    Some(name.clone())
629                } else {
630                    None
631                }
632            })
633            .collect::<Vec<_>>()
634            .join(" -> ");
635        Ok(PipelineConfig {
636            name: "default".to_string(),
637            stages,
638            flow,
639        })
640    }
641    fn convert_memory(
642        &self,
643        memory: MemoryDecl,
644    ) -> Result<MemoryConfig, HlxError> {
645        let embeddings = if let Some(e) = memory.embeddings {
646            EmbeddingConfig {
647                model: e.model,
648                dimensions: e.dimensions,
649                batch_size: e
650                    .properties
651                    .get("batch_size")
652                    .and_then(|v| v.as_number())
653                    .map(|n| n as u32),
654            }
655        } else {
656            EmbeddingConfig {
657                model: String::new(),
658                dimensions: 0,
659                batch_size: None,
660            }
661        };
662        Ok(MemoryConfig {
663            provider: memory.provider,
664            connection: memory.connection,
665            embeddings,
666            cache_size: memory
667                .properties
668                .get("cache_size")
669                .and_then(|v| v.as_number())
670                .map(|n| n as usize),
671            persistence: memory
672                .properties
673                .get("persistence")
674                .and_then(|v| v.as_bool())
675                .unwrap_or(true),
676        })
677    }
678    fn convert_context(
679        &self,
680        context: ContextDecl,
681    ) -> Result<ContextConfig, HlxError> {
682        let mut secrets = HashMap::new();
683        if let Some(s) = context.secrets {
684            for (key, secret_ref) in s {
685                secrets.insert(key, self.convert_secret_ref(secret_ref));
686            }
687        }
688        let mut variables = HashMap::new();
689        for (key, expr) in &context.properties {
690            if key != "debug" && key != "max_tokens" {
691                variables.insert(key.clone(), expr.to_value());
692            }
693        }
694        Ok(ContextConfig {
695            name: context.name,
696            environment: context.environment,
697            debug: context
698                .properties
699                .get("debug")
700                .and_then(|v| v.as_bool())
701                .unwrap_or(false),
702            max_tokens: context
703                .properties
704                .get("max_tokens")
705                .and_then(|v| v.as_number())
706                .map(|n| n as u64),
707            secrets,
708            variables,
709        })
710    }
711    fn convert_secret_ref(&self, secret_ref: SecretRef) -> SecretRef {
712        match secret_ref {
713            SecretRef::Environment(var) => SecretRef::Environment(var),
714            SecretRef::Vault(path) => SecretRef::Vault(path),
715            SecretRef::File(path) => SecretRef::File(path),
716        }
717    }
718    fn convert_crew(&self, crew: CrewDecl) -> Result<CrewConfig, HlxError> {
719        let process_type = crew
720            .process_type
721            .and_then(|p| match p.as_str() {
722                "sequential" => Some(ProcessType::Sequential),
723                "hierarchical" => Some(ProcessType::Hierarchical),
724                "parallel" => Some(ProcessType::Parallel),
725                "consensus" => Some(ProcessType::Consensus),
726                _ => None,
727            })
728            .unwrap_or(ProcessType::Sequential);
729        Ok(CrewConfig {
730            name: crew.name,
731            agents: crew.agents,
732            process_type,
733            manager: crew.properties.get("manager").and_then(|e| e.as_string()),
734            max_iterations: crew
735                .properties
736                .get("max_iterations")
737                .and_then(|e| e.as_number())
738                .map(|n| n as u32),
739            verbose: crew
740                .properties
741                .get("verbose")
742                .and_then(|e| e.as_bool())
743                .unwrap_or(false),
744        })
745    }
746    fn convert_plugin(
747        &self,
748        plugin: PluginDecl,
749    ) -> Result<PluginConfig, HlxError> {
750        let mut config = HashMap::new();
751        for (key, expr) in plugin.config {
752            config.insert(key, self.expression_to_value(expr));
753        }
754        Ok(PluginConfig {
755            name: plugin.name,
756            source: plugin.source,
757            version: plugin.version.unwrap_or_else(|| "latest".to_string()),
758            config,
759        })
760    }
761    fn convert_database(
762        &self,
763        database: DatabaseDecl,
764    ) -> Result<DatabaseConfig, HlxError> {
765        let mut properties = HashMap::new();
766        for (key, expr) in database.properties {
767            properties.insert(key, self.expression_to_value(expr));
768        }
769        let vector_index = database
770            .vector_index
771            .map(|vi| VectorIndexConfig {
772                index_type: vi.index_type,
773                dimensions: vi.dimensions,
774                m: vi.m,
775                ef_construction: vi.ef_construction,
776                distance_metric: vi.distance_metric,
777            });
778        Ok(DatabaseConfig {
779            name: database.name,
780            path: database.path,
781            shards: database.shards,
782            compression: database.compression,
783            cache_size: database.cache_size,
784            vector_index,
785            properties,
786        })
787    }
788    fn expression_to_value(&self, expr: Expression) -> Value {
789        expr.to_value()
790    }
791    fn expression_to_duration(&self, expr: Expression) -> Option<Duration> {
792        match expr {
793            Expression::Duration(d) => Some(d),
794            _ => None,
795        }
796    }
797    fn convert_ast_value_to_types_value(&self, ast_value: Value) -> Value {
798        ast_value
799    }
800    pub fn load_directory<P: AsRef<Path>>(&mut self, dir: P) -> Result<(), HlxError> {
801        let dir_path = dir.as_ref();
802        for entry in fs::read_dir(dir_path)? {
803            let entry = entry?;
804            let path = entry.path();
805            if path.extension().and_then(|s| s.to_str()) == Some("hlx") {
806                let config = self.load_file(&path)?;
807                let name = path
808                    .file_stem()
809                    .and_then(|s| s.to_str())
810                    .unwrap_or("default")
811                    .to_string();
812                self.configs.insert(name, config);
813            }
814        }
815        Ok(())
816    }
817    pub fn get_config(&self, name: &str) -> Option<&HelixConfig> {
818        self.configs.get(name)
819    }
820    pub fn set_context(&mut self, context: String) {
821        self.current_context = Some(context);
822    }
823    pub fn merge_configs(&self, configs: Vec<&HelixConfig>) -> HelixConfig {
824        let mut merged = HelixConfig::default();
825        for config in configs {
826            for (name, project) in &config.projects {
827                merged.projects.insert(name.clone(), project.clone());
828            }
829            for (name, agent) in &config.agents {
830                merged.agents.insert(name.clone(), agent.clone());
831            }
832            for (name, workflow) in &config.workflows {
833                merged.workflows.insert(name.clone(), workflow.clone());
834            }
835            for (name, context) in &config.contexts {
836                merged.contexts.insert(name.clone(), context.clone());
837            }
838            for (name, crew) in &config.crews {
839                merged.crews.insert(name.clone(), crew.clone());
840            }
841            if config.memory.is_some() {
842                merged.memory = config.memory.clone();
843            }
844            merged.plugins.extend(config.plugins.clone());
845            for (section_name, section_data) in &config.sections {
846                merged.sections.insert(section_name.clone(), section_data.clone());
847            }
848        }
849        merged
850    }
851}
852#[derive(Debug, Clone)]
853pub enum DataFormat {
854    Auto,
855    JSON,
856    CSV,
857    Parquet,
858}
859#[derive(Debug, Clone)]
860pub enum TrainingFormat {
861    Preference { prompt_field: String, chosen_field: String, rejected_field: String },
862    Completion {
863        prompt_field: String,
864        completion_field: String,
865        label_field: Option<String>,
866    },
867}
868#[derive(Debug)]
869pub struct GenericJSONDataset {
870    pub data: Vec<serde_json::Value>,
871    pub format: DataFormat,
872    pub schema: Option<serde_json::Value>,
873}
874impl GenericJSONDataset {
875    pub fn new(
876        _paths: &[std::path::PathBuf],
877        _schema: Option<serde_json::Value>,
878        format: DataFormat,
879    ) -> Result<Self, Box<dyn std::error::Error>> {
880        Ok(Self {
881            data: Vec::new(),
882            format,
883            schema: _schema,
884        })
885    }
886    pub fn detect_training_format(
887        &self,
888    ) -> Result<TrainingFormat, Box<dyn std::error::Error>> {
889        if let Some(first) = self.data.first() {
890            if let Some(obj) = first.as_object() {
891                if obj.contains_key("chosen") && obj.contains_key("rejected") {
892                    return Ok(TrainingFormat::Preference {
893                        prompt_field: "prompt".to_string(),
894                        chosen_field: "chosen".to_string(),
895                        rejected_field: "rejected".to_string(),
896                    });
897                } else if obj.contains_key("completion") {
898                    return Ok(TrainingFormat::Completion {
899                        prompt_field: "prompt".to_string(),
900                        completion_field: "completion".to_string(),
901                        label_field: Some("label".to_string()),
902                    });
903                }
904            }
905        }
906        Err("Could not detect training format".into())
907    }
908    pub fn to_training_dataset(
909        &self,
910    ) -> Result<TrainingDataset, Box<dyn std::error::Error>> {
911        Ok(TrainingDataset {
912            samples: self
913                .data
914                .iter()
915                .enumerate()
916                .map(|(i, _)| TrainingSample {
917                    id: i as u64,
918                    prompt: Some("test prompt".to_string()),
919                    chosen: Some("test chosen".to_string()),
920                    rejected: Some("test rejected".to_string()),
921                    completion: Some("test completion".to_string()),
922                    label: Some(1.0),
923                })
924                .collect(),
925        })
926    }
927}
928#[derive(Debug)]
929pub struct TrainingSample {
930    pub id: u64,
931    pub prompt: Option<String>,
932    pub chosen: Option<String>,
933    pub rejected: Option<String>,
934    pub completion: Option<String>,
935    pub label: Option<f64>,
936}
937#[derive(Debug)]
938pub struct TrainingDataset {
939    pub samples: Vec<TrainingSample>,
940}
941impl TrainingDataset {
942    pub fn to_algorithm_format(
943        &self,
944        _algorithm: &str,
945    ) -> Result<AlgorithmFormat, Box<dyn std::error::Error>> {
946        Ok(AlgorithmFormat {
947            format_type: _algorithm.to_string(),
948            data: serde_json::json!({ "samples" : self.samples.len() }),
949        })
950    }
951}
952#[derive(Debug)]
953pub struct AlgorithmFormat {
954    pub format_type: String,
955    pub data: serde_json::Value,
956}
957#[derive(Debug)]
958pub enum HlxError {
959    IoError(std::io::Error),
960    ParseError(String),
961    ValidationError(String),
962    ReferenceError(String),
963}
964impl From<std::io::Error> for HlxError {
965    fn from(err: std::io::Error) -> Self {
966        HlxError::IoError(err)
967    }
968}
969impl From<String> for HlxError {
970    fn from(err: String) -> Self {
971        HlxError::ParseError(err)
972    }
973}
974impl From<crate::atp::parser::ParseError> for HlxError {
975    fn from(err: crate::atp::parser::ParseError) -> Self {
976        HlxError::ParseError(err.to_string())
977    }
978}
979impl std::fmt::Display for HlxError {
980    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
981        match self {
982            HlxError::IoError(e) => write!(f, "IO Error: {}", e),
983            HlxError::ParseError(e) => write!(f, "Parse Error: {}", e),
984            HlxError::ValidationError(e) => write!(f, "Validation Error: {}", e),
985            HlxError::ReferenceError(e) => write!(f, "Reference Error: {}", e),
986        }
987    }
988}
989impl std::error::Error for HlxError {}
990impl std::fmt::Display for Value {
991    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
992        match self {
993            Value::String(s) => write!(f, "{}", s),
994            Value::Number(n) => write!(f, "{}", n),
995            Value::Bool(b) => write!(f, "{}", b),
996            Value::Array(arr) => {
997                write!(f, "[")?;
998                for (i, item) in arr.iter().enumerate() {
999                    if i > 0 { write!(f, ", ")?; }
1000                    write!(f, "{}", item)?;
1001                }
1002                write!(f, "]")
1003            }
1004            Value::Object(obj) => {
1005                write!(f, "{{")?;
1006                for (i, (k, v)) in obj.iter().enumerate() {
1007                    if i > 0 { write!(f, ", ")?; }
1008                    write!(f, "{}: {}", k, v)?;
1009                }
1010                write!(f, "}}")
1011            }
1012            Value::Null => write!(f, "null"),
1013            Value::Duration(d) => write!(f, "{} {:?}", d.value, d.unit),
1014            Value::Reference(r) => write!(f, "@{}", r),
1015            Value::Identifier(i) => write!(f, "{}", i),
1016        }
1017    }
1018}
1019pub fn load_default_config() -> Result<HelixConfig, HlxError> {
1020    let mut loader = HelixLoader::new();
1021    use std::fs;
1022    let mut paths = Vec::new();
1023    let search_dirs = vec![".", "./config", "~/.maestro", "~/.helix"];
1024    for dir in &search_dirs {
1025        let dir_path = if dir.starts_with("~") {
1026            if let Some(home) = std::env::var_os("HOME") {
1027                let mut home_path = std::path::PathBuf::from(home);
1028                if dir.len() > 1 {
1029                    home_path.push(&dir[2..]);
1030                }
1031                home_path
1032            } else {
1033                std::path::PathBuf::from(dir)
1034            }
1035        } else {
1036            std::path::PathBuf::from(dir)
1037        };
1038        if let Ok(entries) = fs::read_dir(&dir_path) {
1039            for entry in entries.flatten() {
1040                let path = entry.path();
1041                if let Some(ext) = path.extension().and_then(|e| e.to_str()) {
1042                    if ext == "hlxb" || ext == "hlx" {
1043                        if let Some(path_str) = path.to_str() {
1044                            paths.push(path_str.to_string());
1045                        }
1046                    }
1047                }
1048            }
1049        }
1050    }
1051    for path in paths {
1052        if Path::new(&path).exists() {
1053            return loader.load_file(path);
1054        }
1055    }
1056    Err(
1057        HlxError::IoError(
1058            std::io::Error::new(
1059                std::io::ErrorKind::NotFound,
1060                "No .hlxbb configuration file found",
1061            ),
1062        ),
1063    )
1064}