openai_tools/common/
function.rs

1//! Function module for OpenAI tools.
2//!
3//! This module provides the `Function` struct and related functionality for representing
4//! and working with OpenAI function calls. Functions are used to define callable operations
5//! that can be invoked by OpenAI's API, including their parameters, descriptions, and
6//! execution modes.
7//!
8//! The `Function` struct is a core component that is used throughout the OpenAI tools library:
9//!
10//! - In [`crate::common::message`] - Used within `ToolCall` structures to represent function calls in messages
11//! - In [`crate::common::tool`] - Used within `Tool` structures to define available functions for OpenAI models
12//! - In [`crate::chat::request`] - Used in Chat Completion API requests for function calling capabilities
13//! - In [`crate::responses::request`] - Used in Responses API requests for structured interactions
14//!
15//! ## Key Features
16//!
17//! - **Serialization/Deserialization**: Custom implementations for clean JSON output
18//! - **Flexible Parameters**: Support for complex parameter schemas via [`crate::common::parameters::Parameters`]
19//! - **Argument Handling**: Support for both JSON string and structured argument formats
20//! - **Strict Mode**: Optional strict execution mode for enhanced validation
21//!
22//! ## Usage Patterns
23//!
24//! Functions are typically used in two main contexts:
25//!
26//! 1. **Tool Definition**: When creating tools that OpenAI models can call
27//! 2. **Function Execution**: When processing function calls from OpenAI responses
28
29use crate::common::{
30    errors::{OpenAIToolError, Result as OpenAIToolResult},
31    parameters::Parameters,
32};
33use serde::{ser::SerializeStruct, Deserialize, Serialize};
34use serde_json::Value;
35use std::collections::HashMap;
36
37/// Represents a function that can be called by OpenAI tools.
38///
39/// This structure contains metadata about a function including its name, description,
40/// parameters, and whether it should be executed in strict mode.
41#[derive(Debug, Clone, Default)]
42pub struct Function {
43    /// The name of the function
44    pub name: String,
45    /// Optional description of what the function does
46    pub description: Option<String>,
47    /// Optional parameters that the function accepts
48    pub parameters: Option<Parameters>,
49    /// Optional arguments passed to the function as key-value pairs
50    pub arguments: Option<HashMap<String, Value>>,
51    /// Whether the function should be executed in strict mode
52    pub strict: bool,
53}
54
55impl Function {
56    /// Creates a new Function instance with the specified parameters.
57    ///
58    /// # Arguments
59    ///
60    /// * `name` - The name of the function
61    /// * `description` - A description of what the function does
62    /// * `parameters` - The parameters that the function accepts
63    /// * `strict` - Whether the function should be executed in strict mode
64    ///
65    /// # Returns
66    ///
67    /// A new Function instance
68    pub fn new<T: AsRef<str>, U: AsRef<str>>(name: T, description: U, parameters: Parameters, strict: bool) -> Self {
69        Self {
70            name: name.as_ref().to_string(),
71            description: Some(description.as_ref().to_string()),
72            parameters: Some(parameters),
73            strict,
74            ..Default::default()
75        }
76    }
77
78    /// Returns the function arguments as a HashMap.
79    ///
80    /// # Returns
81    ///
82    /// * `Ok(HashMap<String, Value>)` - The arguments as a map if they exist
83    /// * `Err(OpenAIToolError)` - If the arguments are not set
84    ///
85    /// # Errors
86    ///
87    /// This function will return an error if the arguments are not set.
88    pub fn arguments_as_map(&self) -> OpenAIToolResult<HashMap<String, Value>> {
89        if let Some(args) = &self.arguments {
90            Ok(args.clone())
91        } else {
92            Err(OpenAIToolError::from(anyhow::anyhow!("Function arguments are not set")))
93        }
94    }
95}
96
97/// Custom serialization implementation for Function.
98///
99/// This implementation ensures that only non-None optional fields are serialized,
100/// keeping the JSON output clean and avoiding null values for optional fields.
101impl Serialize for Function {
102    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
103    where
104        S: serde::Serializer,
105    {
106        let mut state = serializer.serialize_struct("Function", 4)?;
107        state.serialize_field("name", &self.name)?;
108        if let Some(description) = &self.description {
109            state.serialize_field("description", description)?;
110        }
111        if let Some(parameters) = &self.parameters {
112            state.serialize_field("parameters", parameters)?;
113        }
114        if let Some(arguments) = &self.arguments {
115            state.serialize_field("arguments", arguments)?;
116        }
117        state.serialize_field("strict", &self.strict)?;
118        state.end()
119    }
120}
121
122/// Custom deserialization implementation for Function.
123///
124/// This implementation handles the deserialization of Function from JSON,
125/// parsing arguments as JSON strings and converting parameters from JSON objects.
126/// The `name` field is required, while other fields are optional.
127impl<'de> Deserialize<'de> for Function {
128    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
129    where
130        D: serde::Deserializer<'de>,
131    {
132        let mut function = Function::default();
133        let map: HashMap<String, Value> = HashMap::deserialize(deserializer)?;
134
135        if let Some(name) = map.get("name").and_then(Value::as_str) {
136            function.name = name.to_string();
137        } else {
138            return Err(serde::de::Error::missing_field("name"));
139        }
140
141        let arguments = map.get("arguments").and_then(Value::as_str);
142        if let Some(args) = arguments {
143            function.arguments = serde_json::from_str(args).ok();
144        } else {
145            function.arguments = None;
146        }
147
148        let parameters = map.get("parameters").and_then(Value::as_object);
149        if let Some(params) = parameters {
150            function.parameters = Some(Parameters::deserialize(Value::Object(params.clone())).map_err(serde::de::Error::custom)?);
151        } else {
152            function.parameters = None;
153        }
154
155        function.description = map.get("description").and_then(Value::as_str).map(String::from);
156        function.strict = map.get("strict").and_then(Value::as_bool).unwrap_or(false);
157
158        Ok(function)
159    }
160}