Skip to main content

openai_types/responses/
response.rs

1// MANUAL — hand-maintained. py2rust sync will not overwrite.
2// Main Response struct and related types.
3
4use serde::{Deserialize, Serialize};
5use std::collections::HashMap;
6
7use super::create::{Reasoning, ResponseTextConfig};
8use super::output::{FunctionCall, ResponseOutputItem};
9use super::tools::{ResponseTool, ResponseToolChoice};
10
11/// An error returned when the model fails to generate a Response.
12#[derive(Debug, Clone, Deserialize, Serialize)]
13#[cfg_attr(feature = "structured", derive(schemars::JsonSchema))]
14pub struct ResponseError {
15    /// The error code (e.g. "server_error", "rate_limit_exceeded", "invalid_prompt").
16    pub code: String,
17    /// A human-readable description of the error.
18    pub message: String,
19}
20
21/// Details about why the response is incomplete.
22#[derive(Debug, Clone, Deserialize, Serialize)]
23#[cfg_attr(feature = "structured", derive(schemars::JsonSchema))]
24pub struct IncompleteDetails {
25    /// The reason: "max_output_tokens" or "content_filter".
26    #[serde(default)]
27    pub reason: Option<String>,
28}
29
30/// An annotation on response output text (e.g. URL citation, file citation).
31#[derive(Debug, Clone, Deserialize, Serialize)]
32#[cfg_attr(feature = "structured", derive(schemars::JsonSchema))]
33pub struct ResponseAnnotation {
34    /// Annotation type (e.g. "url_citation", "file_citation", "file_path").
35    #[serde(rename = "type")]
36    pub type_: String,
37    /// Start index in the text.
38    #[serde(default)]
39    #[serde(skip_serializing_if = "Option::is_none")]
40    pub start_index: Option<i64>,
41    /// End index in the text.
42    #[serde(default)]
43    #[serde(skip_serializing_if = "Option::is_none")]
44    pub end_index: Option<i64>,
45    /// URL for url_citation annotations.
46    #[serde(default)]
47    #[serde(skip_serializing_if = "Option::is_none")]
48    pub url: Option<String>,
49    /// Title for url_citation annotations.
50    #[serde(default)]
51    #[serde(skip_serializing_if = "Option::is_none")]
52    pub title: Option<String>,
53    /// File ID for file_citation/file_path annotations.
54    #[serde(default)]
55    #[serde(skip_serializing_if = "Option::is_none")]
56    pub file_id: Option<String>,
57}
58
59/// Input token usage details.
60#[derive(Debug, Clone, Deserialize, Serialize)]
61#[cfg_attr(feature = "structured", derive(schemars::JsonSchema))]
62pub struct InputTokensDetails {
63    /// Number of cached tokens.
64    #[serde(default)]
65    #[serde(skip_serializing_if = "Option::is_none")]
66    pub cached_tokens: Option<i64>,
67}
68
69/// Output token usage details.
70#[derive(Debug, Clone, Deserialize, Serialize)]
71#[cfg_attr(feature = "structured", derive(schemars::JsonSchema))]
72pub struct OutputTokensDetails {
73    /// Number of reasoning tokens.
74    #[serde(default)]
75    #[serde(skip_serializing_if = "Option::is_none")]
76    pub reasoning_tokens: Option<i64>,
77}
78
79/// Usage for the Responses API.
80#[derive(Debug, Clone, Deserialize, Serialize)]
81#[cfg_attr(feature = "structured", derive(schemars::JsonSchema))]
82pub struct ResponseUsage {
83    /// Number of input tokens.
84    #[serde(default)]
85    #[serde(skip_serializing_if = "Option::is_none")]
86    pub input_tokens: Option<i64>,
87    /// Number of output tokens.
88    #[serde(default)]
89    #[serde(skip_serializing_if = "Option::is_none")]
90    pub output_tokens: Option<i64>,
91    /// Total tokens used.
92    #[serde(default)]
93    #[serde(skip_serializing_if = "Option::is_none")]
94    pub total_tokens: Option<i64>,
95    /// Detailed input token breakdown.
96    #[serde(default)]
97    #[serde(skip_serializing_if = "Option::is_none")]
98    pub input_tokens_details: Option<InputTokensDetails>,
99    /// Detailed output token breakdown.
100    #[serde(default)]
101    #[serde(skip_serializing_if = "Option::is_none")]
102    pub output_tokens_details: Option<OutputTokensDetails>,
103}
104
105/// Response from `POST /responses`.
106#[derive(Debug, Clone, Deserialize, Serialize)]
107#[cfg_attr(feature = "structured", derive(schemars::JsonSchema))]
108pub struct Response {
109    /// Unique response identifier.
110    pub id: String,
111    /// Object type — always "response".
112    pub object: String,
113    /// Unix timestamp when the response was created.
114    pub created_at: f64,
115    /// Model used for generation.
116    pub model: String,
117    /// Output items (messages, function calls, reasoning, etc.).
118    pub output: Vec<ResponseOutputItem>,
119    /// Response status: "in_progress", "completed", "failed", "incomplete".
120    #[serde(default)]
121    #[serde(skip_serializing_if = "Option::is_none")]
122    pub status: Option<String>,
123    /// Error details if the response failed.
124    #[serde(default)]
125    #[serde(skip_serializing_if = "Option::is_none")]
126    pub error: Option<ResponseError>,
127    /// Details about why the response is incomplete.
128    #[serde(default)]
129    #[serde(skip_serializing_if = "Option::is_none")]
130    pub incomplete_details: Option<IncompleteDetails>,
131    /// System instructions used.
132    #[serde(default)]
133    #[serde(skip_serializing_if = "Option::is_none")]
134    pub instructions: Option<String>,
135    /// Metadata key-value pairs.
136    #[serde(default)]
137    #[serde(skip_serializing_if = "Option::is_none")]
138    pub metadata: Option<HashMap<String, String>>,
139    /// Temperature used.
140    #[serde(default)]
141    #[serde(skip_serializing_if = "Option::is_none")]
142    pub temperature: Option<f64>,
143    /// Top-p (nucleus sampling) used.
144    #[serde(default)]
145    #[serde(skip_serializing_if = "Option::is_none")]
146    pub top_p: Option<f64>,
147    /// Max output tokens limit.
148    #[serde(default)]
149    #[serde(skip_serializing_if = "Option::is_none")]
150    pub max_output_tokens: Option<i64>,
151    /// Previous response ID for multi-turn.
152    #[serde(default)]
153    #[serde(skip_serializing_if = "Option::is_none")]
154    pub previous_response_id: Option<String>,
155    /// Token usage information.
156    #[serde(default)]
157    #[serde(skip_serializing_if = "Option::is_none")]
158    pub usage: Option<ResponseUsage>,
159    /// Tools used in the response.
160    #[serde(default)]
161    #[serde(skip_serializing_if = "Option::is_none")]
162    pub tools: Option<Vec<ResponseTool>>,
163    /// Tool choice setting.
164    #[serde(default)]
165    #[serde(skip_serializing_if = "Option::is_none")]
166    pub tool_choice: Option<ResponseToolChoice>,
167    /// Whether parallel tool calls were enabled.
168    #[serde(default)]
169    #[serde(skip_serializing_if = "Option::is_none")]
170    pub parallel_tool_calls: Option<bool>,
171    /// Truncation strategy.
172    #[serde(default)]
173    #[serde(skip_serializing_if = "Option::is_none")]
174    pub truncation: Option<String>,
175    /// Reasoning configuration.
176    #[serde(default)]
177    #[serde(skip_serializing_if = "Option::is_none")]
178    pub reasoning: Option<Reasoning>,
179    /// Service tier used.
180    #[serde(default)]
181    #[serde(skip_serializing_if = "Option::is_none")]
182    pub service_tier: Option<String>,
183    /// Text output configuration.
184    #[serde(default)]
185    #[serde(skip_serializing_if = "Option::is_none")]
186    pub text: Option<ResponseTextConfig>,
187    /// Unix timestamp when the response completed.
188    #[serde(default)]
189    #[serde(skip_serializing_if = "Option::is_none")]
190    pub completed_at: Option<f64>,
191    /// Whether the response ran in background mode.
192    #[serde(default)]
193    #[serde(skip_serializing_if = "Option::is_none")]
194    pub background: Option<bool>,
195    /// End user identifier.
196    #[serde(default)]
197    #[serde(skip_serializing_if = "Option::is_none")]
198    pub user: Option<String>,
199    /// Top log probabilities count.
200    #[serde(default)]
201    #[serde(skip_serializing_if = "Option::is_none")]
202    pub top_logprobs: Option<i64>,
203    /// Maximum number of tool calls.
204    #[serde(default)]
205    #[serde(skip_serializing_if = "Option::is_none")]
206    pub max_tool_calls: Option<i64>,
207}
208
209impl Response {
210    /// Get the text output, concatenating all text content blocks.
211    pub fn output_text(&self) -> String {
212        let mut result = String::new();
213        for item in &self.output {
214            if let Some(content) = &item.content {
215                for block in content {
216                    if block.type_ == "output_text"
217                        && let Some(text) = &block.text
218                    {
219                        result.push_str(text);
220                    }
221                }
222            }
223        }
224        result
225    }
226
227    /// Extract all function calls from the response output.
228    pub fn function_calls(&self) -> Vec<FunctionCall> {
229        self.output
230            .iter()
231            .filter(|item| item.type_ == "function_call")
232            .map(|item| {
233                let call_id = item
234                    .call_id
235                    .as_deref()
236                    .or(item.id.as_deref())
237                    .unwrap_or("unknown")
238                    .to_string();
239                let name = item.name.clone().unwrap_or_default();
240                let arguments = item
241                    .arguments
242                    .as_deref()
243                    .and_then(|s| serde_json::from_str(s).ok())
244                    .unwrap_or(serde_json::Value::Object(Default::default()));
245                FunctionCall {
246                    call_id,
247                    name,
248                    arguments,
249                }
250            })
251            .collect()
252    }
253
254    /// Check if the response has any function calls.
255    pub fn has_function_calls(&self) -> bool {
256        self.output.iter().any(|item| item.type_ == "function_call")
257    }
258}
259
260// Compat aliases for async-openai migration.
261
262/// Alias for [`InputTokensDetails`].
263pub type InputTokenDetails = InputTokensDetails;
264/// Alias for [`OutputTokensDetails`].
265pub type OutputTokenDetails = OutputTokensDetails;