synapto_interface/
tool.rs1#![doc = include_str!("tool.md")]
2
3use crate::context::ContextRequest;
4use crate::llm::LLMSafe;
5use schemars::JsonSchema;
6use serde::{Deserialize, Serialize};
7
8#[async_trait::async_trait]
9pub trait Tool: Send + Sync + 'static {
10 type Arguments: schemars::JsonSchema
11 + serde::de::DeserializeOwned
12 + LLMSafe
13 + Send
14 + Sync
15 + 'static;
16 const NAME: &'static str;
17 const DESCRIPTION: &'static str;
18 #[doc = " Evaluated dynamically every turn AFTER the ContextProviders have compiled the World State."]
19 #[doc = " `compiled_context` is the JSON value generated by all ContextProviders that the LLM is about to see."]
20 async fn is_available(
21 &self,
22 _ctx_request: &ContextRequest,
23 _compiled_context: &serde_json::Value,
24 ) -> Result<bool, String> {
25 Ok(true)
26 }
27 #[doc = " Executes the tool. The result is serialized and fed back to the LLM."]
28 async fn execute(
29 &self,
30 ctx_request: &ContextRequest,
31 args: Self::Arguments,
32 ) -> Result<serde_json::Value, String>;
33}
34
35#[async_trait::async_trait]
36pub trait ErasedTool: Send + Sync + 'static {
37 fn name(&self) -> &'static str;
38 fn description(&self) -> &'static str;
39 fn schema(&self) -> schemars::Schema;
40 async fn erased_is_available(
41 &self,
42 ctx_request: &ContextRequest,
43 compiled_context: &serde_json::Value,
44 ) -> Result<bool, String>;
45 async fn erased_execute(
46 &self,
47 ctx_request: &ContextRequest,
48 args: serde_json::Value,
49 ) -> Result<serde_json::Value, String>;
50}
51
52#[async_trait::async_trait]
53impl<T> ErasedTool for T
54where
55 T: Tool,
56{
57 fn name(&self) -> &'static str {
58 <T as Tool>::NAME
59 }
60 fn description(&self) -> &'static str {
61 <T as Tool>::DESCRIPTION
62 }
63 fn schema(&self) -> schemars::Schema {
64 schemars::schema_for!(<T as Tool>::Arguments)
65 }
66 async fn erased_is_available(
67 &self,
68 ctx_request: &ContextRequest,
69 compiled_context: &serde_json::Value,
70 ) -> Result<bool, String> {
71 <T as Tool>::is_available(self, ctx_request, compiled_context).await
72 }
73 async fn erased_execute(
74 &self,
75 ctx_request: &ContextRequest,
76 args: serde_json::Value,
77 ) -> Result<serde_json::Value, String> {
78 let parsed_args = serde_json::from_value(args).map_err(|e| e.to_string())?;
79 <T as Tool>::execute(self, ctx_request, parsed_args).await
80 }
81}
82
83#[derive(Default)]
84pub struct ToolRegistryBuilder {
85 pub tools: std::sync::RwLock<std::collections::HashMap<String, std::sync::Arc<dyn ErasedTool>>>,
86}
87
88impl ToolRegistryBuilder {
89 pub fn register<T>(&self, tool: T)
90 where
91 T: ErasedTool + 'static,
92 {
93 let tool_arc: std::sync::Arc<dyn ErasedTool> = std::sync::Arc::new(tool);
94 self.register_erased(tool_arc);
95 }
96 pub fn register_erased(&self, tool: std::sync::Arc<dyn ErasedTool>) {
97 self.tools
98 .write()
99 .unwrap_or_else(|e| panic!("Failed to acquire write lock on tools: {:?}", e))
100 .insert(tool.name().to_string(), tool);
101 }
102 pub fn get(&self, name: &str) -> Option<std::sync::Arc<dyn ErasedTool>> {
103 self.tools
104 .read()
105 .unwrap_or_else(|e| panic!("Failed to acquire read lock on tools: {:?}", e))
106 .get(name)
107 .cloned()
108 }
109 pub fn get_all(&self) -> Vec<std::sync::Arc<dyn ErasedTool>> {
110 self.tools
111 .read()
112 .unwrap_or_else(|e| panic!("Failed to acquire read lock on tools: {:?}", e))
113 .values()
114 .cloned()
115 .collect()
116 }
117}
118
119#[derive(
120 Serialize,
121 Deserialize,
122 JsonSchema,
123 PartialEq,
124 Eq,
125 Debug,
126 Clone,
127 derive_more :: Display,
128 derive_more :: From,
129 derive_more :: Deref,
130)]
131pub struct ToolCallId(pub String);