sim-lib-agent 0.1.1

Agent runtime surfaces for SIM.
Documentation
use super::support::{eval_cx, install_agent_lib, install_roundtrip_codecs};
use sim_kernel::{Expr, Symbol};
use sim_lib_server::{
    Connection, EvalSite, ServerAddress, ServerFrame, eval_request_from_frame,
    server_frame_from_reply,
};
use std::sync::Arc;

#[derive(Clone)]
struct AppendSite {
    label: &'static str,
}

impl EvalSite for AppendSite {
    fn site_kind(&self) -> &'static str {
        "test-append"
    }

    fn address(&self) -> &ServerAddress {
        static LOCAL: std::sync::OnceLock<ServerAddress> = std::sync::OnceLock::new();
        LOCAL.get_or_init(|| ServerAddress::Local)
    }

    fn codecs(&self) -> &[Symbol] {
        &[]
    }

    fn answer(
        &self,
        cx: &mut sim_kernel::Cx,
        frame: ServerFrame,
    ) -> sim_kernel::Result<ServerFrame> {
        let consistency = frame.envelope.consistency;
        let request = eval_request_from_frame(cx, &frame)?;
        let mut items = match request.expr {
            Expr::List(items) => items,
            Expr::Nil => Vec::new(),
            expr => vec![expr],
        };
        items.push(Expr::String(self.label.to_owned()));
        let value = cx.factory().expr(Expr::List(items))?;
        let diagnostics = cx.take_diagnostics();
        server_frame_from_reply(
            cx,
            &frame.codec,
            sim_kernel::EvalReply {
                value,
                diagnostics,
                trace: None,
            },
            consistency,
        )
    }

    fn as_any(&self) -> &dyn std::any::Any {
        self
    }
}

fn tagged_connection(cx: &mut sim_kernel::Cx, role: &str, label: &'static str) -> Connection {
    Connection::with_session(
        ServerAddress::Local,
        Symbol::qualified("codec", "lisp"),
        crate::installed_codecs(cx),
        Arc::new(AppendSite { label }),
        Some(Symbol::new(role)),
        sim_lib_server::IsolationPolicy::default(),
    )
    .unwrap()
}

#[test]
fn r17_swarm_budget_stops_after_two_turns_and_records_transcript() {
    let mut cx = eval_cx();
    install_roundtrip_codecs(&mut cx);
    install_agent_lib(&mut cx).unwrap();
    cx.grant_named("swarm-launch");

    let worker_conn = tagged_connection(&mut cx, "worker", "worker");
    let critic_conn = tagged_connection(&mut cx, "critic", "critic");
    let worker = cx.factory().opaque(Arc::new(worker_conn)).unwrap();
    let critic = cx.factory().opaque(Arc::new(critic_conn)).unwrap();
    cx.registry_mut()
        .register_value(Symbol::qualified("test", "worker"), worker.clone())
        .unwrap();
    cx.registry_mut()
        .register_value(Symbol::qualified("test", "critic"), critic.clone())
        .unwrap();
    let blackboard = cx
        .call_function(
            &Symbol::qualified("memory", "blackboard"),
            sim_kernel::Args::new(vec![cx.factory().string("r17-board".to_owned()).unwrap()]),
        )
        .unwrap();

    let swarm = cx
        .call_function(
            &Symbol::qualified("swarm", "make"),
            sim_kernel::Args::new(vec![
                cx.factory().symbol(Symbol::new(":agents")).unwrap(),
                cx.factory()
                    .expr(Expr::List(vec![
                        Expr::Symbol(Symbol::qualified("test", "worker")),
                        Expr::Symbol(Symbol::qualified("test", "critic")),
                    ]))
                    .unwrap(),
                cx.factory().symbol(Symbol::new(":max-turns")).unwrap(),
                cx.factory()
                    .number_literal(Symbol::qualified("numbers", "f64"), "2".to_owned())
                    .unwrap(),
                cx.factory().symbol(Symbol::new(":blackboard")).unwrap(),
                blackboard.clone(),
            ]),
        )
        .unwrap();

    let launched = cx
        .call_function(
            &Symbol::qualified("swarm", "launch"),
            sim_kernel::Args::new(vec![
                swarm.clone(),
                cx.factory().expr(Expr::List(Vec::new())).unwrap(),
            ]),
        )
        .unwrap();
    let launched_expr = launched.object().as_expr(&mut cx).unwrap();
    let launched_text = format!("{launched_expr:?}");
    assert!(launched_text.contains("budget-exhausted") || launched_text.contains("transcript"));

    let status = cx
        .call_function(
            &Symbol::qualified("swarm", "status"),
            sim_kernel::Args::new(vec![swarm.clone()]),
        )
        .unwrap();
    let status_expr = status.object().as_expr(&mut cx).unwrap();
    let status_text = format!("{status_expr:?}");
    assert!(status_text.contains("turns-used"));
    assert!(status_text.contains("2"));

    let explain = cx
        .call_function(
            &Symbol::qualified("swarm", "explain"),
            sim_kernel::Args::new(vec![swarm.clone()]),
        )
        .unwrap();
    let explain_text = format!("{:?}", explain.object().as_expr(&mut cx).unwrap());
    assert!(explain_text.contains("worker"));
    assert!(explain_text.contains("critic"));

    let blackboard_entries = cx
        .call_function(
            &Symbol::qualified("memory", "scan"),
            sim_kernel::Args::new(vec![blackboard]),
        )
        .unwrap();
    let board_text = format!(
        "{:?}",
        blackboard_entries.object().as_expr(&mut cx).unwrap()
    );
    assert!(board_text.contains("turn"));
}

#[test]
fn r17_realize_fabric_uses_same_loop_runtime() {
    let mut cx = eval_cx();
    install_roundtrip_codecs(&mut cx);
    install_agent_lib(&mut cx).unwrap();
    cx.grant_named("swarm-launch");

    let worker_conn = tagged_connection(&mut cx, "worker", "alpha");
    let worker = cx.factory().opaque(Arc::new(worker_conn)).unwrap();
    let swarm = cx
        .call_function(
            &Symbol::qualified("swarm", "make"),
            sim_kernel::Args::new(vec![
                cx.factory().symbol(Symbol::new(":agents")).unwrap(),
                worker,
                cx.factory().symbol(Symbol::new(":max-turns")).unwrap(),
                cx.factory()
                    .number_literal(Symbol::qualified("numbers", "f64"), "1".to_owned())
                    .unwrap(),
            ]),
        )
        .unwrap();
    let fabric = swarm.object().as_eval_fabric().unwrap();
    let reply = fabric
        .realize(
            &mut cx,
            sim_kernel::EvalRequest {
                expr: Expr::List(vec![Expr::String("seed".to_owned())]),
                mode: sim_kernel::EvalMode::Eval,
                result_shape: None,
                answer_limit: None,
                stream_buffer: None,
                stream: false,
                required_capabilities: Vec::new(),
                deadline: None,
                consistency: sim_kernel::Consistency::LocalFirst,
                trace: false,
            },
        )
        .unwrap();
    let text = format!("{:?}", reply.value.object().as_expr(&mut cx).unwrap());
    assert!(text.contains("transcript"));
    assert!(text.contains("alpha"));
}