portalis_core/
agent.rs

1//! Agent trait and core abstractions
2//!
3//! Defines the Agent trait that all specialized agents implement.
4//! Following London School TDD: agents are tested via mocked interactions.
5
6use async_trait::async_trait;
7use serde::{Deserialize, Serialize};
8use std::fmt;
9use uuid::Uuid;
10
11use crate::Result;
12
13/// Unique identifier for agents
14#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
15pub struct AgentId(Uuid);
16
17impl AgentId {
18    /// Create a new random agent ID
19    pub fn new() -> Self {
20        Self(Uuid::new_v4())
21    }
22}
23
24impl Default for AgentId {
25    fn default() -> Self {
26        Self::new()
27    }
28}
29
30impl fmt::Display for AgentId {
31    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
32        write!(f, "{}", self.0)
33    }
34}
35
36/// Capabilities that agents can provide
37#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
38pub enum AgentCapability {
39    /// Parse Python source code into AST
40    Parsing,
41    /// Analyze code and infer types
42    TypeInference,
43    /// Extract API contracts
44    ApiExtraction,
45    /// Generate Rust specifications
46    SpecificationGeneration,
47    /// Transpile Python to Rust
48    CodeGeneration,
49    /// Compile Rust to WASM
50    Compilation,
51    /// Execute tests and validate
52    Testing,
53    /// Package artifacts for deployment
54    Packaging,
55}
56
57/// Core Agent trait that all specialized agents implement
58///
59/// Following London School TDD:
60/// - Agents communicate via message passing
61/// - Dependencies are injected and easily mocked
62/// - Behavior is tested via interaction testing
63#[async_trait]
64pub trait Agent: Send + Sync {
65    /// Input type for this agent
66    type Input: Send + Sync;
67
68    /// Output type for this agent
69    type Output: Send + Sync;
70
71    /// Execute the agent's primary function
72    ///
73    /// This is the main entry point for agent execution.
74    /// Implementations should be pure and side-effect free where possible.
75    async fn execute(&self, input: Self::Input) -> Result<Self::Output>;
76
77    /// Get the agent's unique identifier
78    fn id(&self) -> AgentId;
79
80    /// Get the agent's human-readable name
81    fn name(&self) -> &str;
82
83    /// Get the capabilities this agent provides
84    fn capabilities(&self) -> Vec<AgentCapability>;
85
86    /// Validate that the agent can handle the given input
87    ///
88    /// Default implementation always returns Ok(())
89    fn validate_input(&self, _input: &Self::Input) -> Result<()> {
90        Ok(())
91    }
92}
93
94/// Agent metadata for registration and discovery
95#[derive(Debug, Clone, Serialize, Deserialize)]
96pub struct AgentMetadata {
97    pub id: AgentId,
98    pub name: String,
99    pub capabilities: Vec<AgentCapability>,
100    pub version: String,
101}
102
103impl AgentMetadata {
104    /// Create new agent metadata
105    pub fn new(id: AgentId, name: impl Into<String>, capabilities: Vec<AgentCapability>) -> Self {
106        Self {
107            id,
108            name: name.into(),
109            capabilities,
110            version: env!("CARGO_PKG_VERSION").to_string(),
111        }
112    }
113}
114
115#[cfg(test)]
116mod tests {
117    use super::*;
118
119    #[test]
120    fn test_agent_id_creation() {
121        let id1 = AgentId::new();
122        let id2 = AgentId::new();
123        assert_ne!(id1, id2);
124    }
125
126    #[test]
127    fn test_agent_id_display() {
128        let id = AgentId::new();
129        let display = format!("{}", id);
130        assert!(!display.is_empty());
131    }
132
133    #[test]
134    fn test_agent_metadata_creation() {
135        let id = AgentId::new();
136        let metadata = AgentMetadata::new(
137            id,
138            "TestAgent",
139            vec![AgentCapability::Parsing],
140        );
141
142        assert_eq!(metadata.id, id);
143        assert_eq!(metadata.name, "TestAgent");
144        assert_eq!(metadata.capabilities.len(), 1);
145    }
146}