tea-tools 0.1.0

Portable tool specifications and execution contracts for tea-rs
Documentation
use std::str::FromStr;

use serde_json::{Value, json};
use tea_protocol::{ProtocolMetadata, ToolCallId};
use tea_tools::{
    MAX_TOOL_VALUE_BYTES, MAX_TOOL_VALUE_DEPTH, ToolInvocation, ToolInvocationError, ToolName,
};

fn invocation(arguments: Value) -> Result<ToolInvocation, ToolInvocationError> {
    ToolInvocation::new(
        ToolCallId::from_str("0195a0b1-7100-7000-8000-0aa7aa000001").unwrap(),
        ToolName::from_str("bounded_input").unwrap(),
        arguments,
        ProtocolMetadata::default(),
    )
}

fn nested_object(layers: usize) -> Value {
    let mut value = json!({});
    for _ in 0..layers {
        value = json!({"next": value});
    }
    value
}

#[test]
fn tool_arguments_reject_oversized_payloads_before_execution() {
    assert!(invocation(json!({"data": "x".repeat(MAX_TOOL_VALUE_BYTES / 2)})).is_ok());
    assert_eq!(
        invocation(json!({"data": "x".repeat(MAX_TOOL_VALUE_BYTES)})).unwrap_err(),
        ToolInvocationError::ArgumentsOutOfBounds
    );
}

#[test]
fn tool_arguments_reject_values_deeper_than_the_documented_limit() {
    assert!(invocation(nested_object(MAX_TOOL_VALUE_DEPTH - 1)).is_ok());
    assert_eq!(
        invocation(nested_object(MAX_TOOL_VALUE_DEPTH)).unwrap_err(),
        ToolInvocationError::ArgumentsOutOfBounds
    );
}

#[test]
fn generated_argument_boundaries_return_stable_results() {
    for layers in 0..=MAX_TOOL_VALUE_DEPTH + 8 {
        let result = invocation(nested_object(layers));
        if layers < MAX_TOOL_VALUE_DEPTH {
            assert!(result.is_ok(), "layers {layers}");
        } else {
            assert_eq!(
                result.unwrap_err(),
                ToolInvocationError::ArgumentsOutOfBounds,
                "layers {layers}"
            );
        }
    }
}