vv_agent/types/
tool_calls.rs1use serde::{Deserialize, Serialize};
2use serde_json::Value;
3
4use super::{Message, Metadata, ToolArguments, ToolDirective, ToolResultStatus};
5
6#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
7pub struct ToolCall {
8 pub id: String,
9 pub name: String,
10 pub arguments: ToolArguments,
11 pub extra_content: Option<Value>,
12}
13
14impl ToolCall {
15 pub fn new(id: impl Into<String>, name: impl Into<String>, arguments: ToolArguments) -> Self {
16 Self {
17 id: id.into(),
18 name: name.into(),
19 arguments,
20 extra_content: None,
21 }
22 }
23
24 pub fn from_raw_arguments(
25 id: impl Into<String>,
26 name: impl Into<String>,
27 raw_arguments: Value,
28 ) -> Self {
29 let id = id.into();
30 let name = name.into();
31 match parse_raw_tool_arguments(&raw_arguments) {
32 Ok(arguments) => Self {
33 id,
34 name,
35 arguments,
36 extra_content: None,
37 },
38 Err((error_code, error)) => Self {
39 id,
40 name,
41 arguments: ToolArguments::new(),
42 extra_content: Some(Value::Object(
43 [
44 ("raw_arguments".to_string(), raw_arguments),
45 ("argument_error_code".to_string(), Value::String(error_code)),
46 ("argument_error".to_string(), Value::String(error)),
47 ]
48 .into_iter()
49 .collect(),
50 )),
51 },
52 }
53 }
54}
55
56fn parse_raw_tool_arguments(raw_arguments: &Value) -> Result<ToolArguments, (String, String)> {
57 match raw_arguments {
58 Value::Null => Ok(ToolArguments::new()),
59 Value::Object(object) => Ok(object.clone().into_iter().collect()),
60 Value::String(raw) => {
61 let stripped = raw.trim();
62 if stripped.is_empty() {
63 return Ok(ToolArguments::new());
64 }
65 let parsed = serde_json::from_str::<Value>(stripped).map_err(|error| {
66 (
67 "invalid_arguments_json".to_string(),
68 format!("Invalid tool arguments JSON: {error}"),
69 )
70 })?;
71 match parsed {
72 Value::Object(object) => Ok(object.into_iter().collect()),
73 _ => Err((
74 "invalid_arguments_payload".to_string(),
75 "Tool arguments must decode to an object".to_string(),
76 )),
77 }
78 }
79 other => Err((
80 "invalid_arguments_type".to_string(),
81 format!("Unsupported tool argument type: {}", json_type_name(other)),
82 )),
83 }
84}
85
86fn json_type_name(value: &Value) -> &'static str {
87 match value {
88 Value::Null => "null",
89 Value::Bool(_) => "bool",
90 Value::Number(_) => "number",
91 Value::String(_) => "string",
92 Value::Array(_) => "array",
93 Value::Object(_) => "object",
94 }
95}
96
97#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
98pub struct ToolExecutionResult {
99 pub tool_call_id: String,
100 pub content: String,
101 pub status: ToolResultStatus,
102 pub directive: ToolDirective,
103 pub error_code: Option<String>,
104 pub metadata: Metadata,
105 pub image_url: Option<String>,
106 pub image_path: Option<String>,
107}
108
109impl ToolExecutionResult {
110 pub fn success(tool_call_id: impl Into<String>, content: impl Into<String>) -> Self {
111 Self {
112 tool_call_id: tool_call_id.into(),
113 content: content.into(),
114 status: ToolResultStatus::Success,
115 directive: ToolDirective::Continue,
116 error_code: None,
117 metadata: Metadata::new(),
118 image_url: None,
119 image_path: None,
120 }
121 }
122
123 pub fn error(tool_call_id: impl Into<String>, content: impl Into<String>) -> Self {
124 Self {
125 status: ToolResultStatus::Error,
126 ..Self::success(tool_call_id, content)
127 }
128 }
129
130 pub fn to_message(&self) -> Message {
131 Message::tool(self.content.clone(), self.tool_call_id.clone())
132 }
133
134 pub fn to_tool_message(&self) -> Message {
135 self.to_message()
136 }
137}