Skip to main content

openai_compat/types/assistants/
responses.rs

1//! Assistants (beta v2) types, mirroring `openai-python/src/openai/types/beta/`.
2//!
3//! Response types are typed at the top level; deeply polymorphic fields
4//! (tools, tool resources, tool choice, response format, required actions,
5//! step details, …) are kept as [`serde_json::Value`] to bound scope. Only
6//! the `text` message-content block and [`RunStatus`] are modelled as enums.
7//!
8//! Streaming runs are intentionally out of scope for this module.
9
10use serde::{Deserialize, Serialize};
11use serde_json::Value;
12
13use crate::pagination::HasId;
14
15// ---------------------------------------------------------------------------
16// Response types
17// ---------------------------------------------------------------------------
18
19/// An assistant, mirroring `types/beta/assistant.py`.
20#[derive(Debug, Clone, Serialize, Deserialize)]
21#[non_exhaustive]
22pub struct Assistant {
23    pub id: String,
24    #[serde(default)]
25    pub created_at: i64,
26    #[serde(default)]
27    pub model: String,
28    #[serde(default)]
29    pub object: String,
30    #[serde(default)]
31    pub name: Option<String>,
32    #[serde(default)]
33    pub description: Option<String>,
34    #[serde(default)]
35    pub instructions: Option<String>,
36    #[serde(default)]
37    pub metadata: Option<Value>,
38    #[serde(default)]
39    pub temperature: Option<f64>,
40    #[serde(default)]
41    pub top_p: Option<f64>,
42    #[serde(default)]
43    pub tools: Vec<Value>,
44    #[serde(default)]
45    pub response_format: Option<Value>,
46    #[serde(default)]
47    pub tool_resources: Option<Value>,
48}
49
50impl HasId for Assistant {
51    fn id(&self) -> Option<&str> {
52        Some(&self.id)
53    }
54}
55
56/// A thread, mirroring `types/beta/thread.py`.
57#[derive(Debug, Clone, Serialize, Deserialize)]
58#[non_exhaustive]
59pub struct Thread {
60    pub id: String,
61    #[serde(default)]
62    pub created_at: i64,
63    #[serde(default)]
64    pub object: String,
65    #[serde(default)]
66    pub metadata: Option<Value>,
67    #[serde(default)]
68    pub tool_resources: Option<Value>,
69}
70
71impl HasId for Thread {
72    fn id(&self) -> Option<&str> {
73        Some(&self.id)
74    }
75}
76
77/// The `text` payload inside a [`MessageContentBlock::Text`].
78#[derive(Debug, Clone, Serialize, Deserialize)]
79#[non_exhaustive]
80pub struct MessageText {
81    pub value: String,
82    #[serde(default)]
83    pub annotations: Vec<Value>,
84}
85
86/// A block of message content. Only the `text` block is modelled; every other
87/// block type (`image_file`, `image_url`, `refusal`, …) falls through to
88/// [`MessageContentBlock::Other`].
89#[derive(Debug, Clone, Serialize, Deserialize)]
90#[serde(tag = "type", rename_all = "snake_case")]
91#[non_exhaustive]
92pub enum MessageContentBlock {
93    /// A block of generated text.
94    Text { text: MessageText },
95    /// Any non-text block (kept minimal on purpose).
96    #[serde(other)]
97    Other,
98}
99
100/// A message on a thread, mirroring `types/beta/threads/message.py`.
101///
102/// Named `ThreadMessage` to avoid colliding with the chat-completions
103/// `Message` request type.
104#[derive(Debug, Clone, Serialize, Deserialize)]
105#[non_exhaustive]
106pub struct ThreadMessage {
107    pub id: String,
108    #[serde(default)]
109    pub thread_id: String,
110    #[serde(default)]
111    pub created_at: i64,
112    #[serde(default)]
113    pub object: String,
114    #[serde(default)]
115    pub role: String,
116    #[serde(default)]
117    pub status: Option<String>,
118    #[serde(default)]
119    pub content: Vec<MessageContentBlock>,
120    #[serde(default)]
121    pub assistant_id: Option<String>,
122    #[serde(default)]
123    pub run_id: Option<String>,
124    #[serde(default)]
125    pub attachments: Option<Vec<Value>>,
126    #[serde(default)]
127    pub completed_at: Option<i64>,
128    #[serde(default)]
129    pub incomplete_at: Option<i64>,
130    #[serde(default)]
131    pub incomplete_details: Option<Value>,
132    #[serde(default)]
133    pub metadata: Option<Value>,
134}
135
136impl HasId for ThreadMessage {
137    fn id(&self) -> Option<&str> {
138        Some(&self.id)
139    }
140}
141
142impl ThreadMessage {
143    /// Extract the concatenated text of the message, if any, by reading the
144    /// first `text` content block's `value`.
145    pub fn text(&self) -> Option<String> {
146        self.content.iter().find_map(|block| match block {
147            MessageContentBlock::Text { text } => Some(text.value.clone()),
148            MessageContentBlock::Other => None,
149        })
150    }
151}
152
153/// The lifecycle status of a [`Run`], mirroring
154/// `types/beta/threads/run_status.py`. Unknown values deserialize to
155/// [`RunStatus::Unknown`].
156#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
157#[serde(rename_all = "snake_case")]
158#[non_exhaustive]
159pub enum RunStatus {
160    Queued,
161    InProgress,
162    RequiresAction,
163    Cancelling,
164    Cancelled,
165    Failed,
166    Completed,
167    Incomplete,
168    Expired,
169    /// A status not recognised by this client version.
170    #[serde(other)]
171    Unknown,
172}
173
174/// Token usage for a completed run or run step.
175#[derive(Debug, Clone, Serialize, Deserialize)]
176#[non_exhaustive]
177pub struct RunUsage {
178    #[serde(default)]
179    pub completion_tokens: i64,
180    #[serde(default)]
181    pub prompt_tokens: i64,
182    #[serde(default)]
183    pub total_tokens: i64,
184}
185
186/// A run of an assistant on a thread, mirroring `types/beta/threads/run.py`.
187#[derive(Debug, Clone, Serialize, Deserialize)]
188#[non_exhaustive]
189pub struct Run {
190    pub id: String,
191    #[serde(default)]
192    pub thread_id: String,
193    #[serde(default)]
194    pub assistant_id: String,
195    #[serde(default)]
196    pub created_at: i64,
197    #[serde(default)]
198    pub object: String,
199    #[serde(default = "run_status_unknown")]
200    pub status: RunStatus,
201    #[serde(default)]
202    pub model: String,
203    #[serde(default)]
204    pub instructions: Option<String>,
205    #[serde(default)]
206    pub parallel_tool_calls: bool,
207    #[serde(default)]
208    pub tools: Vec<Value>,
209    #[serde(default)]
210    pub started_at: Option<i64>,
211    #[serde(default)]
212    pub expires_at: Option<i64>,
213    #[serde(default)]
214    pub cancelled_at: Option<i64>,
215    #[serde(default)]
216    pub failed_at: Option<i64>,
217    #[serde(default)]
218    pub completed_at: Option<i64>,
219    #[serde(default)]
220    pub last_error: Option<Value>,
221    #[serde(default)]
222    pub incomplete_details: Option<Value>,
223    #[serde(default)]
224    pub required_action: Option<Value>,
225    #[serde(default)]
226    pub response_format: Option<Value>,
227    #[serde(default)]
228    pub tool_choice: Option<Value>,
229    #[serde(default)]
230    pub truncation_strategy: Option<Value>,
231    #[serde(default)]
232    pub usage: Option<RunUsage>,
233    #[serde(default)]
234    pub temperature: Option<f64>,
235    #[serde(default)]
236    pub top_p: Option<f64>,
237    #[serde(default)]
238    pub max_completion_tokens: Option<i64>,
239    #[serde(default)]
240    pub max_prompt_tokens: Option<i64>,
241    #[serde(default)]
242    pub metadata: Option<Value>,
243}
244
245fn run_status_unknown() -> RunStatus {
246    RunStatus::Unknown
247}
248
249impl HasId for Run {
250    fn id(&self) -> Option<&str> {
251        Some(&self.id)
252    }
253}
254
255/// A step taken during a run, mirroring `types/beta/threads/runs/run_step.py`.
256#[derive(Debug, Clone, Serialize, Deserialize)]
257#[non_exhaustive]
258pub struct RunStep {
259    pub id: String,
260    #[serde(default)]
261    pub run_id: String,
262    #[serde(default)]
263    pub thread_id: String,
264    #[serde(default)]
265    pub assistant_id: String,
266    #[serde(default)]
267    pub created_at: i64,
268    #[serde(default)]
269    pub object: String,
270    #[serde(rename = "type", default)]
271    pub step_type: String,
272    #[serde(default)]
273    pub status: String,
274    #[serde(default)]
275    pub step_details: Value,
276    #[serde(default)]
277    pub last_error: Option<Value>,
278    #[serde(default)]
279    pub usage: Option<RunUsage>,
280    #[serde(default)]
281    pub metadata: Option<Value>,
282    #[serde(default)]
283    pub expired_at: Option<i64>,
284    #[serde(default)]
285    pub cancelled_at: Option<i64>,
286    #[serde(default)]
287    pub failed_at: Option<i64>,
288    #[serde(default)]
289    pub completed_at: Option<i64>,
290}
291
292impl HasId for RunStep {
293    fn id(&self) -> Option<&str> {
294        Some(&self.id)
295    }
296}
297
298/// Response from `DELETE /assistants/{id}`.
299#[derive(Debug, Clone, Serialize, Deserialize)]
300#[non_exhaustive]
301pub struct AssistantDeleted {
302    pub id: String,
303    pub deleted: bool,
304    #[serde(default)]
305    pub object: String,
306}
307
308/// Response from `DELETE /threads/{id}`.
309#[derive(Debug, Clone, Serialize, Deserialize)]
310#[non_exhaustive]
311pub struct ThreadDeleted {
312    pub id: String,
313    pub deleted: bool,
314    #[serde(default)]
315    pub object: String,
316}
317
318/// Response from `DELETE /threads/{tid}/messages/{mid}`.
319#[derive(Debug, Clone, Serialize, Deserialize)]
320#[non_exhaustive]
321pub struct MessageDeleted {
322    pub id: String,
323    pub deleted: bool,
324    #[serde(default)]
325    pub object: String,
326}
327