language_barrier_core/
tool.rs

1use schemars::JsonSchema;
2use serde::{Serialize, de::DeserializeOwned};
3use serde_json::Value;
4
5use crate::error::Result;
6
7/// Defines the contract for tools that can be used by LLMs
8///
9/// Tools provide a standardized way to extend language models with custom functionality.
10/// Each tool defines its name, description, parameter schema, and execution logic.
11///
12/// # Type Parameters
13///
14/// The `Tool` trait requires that implementors also implement `JsonSchema`, which is
15/// used to automatically generate JSON schemas for tool parameters.
16///
17/// # Examples
18///
19/// ```
20/// use language_barrier_core::Tool;
21/// use schemars::JsonSchema;
22/// use serde::{Deserialize, Serialize};
23///
24/// #[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
25/// struct WeatherRequest {
26///     location: String,
27///     units: Option<String>,
28/// }
29///
30/// impl Tool for WeatherRequest {
31///     fn name(&self) -> &str {
32///         "get_weather"
33///     }
34///
35///     fn description(&self) -> &str {
36///         "Get current weather for a location"
37///     }
38/// }
39/// ```
40pub trait Tool
41where
42    Self: JsonSchema,
43{
44    /// Returns the name of the tool
45    ///
46    /// This name is used to identify the tool in the LLM's API.
47    fn name(&self) -> &str;
48
49    /// Returns the description of the tool
50    ///
51    /// This description is sent to the LLM to help it understand when and how
52    /// to use the tool. It should be clear and concise.
53    fn description(&self) -> &str;
54}
55
56/// Core trait for defining tools with associated input and output types
57///
58/// This trait provides a more flexible and type-safe way to define tools compared
59/// to the original `Tool` trait. It uses associated types to specify the input and output
60/// types for a tool, allowing for better compile-time type checking.
61///
62/// # Type Parameters
63///
64/// * `Input` - The type of input that this tool accepts, must implement `DeserializeOwned` and `JsonSchema`
65/// * `Output` - The type of output that this tool produces, must implement `Serialize`
66///
67/// # Examples
68///
69/// ```
70/// use language_barrier_core::ToolDefinition;
71/// use schemars::JsonSchema;
72/// use serde::{Deserialize, Serialize};
73///
74/// #[derive(Debug, Clone, Deserialize, JsonSchema)]
75/// struct WeatherRequest {
76///     location: String,
77///     units: Option<String>,
78/// }
79///
80/// #[derive(Debug, Clone, Serialize)]
81/// struct WeatherResponse {
82///     temperature: f64,
83///     condition: String,
84///     location: String,
85///     units: String,
86/// }
87///
88/// struct WeatherTool;
89///
90/// impl ToolDefinition for WeatherTool {
91///     type Input = WeatherRequest;
92///     type Output = WeatherResponse;
93///
94///     fn name(&self) -> String {
95///         "get_weather".to_string()
96///     }
97///
98///     fn description(&self) -> String {
99///         "Get current weather for a location".to_string()
100///     }
101/// }
102/// ```
103pub trait ToolDefinition {
104    /// The input type that this tool accepts
105    type Input: DeserializeOwned + JsonSchema + Send + Sync + 'static;
106
107    /// The output type that this tool produces
108    type Output: Serialize + Send + Sync + 'static;
109
110    /// Returns the name of the tool
111    fn name(&self) -> String;
112
113    /// Returns the description of the tool
114    fn description(&self) -> String;
115
116    /// Helper to generate the JSON schema for the input type
117    fn schema(&self) -> Result<Value> {
118        let schema = schemars::schema_for!(Self::Input);
119        serde_json::to_value(schema.schema)
120            .map_err(|e| crate::Error::Other(format!("Schema generation failed: {}", e)))
121    }
122}
123
124/// LLM-facing representation of a tool
125#[derive(Serialize, Debug, Clone)]
126pub struct LlmToolInfo {
127    pub name: String,
128    pub description: String,
129    pub parameters: Value,
130}
131
132/// Represents the tool choice strategy for LLMs
133///
134/// This enum provides a provider-agnostic API for tool choice, mapping to
135/// different provider-specific parameters:
136///
137/// - OpenAI/Mistral: Maps to "auto", "required", "none", or a function object
138/// - Anthropic: Maps to "auto", "any", "none", or a function object
139/// - Gemini: Maps to function_calling_config modes and allowed_function_names
140#[derive(Debug, Clone, PartialEq, Eq)]
141pub enum ToolChoice {
142    /// Allow the model to choose which tool to use (or none)
143    /// 
144    /// - OpenAI/Mistral: "auto"
145    /// - Anthropic: "auto"
146    /// - Gemini: mode="auto"
147    Auto,
148    /// Require the model to use one of the available tools
149    /// 
150    /// - OpenAI/Mistral: "required"
151    /// - Anthropic: "any"
152    /// - Gemini: mode="any"
153    Any,
154    /// Force the model not to use any tools
155    /// 
156    /// - OpenAI/Mistral: "none"
157    /// - Anthropic: "none"
158    /// - Gemini: mode="none"
159    None,
160    /// Require the model to use a specific tool by name
161    /// 
162    /// - OpenAI/Mistral: Object with type="function" and function.name
163    /// - Anthropic: Object with type="function" and function.name
164    /// - Gemini: mode="auto" with allowed_function_names=[name]
165    Specific(String),
166}
167
168impl Default for ToolChoice {
169    fn default() -> Self {
170        Self::Auto
171    }
172}
173
174#[cfg(test)]
175mod tests {
176    use super::*;
177    
178    #[test]
179    fn test_tool_choice_default() {
180        let choice = ToolChoice::default();
181        assert_eq!(choice, ToolChoice::Auto);
182    }
183    
184    #[test]
185    fn test_tool_choice_specific() {
186        let choice = ToolChoice::Specific("weather".to_string());
187        assert!(matches!(choice, ToolChoice::Specific(name) if name == "weather"));
188    }
189}