use crate::budget::{BudgetExceeded, BudgetInfo, BudgetLimit, BudgetManager};
use crate::memory::store::{HnswMemoryIndex, SemanticHit};
use crate::memory::{MemoryEntry, MemoryManager, MemoryType};
use crate::supervisor::Supervisor;
use crate::types::AgentId;
use std::sync::Arc;
pub struct AgentApi {
pub(crate) supervisor: Arc<dyn Supervisor>,
pub(crate) budget_manager: Arc<BudgetManager>,
pub(crate) memory_manager: Arc<MemoryManager>,
pub(crate) hnsw_index: Option<Arc<HnswMemoryIndex>>,
}
impl AgentApi {
pub fn new(
supervisor: Arc<dyn Supervisor>,
budget_manager: Arc<BudgetManager>,
memory_manager: Arc<MemoryManager>,
) -> Self {
Self {
supervisor,
budget_manager,
memory_manager,
hnsw_index: None,
}
}
pub fn set_hnsw_index(&mut self, index: Arc<HnswMemoryIndex>) {
self.hnsw_index = Some(index);
}
pub async fn list(&self) -> anyhow::Result<Vec<crate::types::AgentInfo>> {
self.supervisor
.list()
.await
.map_err(|e| anyhow::anyhow!("supervisor: {e}"))
}
pub async fn kill(&self, agent_id: &str) -> anyhow::Result<()> {
let id = uuid::Uuid::parse_str(agent_id)
.map_err(|e| anyhow::anyhow!("invalid agent id: {e}"))?;
self.supervisor
.kill(id)
.await
.map_err(|e| anyhow::anyhow!("supervisor: {e}"))
}
pub fn check_budget(&self, agent_id: &AgentId) -> BudgetInfo {
self.budget_manager.remaining(agent_id)
}
pub fn set_budget(&self, limit: BudgetLimit) {
self.budget_manager.set_budget(limit);
}
pub fn remove_budget(&self, agent_id: &AgentId) {
self.budget_manager.remove_budget(agent_id);
}
pub fn reserve_budget(&self, agent_id: &AgentId, tokens: u64) -> Result<(), BudgetExceeded> {
self.budget_manager.reserve(agent_id, tokens)
}
pub fn reset_budget(&self, agent_id: &AgentId) {
self.budget_manager.reset_window(agent_id);
}
pub async fn memory_stats(&self) -> (usize, usize) {
(
self.memory_manager.vector_index_size(),
self.memory_manager.total_entries().await,
)
}
pub async fn remember(&self, entry: MemoryEntry) -> anyhow::Result<String> {
self.memory_manager.remember(entry).await
}
pub async fn search_memory(
&self,
query: &str,
memory_type: Option<MemoryType>,
limit: usize,
) -> anyhow::Result<Vec<MemoryEntry>> {
self.memory_manager.search(query, memory_type, limit).await
}
pub async fn semantic_search_memory(
&self,
query: &str,
memory_type: Option<MemoryType>,
limit: usize,
) -> anyhow::Result<Vec<SemanticHit>> {
if let Some(ref hnsw) = self.hnsw_index {
self.memory_manager
.semantic_search(query, memory_type, limit, hnsw)
.await
} else {
let entries = self.search_memory(query, memory_type, limit).await?;
Ok(entries
.into_iter()
.map(|entry| SemanticHit {
entry,
distance: 0.0,
similarity: 0.0,
})
.collect())
}
}
pub fn memory_manager(&self) -> &Arc<MemoryManager> {
&self.memory_manager
}
pub async fn rebuild_hnsw_index(&self) -> anyhow::Result<usize> {
if let Some(ref hnsw) = self.hnsw_index {
self.memory_manager.rebuild_hnsw_index(hnsw).await
} else {
Err(anyhow::anyhow!("HNSW index not initialized"))
}
}
}