Skip to main content

rig_core/embeddings/
tool.rs

1//! The module defines the [ToolSchema] struct, which is used to embed an object that implements [crate::tool::PortableToolEmbedding]
2
3use crate::{Embed, tool::PortableToolEmbedding};
4use serde::Serialize;
5
6use super::embed::EmbedError;
7
8/// Embeddable document that is used as an intermediate representation of a tool when
9/// RAGging tools.
10#[derive(Clone, Serialize, Default, Eq, PartialEq)]
11pub struct ToolSchema {
12    pub name: String,
13    pub context: serde_json::Value,
14    pub embedding_docs: Vec<String>,
15}
16
17impl Embed for ToolSchema {
18    fn embed(&self, embedder: &mut super::embed::TextEmbedder) -> Result<(), EmbedError> {
19        for doc in &self.embedding_docs {
20            embedder.embed(doc.clone());
21        }
22        Ok(())
23    }
24}
25
26impl ToolSchema {
27    /// Convert an embedding-backed tool to a [`ToolSchema`].
28    ///
29    /// # Example
30    /// ```rust
31    /// use rig_core::{
32    ///     embeddings::ToolSchema,
33    ///     tool::{PortableTool, PortableToolEmbedding},
34    /// };
35    ///
36    /// #[derive(Debug, thiserror::Error)]
37    /// #[error("Nothing error")]
38    /// struct NothingError;
39    ///
40    /// #[derive(Debug, thiserror::Error)]
41    /// #[error("Init error")]
42    /// struct InitError;
43    ///
44    /// struct Nothing;
45    /// impl PortableTool for Nothing {
46    ///     const NAME: &'static str = "nothing";
47    ///
48    ///     type Args = ();
49    ///     type Output = ();
50    ///     type Error = NothingError;
51    ///
52    ///     fn description(&self) -> String {
53    ///         "nothing".to_string()
54    ///     }
55    ///
56    ///     fn parameters(&self) -> serde_json::Value {
57    ///         serde_json::json!({})
58    ///     }
59    ///
60    ///     async fn call(&self, _args: Self::Args) -> Result<Self::Output, Self::Error> {
61    ///         Ok(())
62    ///     }
63    /// }
64    ///
65    /// impl PortableToolEmbedding for Nothing {
66    ///     type InitError = InitError;
67    ///     type Context = ();
68    ///     type State = ();
69    ///
70    ///     fn init(_state: Self::State, _context: Self::Context) -> Result<Self, Self::InitError> {
71    ///         Ok(Nothing)
72    ///     }
73    ///
74    ///     fn embedding_docs(&self) -> Vec<String> {
75    ///         vec!["Do nothing.".into()]
76    ///     }
77    ///
78    ///     fn context(&self) -> Self::Context {}
79    /// }
80    ///
81    /// let tool = ToolSchema::try_from(&Nothing).unwrap();
82    ///
83    /// assert_eq!(tool.name, "nothing".to_string());
84    /// assert_eq!(tool.embedding_docs, vec!["Do nothing.".to_string()]);
85    /// ```
86    pub fn try_from<T>(tool: &T) -> Result<Self, EmbedError>
87    where
88        T: PortableToolEmbedding + 'static,
89    {
90        Ok(ToolSchema {
91            name: T::NAME.to_string(),
92            context: serde_json::to_value(tool.context()).map_err(EmbedError::new)?,
93            embedding_docs: tool.embedding_docs(),
94        })
95    }
96}
97
98#[cfg(test)]
99mod tests {
100    use std::convert::Infallible;
101
102    use super::ToolSchema;
103    use crate::tool::{PortableTool, PortableToolEmbedding, ToolExecutionError};
104
105    struct NamedTool;
106
107    impl PortableTool for NamedTool {
108        const NAME: &'static str = "static_name";
109
110        type Error = rig::tool::ToolExecutionError;
111
112        type Args = ();
113        type Output = ();
114
115        fn description(&self) -> String {
116            "A statically named tool".to_string()
117        }
118
119        fn parameters(&self) -> serde_json::Value {
120            serde_json::json!({})
121        }
122
123        async fn call(&self, _args: Self::Args) -> Result<Self::Output, ToolExecutionError> {
124            Ok(())
125        }
126    }
127
128    impl PortableToolEmbedding for NamedTool {
129        type InitError = Infallible;
130        type Context = ();
131        type State = ();
132
133        fn embedding_docs(&self) -> Vec<String> {
134            vec!["named tool".to_string()]
135        }
136
137        fn context(&self) -> Self::Context {}
138
139        fn init(_state: Self::State, _context: Self::Context) -> Result<Self, Self::InitError> {
140            Ok(Self)
141        }
142    }
143
144    #[test]
145    fn try_from_uses_canonical_tool_name() {
146        let schema = ToolSchema::try_from(&NamedTool).unwrap();
147
148        assert_eq!(schema.name, NamedTool::NAME);
149    }
150}