harn-vm 0.10.130

Async bytecode virtual machine for the Harn programming language
Documentation
use crate::value::{VmError, VmValue};

use super::{compact_usize_opt, err};

const COMPACT_OPT_KEYS: &[&str] = &[
    "keep_last",
    "token_threshold",
    "tool_output_max_chars",
    "compact_strategy",
    "hard_limit_tokens",
    "hard_limit_strategy",
    "custom_compactor",
    "mask_callback",
    "compress_callback",
    "policy",
    "compaction_policy",
    "compaction_request",
    "instructions",
    "mode",
    "scope",
    "preserve",
    "drop",
    "extend_default_instructions",
    "author",
];

pub(super) fn build_compact_config(
    opts: &crate::value::DictMap,
) -> Result<crate::orchestration::AutoCompactConfig, VmError> {
    for key in opts.keys() {
        if !COMPACT_OPT_KEYS.contains(&key.as_str()) {
            let expected = COMPACT_OPT_KEYS.join(", ");
            return Err(err(format!(
                "agent_session_compact: unknown option key '{key}' (expected one of: {expected})"
            )));
        }
    }

    let mut config = crate::orchestration::AutoCompactConfig {
        policy: crate::orchestration::parse_compaction_policy_options(
            Some(opts),
            "agent_session_compact",
        )?,
        ..Default::default()
    };
    if let Some(value) = compact_usize_opt(opts, "keep_last")? {
        config.keep_last = value;
    }
    if let Some(value) = compact_usize_opt(opts, "token_threshold")? {
        config.token_threshold = value;
    }
    if let Some(value) = compact_usize_opt(opts, "tool_output_max_chars")? {
        config.tool_output_max_chars = value;
    }
    if let Some(VmValue::String(strategy)) = opts.get("compact_strategy") {
        config.compact_strategy = crate::orchestration::parse_compact_strategy(strategy)?;
        config.policy_strategy =
            crate::orchestration::compact_strategy_name(&config.compact_strategy).to_string();
    }
    if let Some(value) = compact_usize_opt(opts, "hard_limit_tokens")? {
        config.hard_limit_tokens = Some(value);
    }
    if let Some(VmValue::String(strategy)) = opts.get("hard_limit_strategy") {
        config.hard_limit_strategy = crate::orchestration::parse_compact_strategy(strategy)?;
    }
    config.custom_compactor = closure_option(opts, "custom_compactor")?;
    config.mask_callback = closure_option(opts, "mask_callback")?;
    config.compress_callback = closure_option(opts, "compress_callback")?;
    config.request_provenance = crate::orchestration::CompactionRequestProvenance {
        requested_strategy: Some(
            crate::orchestration::compact_strategy_name(&config.compact_strategy).to_string(),
        ),
        threshold_source: opts
            .contains_key("token_threshold")
            .then_some(crate::orchestration::CompactionThresholdSource::TokenThreshold),
    };
    Ok(config)
}

fn closure_option(opts: &crate::value::DictMap, key: &str) -> Result<Option<VmValue>, VmError> {
    let Some(value) = opts.get(key).cloned() else {
        return Ok(None);
    };
    if !matches!(value, VmValue::Closure(_)) {
        return Err(err(format!(
            "agent_session_compact: `{key}` must be a closure"
        )));
    }
    Ok(Some(value))
}