Skip to main content

vv_agent/tools/
outputs.rs

1use std::path::PathBuf;
2
3use serde::{Deserialize, Serialize};
4use serde_json::{json, Value};
5
6use crate::types::{ToolDirective, ToolExecutionResult, ToolResultStatus};
7
8#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
9#[serde(tag = "type", rename_all = "snake_case")]
10pub enum ToolOutput {
11    Text {
12        text: String,
13    },
14    Json {
15        value: Value,
16    },
17    Image {
18        url: Option<String>,
19        path: Option<PathBuf>,
20        mime_type: Option<String>,
21    },
22    File {
23        path: PathBuf,
24        mime_type: Option<String>,
25    },
26    Error {
27        message: String,
28        code: Option<String>,
29        retryable: bool,
30    },
31}
32
33impl ToolOutput {
34    pub fn text(text: impl Into<String>) -> Self {
35        Self::Text { text: text.into() }
36    }
37
38    pub fn json(value: Value) -> Self {
39        Self::Json { value }
40    }
41
42    pub fn error(message: impl Into<String>) -> Self {
43        Self::Error {
44            message: message.into(),
45            code: None,
46            retryable: false,
47        }
48    }
49
50    pub fn with_code(mut self, code: impl Into<String>) -> Self {
51        if let Self::Error { code: field, .. } = &mut self {
52            *field = Some(code.into());
53        }
54        self
55    }
56
57    pub fn retryable(mut self, retryable: bool) -> Self {
58        if let Self::Error {
59            retryable: field, ..
60        } = &mut self
61        {
62            *field = retryable;
63        }
64        self
65    }
66
67    pub fn to_result(&self, tool_call_id: impl Into<String>) -> ToolExecutionResult {
68        let tool_call_id = tool_call_id.into();
69        match self {
70            Self::Text { text } => ToolExecutionResult::success(tool_call_id, text.clone()),
71            Self::Json { value } => {
72                let mut result = ToolExecutionResult::success(tool_call_id, value.to_string());
73                result
74                    .metadata
75                    .insert("output_type".to_string(), json!("json"));
76                result
77            }
78            Self::Image {
79                url,
80                path,
81                mime_type,
82            } => {
83                let mut result = ToolExecutionResult::success(
84                    tool_call_id,
85                    json!({
86                        "url": url,
87                        "path": path,
88                        "mime_type": mime_type,
89                    })
90                    .to_string(),
91                );
92                result
93                    .metadata
94                    .insert("output_type".to_string(), json!("image"));
95                result.image_url = url.clone();
96                result.image_path = path.as_ref().map(|path| path.display().to_string());
97                result
98            }
99            Self::File { path, mime_type } => {
100                let mut result = ToolExecutionResult::success(
101                    tool_call_id,
102                    json!({
103                        "path": path,
104                        "mime_type": mime_type,
105                    })
106                    .to_string(),
107                );
108                result
109                    .metadata
110                    .insert("output_type".to_string(), json!("file"));
111                result
112            }
113            Self::Error {
114                message,
115                code,
116                retryable,
117            } => ToolExecutionResult {
118                tool_call_id,
119                content: json!({
120                    "ok": false,
121                    "error": message,
122                    "error_code": code,
123                    "retryable": retryable,
124                })
125                .to_string(),
126                status: ToolResultStatus::Error,
127                directive: ToolDirective::Continue,
128                error_code: code.clone(),
129                metadata: [
130                    ("output_type".to_string(), json!("error")),
131                    ("retryable".to_string(), json!(retryable)),
132                ]
133                .into_iter()
134                .collect(),
135                image_url: None,
136                image_path: None,
137            },
138        }
139    }
140}