1use std::borrow::Cow;
13use std::future::Future;
14use std::pin::Pin;
15use std::sync::Arc;
16
17use crate::ToolResult;
18
19pub trait ToolArgParser: Sized {
43 fn parse(value: serde_json::Value) -> Result<Self, serde_json::Error>;
47}
48
49impl<T> ToolArgParser for T
50where
51 T: for<'de> serde::Deserialize<'de>,
52{
53 fn parse(value: serde_json::Value) -> Result<Self, serde_json::Error> {
54 serde_json::from_value(value)
55 }
56}
57
58pub trait ToolArgs: ToolArgParser {
76 const NAME: &'static str;
78 const DESCRIPTION: &'static str;
80 fn __schema() -> serde_json::Value;
82 fn tool_definition() -> ToolDefinition {
84 ToolDefinition {
85 name: Self::NAME.to_string(),
86 description: Self::DESCRIPTION.to_string(),
87 parameters: Self::__schema(),
88 cache_control: None,
89 }
90 }
91}
92
93#[derive(Debug, Clone, PartialEq, Eq)]
97pub enum ParallelSafety {
98 Safe,
100 CategoryExclusive,
102 Exclusive,
104}
105
106#[derive(Debug, Clone, PartialEq, Eq, Hash)]
110pub struct ToolCategory(pub Cow<'static, str>);
111
112impl ToolCategory {
113 pub const FILE_IO: Self = Self(Cow::Borrowed("file_io"));
114 pub const NETWORK: Self = Self(Cow::Borrowed("network"));
115 pub const DATABASE: Self = Self(Cow::Borrowed("database"));
116
117 pub fn custom(name: impl Into<Cow<'static, str>>) -> Self {
118 Self(name.into())
119 }
120}
121
122#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
133pub struct ToolDefinition {
134 pub name: String,
136 pub description: String,
138 pub parameters: serde_json::Value,
140 #[serde(skip_serializing_if = "Option::is_none")]
142 pub cache_control: Option<crate::message::CacheControl>,
143}
144
145impl ToolDefinition {
146 pub fn with_cache(self, cache: crate::message::CacheControl) -> Self {
148 Self {
149 cache_control: Some(cache),
150 ..self
151 }
152 }
153
154 pub fn compute_and_clean_schema<S: schemars::JsonSchema>() -> serde_json::Value {
161 let root = schemars::schema_for!(S);
162 let val = serde_json::to_value(&root)
163 .expect("Failed to serialize JsonSchema; this is a bug in schemars");
164 Self::clean_schema(val)
165 }
166
167 fn clean_schema(mut value: serde_json::Value) -> serde_json::Value {
172 if let Some(obj) = value.as_object_mut() {
173 obj.remove("$schema");
175 obj.remove("$id");
176 obj.remove("title");
177 obj.remove("description");
178 }
179 value
180 }
181}
182
183pub type ToolFn = Arc<
187 dyn Fn(&serde_json::Value) -> Pin<Box<dyn Future<Output = ToolResult> + Send>> + Send + Sync,
188>;
189
190#[doc(hidden)]
195pub fn __tool_box<F>(f: F) -> Pin<Box<dyn Future<Output = ToolResult> + Send>>
196where
197 F: Future<Output = ToolResult> + Send + 'static,
198{
199 Box::pin(f)
200}
201
202#[derive(Clone)]
217pub struct ExecutableTool {
218 pub definition: ToolDefinition,
220 pub safety: ParallelSafety,
222 pub category: Option<ToolCategory>,
224 executor: ToolFn,
226}
227
228impl ExecutableTool {
229 pub fn definition(&self) -> &ToolDefinition {
233 &self.definition
234 }
235
236 pub fn safety(&self) -> &ParallelSafety {
238 &self.safety
239 }
240
241 pub fn category(&self) -> Option<&ToolCategory> {
243 self.category.as_ref()
244 }
245
246 pub fn execute(
248 &self,
249 args: &serde_json::Value,
250 ) -> Pin<Box<dyn Future<Output = ToolResult> + Send>> {
251 (self.executor)(args)
252 }
253
254 pub fn from_fn(
260 def: ToolDefinition,
261 safety: ParallelSafety,
262 category: Option<ToolCategory>,
263 f: ToolFn,
264 ) -> Self {
265 Self {
266 definition: def,
267 safety,
268 category,
269 executor: f,
270 }
271 }
272
273 pub fn safe<F, Fut>(def: ToolDefinition, f: F) -> Self
277 where
278 F: Fn(&serde_json::Value) -> Fut + Send + Sync + 'static,
279 Fut: Future<Output = ToolResult> + Send + 'static,
280 {
281 Self {
282 definition: def,
283 safety: ParallelSafety::Safe,
284 category: None,
285 executor: Arc::new(move |args: &serde_json::Value| Box::pin(f(args))),
286 }
287 }
288
289 pub fn category_exclusive<F, Fut>(def: ToolDefinition, category: ToolCategory, f: F) -> Self
291 where
292 F: Fn(&serde_json::Value) -> Fut + Send + Sync + 'static,
293 Fut: Future<Output = ToolResult> + Send + 'static,
294 {
295 Self {
296 definition: def,
297 safety: ParallelSafety::CategoryExclusive,
298 category: Some(category),
299 executor: Arc::new(move |args: &serde_json::Value| Box::pin(f(args))),
300 }
301 }
302
303 pub fn exclusive<F, Fut>(def: ToolDefinition, f: F) -> Self
305 where
306 F: Fn(&serde_json::Value) -> Fut + Send + Sync + 'static,
307 Fut: Future<Output = ToolResult> + Send + 'static,
308 {
309 Self {
310 definition: def,
311 safety: ParallelSafety::Exclusive,
312 category: None,
313 executor: Arc::new(move |args: &serde_json::Value| Box::pin(f(args))),
314 }
315 }
316
317 pub fn safe_fn<T, F, Fut>(def: ToolDefinition, f: F) -> Self
324 where
325 T: ToolArgParser + Send + 'static,
326 F: Fn(T) -> Fut + Send + Sync + 'static,
327 Fut: Future<Output = ToolResult> + Send + 'static,
328 {
329 let f = Arc::new(f);
330 Self::safe(def, move |value| {
331 let f = Arc::clone(&f);
332 let result = T::parse(value.clone());
333 async move {
334 match result {
335 Ok(parsed) => f(parsed).await,
336 Err(e) => Err(crate::ToolError::invalid_input(format!(
337 "invalid tool arguments: {e}"
338 ))),
339 }
340 }
341 })
342 }
343
344 pub fn category_exclusive_fn<T, F, Fut>(
346 def: ToolDefinition,
347 category: ToolCategory,
348 f: F,
349 ) -> Self
350 where
351 T: ToolArgParser + Send + 'static,
352 F: Fn(T) -> Fut + Send + Sync + 'static,
353 Fut: Future<Output = ToolResult> + Send + 'static,
354 {
355 let f = Arc::new(f);
356 Self::category_exclusive(def, category, move |value| {
357 let f = Arc::clone(&f);
358 let result = T::parse(value.clone());
359 async move {
360 match result {
361 Ok(parsed) => f(parsed).await,
362 Err(e) => Err(crate::ToolError::invalid_input(format!(
363 "invalid tool arguments: {e}"
364 ))),
365 }
366 }
367 })
368 }
369
370 pub fn exclusive_fn<T, F, Fut>(def: ToolDefinition, f: F) -> Self
372 where
373 T: ToolArgParser + Send + 'static,
374 F: Fn(T) -> Fut + Send + Sync + 'static,
375 Fut: Future<Output = ToolResult> + Send + 'static,
376 {
377 let f = Arc::new(f);
378 Self::exclusive(def, move |value| {
379 let f = Arc::clone(&f);
380 let result = T::parse(value.clone());
381 async move {
382 match result {
383 Ok(parsed) => f(parsed).await,
384 Err(e) => Err(crate::ToolError::invalid_input(format!(
385 "invalid tool arguments: {e}"
386 ))),
387 }
388 }
389 })
390 }
391}
392
393#[deprecated(since = "0.5.0", note = "Use `ExecutableTool` instead")]
395pub type ToolRegistration = ExecutableTool;