vv-agent 0.6.2

VectorVein agent runtime, SDK, CLI, tools, and workspace backends
Documentation
use std::collections::BTreeMap;

use serde_json::{json, Value};
use vv_agent::{build_default_registry, ToolCall, ToolContext, ToolResultStatus};

const PNG_1X1: &[u8] = &[
    0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52,
    0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x08, 0x06, 0x00, 0x00, 0x00, 0x1f, 0x15, 0xc4,
    0x89, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x44, 0x41, 0x54, 0x78, 0x9c, 0x63, 0x60, 0x00, 0x00, 0x00,
    0x02, 0x00, 0x01, 0xe2, 0x21, 0xbc, 0x33, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae,
    0x42, 0x60, 0x82,
];

#[test]
fn read_image_from_workspace_file_returns_inline_image_url() {
    let workspace = tempfile::tempdir().expect("workspace");
    let registry = build_default_registry();
    let mut context = ToolContext::new(workspace.path());
    std::fs::write(workspace.path().join("img.png"), PNG_1X1).expect("image");

    let result = registry
        .execute(
            &ToolCall::new(
                "img_1",
                "read_image",
                BTreeMap::from([("path".to_string(), json!("img.png"))]),
            ),
            &mut context,
        )
        .expect("read_image");

    assert_eq!(result.status, ToolResultStatus::Success);
    assert_eq!(result.image_path.as_deref(), Some("img.png"));
    assert!(result
        .image_url
        .as_deref()
        .expect("image url")
        .starts_with("data:image/png;base64,"));
    let payload: Value = serde_json::from_str(&result.content).expect("payload");
    assert_eq!(payload["source"], "workspace");
    assert_eq!(payload["inline_transport"], true);
}

#[test]
fn read_image_from_url_attaches_original_url() {
    let workspace = tempfile::tempdir().expect("workspace");
    let registry = build_default_registry();
    let mut context = ToolContext::new(workspace.path());

    let result = registry
        .execute(
            &ToolCall::new(
                "img_2",
                "read_image",
                BTreeMap::from([("path".to_string(), json!("https://example.com/a.png"))]),
            ),
            &mut context,
        )
        .expect("read_image");

    assert_eq!(result.status, ToolResultStatus::Success);
    assert_eq!(
        result.image_url.as_deref(),
        Some("https://example.com/a.png")
    );
}

#[test]
fn read_image_coerces_scalar_path() {
    let workspace = tempfile::tempdir().expect("workspace");
    let registry = build_default_registry();
    let mut context = ToolContext::new(workspace.path());

    let result = registry
        .execute(
            &ToolCall::new(
                "img_scalar_path",
                "read_image",
                BTreeMap::from([("path".to_string(), json!(123))]),
            ),
            &mut context,
        )
        .expect("read_image");

    assert_eq!(result.status, ToolResultStatus::Error);
    assert_eq!(result.error_code.as_deref(), Some("image_not_found"));
    let payload: Value = serde_json::from_str(&result.content).expect("payload");
    assert_eq!(payload["error"], "image file not found: 123");
}

#[test]
fn read_image_rejects_unsupported_extension() {
    let workspace = tempfile::tempdir().expect("workspace");
    let registry = build_default_registry();
    let mut context = ToolContext::new(workspace.path());
    std::fs::write(workspace.path().join("x.txt"), "not image").expect("text");

    let result = registry
        .execute(
            &ToolCall::new(
                "img_3",
                "read_image",
                BTreeMap::from([("path".to_string(), json!("x.txt"))]),
            ),
            &mut context,
        )
        .expect("read_image");

    assert_eq!(result.status, ToolResultStatus::Error);
    assert_eq!(
        result.error_code.as_deref(),
        Some("unsupported_image_format")
    );
}