Skip to main content

swarm_engine_eval/scenario/
actions.rs

1//! Actions Configuration
2//!
3//! シナリオで利用可能なアクションの定義。
4
5use serde::{Deserialize, Serialize};
6
7// ============================================================================
8// Action Parameter Definition
9// ============================================================================
10
11/// Action のパラメータ定義(TOML 用)
12#[derive(Debug, Clone, Serialize, Deserialize)]
13pub struct ActionParamDef {
14    /// パラメータ名
15    pub name: String,
16    /// 説明
17    pub description: String,
18    /// 必須かどうか
19    #[serde(default)]
20    pub required: bool,
21}
22
23// ============================================================================
24// Action Category
25// ============================================================================
26
27/// Action カテゴリ(TOML 用)
28///
29/// 探索空間への影響を定義する。
30#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
31#[serde(rename_all = "snake_case")]
32pub enum ScenarioActionCategory {
33    /// 新しい探索対象を発見する(Grep, List など)
34    #[default]
35    NodeExpand,
36    /// 既存 Node の状態を遷移させる(Read など)
37    NodeStateChange,
38}
39
40impl ScenarioActionCategory {
41    /// Core の ActionCategory に変換
42    pub fn to_core(&self) -> swarm_engine_core::actions::ActionCategory {
43        match self {
44            Self::NodeExpand => swarm_engine_core::actions::ActionCategory::NodeExpand,
45            Self::NodeStateChange => swarm_engine_core::actions::ActionCategory::NodeStateChange,
46        }
47    }
48}
49
50// ============================================================================
51// Action Definition
52// ============================================================================
53
54/// Action 定義(TOML 用)
55///
56/// シナリオで利用可能なアクションを定義する。
57/// Core の ActionsConfig に変換される。
58#[derive(Debug, Clone, Serialize, Deserialize)]
59pub struct ScenarioActionDef {
60    /// Action 名
61    pub name: String,
62    /// 説明(LLM プロンプト用)
63    pub description: String,
64    /// カテゴリ(探索空間への影響)
65    #[serde(default)]
66    pub category: ScenarioActionCategory,
67    /// パラメータ定義
68    #[serde(default)]
69    pub params: Vec<ActionParamDef>,
70    /// 出力例(LLM プロンプト用 JSON 形式)
71    #[serde(default)]
72    pub example: Option<String>,
73}
74
75// ============================================================================
76// Scenario Actions
77// ============================================================================
78
79/// シナリオの Actions 設定
80///
81/// TOML で定義されたアクションリストを保持し、
82/// Core の ActionsConfig に変換する。
83#[derive(Debug, Clone, Serialize, Deserialize, Default)]
84pub struct ScenarioActions {
85    /// 定義された Action リスト
86    #[serde(default)]
87    pub actions: Vec<ScenarioActionDef>,
88}
89
90impl ScenarioActions {
91    /// Core の ActionsConfig に変換
92    pub fn to_core_config(&self) -> swarm_engine_core::actions::ActionsConfig {
93        use swarm_engine_core::actions::{ActionDef, ActionsConfig, ParamDef};
94
95        let mut config = ActionsConfig::new();
96        for action_def in &self.actions {
97            let mut def = ActionDef::new(&action_def.name, &action_def.description)
98                .category(action_def.category.to_core())
99                .group("default");
100
101            for param in &action_def.params {
102                let param_def = if param.required {
103                    ParamDef::required(&param.name, &param.description)
104                } else {
105                    ParamDef::optional(&param.name, &param.description)
106                };
107                def = def.param(param_def);
108            }
109
110            if let Some(ref ex) = action_def.example {
111                def = def.example(ex);
112            }
113
114            config.add_action(&action_def.name, def);
115        }
116        config
117    }
118
119    /// Action 名のリストを取得
120    pub fn action_names(&self) -> Vec<String> {
121        self.actions.iter().map(|a| a.name.clone()).collect()
122    }
123}
124
125// ============================================================================
126// Tests
127// ============================================================================
128
129#[cfg(test)]
130mod tests {
131    use super::*;
132
133    #[test]
134    fn test_scenario_action_category_default() {
135        let category = ScenarioActionCategory::default();
136        assert_eq!(category, ScenarioActionCategory::NodeExpand);
137    }
138
139    #[test]
140    fn test_scenario_actions_default() {
141        let actions = ScenarioActions::default();
142        assert!(actions.actions.is_empty());
143        assert!(actions.action_names().is_empty());
144    }
145
146    #[test]
147    fn test_scenario_actions_deserialize_toml() {
148        let toml_str = r#"
149            [[actions]]
150            name = "grep"
151            description = "Search for patterns in files"
152            category = "node_expand"
153
154            [[actions.params]]
155            name = "pattern"
156            description = "Search pattern"
157            required = true
158
159            [[actions]]
160            name = "read"
161            description = "Read file contents"
162            category = "node_state_change"
163        "#;
164        let actions: ScenarioActions = toml::from_str(toml_str).unwrap();
165        assert_eq!(actions.actions.len(), 2);
166        assert_eq!(actions.action_names(), vec!["grep", "read"]);
167
168        let grep = &actions.actions[0];
169        assert_eq!(grep.name, "grep");
170        assert_eq!(grep.category, ScenarioActionCategory::NodeExpand);
171        assert_eq!(grep.params.len(), 1);
172        assert!(grep.params[0].required);
173    }
174
175    #[test]
176    fn test_scenario_actions_to_core_config() {
177        let actions = ScenarioActions {
178            actions: vec![ScenarioActionDef {
179                name: "test_action".to_string(),
180                description: "A test action".to_string(),
181                category: ScenarioActionCategory::NodeExpand,
182                params: vec![ActionParamDef {
183                    name: "param1".to_string(),
184                    description: "A parameter".to_string(),
185                    required: true,
186                }],
187                example: Some(r#"{"action": "test_action"}"#.to_string()),
188            }],
189        };
190
191        let core_config = actions.to_core_config();
192        let action_def = core_config.get("test_action");
193        assert!(action_def.is_some());
194    }
195}