Skip to main content

openai_types/shared/
common.rs

1// Manual: canonical definitions of shared types used across domains.
2// Role, FinishReason, Usage, ServiceTier, etc. — one source of truth.
3// All enums have Other(String) catch-all for forward compatibility.
4
5use serde::{Deserialize, Serialize};
6
7// ── Enums with Other(String) catch-all ──
8
9/// Message role in chat/thread conversations.
10#[derive(Debug, Clone, PartialEq, Eq)]
11#[non_exhaustive]
12pub enum Role {
13    System,
14    Developer,
15    User,
16    Assistant,
17    Tool,
18    Function,
19    Other(String),
20}
21
22impl Serialize for Role {
23    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
24        match self {
25            Self::System => serializer.serialize_str("system"),
26            Self::Developer => serializer.serialize_str("developer"),
27            Self::User => serializer.serialize_str("user"),
28            Self::Assistant => serializer.serialize_str("assistant"),
29            Self::Tool => serializer.serialize_str("tool"),
30            Self::Function => serializer.serialize_str("function"),
31            Self::Other(s) => serializer.serialize_str(s),
32        }
33    }
34}
35
36impl<'de> Deserialize<'de> for Role {
37    fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
38        let s = String::deserialize(deserializer)?;
39        match s.as_str() {
40            "system" => Ok(Self::System),
41            "developer" => Ok(Self::Developer),
42            "user" => Ok(Self::User),
43            "assistant" => Ok(Self::Assistant),
44            "tool" => Ok(Self::Tool),
45            "function" => Ok(Self::Function),
46            _ => Ok(Self::Other(s)),
47        }
48    }
49}
50
51/// Reason the model stopped generating tokens.
52#[derive(Debug, Clone, PartialEq, Eq)]
53#[non_exhaustive]
54pub enum FinishReason {
55    Stop,
56    Length,
57    ToolCalls,
58    ContentFilter,
59    FunctionCall,
60    Other(String),
61}
62
63impl Serialize for FinishReason {
64    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
65        match self {
66            Self::Stop => serializer.serialize_str("stop"),
67            Self::Length => serializer.serialize_str("length"),
68            Self::ToolCalls => serializer.serialize_str("tool_calls"),
69            Self::ContentFilter => serializer.serialize_str("content_filter"),
70            Self::FunctionCall => serializer.serialize_str("function_call"),
71            Self::Other(s) => serializer.serialize_str(s),
72        }
73    }
74}
75
76impl<'de> Deserialize<'de> for FinishReason {
77    fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
78        let s = String::deserialize(deserializer)?;
79        match s.as_str() {
80            "stop" => Ok(Self::Stop),
81            "length" => Ok(Self::Length),
82            "tool_calls" => Ok(Self::ToolCalls),
83            "content_filter" => Ok(Self::ContentFilter),
84            "function_call" => Ok(Self::FunctionCall),
85            _ => Ok(Self::Other(s)),
86        }
87    }
88}
89
90impl std::fmt::Display for FinishReason {
91    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
92        match self {
93            Self::Stop => write!(f, "stop"),
94            Self::Length => write!(f, "length"),
95            Self::ToolCalls => write!(f, "tool_calls"),
96            Self::ContentFilter => write!(f, "content_filter"),
97            Self::FunctionCall => write!(f, "function_call"),
98            Self::Other(s) => write!(f, "{s}"),
99        }
100    }
101}
102
103/// Service tier used for the request.
104#[derive(Debug, Clone, PartialEq, Eq)]
105#[non_exhaustive]
106pub enum ServiceTier {
107    Auto,
108    Default,
109    Flex,
110    Scale,
111    Priority,
112    Other(String),
113}
114
115impl Serialize for ServiceTier {
116    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
117        match self {
118            Self::Auto => serializer.serialize_str("auto"),
119            Self::Default => serializer.serialize_str("default"),
120            Self::Flex => serializer.serialize_str("flex"),
121            Self::Scale => serializer.serialize_str("scale"),
122            Self::Priority => serializer.serialize_str("priority"),
123            Self::Other(s) => serializer.serialize_str(s),
124        }
125    }
126}
127
128impl<'de> Deserialize<'de> for ServiceTier {
129    fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
130        let s = String::deserialize(deserializer)?;
131        match s.as_str() {
132            "auto" => Ok(Self::Auto),
133            "default" => Ok(Self::Default),
134            "flex" => Ok(Self::Flex),
135            "scale" => Ok(Self::Scale),
136            "priority" => Ok(Self::Priority),
137            _ => Ok(Self::Other(s)),
138        }
139    }
140}
141
142// ReasoningEffort: canonical definition is in _gen.rs (auto-generated, has full variant set).
143
144/// Search context size for web search.
145#[derive(Debug, Clone, PartialEq, Eq)]
146#[non_exhaustive]
147pub enum SearchContextSize {
148    Low,
149    Medium,
150    High,
151    Other(String),
152}
153
154impl Serialize for SearchContextSize {
155    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
156        match self {
157            Self::Low => serializer.serialize_str("low"),
158            Self::Medium => serializer.serialize_str("medium"),
159            Self::High => serializer.serialize_str("high"),
160            Self::Other(s) => serializer.serialize_str(s),
161        }
162    }
163}
164
165impl<'de> Deserialize<'de> for SearchContextSize {
166    fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
167        let s = String::deserialize(deserializer)?;
168        match s.as_str() {
169            "low" => Ok(Self::Low),
170            "medium" => Ok(Self::Medium),
171            "high" => Ok(Self::High),
172            _ => Ok(Self::Other(s)),
173        }
174    }
175}
176
177/// Sort order for paginated list endpoints.
178#[derive(Debug, Clone, PartialEq, Eq)]
179#[non_exhaustive]
180pub enum SortOrder {
181    Asc,
182    Desc,
183    Other(String),
184}
185
186impl Serialize for SortOrder {
187    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
188        match self {
189            Self::Asc => serializer.serialize_str("asc"),
190            Self::Desc => serializer.serialize_str("desc"),
191            Self::Other(s) => serializer.serialize_str(s),
192        }
193    }
194}
195
196impl<'de> Deserialize<'de> for SortOrder {
197    fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
198        let s = String::deserialize(deserializer)?;
199        match s.as_str() {
200            "asc" => Ok(Self::Asc),
201            "desc" => Ok(Self::Desc),
202            _ => Ok(Self::Other(s)),
203        }
204    }
205}
206
207// ── Structs ──
208
209/// Token usage information returned by the API.
210#[derive(Debug, Clone, Serialize, Deserialize)]
211#[cfg_attr(feature = "structured", derive(schemars::JsonSchema))]
212pub struct Usage {
213    #[serde(default)]
214    pub prompt_tokens: Option<i64>,
215    #[serde(default)]
216    pub completion_tokens: Option<i64>,
217    #[serde(default)]
218    pub total_tokens: Option<i64>,
219    #[serde(default)]
220    pub prompt_tokens_details: Option<PromptTokensDetails>,
221    #[serde(default)]
222    pub completion_tokens_details: Option<CompletionTokensDetails>,
223}
224
225impl Usage {
226    /// Number of prompt tokens served from cache (0 if no cache hit).
227    pub fn cached_tokens(&self) -> i64 {
228        self.prompt_tokens_details
229            .as_ref()
230            .and_then(|d| d.cached_tokens)
231            .unwrap_or(0)
232    }
233
234    /// Cache hit ratio as percentage (0-100).
235    pub fn cache_hit_pct(&self) -> u64 {
236        let input = self.prompt_tokens.unwrap_or(0) as u64;
237        let cached = self.cached_tokens() as u64;
238        if input > 0 { (cached * 100) / input } else { 0 }
239    }
240}
241
242/// Detailed breakdown of prompt token usage.
243#[derive(Debug, Clone, Serialize, Deserialize)]
244#[cfg_attr(feature = "structured", derive(schemars::JsonSchema))]
245pub struct PromptTokensDetails {
246    #[serde(default)]
247    pub cached_tokens: Option<i64>,
248    #[serde(default)]
249    pub audio_tokens: Option<i64>,
250}
251
252/// Detailed breakdown of completion token usage.
253#[derive(Debug, Clone, Serialize, Deserialize)]
254#[cfg_attr(feature = "structured", derive(schemars::JsonSchema))]
255pub struct CompletionTokensDetails {
256    #[serde(default)]
257    pub reasoning_tokens: Option<i64>,
258    #[serde(default)]
259    pub audio_tokens: Option<i64>,
260    #[serde(default)]
261    pub accepted_prediction_tokens: Option<i64>,
262    #[serde(default)]
263    pub rejected_prediction_tokens: Option<i64>,
264}
265
266// ── Generic helpers ──
267
268/// A value that is either "auto" or a fixed number.
269#[derive(Debug, Clone, PartialEq)]
270pub enum AutoOrFixed<T> {
271    Auto,
272    Fixed(T),
273}
274
275impl<T: Serialize> Serialize for AutoOrFixed<T> {
276    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
277        match self {
278            Self::Auto => serializer.serialize_str("auto"),
279            Self::Fixed(v) => v.serialize(serializer),
280        }
281    }
282}
283
284impl<'de, T: Deserialize<'de>> Deserialize<'de> for AutoOrFixed<T> {
285    fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
286        let value = serde_json::Value::deserialize(deserializer)?;
287        match &value {
288            serde_json::Value::String(s) if s == "auto" => Ok(Self::Auto),
289            _ => T::deserialize(value)
290                .map(Self::Fixed)
291                .map_err(serde::de::Error::custom),
292        }
293    }
294}
295
296/// Token limit: either "inf" (unlimited) or a fixed integer.
297#[derive(Debug, Clone, PartialEq)]
298pub enum MaxResponseTokens {
299    Inf,
300    Fixed(i64),
301}
302
303impl Serialize for MaxResponseTokens {
304    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
305        match self {
306            Self::Inf => serializer.serialize_str("inf"),
307            Self::Fixed(v) => serializer.serialize_i64(*v),
308        }
309    }
310}
311
312impl<'de> Deserialize<'de> for MaxResponseTokens {
313    fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
314        let value = serde_json::Value::deserialize(deserializer)?;
315        match &value {
316            serde_json::Value::String(s) if s == "inf" => Ok(Self::Inf),
317            serde_json::Value::Number(n) => n
318                .as_i64()
319                .map(Self::Fixed)
320                .ok_or_else(|| serde::de::Error::custom("expected integer")),
321            _ => Err(serde::de::Error::custom("expected \"inf\" or integer")),
322        }
323    }
324}