1use serde::{Deserialize, Serialize};
2
3#[derive(Debug, Clone, Serialize, Deserialize, Default)]
4pub struct ResponsesRequest {
5 pub model: String,
6 #[serde(skip_serializing_if = "Option::is_none")]
7 pub input: Option<serde_json::Value>,
8 #[serde(skip_serializing_if = "Option::is_none")]
9 pub temperature: Option<f32>,
10 #[serde(skip_serializing_if = "Option::is_none")]
11 pub top_p: Option<f32>,
12 #[serde(skip_serializing_if = "Option::is_none")]
13 pub max_output_tokens: Option<u32>,
14 #[serde(skip_serializing_if = "Option::is_none")]
15 pub user: Option<String>,
16 #[serde(skip_serializing_if = "Option::is_none")]
17 pub stream: Option<bool>,
18 #[serde(skip_serializing_if = "Option::is_none")]
19 pub stream_options: Option<StreamOptions>,
20 #[serde(skip_serializing_if = "Option::is_none")]
21 pub tools: Option<Vec<ToolSpec>>, #[serde(skip_serializing_if = "Option::is_none")]
23 pub tool_choice: Option<serde_json::Value>, #[serde(skip_serializing_if = "Option::is_none")]
26 pub instructions: Option<String>, #[serde(skip_serializing_if = "Option::is_none")]
28 pub metadata: Option<serde_json::Value>, #[serde(skip_serializing_if = "Option::is_none")]
30 pub parallel_tool_calls: Option<bool>, #[serde(skip_serializing_if = "Option::is_none")]
32 pub text: Option<TextConfig>, #[serde(skip_serializing_if = "Option::is_none")]
34 pub seed: Option<i32>, #[serde(skip_serializing_if = "Option::is_none")]
36 pub store: Option<bool>, #[serde(skip_serializing_if = "Option::is_none")]
38 pub reasoning: Option<ReasoningConfig>, #[serde(skip_serializing_if = "Option::is_none")]
40 pub top_logprobs: Option<u32>, #[serde(skip_serializing_if = "Option::is_none")]
42 pub logprobs: Option<bool>, }
44
45impl ResponsesRequest {
46 pub fn text<T: Into<String>>(model: T, input: T) -> Self {
47 Self {
48 model: model.into(),
49 input: Some(serde_json::Value::String(input.into())),
50 ..Default::default()
51 }
52 }
53
54 pub fn with_instructions<T: Into<String>>(model: T, input: T, instructions: T) -> Self {
56 Self {
57 model: model.into(),
58 input: Some(serde_json::Value::String(input.into())),
59 instructions: Some(instructions.into()),
60 ..Default::default()
61 }
62 }
63
64 pub fn json<T: Into<String>>(model: T, input: T) -> Self {
66 Self {
67 model: model.into(),
68 input: Some(serde_json::Value::String(input.into())),
69 text: Some(TextConfig {
70 format: Some(TextFormat::JsonObject),
71 verbosity: None,
72 }),
73 ..Default::default()
74 }
75 }
76
77 pub fn with_temperature<T: Into<String>>(model: T, input: T, temperature: f32) -> Self {
79 Self {
80 model: model.into(),
81 input: Some(serde_json::Value::String(input.into())),
82 temperature: Some(temperature),
83 ..Default::default()
84 }
85 }
86
87 pub fn with_max_tokens(mut self, max_tokens: u32) -> Self {
89 self.max_output_tokens = Some(max_tokens);
90 self
91 }
92
93 pub fn with_seed(mut self, seed: i32) -> Self {
95 self.seed = Some(seed);
96 self
97 }
98
99 pub fn with_parallel_tools(mut self, parallel: bool) -> Self {
101 self.parallel_tool_calls = Some(parallel);
102 self
103 }
104
105 pub fn with_tools(mut self, tools: Vec<ToolSpec>) -> Self {
107 self.tools = Some(tools);
108 self
109 }
110
111 pub fn with_tool_choice<T: serde::Serialize>(mut self, choice: T) -> Self {
113 self.tool_choice = Some(serde_json::to_value(choice).unwrap_or(serde_json::Value::Null));
114 self
115 }
116}
117
118#[derive(Debug, Clone, Serialize, Deserialize, Default)]
119pub struct StreamOptions {
120 #[serde(skip_serializing_if = "Option::is_none")]
121 pub include_usage: Option<bool>,
122}
123
124#[derive(Debug, Clone, Serialize, Deserialize)]
126#[serde(tag = "type", rename_all = "snake_case")]
127pub enum ResponseFormat {
128 Text,
129 JsonObject,
130 JsonSchema { json_schema: JsonSchemaSpec },
131}
132
133#[derive(Debug, Clone, Serialize, Deserialize)]
135pub struct TextConfig {
136 #[serde(skip_serializing_if = "Option::is_none")]
137 pub format: Option<TextFormat>,
138 #[serde(skip_serializing_if = "Option::is_none")]
139 pub verbosity: Option<String>, }
141
142#[derive(Debug, Clone, Serialize, Deserialize)]
143#[serde(tag = "type", rename_all = "snake_case")]
144pub enum TextFormat {
145 Text,
146 JsonObject,
147 JsonSchema { json_schema: JsonSchemaSpec },
148}
149
150#[derive(Debug, Clone, Serialize, Deserialize)]
151pub struct JsonSchemaSpec {
152 pub name: String,
153 #[serde(skip_serializing_if = "Option::is_none")]
154 pub description: Option<String>,
155 pub schema: serde_json::Value,
156 #[serde(skip_serializing_if = "Option::is_none")]
157 pub strict: Option<bool>,
158}
159
160#[derive(Debug, Clone, Serialize, Deserialize)]
162pub struct ReasoningConfig {
163 #[serde(skip_serializing_if = "Option::is_none")]
164 pub effort: Option<String>, #[serde(skip_serializing_if = "Option::is_none")]
166 pub summary: Option<String>,
167}
168
169#[derive(Debug, Clone, Serialize, Deserialize)]
170pub struct ResponsesResponse {
171 pub id: String,
172 pub object: String,
173 #[serde(default)]
174 pub created: Option<u64>,
175 pub model: String,
176 #[serde(default)]
178 pub output: Option<serde_json::Value>,
179 #[serde(default)]
180 pub usage: Option<serde_json::Value>,
181}
182
183impl ResponsesResponse {
184 pub fn output_text(&self) -> Option<String> {
186 let v = self.output.as_ref()?;
187 if let Some(s) = v.as_str() {
189 return Some(s.to_string());
190 }
191 let mut buf = String::new();
195 collect_text(v, &mut buf);
196 if buf.is_empty() {
197 None
198 } else {
199 Some(buf)
200 }
201 }
202
203 pub fn output_json(&self) -> Option<serde_json::Value> {
204 let text = self.output_text()?;
205 serde_json::from_str(&text).ok()
206 }
207}
208
209fn collect_text(v: &serde_json::Value, out: &mut String) {
210 match v {
211 serde_json::Value::String(s) => {
212 if !out.is_empty() {
213 out.push_str("\n");
214 }
215 out.push_str(s);
216 }
217 serde_json::Value::Array(arr) => {
218 for item in arr {
219 collect_text(item, out);
220 }
221 }
222 serde_json::Value::Object(map) => {
223 for (k, val) in map {
224 if k.contains("text") || k == "content" {
225 collect_text(val, out);
226 }
227 }
228 }
229 _ => {}
230 }
231}
232
233#[derive(Debug, Clone, Serialize, Deserialize)]
235pub struct ResponseStreamEvent {
236 #[serde(rename = "type")]
237 pub type_: String,
238 #[serde(default)]
239 pub response: Option<serde_json::Value>,
240 #[serde(default)]
241 pub output_text: Option<String>,
242 #[serde(default)]
243 pub delta: Option<serde_json::Value>,
244 #[serde(default)]
245 pub message: Option<serde_json::Value>,
246 #[serde(default)]
247 pub usage: Option<serde_json::Value>,
248}
249
250#[derive(Debug, Clone, Serialize, Deserialize)]
252pub struct ToolSpec {
253 #[serde(rename = "type")]
254 pub type_: String,
255 pub name: String,
256 #[serde(skip_serializing_if = "Option::is_none")]
257 pub description: Option<String>,
258 #[serde(skip_serializing_if = "Option::is_none")]
259 pub parameters: Option<serde_json::Value>, }
261
262#[derive(Debug, Clone, Serialize, Deserialize)]
263pub struct FunctionCall {
264 pub name: String,
265 pub arguments: serde_json::Value,
266}
267
268impl super::responses::ResponsesResponse {
269 pub fn function_calls(&self) -> Vec<FunctionCall> {
270 let mut out = Vec::new();
271 if let Some(v) = &self.output {
272 collect_function_calls(v, &mut out);
273 }
274 out
275 }
276}
277
278impl super::responses::ResponseStreamEvent {
279 pub fn function_calls(&self) -> Vec<FunctionCall> {
280 let mut out = Vec::new();
281 if let Some(v) = &self.response {
282 collect_function_calls(v, &mut out);
283 }
284 if let Some(v) = &self.delta {
285 collect_function_calls(v, &mut out);
286 }
287 if let Some(v) = &self.message {
288 collect_function_calls(v, &mut out);
289 }
290 out
291 }
292}
293
294fn collect_function_calls(v: &serde_json::Value, out: &mut Vec<FunctionCall>) {
295 match v {
296 serde_json::Value::Object(map) => {
297 if let Some(serde_json::Value::String(t)) = map.get("type") {
298 if t == "function" {
299 if let Some(f) = map.get("function") {
300 if let Some(fc) = as_function_call(f) {
301 out.push(fc);
302 }
303 }
304 for (k, val) in map {
306 if k != "function" {
307 collect_function_calls(val, out);
308 }
309 }
310 return;
311 }
312 }
313 if let Some(fc) = as_function_call(v) {
314 out.push(fc);
315 return;
316 }
317 for (_k, val) in map {
318 collect_function_calls(val, out);
319 }
320 }
321 serde_json::Value::Array(arr) => {
322 for item in arr {
323 collect_function_calls(item, out);
324 }
325 }
326 _ => {}
327 }
328}
329
330fn as_function_call(v: &serde_json::Value) -> Option<FunctionCall> {
331 let m = v.as_object()?;
332 if let Some(f) = m.get("function") {
333 let fobj = f.as_object()?;
334 let name = fobj.get("name")?.as_str()?.to_string();
335 let args = fobj
336 .get("arguments")
337 .cloned()
338 .unwrap_or(serde_json::Value::Null);
339 return Some(FunctionCall {
340 name,
341 arguments: normalize_args(args),
342 });
343 }
344 if let Some(name) = m.get("name").and_then(|s| s.as_str()) {
345 let args = m
346 .get("arguments")
347 .cloned()
348 .unwrap_or(serde_json::Value::Null);
349 return Some(FunctionCall {
350 name: name.to_string(),
351 arguments: normalize_args(args),
352 });
353 }
354 None
355}
356
357fn normalize_args(args: serde_json::Value) -> serde_json::Value {
358 match args {
359 serde_json::Value::String(s) => {
360 serde_json::from_str(&s).unwrap_or(serde_json::Value::String(s))
361 }
362 other => other,
363 }
364}