openai_compat/types/responses/
request.rs1use std::collections::HashMap;
5
6use serde::Serialize;
7
8use super::config::{Reasoning, ResponseIncludable, ResponseTextConfig, Truncation};
9use super::input::Input;
10use super::tools::{Tool, ToolChoice};
11
12#[derive(Debug, Clone, Default, Serialize)]
14pub struct CreateResponseRequest {
15 pub model: String,
16 #[serde(skip_serializing_if = "Input::is_empty")]
19 pub input: Input,
20 #[serde(skip_serializing_if = "Option::is_none")]
21 pub instructions: Option<String>,
22 #[serde(skip_serializing_if = "Option::is_none")]
23 pub max_output_tokens: Option<u64>,
24 #[serde(skip_serializing_if = "Option::is_none")]
25 pub max_tool_calls: Option<u64>,
26 #[serde(skip_serializing_if = "Option::is_none")]
27 pub metadata: Option<HashMap<String, String>>,
28 #[serde(skip_serializing_if = "Option::is_none")]
29 pub previous_response_id: Option<String>,
30 #[serde(skip_serializing_if = "Option::is_none")]
31 pub reasoning: Option<Reasoning>,
32 #[serde(skip_serializing_if = "Option::is_none")]
33 pub store: Option<bool>,
34 #[serde(skip_serializing_if = "Option::is_none")]
35 pub stream: Option<bool>,
36 #[serde(skip_serializing_if = "Option::is_none")]
37 pub temperature: Option<f64>,
38 #[serde(skip_serializing_if = "Option::is_none")]
39 pub text: Option<ResponseTextConfig>,
40 #[serde(skip_serializing_if = "Option::is_none")]
41 pub tool_choice: Option<ToolChoice>,
42 #[serde(skip_serializing_if = "Option::is_none")]
43 pub tools: Option<Vec<Tool>>,
44 #[serde(skip_serializing_if = "Option::is_none")]
45 pub top_p: Option<f64>,
46 #[serde(skip_serializing_if = "Option::is_none")]
47 pub truncation: Option<Truncation>,
48 #[serde(skip_serializing_if = "Option::is_none")]
49 pub include: Option<Vec<ResponseIncludable>>,
50 #[serde(skip_serializing_if = "Option::is_none")]
51 pub background: Option<bool>,
52 #[serde(skip_serializing_if = "Option::is_none")]
53 pub parallel_tool_calls: Option<bool>,
54 #[serde(flatten, skip_serializing_if = "HashMap::is_empty")]
57 pub extra: HashMap<String, serde_json::Value>,
58}
59
60impl CreateResponseRequest {
61 pub fn new(model: impl Into<String>, input: impl Into<Input>) -> Self {
62 Self {
63 model: model.into(),
64 input: input.into(),
65 ..Self::default()
66 }
67 }
68
69 pub fn instructions(mut self, instructions: impl Into<String>) -> Self {
70 self.instructions = Some(instructions.into());
71 self
72 }
73
74 pub fn max_output_tokens(mut self, max: u64) -> Self {
75 self.max_output_tokens = Some(max);
76 self
77 }
78
79 pub fn max_tool_calls(mut self, max: u64) -> Self {
80 self.max_tool_calls = Some(max);
81 self
82 }
83
84 pub fn metadata(mut self, metadata: HashMap<String, String>) -> Self {
85 self.metadata = Some(metadata);
86 self
87 }
88
89 pub fn previous_response_id(mut self, id: impl Into<String>) -> Self {
92 self.previous_response_id = Some(id.into());
93 self
94 }
95
96 pub fn reasoning(mut self, reasoning: Reasoning) -> Self {
97 self.reasoning = Some(reasoning);
98 self
99 }
100
101 pub fn store(mut self, store: bool) -> Self {
102 self.store = Some(store);
103 self
104 }
105
106 pub fn temperature(mut self, temperature: f64) -> Self {
107 self.temperature = Some(temperature);
108 self
109 }
110
111 pub fn text(mut self, text: ResponseTextConfig) -> Self {
112 self.text = Some(text);
113 self
114 }
115
116 pub fn tool_choice(mut self, tool_choice: ToolChoice) -> Self {
117 self.tool_choice = Some(tool_choice);
118 self
119 }
120
121 pub fn tools(mut self, tools: Vec<Tool>) -> Self {
122 self.tools = Some(tools);
123 self
124 }
125
126 pub fn top_p(mut self, top_p: f64) -> Self {
127 self.top_p = Some(top_p);
128 self
129 }
130
131 pub fn truncation(mut self, truncation: Truncation) -> Self {
132 self.truncation = Some(truncation);
133 self
134 }
135
136 pub fn include(mut self, include: Vec<ResponseIncludable>) -> Self {
137 self.include = Some(include);
138 self
139 }
140
141 pub fn background(mut self, background: bool) -> Self {
144 self.background = Some(background);
145 self
146 }
147
148 pub fn parallel_tool_calls(mut self, parallel: bool) -> Self {
149 self.parallel_tool_calls = Some(parallel);
150 self
151 }
152
153 pub fn param(mut self, key: impl Into<String>, value: impl Into<serde_json::Value>) -> Self {
159 self.extra.insert(key.into(), value.into());
160 self
161 }
162}
163
164#[cfg(test)]
165mod tests {
166 use super::super::input::InputItem;
167 use super::*;
168
169 #[test]
170 fn request_skips_none_fields() {
171 let request = CreateResponseRequest::new("gpt-5", "hi");
172 let json = serde_json::to_value(&request).unwrap();
173 assert_eq!(json, serde_json::json!({"model": "gpt-5", "input": "hi"}));
174 }
175
176 #[test]
177 fn request_with_items_input_serializes() {
178 let request =
179 CreateResponseRequest::new("gpt-5", vec![InputItem::user("hi")]).temperature(0.5);
180 let json = serde_json::to_value(&request).unwrap();
181 assert_eq!(json["model"], "gpt-5");
182 assert_eq!(json["temperature"], 0.5);
183 assert_eq!(json["input"][0]["type"], "message");
184 }
185
186 #[test]
187 fn continuation_request_omits_input_entirely() {
188 let request = CreateResponseRequest {
191 model: "gpt-5".to_string(),
192 ..Default::default()
193 }
194 .previous_response_id("resp_1");
195 let json = serde_json::to_value(&request).unwrap();
196 assert_eq!(
197 json,
198 serde_json::json!({"model": "gpt-5", "previous_response_id": "resp_1"})
199 );
200 }
201}