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}