#![allow(dead_code)]
use crate::agency::agent::Agent;
use crate::agency::error::{AgencyError, AgencyResult};
use crate::agency::executor::{ExecutionContext, ExecutionResult, Executor};
use crate::agency::models::AgencyEvent;
use crate::agency::orchestrator::{Orchestrator, OrchestratorResult, Pipeline, Swarm};
use crate::agency::session::{Session, SessionManager};
use crate::agency::tools::ToolRegistry;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::path::PathBuf;
use std::sync::Arc;
use tokio::sync::mpsc;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RuntimeConfig {
pub db_path: PathBuf,
pub default_model: String,
pub max_tool_calls: u32,
pub timeout_seconds: u64,
pub streaming: bool,
#[serde(default, skip_serializing_if = "HashMap::is_empty")]
pub api_keys: HashMap<String, String>,
}
impl Default for RuntimeConfig {
fn default() -> Self {
Self {
db_path: PathBuf::from("Agency_sessions.db"),
default_model: "gemini-2.5-flash".to_string(),
max_tool_calls: 10,
timeout_seconds: 120,
streaming: true,
api_keys: HashMap::new(),
}
}
}
pub struct Runtime {
config: RuntimeConfig,
tool_registry: Arc<ToolRegistry>,
session_manager: Arc<SessionManager>,
executor: Arc<Executor>,
orchestrator: Orchestrator,
agents: HashMap<String, Arc<Agent>>,
}
impl Runtime {
pub fn new() -> AgencyResult<Self> {
Self::with_config(RuntimeConfig::default())
}
pub fn with_config(config: RuntimeConfig) -> AgencyResult<Self> {
let tool_registry = Arc::new(ToolRegistry::with_builtins());
let session_manager = Arc::new(SessionManager::new(&config.db_path)?);
let executor = Arc::new(Executor::new(tool_registry.clone()));
let orchestrator = Orchestrator::new(executor.clone());
Ok(Self {
config,
tool_registry,
session_manager,
executor,
orchestrator,
agents: HashMap::new(),
})
}
pub fn in_memory() -> AgencyResult<Self> {
let tool_registry = Arc::new(ToolRegistry::with_builtins());
let session_manager = Arc::new(SessionManager::in_memory()?);
let executor = Arc::new(Executor::new(tool_registry.clone()));
let orchestrator = Orchestrator::new(executor.clone());
Ok(Self {
config: RuntimeConfig::default(),
tool_registry,
session_manager,
executor,
orchestrator,
agents: HashMap::new(),
})
}
pub fn register_agent(&mut self, agent: Agent) {
self.agents
.insert(agent.name().to_string(), Arc::new(agent));
}
pub fn get_agent(&self, name: &str) -> Option<&Arc<Agent>> {
self.agents.get(name)
}
pub fn list_agents(&self) -> Vec<&str> {
self.agents.keys().map(|s| s.as_str()).collect()
}
pub fn tools(&self) -> &ToolRegistry {
&self.tool_registry
}
pub fn sessions(&self) -> &SessionManager {
&self.session_manager
}
pub async fn run(
&self,
agent_name: &str,
message: &str,
options: Option<RunOptions>,
) -> AgencyResult<ExecutionResult> {
let options = options.unwrap_or_default();
let agent_arc = self
.agents
.get(agent_name)
.ok_or_else(|| AgencyError::AgentNotFound(agent_name.to_string()))?;
let mut session = if let Some(session_id) = &options.session_id {
self.session_manager
.get(session_id)?
.ok_or_else(|| AgencyError::SessionNotFound(session_id.clone()))?
} else {
self.session_manager
.create(agent_name, options.user_id.clone())?
};
let mut ctx = ExecutionContext::new(&session);
ctx.user_id = options.user_id;
ctx.allow_tools = options.allow_tools;
ctx.max_tool_calls = options.max_tool_calls.unwrap_or(self.config.max_tool_calls);
ctx.event_sender = options.event_sender;
let result = self
.executor
.execute(agent_arc.as_ref(), &mut session, message, &mut ctx)
.await?;
self.session_manager.save(&session)?;
Ok(result)
}
pub async fn run_stream(
&self,
agent_name: &str,
message: &str,
options: Option<RunOptions>,
) -> AgencyResult<(ExecutionResult, mpsc::Receiver<AgencyEvent>)> {
let (tx, rx) = mpsc::channel(100);
let mut options = options.unwrap_or_default();
options.event_sender = Some(tx);
let result = self.run(agent_name, message, Some(options)).await?;
Ok((result, rx))
}
pub async fn run_pipeline(
&self,
pipeline: &Pipeline,
input: &str,
options: Option<RunOptions>,
) -> AgencyResult<OrchestratorResult> {
let options = options.unwrap_or_default();
let session = Session::new(&pipeline.name, options.user_id.clone());
let mut ctx = ExecutionContext::new(&session);
ctx.user_id = options.user_id;
ctx.allow_tools = options.allow_tools;
ctx.event_sender = options.event_sender;
self.orchestrator
.run_pipeline(pipeline, input, &mut ctx)
.await
}
pub async fn run_swarm(
&self,
swarm: &Swarm,
input: &str,
options: Option<RunOptions>,
) -> AgencyResult<OrchestratorResult> {
let options = options.unwrap_or_default();
let session = Session::new(&swarm.name, options.user_id.clone());
let mut ctx = ExecutionContext::new(&session);
ctx.user_id = options.user_id;
ctx.allow_tools = options.allow_tools;
ctx.event_sender = options.event_sender;
self.orchestrator.run_swarm(swarm, input, &mut ctx).await
}
pub fn create_session(
&self,
agent_name: &str,
user_id: Option<String>,
) -> AgencyResult<Session> {
self.session_manager.create(agent_name, user_id)
}
pub fn get_session(&self, session_id: &str) -> AgencyResult<Option<Session>> {
self.session_manager.get(session_id)
}
pub fn list_sessions(
&self,
agent_name: &str,
limit: Option<u32>,
) -> AgencyResult<Vec<Session>> {
self.session_manager.list_by_agent(agent_name, limit)
}
pub fn delete_session(&self, session_id: &str) -> AgencyResult<bool> {
self.session_manager.delete(session_id)
}
}
#[derive(Debug, Clone, Default)]
pub struct RunOptions {
pub session_id: Option<String>,
pub user_id: Option<String>,
pub allow_tools: bool,
pub max_tool_calls: Option<u32>,
pub event_sender: Option<mpsc::Sender<AgencyEvent>>,
}
impl RunOptions {
pub fn new() -> Self {
Self {
allow_tools: true,
..Default::default()
}
}
pub fn with_session(mut self, session_id: impl Into<String>) -> Self {
self.session_id = Some(session_id.into());
self
}
pub fn with_user(mut self, user_id: impl Into<String>) -> Self {
self.user_id = Some(user_id.into());
self
}
pub fn with_tools(mut self, allow: bool) -> Self {
self.allow_tools = allow;
self
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::agency::agent::AgentBuilder;
#[tokio::test]
#[ignore = "Integration test - requires API credentials"]
async fn test_runtime() -> AgencyResult<()> {
let mut runtime = Runtime::in_memory()?;
let agent = AgentBuilder::new("assistant")
.description("A helpful assistant")
.instruction("You are a helpful AI assistant.")
.model("gemini-2.5-flash")
.build();
runtime.register_agent(agent);
let result = runtime.run("assistant", "Hello!", None).await?;
assert!(result.success);
assert!(!result.response.is_empty());
Ok(())
}
#[tokio::test]
#[ignore = "Integration test - requires API credentials"]
async fn test_runtime_sessions() -> AgencyResult<()> {
let mut runtime = Runtime::in_memory()?;
let agent = AgentBuilder::new("test_agent")
.instruction("You are a test agent.")
.build();
runtime.register_agent(agent);
let session = runtime.create_session("test_agent", Some("user1".to_string()))?;
assert_eq!(session.agent_name, "test_agent");
let options = RunOptions::new()
.with_session(&session.id)
.with_user("user1");
let result = runtime
.run("test_agent", "Test message", Some(options))
.await?;
assert!(result.success);
let updated_session = runtime.get_session(&session.id)?.unwrap();
assert!(!updated_session.messages.is_empty());
Ok(())
}
}