sim-lib-agent 0.1.1

Agent runtime surfaces for SIM.
Documentation
use super::{Agent, AgentFabric, RuntimeValueSite};
use crate::{
    AgentComponent, BlackboardMemory, FileMemory, PersonaMemory, Tool, VectorMemory, WorkingMemory,
};
use sim_kernel::{Cx, Error, Result, Symbol, Value};
use sim_lib_server::{Connection, EvalSite, ResolvedAddress, ServerAddress};
use std::{
    collections::BTreeMap,
    sync::{Arc, Mutex, OnceLock},
};

#[derive(Clone)]
struct StartedAgent {
    site: Arc<dyn EvalSite>,
    codecs: Vec<Symbol>,
    default_codec: Symbol,
}

fn started_agents() -> &'static Mutex<BTreeMap<String, StartedAgent>> {
    static STARTED: OnceLock<Mutex<BTreeMap<String, StartedAgent>>> = OnceLock::new();
    STARTED.get_or_init(|| Mutex::new(BTreeMap::new()))
}

pub(crate) fn site_from_value(value: &Value) -> Result<Arc<dyn EvalSite>> {
    if let Some(tool) = value.object().downcast_ref::<Tool>() {
        return Ok(Arc::new(tool.clone()));
    }
    if let Some(component) = value.object().downcast_ref::<AgentComponent>() {
        return Ok(Arc::new(component.clone()));
    }
    if let Some(memory) = value.object().downcast_ref::<WorkingMemory>() {
        return Ok(Arc::new(memory.clone()));
    }
    if let Some(memory) = value.object().downcast_ref::<FileMemory>() {
        return Ok(Arc::new(memory.clone()));
    }
    if let Some(memory) = value.object().downcast_ref::<VectorMemory>() {
        return Ok(Arc::new(memory.clone()));
    }
    if let Some(memory) = value.object().downcast_ref::<BlackboardMemory>() {
        return Ok(Arc::new(memory.clone()));
    }
    if let Some(memory) = value.object().downcast_ref::<PersonaMemory>() {
        return Ok(Arc::new(memory.clone()));
    }
    if let Some(agent) = value.object().downcast_ref::<Agent>() {
        return agent.site();
    }
    if let Some(fabric) = value.object().downcast_ref::<AgentFabric>() {
        return Ok(Arc::new(RuntimeValueSite {
            value: value.clone(),
            address: ServerAddress::Local,
            codecs: fabric.codecs.clone(),
            kind: "swarm",
        }));
    }
    Err(Error::TypeMismatch {
        expected: "component, agent, or swarm",
        found: "non-site",
    })
}

pub(crate) fn agent_from_value(value: &Value) -> Result<&Agent> {
    value
        .object()
        .downcast_ref::<Agent>()
        .ok_or(Error::TypeMismatch {
            expected: "agent",
            found: "non-agent",
        })
}

pub(crate) fn fabric_from_value(value: &Value) -> Result<&AgentFabric> {
    value
        .object()
        .downcast_ref::<AgentFabric>()
        .ok_or(Error::TypeMismatch {
            expected: "swarm",
            found: "non-swarm",
        })
}

pub(crate) fn connection_from_value(value: &Value) -> Option<&Connection> {
    value.object().downcast_ref::<Connection>()
}

pub(crate) fn register_started_agent(agent: &Agent) -> Result<()> {
    let state = agent
        .state
        .lock()
        .map_err(|_| Error::PoisonedLock("agent state"))?;
    if state.address.is_none() {
        return Ok(());
    }
    let codecs = state.supported_codecs.clone();
    let default_codec = state.default_codec.clone();
    drop(state);
    let mut started = started_agents()
        .lock()
        .map_err(|_| Error::PoisonedLock("started agents"))?;
    started.insert(
        agent.name.to_string(),
        StartedAgent {
            site: agent.site()?,
            codecs,
            default_codec,
        },
    );
    Ok(())
}

pub(crate) fn resolve_agent_address(
    _cx: &mut Cx,
    address: &ServerAddress,
    offered: &[Symbol],
) -> Result<Option<ResolvedAddress>> {
    let ServerAddress::Agent { agent } = address else {
        return Ok(None);
    };
    let started = started_agents()
        .lock()
        .map_err(|_| Error::PoisonedLock("started agents"))?;
    let Some(started) = started.get(agent) else {
        return Ok(None);
    };
    let selected = offered
        .iter()
        .find(|codec| started.codecs.iter().any(|supported| supported == *codec))
        .cloned()
        .unwrap_or_else(|| started.default_codec.clone());
    Ok(Some(ResolvedAddress {
        site: started.site.clone(),
        selected_codec: selected,
        supported_codecs: started.codecs.clone(),
    }))
}