swarm_engine_eval/scenario/
dependency.rs1use serde::{Deserialize, Serialize};
6
7#[derive(Debug, Clone, Serialize, Deserialize)]
13#[serde(tag = "type", rename_all = "snake_case")]
14pub enum DependencyGraphType {
15 Static {
17 #[serde(default = "default_pattern")]
19 pattern: String,
20 },
21 Custom {
23 edges: Vec<DependencyEdgeConfig>,
25 start: Vec<String>,
27 terminal: Vec<String>,
29 #[serde(default)]
31 param_variants: Vec<ParamVariantConfig>,
32 },
33 Llm {},
35}
36
37impl Default for DependencyGraphType {
38 fn default() -> Self {
39 Self::Static {
40 pattern: default_pattern(),
41 }
42 }
43}
44
45fn default_pattern() -> String {
46 "code_search".to_string()
47}
48
49#[derive(Debug, Clone, Serialize, Deserialize)]
55pub struct DependencyEdgeConfig {
56 pub from: String,
58 pub to: String,
60 #[serde(default = "default_confidence")]
62 pub confidence: f64,
63}
64
65fn default_confidence() -> f64 {
66 0.9
67}
68
69#[derive(Debug, Clone, Serialize, Deserialize)]
78pub struct ParamVariantConfig {
79 pub action: String,
81 pub key: String,
83 pub values: Vec<String>,
85}
86
87#[derive(Debug, Clone, Serialize, Deserialize, Default)]
95pub struct DependencyGraphConfig {
96 #[serde(flatten)]
98 pub graph_type: DependencyGraphType,
99}
100
101impl DependencyGraphConfig {
102 pub fn to_core_graph(
104 &self,
105 available_actions: &[String],
106 ) -> Option<swarm_engine_core::exploration::DependencyGraph> {
107 use swarm_engine_core::exploration::{
108 DependencyGraph, DependencyPlanner, StaticDependencyPlanner,
109 };
110
111 match &self.graph_type {
112 DependencyGraphType::Static { pattern } => {
113 let planner = match pattern.as_str() {
114 "code_search" => StaticDependencyPlanner::new().with_code_search_pattern(),
115 "file_exploration" => {
116 StaticDependencyPlanner::new().with_file_exploration_pattern()
117 }
118 _ => StaticDependencyPlanner::new().with_code_search_pattern(),
119 };
120 planner.plan("task", available_actions).ok()
121 }
122 DependencyGraphType::Custom {
123 edges,
124 start,
125 terminal,
126 param_variants,
127 } => {
128 let mut builder = DependencyGraph::builder()
129 .available_actions(available_actions.iter().cloned())
130 .start_nodes(start.iter().cloned())
131 .terminal_nodes(terminal.iter().cloned());
132
133 for edge in edges {
134 builder = builder.edge(&edge.from, &edge.to, edge.confidence);
135 }
136
137 for pv in param_variants {
139 builder =
140 builder.param_variants(&pv.action, &pv.key, pv.values.iter().cloned());
141 }
142
143 Some(builder.build())
144 }
145 DependencyGraphType::Llm {} => {
146 None
148 }
149 }
150 }
151}
152
153#[cfg(test)]
158mod tests {
159 use super::*;
160
161 #[test]
162 fn test_dependency_graph_type_default() {
163 let graph_type = DependencyGraphType::default();
164 match graph_type {
165 DependencyGraphType::Static { pattern } => {
166 assert_eq!(pattern, "code_search");
167 }
168 _ => panic!("Expected Static variant"),
169 }
170 }
171
172 #[test]
173 fn test_dependency_edge_config_default_confidence() {
174 let toml_str = r#"
175 from = "grep"
176 to = "read"
177 "#;
178 let edge: DependencyEdgeConfig = toml::from_str(toml_str).unwrap();
179 assert_eq!(edge.from, "grep");
180 assert_eq!(edge.to, "read");
181 assert!((edge.confidence - 0.9).abs() < 0.001);
182 }
183
184 #[test]
185 fn test_dependency_graph_config_static() {
186 let config = DependencyGraphConfig::default();
187 let actions = vec!["grep".to_string(), "read".to_string(), "done".to_string()];
188 let graph = config.to_core_graph(&actions);
189 assert!(graph.is_some());
190 }
191}