Skip to main content

gproxy_protocol/wire/openai/
decisions.rs

1//! OpenAI Decisions API (public beta), verified against openai-python on 2026-10-08.
2//! `decision_create_params.py`, `decision.py`, and `decision_input_*_param.py`.
3//! Choice values distinguish booleans from strings; refusals are per question.
4use super::responses::ResponseUsage;
5use crate::{Rest, WireRequest, WireResponse};
6use serde::{Deserialize, Serialize};
7
8#[derive(
9    Debug, Clone, PartialEq, Serialize, Deserialize, gproxy_protocol_macros::DeclaredFields,
10)]
11#[cfg_attr(not(feature = "exhaustive"), non_exhaustive)]
12#[derive(gproxy_protocol_macros::WireBuilder)]
13pub struct CreateDecisionRequestBody {
14    pub model: String,
15    pub input: DecisionInput,
16    pub questions: Vec<DecisionQuestion>,
17    #[serde(default, skip_serializing_if = "Option::is_none")]
18    pub safety_identifier: Option<String>,
19    #[serde(default, flatten, skip_serializing_if = "serde_json::Map::is_empty")]
20    pub rest: Rest,
21}
22
23#[derive(
24    Debug, Clone, PartialEq, Serialize, Deserialize, gproxy_protocol_macros::DeclaredFields,
25)]
26#[cfg_attr(not(feature = "exhaustive"), non_exhaustive)]
27#[serde(untagged)]
28pub enum DecisionInput {
29    Text(String),
30    Messages(Vec<DecisionInputMessage>),
31}
32
33#[derive(
34    Debug, Clone, PartialEq, Serialize, Deserialize, gproxy_protocol_macros::DeclaredFields,
35)]
36#[cfg_attr(not(feature = "exhaustive"), non_exhaustive)]
37#[derive(gproxy_protocol_macros::WireBuilder)]
38pub struct DecisionInputMessage {
39    pub role: DecisionRole,
40    #[serde(default, skip_serializing_if = "Option::is_none")]
41    #[serde(rename = "type")]
42    pub type_: Option<DecisionMessageType>,
43    pub content: DecisionContent,
44    #[serde(default, flatten, skip_serializing_if = "serde_json::Map::is_empty")]
45    pub rest: Rest,
46}
47
48#[derive(
49    Debug, Clone, PartialEq, Serialize, Deserialize, gproxy_protocol_macros::DeclaredFields,
50)]
51#[cfg_attr(not(feature = "exhaustive"), non_exhaustive)]
52#[serde(rename_all = "snake_case")]
53pub enum DecisionRole {
54    User,
55}
56
57#[derive(
58    Debug, Clone, PartialEq, Serialize, Deserialize, gproxy_protocol_macros::DeclaredFields,
59)]
60#[cfg_attr(not(feature = "exhaustive"), non_exhaustive)]
61#[serde(rename_all = "snake_case")]
62pub enum DecisionMessageType {
63    Message,
64}
65
66#[derive(
67    Debug, Clone, PartialEq, Serialize, Deserialize, gproxy_protocol_macros::DeclaredFields,
68)]
69#[cfg_attr(not(feature = "exhaustive"), non_exhaustive)]
70#[serde(untagged)]
71pub enum DecisionContent {
72    Text(String),
73    Parts(Vec<DecisionInputPart>),
74}
75
76#[derive(
77    Debug, Clone, PartialEq, Serialize, Deserialize, gproxy_protocol_macros::DeclaredFields,
78)]
79#[cfg_attr(not(feature = "exhaustive"), non_exhaustive)]
80#[serde(tag = "type", rename_all = "snake_case")]
81pub enum DecisionInputPart {
82    InputText {
83        text: String,
84        #[serde(default, flatten, skip_serializing_if = "serde_json::Map::is_empty")]
85        rest: Rest,
86    },
87    InputImage {
88        image_url: String,
89        #[serde(
90            default,
91            deserialize_with = "super::responses::input::present_nullable",
92            skip_serializing_if = "Option::is_none"
93        )]
94        detail: Option<Option<DecisionImageDetail>>,
95        #[serde(default, flatten, skip_serializing_if = "serde_json::Map::is_empty")]
96        rest: Rest,
97    },
98}
99
100#[derive(
101    Debug, Clone, PartialEq, Serialize, Deserialize, gproxy_protocol_macros::DeclaredFields,
102)]
103#[cfg_attr(not(feature = "exhaustive"), non_exhaustive)]
104#[serde(rename_all = "snake_case")]
105pub enum DecisionImageDetail {
106    Low,
107    High,
108    Auto,
109    Original,
110}
111
112#[derive(
113    Debug, Clone, PartialEq, Serialize, Deserialize, gproxy_protocol_macros::DeclaredFields,
114)]
115#[cfg_attr(not(feature = "exhaustive"), non_exhaustive)]
116#[serde(untagged)]
117pub enum DecisionChoiceValue {
118    String(String),
119    Boolean(bool),
120}
121
122#[derive(
123    Debug, Clone, PartialEq, Serialize, Deserialize, gproxy_protocol_macros::DeclaredFields,
124)]
125#[cfg_attr(not(feature = "exhaustive"), non_exhaustive)]
126#[derive(gproxy_protocol_macros::WireBuilder)]
127pub struct DecisionChoice {
128    pub value: DecisionChoiceValue,
129    #[serde(default, skip_serializing_if = "Option::is_none")]
130    pub description: Option<String>,
131    #[serde(default, flatten, skip_serializing_if = "serde_json::Map::is_empty")]
132    pub rest: Rest,
133}
134
135#[derive(
136    Debug, Clone, PartialEq, Serialize, Deserialize, gproxy_protocol_macros::DeclaredFields,
137)]
138#[cfg_attr(not(feature = "exhaustive"), non_exhaustive)]
139#[derive(gproxy_protocol_macros::WireBuilder)]
140pub struct DecisionScoreLevel {
141    pub label: String,
142    #[serde(default, skip_serializing_if = "Option::is_none")]
143    pub description: Option<String>,
144    #[serde(default, flatten, skip_serializing_if = "serde_json::Map::is_empty")]
145    pub rest: Rest,
146}
147
148#[derive(
149    Debug, Clone, PartialEq, Serialize, Deserialize, gproxy_protocol_macros::DeclaredFields,
150)]
151#[cfg_attr(not(feature = "exhaustive"), non_exhaustive)]
152#[serde(tag = "type", rename_all = "snake_case")]
153pub enum DecisionQuestion {
154    Predicate {
155        #[serde(default, skip_serializing_if = "Option::is_none")]
156        name: Option<String>,
157        instructions: String,
158        #[serde(default, flatten, skip_serializing_if = "serde_json::Map::is_empty")]
159        rest: Rest,
160    },
161    Choice {
162        #[serde(default, skip_serializing_if = "Option::is_none")]
163        name: Option<String>,
164        instructions: String,
165        choices: Vec<DecisionChoice>,
166        #[serde(default, flatten, skip_serializing_if = "serde_json::Map::is_empty")]
167        rest: Rest,
168    },
169    Score {
170        #[serde(default, skip_serializing_if = "Option::is_none")]
171        name: Option<String>,
172        instructions: String,
173        levels: Vec<DecisionScoreLevel>,
174        #[serde(default, flatten, skip_serializing_if = "serde_json::Map::is_empty")]
175        rest: Rest,
176    },
177}
178
179#[derive(
180    Debug, Clone, PartialEq, Serialize, Deserialize, gproxy_protocol_macros::DeclaredFields,
181)]
182#[cfg_attr(not(feature = "exhaustive"), non_exhaustive)]
183#[derive(gproxy_protocol_macros::WireBuilder)]
184pub struct CreateDecisionResponseBody {
185    pub model: String,
186    pub answers: Vec<DecisionAnswer>,
187    pub usage: ResponseUsage,
188    #[serde(default, flatten, skip_serializing_if = "serde_json::Map::is_empty")]
189    pub rest: Rest,
190}
191
192#[derive(
193    Debug, Clone, PartialEq, Serialize, Deserialize, gproxy_protocol_macros::DeclaredFields,
194)]
195#[cfg_attr(not(feature = "exhaustive"), non_exhaustive)]
196#[derive(gproxy_protocol_macros::WireBuilder)]
197pub struct DecisionChoiceProbability {
198    pub value: DecisionChoiceValue,
199    pub probability: f64,
200    #[serde(default, flatten, skip_serializing_if = "serde_json::Map::is_empty")]
201    pub rest: Rest,
202}
203
204#[derive(
205    Debug, Clone, PartialEq, Serialize, Deserialize, gproxy_protocol_macros::DeclaredFields,
206)]
207#[cfg_attr(not(feature = "exhaustive"), non_exhaustive)]
208#[derive(gproxy_protocol_macros::WireBuilder)]
209pub struct DecisionScoreProbability {
210    pub label: String,
211    pub value: i64,
212    pub probability: f64,
213    #[serde(default, flatten, skip_serializing_if = "serde_json::Map::is_empty")]
214    pub rest: Rest,
215}
216
217#[derive(
218    Debug, Clone, PartialEq, Serialize, Deserialize, gproxy_protocol_macros::DeclaredFields,
219)]
220#[cfg_attr(not(feature = "exhaustive"), non_exhaustive)]
221#[serde(tag = "type", rename_all = "snake_case")]
222pub enum DecisionAnswer {
223    Predicate {
224        #[serde(default, skip_serializing_if = "Option::is_none")]
225        name: Option<String>,
226        probability: f64,
227        #[serde(default, flatten, skip_serializing_if = "serde_json::Map::is_empty")]
228        rest: Rest,
229    },
230    Choice {
231        #[serde(default, skip_serializing_if = "Option::is_none")]
232        name: Option<String>,
233        choice: DecisionChoiceValue,
234        confidence: f64,
235        probabilities: Vec<DecisionChoiceProbability>,
236        #[serde(default, flatten, skip_serializing_if = "serde_json::Map::is_empty")]
237        rest: Rest,
238    },
239    Score {
240        #[serde(default, skip_serializing_if = "Option::is_none")]
241        name: Option<String>,
242        score: f64,
243        confidence: f64,
244        probabilities: Vec<DecisionScoreProbability>,
245        #[serde(default, flatten, skip_serializing_if = "serde_json::Map::is_empty")]
246        rest: Rest,
247    },
248    Refusal {
249        #[serde(default, skip_serializing_if = "Option::is_none")]
250        name: Option<String>,
251        #[serde(default, flatten, skip_serializing_if = "serde_json::Map::is_empty")]
252        rest: Rest,
253    },
254}
255
256pub type CreateDecisionRequest = WireRequest<CreateDecisionRequestBody>;
257pub type CreateDecisionResponse = WireResponse<CreateDecisionResponseBody>;