rig_core/providers/openai/extension/
responses.rs1use serde::{Deserialize, Serialize};
5use serde_json::Value;
6
7use crate::completion::provider_options::reply_field;
8
9#[non_exhaustive]
12#[derive(Clone, Debug, Default, PartialEq, Serialize)]
13pub struct OpenAiResponsesOptions {
14 #[serde(skip_serializing_if = "Vec::is_empty")]
19 pub input: Vec<Value>,
20 #[serde(skip_serializing_if = "Option::is_none")]
23 pub reasoning: Option<ReasoningOptions>,
24 #[serde(skip_serializing_if = "Vec::is_empty")]
27 pub include: Vec<Include>,
28 #[serde(skip_serializing_if = "Option::is_none")]
31 pub conversation: Option<String>,
32 #[serde(skip_serializing_if = "Option::is_none")]
35 pub truncation: Option<Truncation>,
36 #[serde(skip_serializing_if = "Vec::is_empty")]
38 pub context_management: Vec<ContextManagement>,
39 #[serde(skip_serializing_if = "Option::is_none")]
42 pub prompt_cache_options: Option<PromptCacheOptions>,
43 #[serde(skip_serializing_if = "Option::is_none")]
47 pub background: Option<bool>,
48 #[serde(skip_serializing_if = "Option::is_none")]
50 pub max_tool_calls: Option<u32>,
51 #[serde(skip_serializing_if = "Option::is_none")]
54 pub top_logprobs: Option<u8>,
55 #[serde(skip_serializing_if = "Option::is_none")]
57 pub access_programs: Option<AccessPrograms>,
58}
59
60impl OpenAiResponsesOptions {
61 #[must_use]
63 pub fn input(mut self, items: impl IntoIterator<Item = Value>) -> Self {
64 self.input.extend(items);
65 self
66 }
67
68 #[must_use]
70 pub fn reasoning_summary(mut self, summary: ReasoningSummary) -> Self {
71 self.reasoning.get_or_insert_with(Default::default).summary = Some(summary);
72 self
73 }
74
75 #[must_use]
77 pub fn reasoning_mode(mut self, mode: ReasoningMode) -> Self {
78 self.reasoning.get_or_insert_with(Default::default).mode = Some(mode);
79 self
80 }
81
82 #[must_use]
84 pub fn reasoning_context(mut self, context: ReasoningContext) -> Self {
85 self.reasoning.get_or_insert_with(Default::default).context = Some(context);
86 self
87 }
88
89 #[must_use]
91 pub fn include(mut self, include: impl IntoIterator<Item = Include>) -> Self {
92 self.include.extend(include);
93 self
94 }
95
96 #[must_use]
98 pub fn conversation(mut self, id: impl Into<String>) -> Self {
99 self.conversation = Some(id.into());
100 self
101 }
102
103 #[must_use]
105 pub fn truncation(mut self, truncation: Truncation) -> Self {
106 self.truncation = Some(truncation);
107 self
108 }
109
110 #[must_use]
112 pub fn context_management(
113 mut self,
114 entries: impl IntoIterator<Item = ContextManagement>,
115 ) -> Self {
116 self.context_management.extend(entries);
117 self
118 }
119
120 #[must_use]
122 pub fn prompt_cache_comparison(mut self, response_id: impl Into<String>) -> Self {
123 self.prompt_cache_options = Some(PromptCacheOptions {
124 comparison_response_id: Some(response_id.into()),
125 });
126 self
127 }
128
129 #[must_use]
131 pub fn background(mut self, background: bool) -> Self {
132 self.background = Some(background);
133 self
134 }
135
136 #[must_use]
138 pub fn max_tool_calls(mut self, max: u32) -> Self {
139 self.max_tool_calls = Some(max);
140 self
141 }
142
143 #[must_use]
145 pub fn top_logprobs(mut self, count: u8) -> Self {
146 self.top_logprobs = Some(count);
147 self
148 }
149
150 #[must_use]
152 pub fn access_programs(mut self, programs: AccessPrograms) -> Self {
153 self.access_programs = Some(programs);
154 self
155 }
156}
157
158#[non_exhaustive]
160#[derive(Clone, Debug, Default, PartialEq, Serialize)]
161pub struct ReasoningOptions {
162 #[serde(skip_serializing_if = "Option::is_none")]
164 pub summary: Option<ReasoningSummary>,
165 #[serde(skip_serializing_if = "Option::is_none")]
167 pub mode: Option<ReasoningMode>,
168 #[serde(skip_serializing_if = "Option::is_none")]
170 pub context: Option<ReasoningContext>,
171}
172
173#[non_exhaustive]
175#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize)]
176#[serde(rename_all = "snake_case")]
177pub enum ReasoningSummary {
178 Auto,
180 Concise,
182 Detailed,
184}
185
186#[non_exhaustive]
189#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize)]
190#[serde(rename_all = "snake_case")]
191pub enum ReasoningMode {
192 Standard,
194 Pro,
196}
197
198#[non_exhaustive]
201#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize)]
202#[serde(rename_all = "snake_case")]
203pub enum ReasoningContext {
204 Auto,
206 AllTurns,
208 CurrentTurn,
210}
211
212#[non_exhaustive]
214#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize)]
215pub enum Include {
216 #[serde(rename = "file_search_call.results")]
218 FileSearchCallResults,
219 #[serde(rename = "web_search_call.results")]
221 WebSearchCallResults,
222 #[serde(rename = "web_search_call.action.sources")]
224 WebSearchCallActionSources,
225 #[serde(rename = "message.input_image.image_url")]
227 MessageInputImageImageUrl,
228 #[serde(rename = "computer_call_output.output.image_url")]
230 ComputerCallOutputOutputImageUrl,
231 #[serde(rename = "code_interpreter_call.outputs")]
233 CodeInterpreterCallOutputs,
234 #[serde(rename = "reasoning.encrypted_content")]
236 ReasoningEncryptedContent,
237 #[serde(rename = "message.output_text.logprobs")]
239 MessageOutputTextLogprobs,
240}
241
242#[non_exhaustive]
244#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize)]
245#[serde(rename_all = "snake_case")]
246pub enum Truncation {
247 Auto,
249 Disabled,
251}
252
253#[non_exhaustive]
255#[derive(Clone, Debug, PartialEq, Serialize)]
256#[serde(tag = "type", rename_all = "snake_case")]
257pub enum ContextManagement {
258 Compaction {
261 #[serde(skip_serializing_if = "Option::is_none")]
263 compact_threshold: Option<u32>,
264 },
265}
266
267impl ContextManagement {
268 pub fn compaction(threshold: Option<u32>) -> Self {
270 Self::Compaction {
271 compact_threshold: threshold,
272 }
273 }
274}
275
276#[non_exhaustive]
279#[derive(Clone, Debug, Default, PartialEq, Serialize)]
280pub struct PromptCacheOptions {
281 #[serde(skip_serializing_if = "Option::is_none")]
284 pub comparison_response_id: Option<String>,
285}
286
287#[non_exhaustive]
289#[derive(Clone, Debug, PartialEq, Serialize)]
290pub struct AccessPrograms {
291 pub cyber: CyberAccess,
293}
294
295impl AccessPrograms {
296 pub fn cyber(access: CyberAccess) -> Self {
298 Self { cyber: access }
299 }
300}
301
302#[non_exhaustive]
304#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize)]
305#[serde(rename_all = "snake_case")]
306pub enum CyberAccess {
307 Standard,
309 DaybreakBlue,
311 DaybreakRed,
313}
314
315#[non_exhaustive]
317#[derive(Clone, Debug, PartialEq, Eq, Deserialize)]
318pub struct ItemPhase {
319 pub id: String,
321 #[serde(default)]
323 pub phase: Option<String>,
324}
325
326pub(crate) struct Envelope {
329 pub(crate) service_tier: Option<String>,
330 pub(crate) reasoning_effort: Option<String>,
331 pub(crate) reasoning_summary: Option<String>,
332 pub(crate) reasoning_mode: Option<String>,
333 pub(crate) reasoning_context: Option<String>,
334 pub(crate) prompt_cache_retention: Option<String>,
335 pub(crate) incomplete_reason: Option<String>,
336 pub(crate) phases: Option<Vec<ItemPhase>>,
337}
338
339impl Envelope {
340 pub(crate) fn from_reply(raw: &Value) -> Result<Self, serde_json::Error> {
343 let reasoning = raw.get("reasoning").filter(|value| value.is_object());
344 let reasoning = |key: &str| match reasoning {
345 Some(reasoning) => reply_field::<String>(reasoning, &format!("/{key}")),
346 None => Ok(None),
347 };
348 let messages = raw
349 .get("output")
350 .and_then(Value::as_array)
351 .into_iter()
352 .flatten()
353 .filter(|item| item.get("type").and_then(Value::as_str) == Some("message"))
354 .map(ItemPhase::deserialize)
355 .collect::<Result<Vec<_>, _>>()?;
356 Ok(Self {
357 service_tier: reply_field(raw, "/service_tier")?,
358 reasoning_effort: reasoning("effort")?,
359 reasoning_summary: reasoning("summary")?,
360 reasoning_mode: reasoning("mode")?,
361 reasoning_context: reasoning("context")?,
362 prompt_cache_retention: reply_field(raw, "/prompt_cache_retention")?,
363 incomplete_reason: reply_field(raw, "/incomplete_details/reason")?,
364 phases: (!messages.is_empty()).then_some(messages),
365 })
366 }
367}
368
369#[cfg(test)]
370mod tests;