use serde::{Deserialize, Serialize};
use serde_json::Value;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FunctionDeclaration {
name: String,
description: String,
parameters: Value,
}
impl FunctionDeclaration {
pub fn new(name: impl Into<String>, description: impl Into<String>, parameters: Value) -> Self {
Self {
name: name.into(),
description: description.into(),
parameters,
}
}
#[must_use]
pub fn name(&self) -> &str {
&self.name
}
#[must_use]
pub fn description(&self) -> &str {
&self.description
}
#[must_use]
pub const fn parameters(&self) -> &Value {
&self.parameters
}
#[must_use]
pub fn into_parts(self) -> (String, String, Value) {
(self.name, self.description, self.parameters)
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ToolCall {
id: String,
name: String,
arguments: Value,
#[serde(default, skip_serializing_if = "Option::is_none")]
provider_metadata: Option<Value>,
}
impl ToolCall {
pub fn new(id: impl Into<String>, name: impl Into<String>, arguments: Value) -> Self {
Self {
id: id.into(),
name: name.into(),
arguments,
provider_metadata: None,
}
}
#[must_use]
pub fn id(&self) -> &str {
&self.id
}
#[must_use]
pub fn name(&self) -> &str {
&self.name
}
#[must_use]
pub const fn arguments(&self) -> &Value {
&self.arguments
}
#[must_use]
pub const fn provider_metadata(&self) -> Option<&Value> {
self.provider_metadata.as_ref()
}
#[must_use]
pub fn with_provider_metadata(mut self, metadata: Value) -> Self {
self.provider_metadata = Some(metadata);
self
}
#[must_use]
pub fn into_parts(self) -> (String, String, Value) {
(self.id, self.name, self.arguments)
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ToolResult {
success: bool,
data: Value,
error_message: Option<String>,
}
impl ToolResult {
#[must_use]
pub const fn success(data: Value) -> Self {
Self {
success: true,
data,
error_message: None,
}
}
pub fn error(message: impl Into<String>) -> Self {
Self {
success: false,
data: Value::Null,
error_message: Some(message.into()),
}
}
#[must_use]
pub const fn is_success(&self) -> bool {
self.success
}
#[must_use]
pub const fn is_error(&self) -> bool {
!self.success
}
#[must_use]
pub const fn data(&self) -> &Value {
&self.data
}
#[must_use]
pub fn error_message(&self) -> Option<&str> {
self.error_message.as_deref()
}
#[must_use]
pub fn into_data(self) -> Value {
self.data
}
#[must_use]
pub fn into_parts(self) -> (bool, Value, Option<String>) {
(self.success, self.data, self.error_message)
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ToolResponse {
tool_call_id: String,
result: ToolResult,
}
impl ToolResponse {
pub fn new(tool_call_id: impl Into<String>, result: ToolResult) -> Self {
Self {
tool_call_id: tool_call_id.into(),
result,
}
}
#[must_use]
pub fn tool_call_id(&self) -> &str {
&self.tool_call_id
}
#[must_use]
pub const fn result(&self) -> &ToolResult {
&self.result
}
#[must_use]
pub fn into_result(self) -> ToolResult {
self.result
}
#[must_use]
pub fn into_parts(self) -> (String, ToolResult) {
(self.tool_call_id, self.result)
}
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn function_declaration_accessors() {
let declaration =
FunctionDeclaration::new("echo", "Echo back input", json!({"type": "object"}));
assert_eq!(declaration.name(), "echo");
assert_eq!(declaration.description(), "Echo back input");
assert_eq!(declaration.parameters(), &json!({"type": "object"}));
let (name, description, _params) = declaration.into_parts();
assert_eq!(name, "echo");
assert_eq!(description, "Echo back input");
}
#[test]
fn tool_result_success_and_error_variants() {
let ok = ToolResult::success(json!({"value": 1}));
assert!(ok.is_success());
assert!(!ok.is_error());
assert_eq!(ok.data(), &json!({"value": 1}));
assert!(ok.error_message().is_none());
let err = ToolResult::error("something went wrong");
assert!(err.is_error());
assert_eq!(err.error_message(), Some("something went wrong"));
}
#[test]
fn tool_response_correlates_to_call() {
let result = ToolResult::success(json!({}));
let response = ToolResponse::new("call-123", result.clone());
assert_eq!(response.tool_call_id(), "call-123");
assert_eq!(response.result().is_success(), result.is_success());
let (id, inner) = response.into_parts();
assert_eq!(id, "call-123");
assert!(inner.is_success());
}
}