1use crate::chat_compl::SearchParameters;
16use crate::error::XaiError;
17use crate::error::check_for_model_error;
18use crate::traits::{ClientConfig, ResponsesFetcher};
19use reqwest::Method;
20use serde::{Deserialize, Serialize};
21
22#[derive(Debug, Clone, Serialize, Deserialize)]
25#[serde(untagged)]
26pub enum ResponseInput {
27 Text(String),
28 Messages(Vec<serde_json::Value>),
29}
30
31#[derive(Debug, Clone, Serialize, Deserialize)]
33pub struct ReasoningConfig {
34 #[serde(skip_serializing_if = "Option::is_none")]
36 pub effort: Option<String>,
37 #[serde(skip_serializing_if = "Option::is_none")]
39 pub generate_summary: Option<String>,
40 #[serde(skip_serializing_if = "Option::is_none")]
42 pub summary: Option<String>,
43}
44
45#[derive(Debug, Clone, Serialize, Deserialize)]
46pub struct CreateResponseRequest {
47 pub model: String,
48 pub input: ResponseInput,
49 #[serde(skip_serializing_if = "Option::is_none")]
50 pub instructions: Option<String>,
51 #[serde(skip_serializing_if = "Option::is_none")]
52 pub previous_response_id: Option<String>,
53 #[serde(skip_serializing_if = "Option::is_none")]
54 pub store: Option<bool>,
55 #[serde(skip_serializing_if = "Option::is_none")]
56 pub stream: Option<bool>,
57 #[serde(skip_serializing_if = "Option::is_none")]
58 pub temperature: Option<f32>,
59 #[serde(skip_serializing_if = "Option::is_none")]
60 pub top_p: Option<f32>,
61 #[serde(skip_serializing_if = "Option::is_none")]
62 pub max_output_tokens: Option<u32>,
63 #[serde(skip_serializing_if = "Option::is_none")]
64 pub max_turns: Option<u32>,
65 #[serde(skip_serializing_if = "Option::is_none")]
66 pub reasoning: Option<ReasoningConfig>,
67 #[serde(skip_serializing_if = "Option::is_none")]
68 pub search_parameters: Option<SearchParameters>,
69 #[serde(skip_serializing_if = "Option::is_none")]
70 pub tools: Option<Vec<serde_json::Value>>,
71 #[serde(skip_serializing_if = "Option::is_none")]
72 pub tool_choice: Option<serde_json::Value>,
73 #[serde(skip_serializing_if = "Option::is_none")]
74 pub parallel_tool_calls: Option<bool>,
75 #[serde(skip_serializing_if = "Option::is_none")]
77 pub text: Option<serde_json::Value>,
78 #[serde(skip_serializing_if = "Option::is_none")]
79 pub logprobs: Option<bool>,
80 #[serde(skip_serializing_if = "Option::is_none")]
81 pub top_logprobs: Option<u32>,
82 #[serde(skip_serializing_if = "Option::is_none")]
83 pub user: Option<String>,
84 #[serde(skip_serializing_if = "Option::is_none")]
86 pub context_management: Option<Vec<serde_json::Value>>,
87 #[serde(skip_serializing_if = "Option::is_none")]
89 pub include: Option<Vec<String>>,
90 #[serde(skip_serializing_if = "Option::is_none")]
92 pub service_tier: Option<String>,
93 #[serde(skip_serializing_if = "Option::is_none")]
95 pub truncation: Option<String>,
96 #[serde(skip_serializing_if = "Option::is_none")]
98 pub background: Option<bool>,
99 #[serde(skip_serializing_if = "Option::is_none")]
100 pub prompt_cache_key: Option<String>,
101 #[serde(skip_serializing_if = "Option::is_none")]
102 pub metadata: Option<serde_json::Value>,
103}
104
105#[derive(Debug, Clone, Serialize, Deserialize)]
106pub struct ResponseInputTokensDetails {
107 pub cached_tokens: u32,
108}
109
110#[derive(Debug, Clone, Serialize, Deserialize)]
111pub struct ResponseOutputTokensDetails {
112 pub reasoning_tokens: u32,
113}
114
115#[derive(Debug, Clone, Serialize, Deserialize)]
116pub struct ServerSideToolUsageDetails {
117 pub code_interpreter_calls: u32,
118 pub document_search_calls: u32,
119 pub file_search_calls: u32,
120 pub mcp_calls: u32,
121 pub web_search_calls: u32,
122 pub x_search_calls: u32,
123}
124
125#[derive(Debug, Clone, Serialize, Deserialize)]
126pub struct ResponseUsage {
127 pub input_tokens: u32,
128 pub output_tokens: u32,
129 pub total_tokens: u32,
130 pub input_tokens_details: ResponseInputTokensDetails,
131 pub output_tokens_details: ResponseOutputTokensDetails,
132 pub num_sources_used: u32,
133 pub num_server_side_tools_used: u32,
134 #[serde(skip_serializing_if = "Option::is_none")]
135 pub cost_in_usd_ticks: Option<i64>,
136 #[serde(skip_serializing_if = "Option::is_none")]
137 pub cost_in_nano_usd: Option<i64>,
138 #[serde(skip_serializing_if = "Option::is_none")]
139 pub server_side_tool_usage_details: Option<ServerSideToolUsageDetails>,
140}
141
142#[derive(Debug, Clone, Serialize, Deserialize)]
144pub struct ResponseObject {
145 pub id: String,
146 pub object: String,
147 pub model: String,
148 pub status: String,
150 pub created_at: u64,
151 #[serde(skip_serializing_if = "Option::is_none")]
152 pub completed_at: Option<u64>,
153 pub output: Vec<serde_json::Value>,
155 #[serde(skip_serializing_if = "Option::is_none")]
156 pub usage: Option<ResponseUsage>,
157 #[serde(skip_serializing_if = "Option::is_none")]
158 pub instructions: Option<String>,
159 #[serde(skip_serializing_if = "Option::is_none")]
160 pub previous_response_id: Option<String>,
161 pub store: bool,
162 pub parallel_tool_calls: bool,
163 pub tool_choice: serde_json::Value,
164 pub tools: Vec<serde_json::Value>,
165 pub text: serde_json::Value,
166 pub truncation: String,
167 pub service_tier: String,
168 pub top_logprobs: u32,
169 pub frequency_penalty: f64,
170 pub presence_penalty: f64,
171 pub metadata: serde_json::Value,
172 #[serde(skip_serializing_if = "Option::is_none")]
173 pub temperature: Option<f32>,
174 #[serde(skip_serializing_if = "Option::is_none")]
175 pub top_p: Option<f32>,
176 #[serde(skip_serializing_if = "Option::is_none")]
177 pub max_output_tokens: Option<u32>,
178 #[serde(skip_serializing_if = "Option::is_none")]
179 pub reasoning: Option<ReasoningConfig>,
180 #[serde(skip_serializing_if = "Option::is_none")]
181 pub user: Option<String>,
182 #[serde(skip_serializing_if = "Option::is_none")]
183 pub safety_identifier: Option<String>,
184 #[serde(skip_serializing_if = "Option::is_none")]
185 pub prompt_cache_key: Option<String>,
186 #[serde(skip_serializing_if = "Option::is_none")]
187 pub incomplete_details: Option<serde_json::Value>,
188 #[serde(skip_serializing_if = "Option::is_none")]
189 pub error: Option<serde_json::Value>,
190 pub background: bool,
191 #[serde(skip_serializing_if = "Option::is_none")]
192 pub max_tool_calls: Option<u32>,
193}
194
195#[derive(Debug, Clone, Serialize, Deserialize)]
197pub struct DeleteResponseObject {
198 pub id: String,
199 pub object: String,
200 pub deleted: bool,
201}
202
203#[derive(Debug, Clone)]
204pub struct ResponsesRequestBuilder<T: ClientConfig + Clone + Send + Sync> {
205 client: T,
206 request: CreateResponseRequest,
207}
208
209impl<T> ResponsesRequestBuilder<T>
210where
211 T: ClientConfig + Clone + Send + Sync,
212{
213 pub fn new(client: T, model: impl Into<String>, input: ResponseInput) -> Self {
214 Self {
215 client,
216 request: CreateResponseRequest {
217 model: model.into(),
218 input,
219 instructions: None,
220 previous_response_id: None,
221 store: None,
222 stream: None,
223 temperature: None,
224 top_p: None,
225 max_output_tokens: None,
226 max_turns: None,
227 reasoning: None,
228 search_parameters: None,
229 tools: None,
230 tool_choice: None,
231 parallel_tool_calls: None,
232 text: None,
233 logprobs: None,
234 top_logprobs: None,
235 user: None,
236 context_management: None,
237 include: None,
238 service_tier: None,
239 truncation: None,
240 background: None,
241 prompt_cache_key: None,
242 metadata: None,
243 },
244 }
245 }
246
247 pub fn instructions(mut self, instructions: impl Into<String>) -> Self {
248 self.request.instructions = Some(instructions.into());
249 self
250 }
251
252 pub fn previous_response_id(mut self, id: impl Into<String>) -> Self {
253 self.request.previous_response_id = Some(id.into());
254 self
255 }
256
257 pub fn store(mut self, store: bool) -> Self {
258 self.request.store = Some(store);
259 self
260 }
261
262 pub fn stream(mut self, stream: bool) -> Self {
263 self.request.stream = Some(stream);
264 self
265 }
266
267 pub fn temperature(mut self, temperature: f32) -> Self {
268 self.request.temperature = Some(temperature);
269 self
270 }
271
272 pub fn top_p(mut self, top_p: f32) -> Self {
273 self.request.top_p = Some(top_p);
274 self
275 }
276
277 pub fn max_output_tokens(mut self, max_output_tokens: u32) -> Self {
278 self.request.max_output_tokens = Some(max_output_tokens);
279 self
280 }
281
282 pub fn max_turns(mut self, max_turns: u32) -> Self {
283 self.request.max_turns = Some(max_turns);
284 self
285 }
286
287 pub fn reasoning(mut self, reasoning: ReasoningConfig) -> Self {
288 self.request.reasoning = Some(reasoning);
289 self
290 }
291
292 pub fn search_parameters(mut self, search_parameters: SearchParameters) -> Self {
293 self.request.search_parameters = Some(search_parameters);
294 self
295 }
296
297 pub fn tools(mut self, tools: Vec<serde_json::Value>) -> Self {
298 self.request.tools = Some(tools);
299 self
300 }
301
302 pub fn tool_choice(mut self, tool_choice: serde_json::Value) -> Self {
303 self.request.tool_choice = Some(tool_choice);
304 self
305 }
306
307 pub fn parallel_tool_calls(mut self, parallel_tool_calls: bool) -> Self {
308 self.request.parallel_tool_calls = Some(parallel_tool_calls);
309 self
310 }
311
312 pub fn logprobs(mut self, logprobs: bool) -> Self {
313 self.request.logprobs = Some(logprobs);
314 self
315 }
316
317 pub fn top_logprobs(mut self, top_logprobs: u32) -> Self {
318 self.request.top_logprobs = Some(top_logprobs);
319 self
320 }
321
322 pub fn user(mut self, user: impl Into<String>) -> Self {
323 self.request.user = Some(user.into());
324 self
325 }
326
327 pub fn include(mut self, include: Vec<String>) -> Self {
328 self.request.include = Some(include);
329 self
330 }
331
332 pub fn prompt_cache_key(mut self, key: impl Into<String>) -> Self {
333 self.request.prompt_cache_key = Some(key.into());
334 self
335 }
336
337 pub fn build(self) -> Result<CreateResponseRequest, XaiError> {
338 if self.request.model.trim().is_empty() {
339 return Err(XaiError::Validation("model cannot be empty".to_string()));
340 }
341 Ok(self.request)
342 }
343}
344
345impl<T> ResponsesFetcher for ResponsesRequestBuilder<T>
346where
347 T: ClientConfig + Clone + Send + Sync,
348{
349 async fn create_response(
350 &self,
351 request: CreateResponseRequest,
352 ) -> Result<ResponseObject, XaiError> {
353 let response = self
354 .client
355 .request(Method::POST, "responses")?
356 .json(&request)
357 .send()
358 .await?;
359
360 if response.status().is_success() {
361 let obj = response.json::<ResponseObject>().await?;
362 Ok(obj)
363 } else {
364 let error_body = response.text().await.unwrap_or_else(|_| "".to_string());
365 if let Some(model_error) = check_for_model_error(&error_body) {
366 return Err(model_error);
367 }
368 Err(XaiError::Http(error_body))
369 }
370 }
371
372 async fn get_response(&self, response_id: &str) -> Result<ResponseObject, XaiError> {
373 let url = format!("responses/{}", response_id);
374 let response = self.client.request(Method::GET, &url)?.send().await?;
375
376 if response.status().is_success() {
377 let obj = response.json::<ResponseObject>().await?;
378 Ok(obj)
379 } else {
380 let error_body = response.text().await.unwrap_or_else(|_| "".to_string());
381 if let Some(model_error) = check_for_model_error(&error_body) {
382 return Err(model_error);
383 }
384 Err(XaiError::Http(error_body))
385 }
386 }
387
388 async fn delete_response(&self, response_id: &str) -> Result<DeleteResponseObject, XaiError> {
389 let url = format!("responses/{}", response_id);
390 let response = self.client.request(Method::DELETE, &url)?.send().await?;
391
392 if response.status().is_success() {
393 let obj = response.json::<DeleteResponseObject>().await?;
394 Ok(obj)
395 } else {
396 let error_body = response.text().await.unwrap_or_else(|_| "".to_string());
397 if let Some(model_error) = check_for_model_error(&error_body) {
398 return Err(model_error);
399 }
400 Err(XaiError::Http(error_body))
401 }
402 }
403}
404