1use serde::{Deserialize, Serialize};
2use serde_json::Value;
3use std::time::{Duration, Instant};
4
5#[derive(Debug, Clone, Serialize, Deserialize)]
7pub struct ToolCall {
8 pub id: String,
10 pub name: String,
12 pub arguments: Value,
14}
15
16impl ToolCall {
17 pub fn new(id: String, name: String, arguments: Value) -> Self {
19 Self {
20 id,
21 name,
22 arguments,
23 }
24 }
25
26 pub fn from_openai_format(tool_call: &Value) -> Option<Self> {
28 let id = tool_call.get("id")?.as_str()?.to_string();
29 let function = tool_call.get("function")?;
30 let name = function.get("name")?.as_str()?.to_string();
31
32 let arguments_str = function.get("arguments")?.as_str()?;
33 let arguments: Value = serde_json::from_str(arguments_str).ok()?;
34
35 Some(Self {
36 id,
37 name,
38 arguments,
39 })
40 }
41
42 pub fn to_openai_format(&self) -> Value {
44 serde_json::json!({
45 "id": self.id,
46 "type": "function",
47 "function": {
48 "name": self.name,
49 "arguments": serde_json::to_string(&self.arguments).unwrap_or_default()
50 }
51 })
52 }
53
54 pub fn describe(&self) -> String {
56 format!("{}({})", self.name, self.arguments)
57 }
58}
59
60#[derive(Debug, Clone, Serialize, Deserialize)]
62pub struct ToolOutput {
63 pub tool_call_id: String,
65 pub tool_name: String,
67 pub output: Value,
69 pub is_final: bool,
71 pub is_error: bool,
73 pub duration_ms: Option<u128>,
75}
76
77impl ToolOutput {
78 pub fn success(tool_call_id: String, tool_name: String, output: Value) -> Self {
80 Self {
81 tool_call_id,
82 tool_name,
83 output,
84 is_final: false,
85 is_error: false,
86 duration_ms: None,
87 }
88 }
89
90 pub fn error(tool_call_id: String, tool_name: String, error_msg: String) -> Self {
92 Self {
93 tool_call_id,
94 tool_name,
95 output: serde_json::json!({
96 "error": {
97 "message": error_msg
98 }
99 }),
100 is_final: false,
101 is_error: true,
102 duration_ms: None,
103 }
104 }
105
106 pub fn with_duration(mut self, duration: Duration) -> Self {
108 self.duration_ms = Some(duration.as_millis());
109 self
110 }
111
112 pub fn as_final(mut self) -> Self {
114 self.is_final = true;
115 self
116 }
117
118 pub fn as_string(&self) -> String {
120 match &self.output {
121 Value::String(s) => s.clone(),
122 other => other.to_string(),
123 }
124 }
125
126 pub fn to_openai_message(&self) -> Value {
128 serde_json::json!({
129 "role": "tool",
130 "tool_call_id": self.tool_call_id,
131 "content": self.as_string()
132 })
133 }
134}
135
136#[derive(Debug)]
138pub struct ToolExecution {
139 pub tool_call: ToolCall,
140 start_time: Instant,
141}
142
143impl ToolExecution {
144 pub fn start(tool_call: ToolCall) -> Self {
146 Self {
147 tool_call,
148 start_time: Instant::now(),
149 }
150 }
151
152 pub fn complete(self, output: Value, is_error: bool) -> ToolOutput {
154 let duration = self.start_time.elapsed();
155 ToolOutput {
156 tool_call_id: self.tool_call.id,
157 tool_name: self.tool_call.name,
158 output,
159 is_final: false,
160 is_error,
161 duration_ms: Some(duration.as_millis()),
162 }
163 }
164
165 pub fn complete_with_error(self, error_msg: String) -> ToolOutput {
167 let duration = self.start_time.elapsed();
168 ToolOutput::error(self.tool_call.id, self.tool_call.name, error_msg).with_duration(duration)
169 }
170}
171
172#[cfg(test)]
173mod tests {
174 use super::*;
175
176 #[test]
177 fn test_tool_call_creation() {
178 let call = ToolCall::new(
179 "call_123".to_string(),
180 "test_tool".to_string(),
181 serde_json::json!({"arg": "value"}),
182 );
183 assert_eq!(call.id, "call_123");
184 assert_eq!(call.name, "test_tool");
185 }
186
187 #[test]
188 fn test_tool_call_from_openai() {
189 let openai_format = serde_json::json!({
190 "id": "call_456",
191 "type": "function",
192 "function": {
193 "name": "calculator",
194 "arguments": "{\"expression\": \"2+2\"}"
195 }
196 });
197
198 let call = ToolCall::from_openai_format(&openai_format).unwrap();
199 assert_eq!(call.id, "call_456");
200 assert_eq!(call.name, "calculator");
201 assert_eq!(call.arguments["expression"], "2+2");
202 }
203
204 #[test]
205 fn test_tool_output_success() {
206 let output = ToolOutput::success(
207 "call_789".to_string(),
208 "test".to_string(),
209 serde_json::json!("result"),
210 );
211 assert!(!output.is_error);
212 assert!(!output.is_final);
213 assert_eq!(output.tool_call_id, "call_789");
214 }
215
216 #[test]
217 fn test_tool_output_error() {
218 let output = ToolOutput::error(
219 "call_999".to_string(),
220 "test".to_string(),
221 "Something went wrong".to_string(),
222 );
223 assert!(output.is_error);
224 assert_eq!(output.tool_name, "test");
225 }
226
227 #[test]
228 fn test_tool_execution_timing() {
229 let call = ToolCall::new("call_123".to_string(), "test".to_string(), Value::Null);
230 let execution = ToolExecution::start(call);
231 let output = execution.complete(serde_json::json!("result"), false);
232 assert!(output.duration_ms.is_some());
233 }
234}