use serde::{Deserialize, Serialize, de::DeserializeOwned};
use serde_json::{Value, json};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct Tool {
pub name: String,
pub description: String,
pub input_schema: Value,
}
impl Tool {
pub fn new(name: impl Into<String>, description: impl Into<String>) -> Self {
Self {
name: name.into(),
description: description.into(),
input_schema: json!({"type": "object", "properties": {}}),
}
}
pub fn schema(mut self, schema: Value) -> Self {
self.input_schema = schema;
self
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct ToolCall {
pub id: String,
pub name: String,
pub arguments: String,
}
impl ToolCall {
pub fn new(
id: impl Into<String>,
name: impl Into<String>,
arguments: impl Into<String>,
) -> Self {
Self {
id: id.into(),
name: name.into(),
arguments: arguments.into(),
}
}
pub fn parse<T: DeserializeOwned>(&self) -> serde_json::Result<T> {
serde_json::from_str(&self.arguments)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(Debug, PartialEq, Deserialize)]
struct Lookup {
value: i64,
}
#[test]
fn arguments_parse_on_demand() {
let call = ToolCall::new("call-a", "lookup", r#"{"value":1}"#);
assert_eq!(call.parse::<Lookup>().unwrap(), Lookup { value: 1 });
assert!(ToolCall::new("x", "lookup", "{").parse::<Lookup>().is_err());
}
#[test]
fn a_new_tool_takes_no_arguments() {
let tool = Tool::new("now", "The current time.");
assert_eq!(
tool.input_schema,
json!({"type": "object", "properties": {}})
);
}
}