openai_rust_sdk/models/
containers.rs

1//! # Container Models
2//!
3//! Data structures for container management and code execution
4
5use crate::{De, Ser};
6use serde::{self, Deserialize, Serialize};
7use std::collections::HashMap;
8
9/// Container configuration for creation
10#[derive(Debug, Clone, Ser, De)]
11pub struct ContainerConfig {
12    /// Container name (optional)
13    #[serde(skip_serializing_if = "Option::is_none")]
14    pub name: Option<String>,
15
16    /// Container metadata
17    #[serde(skip_serializing_if = "Option::is_none")]
18    pub metadata: Option<HashMap<String, serde_json::Value>>,
19
20    /// Python version to use (e.g., "3.9", "3.10", "3.11")
21    #[serde(skip_serializing_if = "Option::is_none")]
22    pub python_version: Option<String>,
23
24    /// Pre-installed libraries
25    #[serde(skip_serializing_if = "Option::is_none")]
26    pub libraries: Option<Vec<String>>,
27
28    /// Memory limit in MB
29    #[serde(skip_serializing_if = "Option::is_none")]
30    pub memory_limit_mb: Option<u32>,
31
32    /// CPU limit (0.5, 1.0, 2.0, etc.)
33    #[serde(skip_serializing_if = "Option::is_none")]
34    pub cpu_limit: Option<f32>,
35
36    /// Container expiration time in minutes (default: 20)
37    #[serde(skip_serializing_if = "Option::is_none")]
38    pub expiration_minutes: Option<u32>,
39}
40
41impl Default for ContainerConfig {
42    fn default() -> Self {
43        Self {
44            name: None,
45            metadata: None,
46            python_version: Some("3.11".to_string()),
47            libraries: None,
48            memory_limit_mb: None,
49            cpu_limit: None,
50            expiration_minutes: None,
51        }
52    }
53}
54
55/// Container instance
56#[derive(Debug, Clone, Ser, De)]
57pub struct Container {
58    /// Unique container ID
59    pub id: String,
60
61    /// Container object type
62    pub object: String,
63
64    /// Container name
65    #[serde(skip_serializing_if = "Option::is_none")]
66    pub name: Option<String>,
67
68    /// Container status
69    pub status: ContainerStatus,
70
71    /// Creation timestamp
72    pub created_at: u64,
73
74    /// Last activity timestamp
75    pub last_activity_at: u64,
76
77    /// Expiration timestamp
78    pub expires_at: u64,
79
80    /// Python version
81    pub python_version: String,
82
83    /// Installed libraries
84    #[serde(default)]
85    pub libraries: Vec<String>,
86
87    /// Container metadata
88    #[serde(default)]
89    pub metadata: HashMap<String, serde_json::Value>,
90
91    /// Files in the container
92    #[serde(skip_serializing_if = "Option::is_none")]
93    pub files: Option<Vec<ContainerFile>>,
94
95    /// Memory usage in MB
96    #[serde(skip_serializing_if = "Option::is_none")]
97    pub memory_usage_mb: Option<u32>,
98
99    /// CPU usage percentage
100    #[serde(skip_serializing_if = "Option::is_none")]
101    pub cpu_usage_percent: Option<f32>,
102}
103
104/// Container status
105#[derive(Debug, Clone, Ser, De, PartialEq, Eq)]
106#[serde(rename_all = "lowercase")]
107pub enum ContainerStatus {
108    /// Container is being created
109    Creating,
110    /// Container is ready for use
111    Active,
112    /// Container is idle (no recent activity)
113    Idle,
114    /// Container is expired
115    Expired,
116    /// Container is being deleted
117    Deleting,
118    /// Container has an error
119    Error,
120}
121
122/// File in a container
123#[derive(Debug, Clone, Ser, De)]
124pub struct ContainerFile {
125    /// File ID
126    pub id: String,
127
128    /// File object type
129    pub object: String,
130
131    /// File name
132    pub filename: String,
133
134    /// File size in bytes
135    pub size: u64,
136
137    /// File MIME type
138    #[serde(skip_serializing_if = "Option::is_none")]
139    pub mime_type: Option<String>,
140
141    /// Creation timestamp
142    pub created_at: u64,
143
144    /// Last modified timestamp
145    #[serde(skip_serializing_if = "Option::is_none")]
146    pub modified_at: Option<u64>,
147
148    /// File path in container
149    pub path: String,
150
151    /// Whether file is readable
152    #[serde(default = "default_true")]
153    pub readable: bool,
154
155    /// Whether file is writable
156    #[serde(default = "default_true")]
157    pub writable: bool,
158
159    /// Whether file is executable
160    #[serde(default)]
161    pub executable: bool,
162}
163
164fn default_true() -> bool {
165    true
166}
167
168/// List of containers
169#[derive(Debug, Clone, Ser, De)]
170pub struct ContainerList {
171    /// List object type
172    pub object: String,
173
174    /// List of containers
175    pub data: Vec<Container>,
176
177    /// First container ID in the list
178    #[serde(skip_serializing_if = "Option::is_none")]
179    pub first_id: Option<String>,
180
181    /// Last container ID in the list
182    #[serde(skip_serializing_if = "Option::is_none")]
183    pub last_id: Option<String>,
184
185    /// Whether there are more containers
186    pub has_more: bool,
187}
188
189/// List of files in a container
190#[derive(Debug, Clone, Ser, De)]
191pub struct ContainerFileList {
192    /// List object type
193    pub object: String,
194
195    /// List of files
196    pub data: Vec<ContainerFile>,
197
198    /// Total number of files
199    pub total: u32,
200}
201
202/// Code execution request
203#[derive(Debug, Clone, Ser, De)]
204pub struct CodeExecutionRequest {
205    /// Python code to execute
206    pub code: String,
207
208    /// Execution timeout in milliseconds
209    #[serde(skip_serializing_if = "Option::is_none")]
210    pub timeout_ms: Option<u32>,
211
212    /// Whether to include output in response
213    #[serde(skip_serializing_if = "Option::is_none")]
214    pub include_output: Option<bool>,
215}
216
217/// Code execution result
218#[derive(Debug, Clone, Ser, De)]
219pub struct CodeExecutionResult {
220    /// Execution ID
221    pub id: String,
222
223    /// Execution status
224    pub status: ExecutionStatus,
225
226    /// Standard output
227    #[serde(skip_serializing_if = "Option::is_none")]
228    pub stdout: Option<String>,
229
230    /// Standard error
231    #[serde(skip_serializing_if = "Option::is_none")]
232    pub stderr: Option<String>,
233
234    /// Exit code
235    #[serde(skip_serializing_if = "Option::is_none")]
236    pub exit_code: Option<i32>,
237
238    /// Execution time in milliseconds
239    #[serde(skip_serializing_if = "Option::is_none")]
240    pub execution_time_ms: Option<u32>,
241
242    /// Files created during execution
243    #[serde(default)]
244    pub created_files: Vec<ContainerFile>,
245
246    /// Files modified during execution
247    #[serde(default)]
248    pub modified_files: Vec<ContainerFile>,
249
250    /// Execution error if any
251    #[serde(skip_serializing_if = "Option::is_none")]
252    pub error: Option<String>,
253
254    /// Memory used during execution in MB
255    #[serde(skip_serializing_if = "Option::is_none")]
256    pub memory_used_mb: Option<u32>,
257
258    /// Citations for code output
259    #[serde(default)]
260    pub citations: Vec<CodeCitation>,
261}
262
263/// Execution status
264#[derive(Debug, Clone, Ser, De, PartialEq, Eq)]
265#[serde(rename_all = "lowercase")]
266pub enum ExecutionStatus {
267    /// Code is being executed
268    Running,
269    /// Execution completed successfully
270    Success,
271    /// Execution failed with error
272    Failed,
273    /// Execution timed out
274    Timeout,
275    /// Execution was cancelled
276    Cancelled,
277}
278
279/// Code citation for tracking sources
280#[derive(Debug, Clone, Ser, De)]
281pub struct CodeCitation {
282    /// Citation type (e.g., "file", "url", "library")
283    pub citation_type: String,
284
285    /// Source of the citation
286    pub source: String,
287
288    /// Line numbers referenced (if applicable)
289    #[serde(skip_serializing_if = "Option::is_none")]
290    pub lines: Option<Vec<u32>>,
291
292    /// Additional metadata
293    #[serde(default)]
294    pub metadata: HashMap<String, serde_json::Value>,
295}
296
297/// Parameters for listing containers
298#[derive(Debug, Clone, Ser, De)]
299pub struct ListContainersParams {
300    /// Maximum number of containers to return
301    #[serde(skip_serializing_if = "Option::is_none")]
302    pub limit: Option<u32>,
303
304    /// Starting container ID for pagination
305    #[serde(skip_serializing_if = "Option::is_none")]
306    pub after: Option<String>,
307
308    /// Ending container ID for pagination
309    #[serde(skip_serializing_if = "Option::is_none")]
310    pub before: Option<String>,
311
312    /// Filter by status
313    #[serde(skip_serializing_if = "Option::is_none")]
314    pub status: Option<ContainerStatus>,
315
316    /// Order by creation date ("asc" or "desc")
317    #[serde(skip_serializing_if = "Option::is_none")]
318    pub order: Option<String>,
319}
320
321/// Enhanced Code Interpreter configuration with container support
322#[derive(Debug, Clone, Ser, De)]
323pub struct EnhancedCodeInterpreterConfig {
324    /// Container mode: "auto" or "explicit"
325    #[serde(skip_serializing_if = "Option::is_none")]
326    pub container_mode: Option<ContainerMode>,
327
328    /// Container ID for explicit mode
329    #[serde(skip_serializing_if = "Option::is_none")]
330    pub container_id: Option<String>,
331
332    /// Container configuration for auto mode
333    #[serde(skip_serializing_if = "Option::is_none")]
334    pub container_config: Option<ContainerConfig>,
335
336    /// Programming language (currently only Python)
337    #[serde(skip_serializing_if = "Option::is_none")]
338    pub language: Option<String>,
339
340    /// Maximum execution time in milliseconds
341    #[serde(skip_serializing_if = "Option::is_none")]
342    pub max_execution_time_ms: Option<u32>,
343
344    /// Available libraries/packages
345    #[serde(skip_serializing_if = "Option::is_none")]
346    pub libraries: Option<Vec<String>>,
347
348    /// File IDs accessible to the interpreter
349    #[serde(skip_serializing_if = "Option::is_none")]
350    pub file_ids: Option<Vec<String>>,
351
352    /// Whether to persist container after execution
353    #[serde(skip_serializing_if = "Option::is_none")]
354    pub persist_container: Option<bool>,
355
356    /// Whether to include file citations in responses
357    #[serde(skip_serializing_if = "Option::is_none")]
358    pub include_citations: Option<bool>,
359}
360
361/// Container mode for Code Interpreter
362#[derive(Debug, Clone, Ser, De, PartialEq, Eq)]
363#[serde(rename_all = "lowercase")]
364#[derive(Default)]
365pub enum ContainerMode {
366    /// Automatically create and manage containers
367    #[default]
368    Auto,
369    /// Use explicitly created containers
370    Explicit,
371}
372
373/// Builder for container configuration
374pub struct ContainerBuilder {
375    /// The container configuration being built
376    config: ContainerConfig,
377}
378
379impl Default for ContainerBuilder {
380    fn default() -> Self {
381        Self::new()
382    }
383}
384
385impl ContainerBuilder {
386    /// Create a new container builder
387    #[must_use]
388    pub fn new() -> Self {
389        Self {
390            config: ContainerConfig::default(),
391        }
392    }
393
394    /// Set container name
395    pub fn name(mut self, name: impl Into<String>) -> Self {
396        self.config.name = Some(name.into());
397        self
398    }
399
400    /// Set Python version
401    pub fn python_version(mut self, version: impl Into<String>) -> Self {
402        self.config.python_version = Some(version.into());
403        self
404    }
405
406    /// Add libraries
407    #[must_use]
408    pub fn libraries(mut self, libs: Vec<String>) -> Self {
409        self.config.libraries = Some(libs);
410        self
411    }
412
413    /// Add a single library
414    pub fn add_library(mut self, lib: impl Into<String>) -> Self {
415        let libs = self.config.libraries.get_or_insert_with(Vec::new);
416        libs.push(lib.into());
417        self
418    }
419
420    /// Set memory limit in MB
421    #[must_use]
422    pub fn memory_limit_mb(mut self, limit: u32) -> Self {
423        self.config.memory_limit_mb = Some(limit);
424        self
425    }
426
427    /// Set CPU limit
428    #[must_use]
429    pub fn cpu_limit(mut self, limit: f32) -> Self {
430        self.config.cpu_limit = Some(limit);
431        self
432    }
433
434    /// Set expiration time in minutes
435    #[must_use]
436    pub fn expiration_minutes(mut self, minutes: u32) -> Self {
437        self.config.expiration_minutes = Some(minutes);
438        self
439    }
440
441    /// Add metadata
442    pub fn metadata(mut self, key: impl Into<String>, value: serde_json::Value) -> Self {
443        let metadata = self.config.metadata.get_or_insert_with(HashMap::new);
444        metadata.insert(key.into(), value);
445        self
446    }
447
448    /// Build the container configuration
449    #[must_use]
450    pub fn build(self) -> ContainerConfig {
451        self.config
452    }
453}
454
455#[cfg(test)]
456mod tests {
457    use super::*;
458
459    #[test]
460    fn test_container_builder() {
461        let config = ContainerBuilder::new()
462            .name("test-container")
463            .python_version("3.11")
464            .add_library("numpy")
465            .add_library("pandas")
466            .memory_limit_mb(1024)
467            .cpu_limit(2.0)
468            .build();
469
470        assert_eq!(config.name, Some("test-container".to_string()));
471        assert_eq!(config.python_version, Some("3.11".to_string()));
472        assert_eq!(
473            config.libraries,
474            Some(vec!["numpy".to_string(), "pandas".to_string()])
475        );
476        assert_eq!(config.memory_limit_mb, Some(1024));
477        assert_eq!(config.cpu_limit, Some(2.0));
478    }
479}