1use crate::json_repair::{parse_tolerant_json, JsonRepairError};
9use serde::{de::DeserializeOwned, Deserialize, Serialize};
10
11#[derive(Debug, Clone, Serialize, Deserialize)]
15pub struct ToolCall {
16 pub id: String,
18
19 #[serde(rename = "type")]
21 pub tool_type: String,
22
23 pub function: FunctionCall,
25}
26
27impl ToolCall {
28 #[deprecated(note = "use ToolCall::builder(id).name(..).arguments(..).build() instead")]
34 pub fn new(
35 id: impl Into<String>,
36 name: impl Into<String>,
37 arguments: impl Into<String>,
38 ) -> Self {
39 Self {
40 id: id.into(),
41 tool_type: "function".to_string(),
42 function: FunctionCall {
43 name: name.into(),
44 arguments: arguments.into(),
45 },
46 }
47 }
48
49 pub fn builder(id: impl Into<String>) -> ToolCallBuilder {
51 ToolCallBuilder::new(id)
52 }
53
54 pub fn name(&self) -> &str {
56 &self.function.name
57 }
58
59 pub fn arguments(&self) -> &str {
61 &self.function.arguments
62 }
63
64 pub fn parse_arguments<T: DeserializeOwned>(&self) -> Result<T, JsonRepairError> {
70 parse_tolerant_json(&self.function.arguments)
71 }
72}
73
74#[derive(Debug, Clone)]
90pub struct ToolCallBuilder {
91 id: String,
92 tool_type: String,
93 function: FunctionCall,
94}
95
96impl ToolCallBuilder {
97 pub fn new(id: impl Into<String>) -> Self {
99 Self {
100 id: id.into(),
101 tool_type: "function".to_string(),
102 function: FunctionCall {
103 name: String::new(),
104 arguments: String::new(),
105 },
106 }
107 }
108
109 pub fn name(mut self, name: impl Into<String>) -> Self {
111 self.function.name = name.into();
112 self
113 }
114
115 pub fn arguments(mut self, arguments: impl Into<String>) -> Self {
117 self.function.arguments = arguments.into();
118 self
119 }
120
121 pub fn build(self) -> ToolCall {
123 ToolCall {
124 id: self.id,
125 tool_type: self.tool_type,
126 function: self.function,
127 }
128 }
129}
130
131#[derive(Debug, Clone, Serialize, Deserialize)]
133pub struct FunctionCall {
134 pub name: String,
136
137 pub arguments: String,
139}
140
141#[derive(Debug, Clone, Serialize, Deserialize)]
143pub struct ToolCallResult {
144 pub tool_call_id: String,
146
147 pub role: String,
149
150 pub content: String,
152}
153
154impl ToolCallResult {
155 pub fn new(tool_call_id: impl Into<String>, content: impl Into<String>) -> Self {
157 Self {
158 tool_call_id: tool_call_id.into(),
159 role: "tool".to_string(),
160 content: content.into(),
161 }
162 }
163}
164
165#[cfg(test)]
166mod tests {
167 use super::*;
168 use serde_json::json;
169 use std::collections::HashMap;
170
171 #[test]
172 fn test_tool_call() {
173 let call = ToolCall::builder("call_123")
174 .name("calculator")
175 .arguments(json!({"expression": "2 + 3"}).to_string())
176 .build();
177
178 assert_eq!(call.id, "call_123");
179 assert_eq!(call.name(), "calculator");
180
181 let args: HashMap<String, String> = call.parse_arguments().unwrap();
182 assert_eq!(args.get("expression").unwrap(), "2 + 3");
183 }
184
185 #[test]
186 fn test_parse_arguments_tolerates_messy_llm_json() {
187 let call = ToolCall::builder("call_456")
189 .name("weather")
190 .arguments(r#"{"city": "beijing", "unit": "celsius",} plus extra text"#)
191 .build();
192
193 let args: HashMap<String, String> = call.parse_arguments().unwrap();
194 assert_eq!(args.get("city").unwrap(), "beijing");
195 assert_eq!(args.get("unit").unwrap(), "celsius");
196 }
197
198 #[test]
199 fn test_tool_call_result() {
200 let result = ToolCallResult::new("call_123", "5");
201
202 assert_eq!(result.tool_call_id, "call_123");
203 assert_eq!(result.role, "tool");
204 assert_eq!(result.content, "5");
205 }
206}