1use std::future::Future;
4use std::pin::Pin;
5use std::sync::Arc;
6
7use async_trait::async_trait;
8use serde_json::Value;
9
10use crate::context::ToolCallContext;
11use crate::error::ToolError;
12use crate::metadata::ToolMetadata;
13use crate::stream::{ToolStream, terminal_only};
14
15#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
17#[allow(
18 clippy::derive_partial_eq_without_eq,
19 reason = "JSON Schema Value is not Eq"
20)]
21pub struct ToolDefinition {
22 pub name: String,
24 pub description: String,
26 pub parameters: Value,
28}
29
30#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
32#[allow(
33 clippy::derive_partial_eq_without_eq,
34 reason = "optional JSON Value is not Eq"
35)]
36pub struct ToolResult {
37 pub content: String,
39 #[serde(default, skip_serializing_if = "Option::is_none")]
41 pub structured: Option<Value>,
42 #[serde(default)]
44 pub is_error: bool,
45}
46
47impl ToolResult {
48 #[must_use]
50 pub fn text(content: impl Into<String>) -> Self {
51 Self {
52 content: content.into(),
53 structured: None,
54 is_error: false,
55 }
56 }
57
58 #[must_use]
60 pub fn error(content: impl Into<String>) -> Self {
61 Self {
62 content: content.into(),
63 structured: None,
64 is_error: true,
65 }
66 }
67}
68
69#[async_trait]
71pub trait DynTool: Send + Sync {
72 fn name(&self) -> &str;
74 fn description(&self) -> &str;
76 fn parameters(&self) -> Value;
78 fn metadata(&self) -> ToolMetadata {
80 ToolMetadata::default()
81 }
82 fn definition(&self) -> ToolDefinition {
84 ToolDefinition {
85 name: self.name().to_owned(),
86 description: self.description().to_owned(),
87 parameters: self.parameters(),
88 }
89 }
90 async fn call(&self, ctx: ToolCallContext, arguments: Value) -> Result<ToolResult, ToolError>;
94
95 async fn execute(&self, ctx: ToolCallContext, arguments: Value) -> ToolStream {
97 let result = self.call(ctx, arguments).await;
98 terminal_only(result)
99 }
100}
101
102pub type SharedTool = Arc<dyn DynTool>;
104
105pub type BoxFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>;