mod qudag_stubs;
use std::collections::HashMap;
use serde::{Deserialize, Serialize};
use thiserror::Error;
use uuid::Uuid;
pub use crate::qudag_stubs::qudag_mcp::{MCPClient, MCPMessage, MCPError, Tool, ToolCall, ToolResult};
pub mod claude;
pub mod agents;
pub mod tools;
pub mod tasks;
pub mod memory;
#[cfg(feature = "rules-integration")]
pub mod rules_integration;
#[cfg(feature = "database")]
pub mod database;
#[derive(Error, Debug)]
pub enum AIError {
#[error("MCP error: {0}")]
MCP(#[from] MCPError),
#[error("Claude API error: {0}")]
Claude(String),
#[error("Agent not found: {0}")]
AgentNotFound(String),
#[error("Task execution error: {0}")]
TaskExecution(String),
#[error("Tool error: {0}")]
Tool(String),
#[error("Memory error: {0}")]
Memory(String),
#[error("Configuration error: {0}")]
Configuration(String),
#[error("Network error: {0}")]
Network(#[from] reqwest::Error),
#[error("Serialization error: {0}")]
Serialization(#[from] serde_json::Error),
}
pub type Result<T> = std::result::Result<T, AIError>;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AIConfig {
pub claude: claude::ClaudeConfig,
pub mcp: MCPClientConfig,
pub agents: AgentConfig,
pub memory: MemoryConfig,
#[cfg(feature = "database")]
pub database_url: Option<String>,
}
impl Default for AIConfig {
fn default() -> Self {
Self {
claude: claude::ClaudeConfig::default(),
mcp: MCPClientConfig::default(),
agents: AgentConfig::default(),
memory: MemoryConfig::default(),
#[cfg(feature = "database")]
database_url: None,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MCPClientConfig {
pub server_url: String,
pub timeout: u64,
pub max_connections: usize,
pub retry_attempts: u32,
pub available_tools: Vec<String>,
}
impl Default for MCPClientConfig {
fn default() -> Self {
Self {
server_url: "http://localhost:3000".to_string(),
timeout: 30,
max_connections: 10,
retry_attempts: 3,
available_tools: vec![
"code_execution".to_string(),
"file_operations".to_string(),
"web_search".to_string(),
"data_analysis".to_string(),
],
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AgentConfig {
pub max_agents: usize,
pub default_capabilities: Vec<String>,
pub spawn_config: SpawnConfig,
}
impl Default for AgentConfig {
fn default() -> Self {
Self {
max_agents: 100,
default_capabilities: vec![
"reasoning".to_string(),
"code_generation".to_string(),
"data_analysis".to_string(),
"communication".to_string(),
],
spawn_config: SpawnConfig::default(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SpawnConfig {
pub default_model: String,
pub default_system_prompt: String,
pub default_temperature: f32,
pub max_tokens: u32,
}
impl Default for SpawnConfig {
fn default() -> Self {
Self {
default_model: "claude-3-opus-20240229".to_string(),
default_system_prompt: "You are a helpful AI agent in the DAA system. You can execute tasks, analyze data, and collaborate with other agents.".to_string(),
default_temperature: 0.7,
max_tokens: 4096,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MemoryConfig {
pub max_entries_per_agent: usize,
pub retention_hours: u64,
pub persistent: bool,
}
impl Default for MemoryConfig {
fn default() -> Self {
Self {
max_entries_per_agent: 1000,
retention_hours: 24 * 7, persistent: true,
}
}
}
pub struct AISystem {
config: AIConfig,
mcp_client: MCPClient,
claude_client: claude::ClaudeClient,
agent_manager: agents::AgentManager,
task_manager: tasks::TaskManager,
tool_registry: tools::ToolRegistry,
memory: memory::MemorySystem,
#[cfg(feature = "database")]
database: Option<database::DatabaseManager>,
}
impl AISystem {
pub async fn new(config: AIConfig) -> Result<Self> {
let mcp_client = MCPClient::new(&config.mcp.server_url).await
.map_err(AIError::MCP)?;
let claude_client = claude::ClaudeClient::new(config.claude.clone()).await?;
let agent_manager = agents::AgentManager::new(config.agents.clone());
let task_manager = tasks::TaskManager::new();
let tool_registry = tools::ToolRegistry::new();
let memory = memory::MemorySystem::new(config.memory.clone());
#[cfg(feature = "database")]
let database = if let Some(db_url) = &config.database_url {
Some(database::DatabaseManager::new(db_url).await?)
} else {
None
};
Ok(Self {
config,
mcp_client,
claude_client,
agent_manager,
task_manager,
tool_registry,
memory,
#[cfg(feature = "database")]
database,
})
}
pub async fn initialize(&mut self) -> Result<()> {
tracing::info!("Initializing DAA AI System");
self.mcp_client.connect().await.map_err(AIError::MCP)?;
self.register_default_tools().await?;
self.memory.initialize().await?;
#[cfg(feature = "database")]
if let Some(db) = &mut self.database {
db.initialize().await?;
}
tracing::info!("DAA AI System initialized successfully");
Ok(())
}
pub async fn spawn_agent(
&mut self,
agent_type: agents::AgentType,
capabilities: Option<Vec<String>>,
custom_config: Option<HashMap<String, String>>,
) -> Result<String> {
let agent = self.agent_manager.spawn_agent(
agent_type,
capabilities.unwrap_or_else(|| self.config.agents.default_capabilities.clone()),
custom_config,
).await?;
self.memory.store_agent_metadata(&agent.id, &agent).await?;
tracing::info!("Spawned new agent: {} ({})", agent.id, agent.agent_type);
Ok(agent.id)
}
pub async fn execute_task(
&mut self,
agent_id: &str,
task: tasks::Task,
) -> Result<tasks::TaskResult> {
let agent = self.agent_manager.get_agent(agent_id).await?;
let result = self.claude_client.execute_task(&agent, &task).await?;
self.memory.store_task_result(agent_id, &task.id, &result).await?;
#[cfg(feature = "database")]
if let Some(db) = &mut self.database {
db.record_task_execution(agent_id, &task, &result).await?;
}
tracing::info!("Task {} executed by agent {}", task.id, agent_id);
Ok(result)
}
pub async fn use_tool(
&mut self,
agent_id: &str,
tool_name: &str,
parameters: HashMap<String, serde_json::Value>,
) -> Result<ToolResult> {
let tool_call = ToolCall {
id: Uuid::new_v4().to_string(),
name: tool_name.to_string(),
parameters,
};
let result = self.mcp_client.call_tool(tool_call).await
.map_err(AIError::MCP)?;
self.memory.store_tool_usage(agent_id, tool_name, &result).await?;
tracing::info!("Tool {} used by agent {}", tool_name, agent_id);
Ok(result)
}
pub async fn get_agent_memory(&self, agent_id: &str) -> Result<Vec<memory::MemoryEntry>> {
self.memory.get_agent_memory(agent_id).await
}
pub async fn store_memory(
&mut self,
agent_id: &str,
key: String,
data: serde_json::Value,
metadata: Option<HashMap<String, String>>,
) -> Result<()> {
self.memory.store(agent_id, key, data, metadata).await
}
pub async fn get_statistics(&self) -> AIStatistics {
AIStatistics {
total_agents: self.agent_manager.get_agent_count().await,
active_tasks: self.task_manager.get_active_task_count().await,
total_tools: self.tool_registry.get_tool_count().await,
memory_entries: self.memory.get_total_entries().await,
uptime_seconds: 0, }
}
async fn register_default_tools(&mut self) -> Result<()> {
for tool_name in &self.config.mcp.available_tools {
let tool = tools::create_default_tool(tool_name)?;
self.tool_registry.register_tool(tool).await?;
}
Ok(())
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AIStatistics {
pub total_agents: u64,
pub active_tasks: u64,
pub total_tools: u64,
pub memory_entries: u64,
pub uptime_seconds: u64,
}
impl std::fmt::Display for AIStatistics {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"AI Stats: Agents={}, Active Tasks={}, Tools={}, Memory={}, Uptime={}s",
self.total_agents,
self.active_tasks,
self.total_tools,
self.memory_entries,
self.uptime_seconds
)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_ai_system_creation() {
let config = AIConfig::default();
assert!(true); }
#[test]
fn test_config_defaults() {
let config = AIConfig::default();
assert_eq!(config.claude.model, "claude-3-opus-20240229");
assert_eq!(config.mcp.timeout, 30);
assert_eq!(config.agents.max_agents, 100);
}
#[test]
fn test_statistics_display() {
let stats = AIStatistics {
total_agents: 5,
active_tasks: 3,
total_tools: 10,
memory_entries: 100,
uptime_seconds: 3600,
};
let display = stats.to_string();
assert!(display.contains("Agents=5"));
assert!(display.contains("Active Tasks=3"));
assert!(display.contains("Tools=10"));
}
}