harn-vm 0.10.122

Async bytecode virtual machine for the Harn programming language
Documentation
use super::routing_test_support::{extract_with_options, ScopedEnvVar};
use super::*;

fn explicit_option_error(option: &str, value: VmValue) -> VmError {
    let options = crate::value::DictMap::from_iter([
        (
            crate::value::intern_key("provider"),
            VmValue::String(arcstr::ArcStr::from("local")),
        ),
        (
            crate::value::intern_key("model"),
            VmValue::String(arcstr::ArcStr::from("unsupported-generation")),
        ),
        (crate::value::intern_key(option), value),
    ]);
    extract_llm_options(&[
        VmValue::String(arcstr::ArcStr::from("hello")),
        VmValue::Nil,
        VmValue::dict(options),
    ])
    .expect_err("authored generation denial must reject")
}

fn gemini_interactions_options() -> crate::value::DictMap {
    crate::value::DictMap::from_iter([
        (
            crate::value::intern_key("provider"),
            VmValue::String(arcstr::ArcStr::from("gemini")),
        ),
        (
            crate::value::intern_key("model"),
            VmValue::String(arcstr::ArcStr::from("gemini-3.5-flash-lite")),
        ),
    ])
}

#[test]
fn portable_generation_denials_share_one_typed_admission_gate() {
    crate::llm::capabilities::set_user_overrides_toml(concat!(
        "[[provider.local]]\n",
        "model_match = \"unsupported-generation\"\n",
        "temperature_supported = false\n",
        "top_p_supported = false\n",
        "top_k_supported = false\n",
        "seed_supported = false\n",
        "frequency_penalty_supported = false\n",
        "presence_penalty_supported = false\n",
        "stop_supported = false\n",
    ))
    .expect("explicit generation capability fixture");

    let cases = [
        ("temperature", VmValue::Float(0.2)),
        ("top_p", VmValue::Float(0.9)),
        ("top_k", VmValue::Int(20)),
        ("seed", VmValue::Int(42)),
        ("frequency_penalty", VmValue::Float(0.2)),
        ("presence_penalty", VmValue::Float(0.2)),
        (
            "stop",
            VmValue::List(std::sync::Arc::new(vec![VmValue::String(
                arcstr::ArcStr::from("END"),
            )])),
        ),
    ];
    for (option, value) in cases {
        let error = explicit_option_error(option, value);
        assert!(
            error.to_string().contains(&format!(
                "option `{option}` is not supported by `unsupported-generation` (provider `local`)."
            )),
            "unexpected error for {option}: {error}"
        );
        assert!(error.to_string().contains("harn provider catalog matrix"));
    }

    crate::llm::capabilities::clear_user_overrides();
}

#[test]
fn reasoning_scoped_generation_denial_preserves_the_non_reasoning_route() {
    crate::llm::capabilities::set_user_overrides_toml(concat!(
        "[[provider.local]]\n",
        "model_match = \"conditional-generation\"\n",
        "thinking_modes = [\"effort\"]\n",
        "reasoning_none_supported = true\n",
        "reasoning_excluded_portable_options = [\"temperature\"]\n",
    ))
    .expect("conditional generation capability fixture");

    let options = |effort: Option<&str>| {
        let mut options = crate::value::DictMap::from_iter([
            (
                crate::value::intern_key("provider"),
                VmValue::String(arcstr::ArcStr::from("local")),
            ),
            (
                crate::value::intern_key("model"),
                VmValue::String(arcstr::ArcStr::from("conditional-generation")),
            ),
            (crate::value::intern_key("temperature"), VmValue::Float(0.2)),
        ]);
        if let Some(effort) = effort {
            options.insert(
                crate::value::intern_key("effort"),
                VmValue::String(arcstr::ArcStr::from(effort)),
            );
        }
        options
    };
    let extract = |options| {
        extract_llm_options(&[
            VmValue::String(arcstr::ArcStr::from("hello")),
            VmValue::Nil,
            VmValue::dict(options),
        ])
    };

    extract(options(None)).expect("temperature remains valid with reasoning disabled");
    extract(options(Some("none"))).expect("effort none keeps reasoning disabled");
    let error = extract(options(Some("low")))
        .expect_err("temperature must be rejected when reasoning is enabled");

    crate::llm::capabilities::clear_user_overrides();
    assert!(
        error
            .to_string()
            .contains("option `temperature` is not supported while reasoning is enabled"),
        "unexpected conditional-admission error: {error}"
    );
}

#[test]
fn gemini_interactions_penalties_fail_before_request_construction() {
    let _guard = crate::llm::env_guard();
    // The shared guard restores this inert test credential before another test
    // can observe it. Extraction reaches admission but never dispatches.
    let _key = ScopedEnvVar::set("GEMINI_API_KEY", "test-key");

    for (option, value) in [
        ("frequency_penalty", VmValue::Float(0.2)),
        ("presence_penalty", VmValue::Float(0.2)),
    ] {
        let mut options = gemini_interactions_options();
        options.insert(crate::value::intern_key(option), value);
        let error = extract_with_options(options)
            .expect_err("Interactions must reject a portable penalty before transport");
        assert!(
            error.to_string().contains(&format!(
                "option `{option}` is not supported by `gemini-3.5-flash-lite` (provider `gemini`)."
            )),
            "unexpected error for {option}: {error}"
        );
    }

    extract_with_options(gemini_interactions_options())
        .expect("an Interactions call without penalties remains admissible");
}

#[test]
fn deepseek_v4_admits_only_its_documented_reasoning_efforts() {
    let _deepseek_key = ScopedEnvVar::set("DEEPSEEK_API_KEY", "test-key");
    for model in ["deepseek-v4-flash", "deepseek-v4-pro"] {
        for raw in ["low", "high", "max"] {
            let options = crate::value::DictMap::from_iter([
                (
                    crate::value::intern_key("provider"),
                    VmValue::String(arcstr::ArcStr::from("deepseek")),
                ),
                (
                    crate::value::intern_key("model"),
                    VmValue::String(arcstr::ArcStr::from(model)),
                ),
                (
                    crate::value::intern_key("effort"),
                    VmValue::String(arcstr::ArcStr::from(raw)),
                ),
            ]);

            extract_with_options(options).expect("documented DeepSeek effort should be admitted");
        }
    }

    let options = crate::value::DictMap::from_iter([
        (
            crate::value::intern_key("provider"),
            VmValue::String(arcstr::ArcStr::from("deepseek")),
        ),
        (
            crate::value::intern_key("model"),
            VmValue::String(arcstr::ArcStr::from("deepseek-v4-flash")),
        ),
        (
            crate::value::intern_key("effort"),
            VmValue::String(arcstr::ArcStr::from("medium")),
        ),
    ]);
    let err = extract_with_options(options).expect_err("undocumented effort must stay rejected");

    assert!(
        err.to_string()
            .contains("supported reasoning_effort values: low, high, max"),
        "unexpected error: {err}"
    );
}

#[test]
fn deepseek_v4_rejects_sampling_controls_that_thinking_ignores() {
    let _deepseek_key = ScopedEnvVar::set("DEEPSEEK_API_KEY", "test-key");
    let controls = [
        ("temperature", VmValue::Float(0.2)),
        ("top_p", VmValue::Float(0.9)),
        ("presence_penalty", VmValue::Float(0.2)),
        ("frequency_penalty", VmValue::Float(0.2)),
    ];

    for model in ["deepseek-v4-flash", "deepseek-v4-pro"] {
        for (option, value) in &controls {
            let options = |effort: Option<&str>| {
                let mut options = crate::value::DictMap::from_iter([
                    (
                        crate::value::intern_key("provider"),
                        VmValue::String(arcstr::ArcStr::from("deepseek")),
                    ),
                    (
                        crate::value::intern_key("model"),
                        VmValue::String(arcstr::ArcStr::from(model)),
                    ),
                    (crate::value::intern_key(option), value.clone()),
                ]);
                if let Some(effort) = effort {
                    options.insert(
                        crate::value::intern_key("effort"),
                        VmValue::String(arcstr::ArcStr::from(effort)),
                    );
                }
                options
            };

            extract_with_options(options(None))
                .expect("sampling control remains valid when thinking is disabled");
            let error = extract_with_options(options(Some("high")))
                .expect_err("ignored sampling control must fail before transport");
            assert!(
                error.to_string().contains(&format!(
                    "option `{option}` is not supported while reasoning is enabled"
                )),
                "unexpected {model} error for {option}: {error}"
            );
        }
    }
}