use crate::{CoalescentError, Result};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use uuid::Uuid;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct AgentId(Uuid);
impl AgentId {
pub fn new() -> Self {
Self(Uuid::new_v4())
}
pub fn from_string(s: &str) -> Result<Self> {
let uuid = Uuid::parse_str(s)
.map_err(|_| CoalescentError::agent("Invalid agent ID format"))?;
Ok(Self(uuid))
}
pub fn as_uuid(&self) -> &Uuid {
&self.0
}
}
impl Default for AgentId {
fn default() -> Self {
Self::new()
}
}
impl std::fmt::Display for AgentId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AgentCapability {
pub name: String,
pub description: String,
pub performance_score: f64,
pub requirements: HashMap<String, String>,
}
impl AgentCapability {
pub fn new<S: Into<String>>(name: S, description: S, performance_score: f64) -> Result<Self> {
if !(0.0..=1.0).contains(&performance_score) {
return Err(CoalescentError::agent("Performance score must be between 0.0 and 1.0"));
}
Ok(Self {
name: name.into(),
description: description.into(),
performance_score,
requirements: HashMap::new(),
})
}
pub fn with_requirement<K: Into<String>, V: Into<String>>(mut self, key: K, value: V) -> Self {
self.requirements.insert(key.into(), value.into());
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Agent {
pub id: AgentId,
pub name: String,
pub agent_type: String,
pub status: AgentStatus,
pub capabilities: Vec<AgentCapability>,
pub metadata: HashMap<String, String>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum AgentStatus {
Available,
Busy { task_id: Option<String> },
Offline,
Error { message: String },
}
impl Agent {
pub fn new<S: Into<String>>(agent_type: S, name: S) -> Result<Self> {
Ok(Self {
id: AgentId::new(),
name: name.into(),
agent_type: agent_type.into(),
status: AgentStatus::Available,
capabilities: Vec::new(),
metadata: HashMap::new(),
})
}
pub fn with_capabilities<S: Into<String>>(
agent_type: S,
name: S,
capability_names: Vec<&str>
) -> Result<Self> {
let mut agent = Self::new(agent_type, name)?;
for cap_name in capability_names {
let capability = AgentCapability::new(
cap_name,
cap_name, 0.8 )?;
agent.add_capability(capability);
}
Ok(agent)
}
pub fn add_capability(&mut self, capability: AgentCapability) {
self.capabilities.push(capability);
}
pub fn has_capability(&self, capability_name: &str) -> bool {
self.capabilities.iter().any(|cap| cap.name == capability_name)
}
pub fn get_capability(&self, capability_name: &str) -> Option<&AgentCapability> {
self.capabilities.iter().find(|cap| cap.name == capability_name)
}
pub fn set_status(&mut self, status: AgentStatus) {
self.status = status;
}
pub fn is_available(&self) -> bool {
matches!(self.status, AgentStatus::Available)
}
pub fn add_metadata<K: Into<String>, V: Into<String>>(&mut self, key: K, value: V) {
self.metadata.insert(key.into(), value.into());
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_agent_creation() -> Result<()> {
let agent = Agent::new("gpt-4", "Test Agent")?;
assert_eq!(agent.agent_type, "gpt-4");
assert_eq!(agent.name, "Test Agent");
assert!(agent.is_available());
Ok(())
}
#[test]
fn test_capability_management() -> Result<()> {
let mut agent = Agent::new("test", "Test Agent")?;
let capability = AgentCapability::new("text_generation", "Generate text", 0.9)?;
agent.add_capability(capability);
assert!(agent.has_capability("text_generation"));
assert!(!agent.has_capability("image_generation"));
Ok(())
}
#[test]
fn test_agent_id_operations() -> Result<()> {
let id1 = AgentId::new();
let id2 = AgentId::new();
assert_ne!(id1, id2);
let id_str = id1.to_string();
let id3 = AgentId::from_string(&id_str)?;
assert_eq!(id1, id3);
Ok(())
}
}