vv-agent 0.4.7

VectorVein agent runtime, SDK, CLI, tools, and workspace backends
Documentation
use std::path::PathBuf;

use serde::{Deserialize, Serialize};
use serde_json::{json, Value};

use crate::types::{ToolDirective, ToolExecutionResult, ToolResultStatus};

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum ToolOutput {
    Text {
        text: String,
    },
    Json {
        value: Value,
    },
    Image {
        url: Option<String>,
        path: Option<PathBuf>,
        mime_type: Option<String>,
    },
    File {
        path: PathBuf,
        mime_type: Option<String>,
    },
    Error {
        message: String,
        code: Option<String>,
        retryable: bool,
    },
}

impl ToolOutput {
    pub fn text(text: impl Into<String>) -> Self {
        Self::Text { text: text.into() }
    }

    pub fn json(value: Value) -> Self {
        Self::Json { value }
    }

    pub fn error(message: impl Into<String>) -> Self {
        Self::Error {
            message: message.into(),
            code: None,
            retryable: false,
        }
    }

    pub fn with_code(mut self, code: impl Into<String>) -> Self {
        if let Self::Error { code: field, .. } = &mut self {
            *field = Some(code.into());
        }
        self
    }

    pub fn retryable(mut self, retryable: bool) -> Self {
        if let Self::Error {
            retryable: field, ..
        } = &mut self
        {
            *field = retryable;
        }
        self
    }

    pub fn to_result(&self, tool_call_id: impl Into<String>) -> ToolExecutionResult {
        let tool_call_id = tool_call_id.into();
        match self {
            Self::Text { text } => ToolExecutionResult::success(tool_call_id, text.clone()),
            Self::Json { value } => {
                let mut result = ToolExecutionResult::success(tool_call_id, value.to_string());
                result
                    .metadata
                    .insert("output_type".to_string(), json!("json"));
                result
            }
            Self::Image {
                url,
                path,
                mime_type,
            } => {
                let mut result = ToolExecutionResult::success(
                    tool_call_id,
                    json!({
                        "url": url,
                        "path": path,
                        "mime_type": mime_type,
                    })
                    .to_string(),
                );
                result
                    .metadata
                    .insert("output_type".to_string(), json!("image"));
                result.image_url = url.clone();
                result.image_path = path.as_ref().map(|path| path.display().to_string());
                result
            }
            Self::File { path, mime_type } => {
                let mut result = ToolExecutionResult::success(
                    tool_call_id,
                    json!({
                        "path": path,
                        "mime_type": mime_type,
                    })
                    .to_string(),
                );
                result
                    .metadata
                    .insert("output_type".to_string(), json!("file"));
                result
            }
            Self::Error {
                message,
                code,
                retryable,
            } => ToolExecutionResult {
                tool_call_id,
                content: json!({
                    "ok": false,
                    "error": message,
                    "error_code": code,
                    "retryable": retryable,
                })
                .to_string(),
                status: ToolResultStatus::Error,
                directive: ToolDirective::Continue,
                error_code: code.clone(),
                metadata: [
                    ("output_type".to_string(), json!("error")),
                    ("retryable".to_string(), json!(retryable)),
                ]
                .into_iter()
                .collect(),
                image_url: None,
                image_path: None,
            },
        }
    }
}