sim-lib-agent 0.1.2

Agent runtime surfaces for SIM.
Documentation
use super::super::store::snapshot_entries;
use super::types::{
    BlackboardMemory, FileMemory, PersonaMemory, VectorMemory, WorkingMemory,
    resolve_memory_backend,
};
use sim_kernel::{Args, CapabilityName, Cx, Error, Result, Value};
use std::sync::Arc;

use crate::{FILE_WRITE_CAPABILITY, parse_component_options, path_option};

pub(crate) fn memory_working_value(cx: &mut Cx, args: Args) -> Result<Value> {
    if !args.values().is_empty() {
        return Err(Error::Eval(
            "memory/working expects no arguments".to_owned(),
        ));
    }
    cx.factory()
        .opaque(Arc::new(WorkingMemory::new(crate::installed_codecs(cx))))
}

pub(crate) fn memory_file_value(cx: &mut Cx, args: Args) -> Result<Value> {
    let [path] = args.values() else {
        return Err(Error::Eval(
            "memory/file expects exactly one path argument".to_owned(),
        ));
    };
    cx.require(&CapabilityName::new(FILE_WRITE_CAPABILITY))?;
    let path = crate::string_from_value(cx, path.clone(), "memory/file expects a string path")?;
    cx.factory().opaque(Arc::new(FileMemory::open(
        path,
        crate::installed_codecs(cx),
    )?))
}

pub(crate) fn memory_blackboard_value(cx: &mut Cx, args: Args) -> Result<Value> {
    let board = match args.values() {
        [] => "ephemeral".to_owned(),
        [value] => crate::stringish_from_value(
            cx,
            value.clone(),
            "memory/blackboard expects a symbol or string name",
        )?,
        _ => {
            return Err(Error::Eval(
                "memory/blackboard expects at most one board name".to_owned(),
            ));
        }
    };
    cx.factory().opaque(Arc::new(BlackboardMemory::new(
        board,
        crate::installed_codecs(cx),
    )))
}

pub(crate) fn memory_vector_value(cx: &mut Cx, args: Args) -> Result<Value> {
    let options = parse_component_options(cx, args, "memory/vector")?;
    let path = path_option(cx, &options, "path")?;
    if path.is_some() {
        cx.require(&CapabilityName::new(FILE_WRITE_CAPABILITY))?;
    }
    cx.factory().opaque(Arc::new(VectorMemory::open(
        path,
        crate::installed_codecs(cx),
    )?))
}

pub(crate) fn memory_persona_value(cx: &mut Cx, args: Args) -> Result<Value> {
    let options = parse_component_options(cx, args, "memory/persona")?;
    let path = path_option(cx, &options, "path")?;
    if path.is_some() {
        cx.require(&CapabilityName::new(FILE_WRITE_CAPABILITY))?;
    }
    cx.factory().opaque(Arc::new(PersonaMemory::open(
        path,
        crate::installed_codecs(cx),
    )?))
}

pub(crate) fn memory_append_value(cx: &mut Cx, args: Args) -> Result<Value> {
    let [memory, msg] = args.values() else {
        return Err(Error::Eval(
            "memory/append expects a memory and one message".to_owned(),
        ));
    };
    resolve_memory_backend(memory)?.append(cx, msg.clone())?;
    cx.factory().nil()
}

pub(crate) fn memory_recent_value(cx: &mut Cx, args: Args) -> Result<Value> {
    let [memory, count] = args.values() else {
        return Err(Error::Eval(
            "memory/recent expects a memory and a count".to_owned(),
        ));
    };
    let count = crate::u32_from_value(cx, count.clone(), "memory/recent expects an integer count")?;
    let recent = resolve_memory_backend(memory)?.recent(cx, count)?;
    cx.factory().list(recent)
}

pub(crate) fn memory_scan_value(cx: &mut Cx, args: Args) -> Result<Value> {
    let memory = match args.values() {
        [memory] => memory,
        [memory, _count] => memory,
        _ => {
            return Err(Error::Eval(
                "memory/scan expects a memory and optional count".to_owned(),
            ));
        }
    };
    let entries = if let [_, count] = args.values() {
        let count =
            crate::u32_from_value(cx, count.clone(), "memory/scan expects an integer count")?;
        resolve_memory_backend(memory)?.recent(cx, count)?
    } else {
        let snapshot = resolve_memory_backend(memory)?.snapshot(cx)?;
        snapshot_entries(snapshot)?
            .into_iter()
            .map(|expr| crate::expr_to_value(cx, &expr))
            .collect::<Result<Vec<_>>>()?
    };
    cx.factory().list(entries)
}

pub(crate) fn memory_search_value(cx: &mut Cx, args: Args) -> Result<Value> {
    let [memory, query, k] = args.values() else {
        return Err(Error::Eval(
            "memory/search expects a memory, query, and count".to_owned(),
        ));
    };
    let k = crate::u32_from_value(cx, k.clone(), "memory/search expects an integer count")?;
    let query = query.object().as_expr(cx)?;
    let results = resolve_memory_backend(memory)?.search(cx, query, k)?;
    cx.factory().list(results)
}

pub(crate) fn memory_snapshot_value(cx: &mut Cx, args: Args) -> Result<Value> {
    let [memory] = args.values() else {
        return Err(Error::Eval(
            "memory/snapshot expects exactly one memory".to_owned(),
        ));
    };
    let snapshot = resolve_memory_backend(memory)?.snapshot(cx)?;
    cx.factory().expr(snapshot)
}

pub(crate) fn memory_restore_value(cx: &mut Cx, args: Args) -> Result<Value> {
    let [memory, snapshot] = args.values() else {
        return Err(Error::Eval(
            "memory/restore expects a memory and a snapshot".to_owned(),
        ));
    };
    let snapshot = snapshot.object().as_expr(cx)?;
    resolve_memory_backend(memory)?.restore(cx, snapshot)?;
    cx.factory().nil()
}