assay-core 3.9.1

High-performance evaluation framework for LLM agents (Core)
Documentation
use super::{ToolCall, TraceExplainer};
use crate::model::{Policy, SequenceRule, ToolsPolicy};
use crate::on_error::ErrorPolicy;

fn make_policy(rules: Vec<SequenceRule>) -> Policy {
    Policy {
        version: "1.1".to_string(),
        name: "test".to_string(),
        metadata: None,
        tools: ToolsPolicy::default(),
        sequences: rules,
        aliases: std::collections::HashMap::new(),
        on_error: ErrorPolicy::default(),
    }
}

#[test]
fn test_explain_simple_trace() {
    let policy = make_policy(vec![SequenceRule::Before {
        first: "Search".to_string(),
        then: "Create".to_string(),
    }]);

    let explainer = TraceExplainer::new(policy);
    let trace = vec![
        ToolCall {
            tool: "Search".to_string(),
            args: None,
        },
        ToolCall {
            tool: "Create".to_string(),
            args: None,
        },
    ];

    let explanation = explainer.explain(&trace);

    assert_eq!(explanation.total_steps, 2);
    assert_eq!(explanation.allowed_steps, 2);
    assert_eq!(explanation.blocked_steps, 0);
}

#[test]
fn test_explain_blocked_trace() {
    let policy = make_policy(vec![SequenceRule::Before {
        first: "Search".to_string(),
        then: "Create".to_string(),
    }]);

    let explainer = TraceExplainer::new(policy);
    let trace = vec![ToolCall {
        tool: "Create".to_string(),
        args: None,
    }];

    let explanation = explainer.explain(&trace);

    assert_eq!(explanation.blocked_steps, 1);
    assert_eq!(explanation.first_block_index, Some(0));
    assert!(!explanation.blocking_rules.is_empty());
}

#[test]
fn test_explain_max_calls() {
    let policy = make_policy(vec![SequenceRule::MaxCalls {
        tool: "API".to_string(),
        max: 2,
    }]);

    let explainer = TraceExplainer::new(policy);
    let trace = vec![
        ToolCall {
            tool: "API".to_string(),
            args: None,
        },
        ToolCall {
            tool: "API".to_string(),
            args: None,
        },
        ToolCall {
            tool: "API".to_string(),
            args: None,
        },
    ];

    let explanation = explainer.explain(&trace);

    assert_eq!(explanation.allowed_steps, 2);
    assert_eq!(explanation.blocked_steps, 1);
    assert_eq!(explanation.first_block_index, Some(2));
}

#[test]
fn test_terminal_output() {
    let policy = make_policy(vec![]);
    let explainer = TraceExplainer::new(policy);
    let trace = vec![ToolCall {
        tool: "Search".to_string(),
        args: None,
    }];

    let explanation = explainer.explain(&trace);
    let output = explanation.to_terminal();

    assert!(output.contains("Timeline:"));
    assert!(output.contains("[0]"));
    assert!(output.contains("Search"));
}