vv-agent 0.4.7

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

use serde_json::{json, Value};

use super::base::ToolContext;
use super::registry::ToolRegistry;
use crate::types::{ToolCall, ToolDirective, ToolExecutionResult, ToolResultStatus};

pub fn dispatch_tool_call(
    registry: &ToolRegistry,
    context: &mut ToolContext,
    call: &ToolCall,
) -> ToolExecutionResult {
    if let Some(error) = argument_error_result(call) {
        return error;
    }

    match registry.execute(call, context) {
        Ok(result) => normalize_result(call, result),
        Err(_) => error_result(
            &call.id,
            format!("Unknown tool: {}", call.name),
            Some("tool_not_found"),
        ),
    }
}

fn argument_error_result(call: &ToolCall) -> Option<ToolExecutionResult> {
    let extra = call.extra_content.as_ref()?.as_object()?;
    let error_code = extra
        .get("argument_error_code")
        .and_then(Value::as_str)
        .unwrap_or("invalid_arguments");
    let error = extra
        .get("argument_error")
        .and_then(Value::as_str)
        .unwrap_or("Invalid tool arguments");
    Some(error_result(&call.id, error, Some(error_code)))
}

fn normalize_result(call: &ToolCall, mut result: ToolExecutionResult) -> ToolExecutionResult {
    if needs_tool_call_id(&result.tool_call_id) {
        result.tool_call_id = call.id.clone();
    }
    if result.directive == ToolDirective::WaitUser && result.status == ToolResultStatus::Success {
        result.status = ToolResultStatus::WaitResponse;
    }
    result
}

fn needs_tool_call_id(value: &str) -> bool {
    let stripped = value.trim();
    stripped.is_empty() || stripped == "pending"
}

fn error_result(
    tool_call_id: &str,
    message: impl Into<String>,
    error_code: Option<&str>,
) -> ToolExecutionResult {
    let error_code = error_code.map(str::to_string);
    ToolExecutionResult {
        tool_call_id: tool_call_id.to_string(),
        content: json!({
            "ok": false,
            "error": message.into(),
            "error_code": error_code,
        })
        .to_string(),
        status: ToolResultStatus::Error,
        directive: ToolDirective::Continue,
        error_code,
        metadata: BTreeMap::new(),
        image_url: None,
        image_path: None,
    }
}