harn-vm 0.10.39

Async bytecode virtual machine for the Harn programming language
Documentation
use std::collections::BTreeMap;

use super::*;
use crate::value::VmValue;

#[test]
fn response_event_and_returned_usage_share_priced_cost() {
    let _guard = crate::llm::env_guard();
    crate::llm_config::clear_user_overrides();

    let priced = crate::llm::api::LlmResult {
        text: "priced result".to_string(),
        tool_calls: Vec::new(),
        raw_tool_calls: Vec::new(),
        input_tokens: 1_000,
        output_tokens: 1_000,
        cache_read_tokens: 800,
        cache_write_tokens: 0,
        cache_supported: true,
        model: "claude-sonnet-4-20250514".to_string(),
        provider: "anthropic".to_string(),
        thinking: None,
        thinking_summary: None,
        stop_reason: Some("stop".to_string()),
        served_fast: false,
        blocks: Vec::new(),
        logprobs: Vec::new(),
        telemetry: crate::llm::api::ProviderTelemetry::default(),
    };
    let mut uncached = priced.clone();
    uncached.cache_read_tokens = 0;
    assert!(
        priced.priced_cost_usd().expect("cache-priced result")
            < uncached.priced_cost_usd().expect("uncached result")
    );

    let mut unpriced = priced.clone();
    unpriced.provider = "nonexistent_provider".to_string();
    unpriced.model = "ghost-model".to_string();
    let mut local = priced.clone();
    local.provider = "local".to_string();
    local.model = "no-such-local-model".to_string();

    let dir = tempfile::tempdir().expect("tempdir");
    push_llm_transcript_dir(dir.path().to_str().expect("utf8"));
    dump_llm_response(0, "call-priced", &priced, 1, None, None);
    dump_llm_response(1, "call-unpriced", &unpriced, 1, None, None);
    dump_llm_response(2, "call-local", &local, 1, None, None);
    pop_llm_transcript_dir();

    let transcript =
        std::fs::read_to_string(dir.path().join("llm_transcript.jsonl")).expect("read transcript");
    let events = transcript
        .lines()
        .map(|line| serde_json::from_str::<serde_json::Value>(line).expect("parse event"))
        .filter(|event| event["type"] == "provider_call_response")
        .collect::<Vec<_>>();
    assert_eq!(events.len(), 3);

    let vm_usage_cost = |result: &crate::llm::api::LlmResult| {
        let vm_result = crate::llm::api::vm_build_llm_result(result, None, None, None);
        let result_dict = vm_result.as_dict().expect("result dict");
        let Some(VmValue::Dict(usage)) = result_dict.get("usage") else {
            panic!("missing usage dict: {result_dict:?}");
        };
        let usage = crate::llm::vm_value_to_json(&VmValue::Dict(usage.clone()));
        usage
            .as_object()
            .and_then(|usage| usage.get("cost_usd"))
            .cloned()
            .expect("cost_usd")
    };
    let expected_cost = priced.priced_cost_usd().expect("catalog-priced result");
    let span_usage = crate::tracing::LlmCallUsage {
        model: priced.model.clone(),
        provider: priced.provider.clone(),
        input_tokens: priced.input_tokens,
        output_tokens: priced.output_tokens,
        cache_read_tokens: priced.cache_read_tokens,
        cache_write_tokens: priced.cache_write_tokens,
        cost_usd: priced.priced_cost_usd(),
    };
    let span_metadata: BTreeMap<_, _> = span_usage.metadata_pairs().into_iter().collect();

    assert_eq!(events[0]["cost_usd"], serde_json::json!(expected_cost));
    assert_eq!(vm_usage_cost(&priced), events[0]["cost_usd"]);
    assert_eq!(
        span_metadata[crate::tracing::meta::COST_USD],
        events[0]["cost_usd"]
    );
    assert_eq!(events[1]["cost_usd"], serde_json::Value::Null);
    assert_eq!(vm_usage_cost(&unpriced), serde_json::Value::Null);
    assert_eq!(events[2]["cost_usd"], serde_json::json!(0.0));
    assert_eq!(vm_usage_cost(&local), serde_json::json!(0.0));
}