Skip to main content

llm/
tools.rs

1use schemars::JsonSchema;
2use serde::{Deserialize, Serialize};
3
4#[doc = include_str!("docs/tools.md")]
5#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
6pub struct ToolDefinition {
7    pub name: String,
8    pub description: String,
9    pub parameters: serde_json::Value,
10    pub server: Option<String>,
11    #[serde(default, skip_serializing_if = "Option::is_none")]
12    pub annotations: Option<ToolAnnotations>,
13}
14
15#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
16#[serde(rename_all = "camelCase")]
17pub struct ToolAnnotations {
18    pub title: Option<String>,
19    pub read_only_hint: Option<bool>,
20    pub destructive_hint: Option<bool>,
21    pub idempotent_hint: Option<bool>,
22    pub open_world_hint: Option<bool>,
23}
24
25impl ToolDefinition {
26    pub fn new(name: impl Into<String>, description: impl Into<String>, parameters: serde_json::Value) -> Self {
27        Self { name: name.into(), description: description.into(), parameters, server: None, annotations: None }
28    }
29
30    pub fn with_server(mut self, server: impl Into<String>) -> Self {
31        self.server = Some(server.into());
32        self
33    }
34
35    pub fn with_annotations(mut self, annotations: impl Into<Option<ToolAnnotations>>) -> Self {
36        self.annotations = annotations.into();
37        self
38    }
39}
40
41impl ToolAnnotations {
42    pub fn read_only() -> Self {
43        Self { read_only_hint: Some(true), ..Self::default() }
44    }
45}
46
47/// Tool call request from the LLM
48#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
49pub struct ToolCallRequest {
50    pub id: String,
51    pub name: String,
52    pub arguments: String,
53}
54
55/// Successful result of a tool call
56#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
57pub struct ToolCallResult {
58    pub id: String,
59    pub name: String,
60    pub arguments: String,
61    pub result: String,
62}
63
64/// Error result of a tool call
65#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
66pub struct ToolCallError {
67    pub id: String,
68    pub name: String,
69    pub arguments: Option<String>,
70    pub error: String,
71}
72
73impl ToolCallRequest {
74    pub(crate) fn input_json(&self) -> serde_json::Value {
75        serde_json::from_str(&self.arguments).unwrap_or_else(|_| serde_json::json!({}))
76    }
77}
78
79pub(crate) fn tool_result_parts(result: &Result<ToolCallResult, ToolCallError>) -> (&str, &str, bool) {
80    match result {
81        Ok(result) => (&result.id, &result.result, false),
82        Err(error) => (&error.id, &error.error, true),
83    }
84}
85
86pub(crate) fn tool_result_request(result: &Result<ToolCallResult, ToolCallError>) -> ToolCallRequest {
87    match result {
88        Ok(result) => {
89            ToolCallRequest { id: result.id.clone(), name: result.name.clone(), arguments: result.arguments.clone() }
90        }
91        Err(error) => ToolCallRequest {
92            id: error.id.clone(),
93            name: error.name.clone(),
94            arguments: error.arguments.clone().unwrap_or_default(),
95        },
96    }
97}
98
99impl ToolCallError {
100    pub fn from_request(request: &ToolCallRequest, error: impl Into<String>) -> Self {
101        Self {
102            id: request.id.clone(),
103            name: request.name.clone(),
104            arguments: Some(request.arguments.clone()),
105            error: error.into(),
106        }
107    }
108}