use serde::{Deserialize, Serialize};
use super::ToolFunction;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ToolDefinition {
#[serde(rename = "type")]
pub type_field: String,
pub function: ToolFunction,
}
impl ToolDefinition {
pub fn function(name: impl Into<String>, parameters: serde_json::Value) -> Self {
Self {
type_field: "function".to_string(),
function: ToolFunction::new(name, parameters),
}
}
pub fn function_no_params(name: impl Into<String>) -> Self {
Self {
type_field: "function".to_string(),
function: ToolFunction::no_params(name),
}
}
pub fn from_function(function: ToolFunction) -> Self {
Self {
type_field: "function".to_string(),
function,
}
}
pub fn with_description(mut self, description: impl Into<String>) -> Self {
self.function.description = Some(description.into());
self
}
pub fn name(&self) -> &str {
&self.function.name
}
pub fn description(&self) -> Option<&str> {
self.function.description.as_deref()
}
pub fn parameters(&self) -> &serde_json::Value {
&self.function.parameters
}
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn test_tool_definition_function() {
let tool = ToolDefinition::function(
"test",
json!({
"type": "object",
"properties": {}
}),
);
assert_eq!(tool.type_field, "function");
assert_eq!(tool.function.name, "test");
assert!(tool.function.description.is_none());
}
#[test]
fn test_tool_definition_function_no_params() {
let tool = ToolDefinition::function_no_params("get_time");
assert_eq!(tool.type_field, "function");
assert_eq!(tool.function.name, "get_time");
assert_eq!(tool.function.parameters["type"], "object");
}
#[test]
fn test_tool_definition_from_function() {
let func = ToolFunction::new("custom", json!({})).with_description("Custom func");
let tool = ToolDefinition::from_function(func);
assert_eq!(tool.type_field, "function");
assert_eq!(tool.function.name, "custom");
assert_eq!(tool.function.description, Some("Custom func".to_string()));
}
#[test]
fn test_tool_definition_with_description() {
let tool = ToolDefinition::function("test", json!({})).with_description("A test tool");
assert_eq!(tool.function.description, Some("A test tool".to_string()));
}
#[test]
fn test_tool_definition_name() {
let tool = ToolDefinition::function("my_func", json!({}));
assert_eq!(tool.name(), "my_func");
}
#[test]
fn test_tool_definition_description() {
let tool = ToolDefinition::function("test", json!({}));
assert!(tool.description().is_none());
let tool = tool.with_description("Does stuff");
assert_eq!(tool.description(), Some("Does stuff"));
}
#[test]
fn test_tool_definition_parameters() {
let params = json!({
"type": "object",
"properties": {
"x": {"type": "number"}
}
});
let tool = ToolDefinition::function("calc", params.clone());
assert_eq!(tool.parameters(), ¶ms);
}
#[test]
fn test_tool_definition_serialize() {
let tool = ToolDefinition::function(
"get_weather",
json!({
"type": "object",
"properties": {
"location": {"type": "string"}
},
"required": ["location"]
}),
)
.with_description("Get weather");
let json = serde_json::to_value(&tool).unwrap();
assert_eq!(json["type"], "function");
assert_eq!(json["function"]["name"], "get_weather");
assert_eq!(json["function"]["description"], "Get weather");
assert_eq!(json["function"]["parameters"]["type"], "object");
}
#[test]
fn test_tool_definition_deserialize() {
let json = r#"{
"type": "function",
"function": {
"name": "search",
"description": "Search the web",
"parameters": {
"type": "object",
"properties": {
"query": {"type": "string"}
}
}
}
}"#;
let tool: ToolDefinition = serde_json::from_str(json).unwrap();
assert_eq!(tool.type_field, "function");
assert_eq!(tool.function.name, "search");
assert_eq!(
tool.function.description,
Some("Search the web".to_string())
);
}
#[test]
fn test_tool_definition_serialize_matches_api_format() {
let tool = ToolDefinition::function(
"get_weather",
json!({
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "City name"
},
"unit": {
"type": "string",
"enum": ["celsius", "fahrenheit"]
}
},
"required": ["location"]
}),
)
.with_description("Get the current weather for a location");
let json_value = serde_json::to_value(&tool).unwrap();
let json_string = serde_json::to_string_pretty(&json_value).unwrap();
assert!(json_string.contains("\"type\": \"function\""));
assert!(json_string.contains("\"name\": \"get_weather\""));
assert!(json_string.contains("\"description\":"));
assert!(json_string.contains("\"parameters\":"));
}
#[test]
fn test_tool_definition_clone() {
let tool = ToolDefinition::function("test", json!({})).with_description("desc");
let cloned = tool.clone();
assert_eq!(tool, cloned);
}
#[test]
fn test_tool_definition_equality() {
let tool1 = ToolDefinition::function("a", json!({}));
let tool2 = ToolDefinition::function("a", json!({}));
let tool3 = ToolDefinition::function("b", json!({}));
assert_eq!(tool1, tool2);
assert_ne!(tool1, tool3);
}
}