vv_agent/tools/
outputs.rs1use 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}