Skip to main content

openai_compat/resources/
responses.rs

1//! `/responses` resource, mirroring `resources/responses/responses.py`:
2//! create (+streaming), retrieve (+streaming resume for background mode),
3//! delete, cancel, and the `input_items` sub-resource.
4
5use reqwest::Method;
6
7use crate::client::Client;
8use crate::error::OpenAIError;
9use crate::pagination::List;
10use crate::request::RequestOptions;
11use crate::streaming::EventStream;
12use crate::types::responses::{CreateResponseRequest, Response, ResponseIncludable, ResponseItem, ResponseStreamEvent};
13
14/// Query parameters for `retrieve`.
15#[derive(Debug, Clone, Default)]
16pub struct RetrieveParams {
17    pub include: Option<Vec<ResponseIncludable>>,
18    pub include_obfuscation: Option<bool>,
19}
20
21impl RetrieveParams {
22    pub fn new() -> Self {
23        Self::default()
24    }
25
26    pub fn include(mut self, include: Vec<ResponseIncludable>) -> Self {
27        self.include = Some(include);
28        self
29    }
30
31    pub fn include_obfuscation(mut self, include_obfuscation: bool) -> Self {
32        self.include_obfuscation = Some(include_obfuscation);
33        self
34    }
35
36    fn to_query(&self) -> Vec<(String, String)> {
37        let mut query = Vec::new();
38        if let Some(include) = &self.include {
39            for item in include {
40                query.push(("include[]".into(), item.as_str().to_string()));
41            }
42        }
43        if let Some(include_obfuscation) = self.include_obfuscation {
44            query.push(("include_obfuscation".into(), include_obfuscation.to_string()));
45        }
46        query
47    }
48}
49
50/// Query parameters for `input_items().list()`.
51#[derive(Debug, Clone, Default)]
52pub struct InputItemsListParams {
53    pub after: Option<String>,
54    pub before: Option<String>,
55    pub limit: Option<u32>,
56    pub order: Option<String>,
57    pub include: Option<Vec<ResponseIncludable>>,
58}
59
60impl InputItemsListParams {
61    pub fn new() -> Self {
62        Self::default()
63    }
64
65    pub fn after(mut self, after: impl Into<String>) -> Self {
66        self.after = Some(after.into());
67        self
68    }
69
70    pub fn limit(mut self, limit: u32) -> Self {
71        self.limit = Some(limit);
72        self
73    }
74
75    /// Sort order: `"asc"` or `"desc"`.
76    pub fn order(mut self, order: impl Into<String>) -> Self {
77        self.order = Some(order.into());
78        self
79    }
80
81    pub fn include(mut self, include: Vec<ResponseIncludable>) -> Self {
82        self.include = Some(include);
83        self
84    }
85
86    fn to_query(&self) -> Vec<(String, String)> {
87        let mut query = crate::pagination::cursor_query(
88            self.after.as_deref(),
89            self.before.as_deref(),
90            self.limit,
91            self.order.as_deref(),
92        );
93        if let Some(include) = &self.include {
94            for item in include {
95                query.push(("include[]".into(), item.as_str().to_string()));
96            }
97        }
98        query
99    }
100}
101
102/// The `/responses` resource.
103#[derive(Debug, Clone)]
104pub struct Responses {
105    client: Client,
106}
107
108impl Responses {
109    pub(crate) fn new(client: Client) -> Self {
110        Self { client }
111    }
112
113    /// Create a response and wait for the full result.
114    pub async fn create(&self, mut request: CreateResponseRequest) -> Result<Response, OpenAIError> {
115        request.stream = None;
116        let body = serde_json::to_value(&request)?;
117        self.client
118            .execute(Method::POST, "/responses", RequestOptions::json(body))
119            .await
120    }
121
122    /// Create a response and stream events as they are generated.
123    pub async fn create_stream(
124        &self,
125        mut request: CreateResponseRequest,
126    ) -> Result<EventStream<ResponseStreamEvent>, OpenAIError> {
127        request.stream = Some(true);
128        let body = serde_json::to_value(&request)?;
129        let response = self
130            .client
131            .execute_raw(Method::POST, "/responses", RequestOptions::json(body))
132            .await?;
133        Ok(EventStream::new(response))
134    }
135
136    /// Retrieve a response by id.
137    pub async fn retrieve(
138        &self,
139        response_id: &str,
140        params: Option<RetrieveParams>,
141    ) -> Result<Response, OpenAIError> {
142        let query = params.map(|p| p.to_query()).unwrap_or_default();
143        self.client
144            .execute(
145                Method::GET,
146                &format!("/responses/{response_id}"),
147                RequestOptions::query(query),
148            )
149            .await
150    }
151
152    /// Resume streaming a background response, optionally starting after a
153    /// given sequence number.
154    pub async fn retrieve_stream(
155        &self,
156        response_id: &str,
157        starting_after: Option<u64>,
158    ) -> Result<EventStream<ResponseStreamEvent>, OpenAIError> {
159        let mut query = vec![("stream".to_string(), "true".to_string())];
160        if let Some(starting_after) = starting_after {
161            query.push(("starting_after".to_string(), starting_after.to_string()));
162        }
163        let response = self
164            .client
165            .execute_raw(
166                Method::GET,
167                &format!("/responses/{response_id}"),
168                RequestOptions::query(query),
169            )
170            .await?;
171        Ok(EventStream::new(response))
172    }
173
174    /// Delete a response.
175    pub async fn delete(&self, response_id: &str) -> Result<(), OpenAIError> {
176        self.client
177            .execute_no_content(
178                Method::DELETE,
179                &format!("/responses/{response_id}"),
180                RequestOptions::default(),
181            )
182            .await
183    }
184
185    /// Cancel an in-progress background response.
186    pub async fn cancel(&self, response_id: &str) -> Result<Response, OpenAIError> {
187        self.client
188            .execute(
189                Method::POST,
190                &format!("/responses/{response_id}/cancel"),
191                RequestOptions::default(),
192            )
193            .await
194    }
195
196    /// The `input_items` sub-resource for `response_id`.
197    pub fn input_items(&self, response_id: impl Into<String>) -> ResponseInputItems {
198        ResponseInputItems {
199            client: self.client.clone(),
200            response_id: response_id.into(),
201        }
202    }
203}
204
205/// `GET /responses/{response_id}/input_items` — cursor-paginated list of the
206/// items that produced a response.
207#[derive(Debug, Clone)]
208pub struct ResponseInputItems {
209    client: Client,
210    response_id: String,
211}
212
213impl ResponseInputItems {
214    /// Fetch a single page.
215    pub async fn list(
216        &self,
217        params: Option<InputItemsListParams>,
218    ) -> Result<List<ResponseItem>, OpenAIError> {
219        let query = params.map(|p| p.to_query()).unwrap_or_default();
220        self.client
221            .execute(
222                Method::GET,
223                &format!("/responses/{}/input_items", self.response_id),
224                RequestOptions::query(query),
225            )
226            .await
227    }
228
229    /// Fetch every item, following pagination cursors.
230    pub async fn list_all(
231        &self,
232        params: Option<InputItemsListParams>,
233    ) -> Result<Vec<ResponseItem>, OpenAIError> {
234        let query = params.map(|p| p.to_query()).unwrap_or_default();
235        self.client
236            .paginate_all(
237                &format!("/responses/{}/input_items", self.response_id),
238                query,
239            )
240            .await
241    }
242}