Skip to main content

evo_common/
messages.rs

1use serde::{Deserialize, Serialize};
2use std::collections::HashMap;
3
4#[derive(Debug, Clone, Serialize, Deserialize)]
5pub struct AgentRegister {
6    pub agent_id: String,
7    pub role: AgentRole,
8    pub capabilities: Vec<String>,
9}
10
11#[derive(Debug, Clone, Serialize, Deserialize)]
12pub struct AgentStatus {
13    pub agent_id: String,
14    pub status: RunnerStatus,
15    pub metrics: HashMap<String, serde_json::Value>,
16}
17
18#[derive(Debug, Clone, Serialize, Deserialize)]
19pub struct AgentSkillReport {
20    pub agent_id: String,
21    pub skill_id: String,
22    pub result: SkillResult,
23    pub score: Option<f64>,
24}
25
26#[derive(Debug, Clone, Serialize, Deserialize)]
27pub struct AgentHealth {
28    pub agent_id: String,
29    pub health_checks: Vec<HealthCheck>,
30}
31
32#[derive(Debug, Clone, Serialize, Deserialize)]
33pub struct KingCommand {
34    pub command: String,
35    pub target_agent: String,
36    pub params: HashMap<String, serde_json::Value>,
37}
38
39#[derive(Debug, Clone, Serialize, Deserialize)]
40pub struct KingConfigUpdate {
41    pub config_type: String,
42    pub new_config_hash: String,
43}
44
45#[derive(Debug, Clone, Serialize, Deserialize)]
46pub struct PipelineNext {
47    pub stage: PipelineStage,
48    pub artifact_id: String,
49    pub metadata: HashMap<String, serde_json::Value>,
50}
51
52#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
53#[serde(rename_all = "snake_case")]
54pub enum AgentRole {
55    SkillManage,
56    Learning,
57    PreLoad,
58    Building,
59    Evaluation,
60    User(String),
61}
62
63#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
64#[serde(rename_all = "snake_case")]
65pub enum RunnerStatus {
66    Starting,
67    Ready,
68    Busy,
69    Error,
70    Shutting,
71}
72
73#[derive(Debug, Clone, Serialize, Deserialize)]
74#[serde(rename_all = "snake_case")]
75pub enum SkillResult {
76    Success,
77    Failure(String),
78    Partial(String),
79}
80
81#[derive(Debug, Clone, Serialize, Deserialize)]
82pub struct HealthCheck {
83    pub name: String,
84    pub endpoint: String,
85    pub healthy: bool,
86    pub latency_ms: Option<u64>,
87    pub error: Option<String>,
88}
89
90#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
91#[serde(rename_all = "snake_case")]
92pub enum PipelineStage {
93    Learning,
94    Building,
95    PreLoad,
96    Evaluation,
97    SkillManage,
98}
99
100#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
101#[serde(rename_all = "snake_case")]
102pub enum PipelineRunStatus {
103    Running,
104    Completed,
105    Failed,
106    TimedOut,
107}
108
109/// Agent reports completion of a pipeline stage back to king.
110#[derive(Debug, Clone, Serialize, Deserialize)]
111pub struct PipelineStageResult {
112    pub run_id: String,
113    pub stage: PipelineStage,
114    pub agent_id: String,
115    pub status: PipelineRunStatus,
116    pub artifact_id: String,
117    pub output: serde_json::Value,
118    pub error: Option<String>,
119}
120
121#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
122#[serde(rename_all = "snake_case")]
123pub enum TaskStatus {
124    Pending,
125    InProgress,
126    Completed,
127    Failed,
128    Cancelled,
129}
130
131// ─── Task management messages ────────────────────────────────────────────────
132
133#[derive(Debug, Clone, Serialize, Deserialize)]
134pub struct TaskCreate {
135    pub task_type: String,
136    #[serde(default)]
137    pub agent_id: Option<String>,
138    #[serde(default = "default_empty_object")]
139    pub payload: serde_json::Value,
140}
141
142#[derive(Debug, Clone, Serialize, Deserialize)]
143pub struct TaskUpdate {
144    pub task_id: String,
145    #[serde(default)]
146    pub status: Option<TaskStatus>,
147    #[serde(default)]
148    pub agent_id: Option<String>,
149    #[serde(default)]
150    pub payload: Option<serde_json::Value>,
151}
152
153#[derive(Debug, Clone, Serialize, Deserialize)]
154pub struct TaskGet {
155    pub task_id: String,
156}
157
158#[derive(Debug, Clone, Serialize, Deserialize)]
159pub struct TaskList {
160    #[serde(default = "default_task_limit")]
161    pub limit: u32,
162    #[serde(default)]
163    pub status: Option<TaskStatus>,
164    #[serde(default)]
165    pub agent_id: Option<String>,
166}
167
168#[derive(Debug, Clone, Serialize, Deserialize)]
169pub struct TaskDelete {
170    pub task_id: String,
171}
172
173#[derive(Debug, Clone, Serialize, Deserialize)]
174pub struct TaskRecord {
175    pub id: String,
176    pub task_type: String,
177    pub status: String,
178    pub agent_id: String,
179    pub payload: serde_json::Value,
180    pub created_at: String,
181    pub updated_at: String,
182}
183
184fn default_task_limit() -> u32 {
185    50
186}
187
188fn default_empty_object() -> serde_json::Value {
189    serde_json::Value::Object(serde_json::Map::new())
190}
191
192pub mod events {
193    pub const AGENT_REGISTER: &str = "agent:register";
194    pub const AGENT_STATUS: &str = "agent:status";
195    pub const AGENT_SKILL_REPORT: &str = "agent:skill_report";
196    pub const AGENT_HEALTH: &str = "agent:health";
197    pub const KING_COMMAND: &str = "king:command";
198    pub const KING_CONFIG_UPDATE: &str = "king:config_update";
199    pub const PIPELINE_NEXT: &str = "pipeline:next";
200
201    // Task management events
202    pub const TASK_CREATE: &str = "task:create";
203    pub const TASK_UPDATE: &str = "task:update";
204    pub const TASK_GET: &str = "task:get";
205    pub const TASK_LIST: &str = "task:list";
206    pub const TASK_DELETE: &str = "task:delete";
207    pub const TASK_CHANGED: &str = "task:changed";
208
209    // Pipeline coordination events
210    pub const PIPELINE_STAGE_RESULT: &str = "pipeline:stage_result";
211
212    // Rooms
213    pub const ROOM_KERNEL: &str = "kernel";
214    pub const ROOM_ROLE_PREFIX: &str = "role:";
215}
216
217#[cfg(test)]
218mod tests {
219    use super::*;
220
221    #[test]
222    fn serialize_agent_register() {
223        let msg = AgentRegister {
224            agent_id: "learning-001".into(),
225            role: AgentRole::Learning,
226            capabilities: vec!["discover".into(), "evaluate".into()],
227        };
228        let json = serde_json::to_string(&msg).unwrap();
229        let deserialized: AgentRegister = serde_json::from_str(&json).unwrap();
230        assert_eq!(deserialized.agent_id, "learning-001");
231        assert_eq!(deserialized.role, AgentRole::Learning);
232    }
233
234    #[test]
235    fn serialize_pipeline_next() {
236        let msg = PipelineNext {
237            stage: PipelineStage::Building,
238            artifact_id: "skill-xyz".into(),
239            metadata: HashMap::new(),
240        };
241        let json = serde_json::to_string(&msg).unwrap();
242        let deserialized: PipelineNext = serde_json::from_str(&json).unwrap();
243        assert_eq!(deserialized.stage, PipelineStage::Building);
244    }
245
246    #[test]
247    fn serialize_task_status() {
248        let status = TaskStatus::InProgress;
249        let json = serde_json::to_string(&status).unwrap();
250        assert_eq!(json, r#""in_progress""#);
251        let de: TaskStatus = serde_json::from_str(&json).unwrap();
252        assert_eq!(de, TaskStatus::InProgress);
253    }
254
255    #[test]
256    fn serialize_task_create() {
257        let msg = TaskCreate {
258            task_type: "build".into(),
259            agent_id: Some("building-001".into()),
260            payload: serde_json::json!({"skill_id": "web-search"}),
261        };
262        let json = serde_json::to_string(&msg).unwrap();
263        let de: TaskCreate = serde_json::from_str(&json).unwrap();
264        assert_eq!(de.task_type, "build");
265        assert_eq!(de.agent_id.unwrap(), "building-001");
266    }
267
268    #[test]
269    fn deserialize_task_list_defaults() {
270        let msg: TaskList = serde_json::from_str("{}").unwrap();
271        assert_eq!(msg.limit, 50);
272        assert!(msg.status.is_none());
273        assert!(msg.agent_id.is_none());
274    }
275
276    #[test]
277    fn serialize_pipeline_run_status() {
278        let status = PipelineRunStatus::Running;
279        let json = serde_json::to_string(&status).unwrap();
280        assert_eq!(json, r#""running""#);
281        let de: PipelineRunStatus = serde_json::from_str(&json).unwrap();
282        assert_eq!(de, PipelineRunStatus::Running);
283
284        let timed_out = PipelineRunStatus::TimedOut;
285        let json = serde_json::to_string(&timed_out).unwrap();
286        assert_eq!(json, r#""timed_out""#);
287    }
288
289    #[test]
290    fn serialize_pipeline_stage_result() {
291        let result = PipelineStageResult {
292            run_id: "run-001".into(),
293            stage: PipelineStage::Learning,
294            agent_id: "learning-001".into(),
295            status: PipelineRunStatus::Completed,
296            artifact_id: "artifact-xyz".into(),
297            output: serde_json::json!({"candidates": 3}),
298            error: None,
299        };
300        let json = serde_json::to_string(&result).unwrap();
301        let de: PipelineStageResult = serde_json::from_str(&json).unwrap();
302        assert_eq!(de.run_id, "run-001");
303        assert_eq!(de.stage, PipelineStage::Learning);
304        assert_eq!(de.status, PipelineRunStatus::Completed);
305        assert!(de.error.is_none());
306    }
307
308    #[test]
309    fn serialize_pipeline_stage_result_with_error() {
310        let result = PipelineStageResult {
311            run_id: "run-002".into(),
312            stage: PipelineStage::Building,
313            agent_id: "building-001".into(),
314            status: PipelineRunStatus::Failed,
315            artifact_id: "".into(),
316            output: serde_json::Value::Null,
317            error: Some("build failed: missing dependency".into()),
318        };
319        let json = serde_json::to_string(&result).unwrap();
320        let de: PipelineStageResult = serde_json::from_str(&json).unwrap();
321        assert_eq!(de.status, PipelineRunStatus::Failed);
322        assert_eq!(de.error.unwrap(), "build failed: missing dependency");
323    }
324
325    #[test]
326    fn serialize_task_update_partial() {
327        let msg = TaskUpdate {
328            task_id: "abc-123".into(),
329            status: Some(TaskStatus::Completed),
330            agent_id: None,
331            payload: None,
332        };
333        let json = serde_json::to_string(&msg).unwrap();
334        let de: TaskUpdate = serde_json::from_str(&json).unwrap();
335        assert_eq!(de.task_id, "abc-123");
336        assert_eq!(de.status, Some(TaskStatus::Completed));
337        assert!(de.agent_id.is_none());
338    }
339}