call_agent/chat/
api.rs

1use std::collections::VecDeque;
2
3use serde::{ser::SerializeStruct, Deserialize, Serialize, Serializer};
4
5use super::function::ToolDef;
6
7use super::prompt::{Choice, Message};
8
9/// API Response Headers struct
10#[derive(Debug, Clone)]
11pub struct APIResponseHeaders {
12    /// Retry-After header value (in seconds)
13    pub retry_after: Option<u64>,
14    /// X-RateLimit-Reset header value (timestamp or seconds)
15    pub reset: Option<u64>,
16    /// X-RateLimit-Remaining header value (number of remaining requests)
17    pub rate_limit: Option<u64>,
18    /// X-RateLimit-Limit header value (maximum allowed requests)
19    pub limit: Option<u64>,
20
21    /// Additional custom headers as key-value pairs
22    pub extra_other: Vec<(String, String)>,
23}
24
25/// API Request structure for sending prompt and function information
26#[derive(Debug, Deserialize)]
27pub struct APIRequest {
28    /// Specifies the model name
29    pub model: String,
30
31    /// Array of prompt messages
32    pub messages: VecDeque<Message>,
33
34    /// Defines the tools available to the model
35    #[serde(skip_serializing_if = "Vec::is_empty")]
36    pub tools: Vec<ToolDef>,
37
38    /// Instructions for function calls:
39    /// - "auto": AI will make one or more function calls if needed
40    /// - "none": No function calls will be made
41    /// - "required": One or more function calls are mandatory
42    /// - {"type": "function", "function": {"name": "<function_name>"}}: Calls the function <function_name>
43    #[serde(skip_serializing_if = "Vec::is_empty")]
44    pub tool_choice: serde_json::Value,
45
46    /// Specifies whether to make parallel tool calls
47    /// default: true
48    #[serde(skip_serializing_if = "Option::is_none")]
49    pub parallel_tool_calls: Option<bool>,
50
51    /// Specifies the diversity of tokens generated by the model
52    /// Range: 0.0..2.0
53    /// default: 0.8 (soft specification)
54    #[serde(skip_serializing_if = "Option::is_none")]
55    pub temperature: Option<f64>,
56
57    /// Specifies the maximum number of tokens generated by the model
58    #[serde(skip_serializing_if = "Option::is_none")]
59    pub max_completion_tokens: Option<u64>,
60
61    /// Specifies the width of the probability distribution for selecting the next token
62    /// Lower values result in more predictable text
63    #[serde(skip_serializing_if = "Option::is_none")]
64    pub top_p: Option<f64>,
65
66    /// Specifies the level of effort for model reasoning:
67    /// - "low": Low effort
68    /// - "medium": Medium effort
69    /// - "high": High effort
70    /// default: "medium"
71    #[serde(skip_serializing_if = "Option::is_none")]
72    pub reasoning_effort: Option<String>,
73
74    /// Specifies whether to apply a repetition penalty to the model
75    /// Range: 2.0..-2.0
76    #[serde(skip_serializing_if = "Option::is_none")]
77    pub presence_penalty: Option<f64>,
78
79    /// Options for performing web search with available models
80    #[serde(skip_serializing_if = "Option::is_none")]
81    pub web_search_options: Option<WebSearchOptions>,
82}
83
84// Custom Serialize implementation for APIRequest
85impl Serialize for APIRequest {
86    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
87    where
88        S: Serializer,
89    {
90        // Serialize with capacity for potential optional fields
91        let mut state = serializer.serialize_struct("APIRequest", 10)?;
92
93        state.serialize_field("model", &self.model)?;
94        state.serialize_field("messages", &self.messages)?;
95
96        // Serialize "tools" only if not empty
97        if !self.tools.is_empty() {
98            state.serialize_field("tools", &self.tools)?;
99        }
100
101        // Serialize "tool_choice" only if it is not equal to the string "none"
102        if self.tool_choice != serde_json::Value::String("none".to_string()) {
103            state.serialize_field("tool_choice", &self.tool_choice)?;
104        }
105
106        // Serialize optional fields if they are present
107        if let Some(parallel_tool_calls) = &self.parallel_tool_calls {
108            state.serialize_field("parallel_tool_calls", parallel_tool_calls)?;
109        }
110        if let Some(temperature) = &self.temperature {
111            state.serialize_field("temperature", temperature)?;
112        }
113        if let Some(max_completion_tokens) = &self.max_completion_tokens {
114            state.serialize_field("max_completion_tokens", max_completion_tokens)?;
115        }
116        if let Some(top_p) = &self.top_p {
117            state.serialize_field("top_p", top_p)?;
118        }
119        if let Some(reasoning_effort) = &self.reasoning_effort {
120            state.serialize_field("reasoning_effort", reasoning_effort)?;
121        }
122        if let Some(presence_penalty) = &self.presence_penalty {
123            state.serialize_field("presence_penalty", presence_penalty)?;
124        }
125
126        state.end()
127    }
128}
129
130/// API Response structure from the server
131#[derive(Debug, Deserialize, Clone)]
132pub struct APIResponse {
133    /// Unique identifier for the API response
134    pub id: String,
135    /// IDK
136    pub object: String,
137    /// Model name used in the response
138    pub model: Option<String>,
139    /// Array of choices (results) returned by the API
140    pub choices: Option<Vec<Choice>>,
141    /// Error information if the request failed
142    pub error: Option<APIError>,
143    /// Information regarding token usage
144    pub usage: Option<APIUsage>,
145    /// Timestamp of when the response was created
146    pub created: Option<u64>,
147}
148
149/// API Error information structure
150#[derive(Debug, Deserialize, Clone)]
151pub struct APIError {
152    /// Error message text
153    pub message: String,
154    /// Error type (renamed from "type" to avoid keyword conflict)
155    #[serde(rename = "type")]
156    pub err_type: String,
157    /// Error code number
158    pub code: i32,
159}
160
161/// API Usage information detailing token counts
162#[derive(Debug, Deserialize, Clone)]
163pub struct APIUsage {
164    /// Number of tokens used in the prompt
165    pub prompt_tokens: Option<u64>,
166    /// Number of tokens used in the response
167    pub completion_tokens: Option<u64>,
168    /// Total number of tokens used (prompt + response)
169    pub total_tokens: Option<u64>,
170}
171
172#[derive(Debug, Clone, Deserialize)]
173pub struct WebSearchOptions {
174    /// Degree of context size used for web search
175    /// - "low"
176    /// - "medium"
177    /// - "high"
178    /// default: "medium"
179    pub search_context_size: Option<String>,
180    pub user_location: UserLocation,
181}
182
183impl Serialize for WebSearchOptions {
184    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
185    where
186        S: Serializer,
187    {
188        let mut state = serializer.serialize_struct("WebSearchOptions", 2)?;
189        // Use "medium" as default if search_context_size is None
190        let size = self.search_context_size.as_deref().unwrap_or("medium");
191        state.serialize_field("search_context_size", size)?;
192        state.serialize_field("user_location", &self.user_location)?;
193        state.end()
194    }
195}
196
197/// User location information for req api web search options
198#[derive(Debug, Clone, Deserialize)]
199pub struct UserLocation {
200    /// Free text input for the city of the user.
201    /// e.g. "San Francisco"
202    pub city: Option<String>,
203    /// The two-letter ISO country code of the user.
204    /// e.g. "US"
205    pub country: Option<String>,
206    /// Free text input for the region of the user.
207    /// e.g. "California"
208    pub region: Option<String>,
209    /// The IANA timezone of the user.
210    /// e.g. "America/Los_Angeles"
211    pub timezone: Option<String>,
212}
213
214impl Serialize for UserLocation {
215    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
216    where
217        S: Serializer,
218    {
219
220        let mut state = serializer.serialize_struct("UserLocation", 2)?;
221
222        // Always set "type" to "approximate"
223        state.serialize_field("type", "approximate")?;
224
225        // Build the "approximate" object with only non-None fields
226        let mut approximate = serde_json::Map::new();
227        if let Some(ref city) = self.city {
228            approximate.insert("city".to_string(), serde_json::Value::String(city.clone()));
229        }
230        if let Some(ref country) = self.country {
231            approximate.insert("country".to_string(), serde_json::Value::String(country.clone()));
232        }
233        if let Some(ref region) = self.region {
234            approximate.insert("region".to_string(), serde_json::Value::String(region.clone()));
235        }
236        if let Some(ref timezone) = self.timezone {
237            approximate.insert("timezone".to_string(), serde_json::Value::String(timezone.clone()));
238        }
239
240        state.serialize_field("approximate", &approximate)?;
241        state.end()
242    }
243}