1#![allow(clippy::uninlined_format_args)]
33#![allow(clippy::no_effect_underscore_binding)]
34#![allow(clippy::doc_markdown)]
35#![allow(clippy::cast_possible_wrap)]
36#![allow(clippy::cast_precision_loss)]
37#![allow(clippy::too_many_lines)]
38#![allow(clippy::missing_docs_in_private_items)]
39#![allow(clippy::unused_async)]
40#![allow(clippy::cast_possible_truncation)]
41#![allow(clippy::cast_lossless)]
42#![allow(clippy::items_after_statements)]
43#![allow(clippy::cast_sign_loss)]
44#![allow(unused_variables)]
45#![allow(unused_imports)]
46#![allow(missing_docs)]
47#![allow(dead_code)]
48
49use openai_ergonomic::{builders::uploads::UploadBuilder, Client, UploadPurpose};
50
51#[derive(Debug, Clone)]
53pub struct UploadInfo {
54 pub id: String,
55 pub filename: String,
56 pub bytes: i32,
57 pub purpose: String,
58 pub status: String,
59}
60
61impl UploadInfo {
62 pub fn new(
63 id: impl Into<String>,
64 filename: impl Into<String>,
65 bytes: i32,
66 purpose: impl Into<String>,
67 status: impl Into<String>,
68 ) -> Self {
69 Self {
70 id: id.into(),
71 filename: filename.into(),
72 bytes,
73 purpose: purpose.into(),
74 status: status.into(),
75 }
76 }
77
78 pub fn display(&self) {
79 println!(" Upload ID: {}", self.id);
80 println!(" Filename: {}", self.filename);
81 println!(
82 " Size: {} bytes ({:.2} MB)",
83 self.bytes,
84 self.bytes as f64 / (1024.0 * 1024.0)
85 );
86 println!(" Purpose: {}", self.purpose);
87 println!(" Status: {}", self.status);
88 }
89
90 pub fn formatted_size(&self) -> String {
91 let bytes = self.bytes as f64;
92 if bytes < 1024.0 {
93 format!("{:.0} B", bytes)
94 } else if bytes < 1024.0 * 1024.0 {
95 format!("{:.2} KB", bytes / 1024.0)
96 } else if bytes < 1024.0 * 1024.0 * 1024.0 {
97 format!("{:.2} MB", bytes / (1024.0 * 1024.0))
98 } else {
99 format!("{:.2} GB", bytes / (1024.0 * 1024.0 * 1024.0))
100 }
101 }
102}
103
104#[tokio::main]
105async fn main() -> Result<(), Box<dyn std::error::Error>> {
106 println!(" OpenAI Ergonomic - Comprehensive Uploads Example\n");
107
108 println!(" Initializing OpenAI client...");
110 let client = match Client::from_env() {
111 Ok(c) => {
112 println!(" Client initialized successfully\n");
113 c.build()
114 }
115 Err(e) => {
116 eprintln!(" Failed to initialize client: {}", e);
117 eprintln!(" Make sure OPENAI_API_KEY is set");
118 return Ok(());
119 }
120 };
121
122 println!();
124 println!(" Example 1: Create Multipart Upload");
125 println!("\n");
126
127 let filename = "large_training_dataset.jsonl";
129 let file_size_mb = 750; let file_size_bytes = file_size_mb * 1024 * 1024;
131 let mime_type = "application/jsonl";
132
133 println!("Creating multipart upload...");
134 println!(" Filename: {}", filename);
135 println!(" Size: {} MB ({} bytes)", file_size_mb, file_size_bytes);
136 println!(" Purpose: fine-tune");
137 println!(" MIME Type: {}", mime_type);
138
139 let builder = client.uploads().builder(
140 filename,
141 UploadPurpose::FineTune,
142 file_size_bytes,
143 mime_type,
144 );
145
146 println!("\n Note: This would create a real multipart upload session.");
147 println!(" Commented out to avoid accidental API calls.\n");
148
149 let demo_upload = UploadInfo::new(
164 "upload-demo123",
165 filename,
166 file_size_bytes,
167 "fine-tune",
168 "pending",
169 );
170 println!(" Demo Upload Created:");
171 demo_upload.display();
172
173 println!("\n");
175 println!(" Example 2: Upload File Parts");
176 println!("\n");
177
178 let upload_id = "upload-demo123";
179 let part_size_mb = 64; let total_parts = (file_size_mb + part_size_mb - 1) / part_size_mb; println!(
183 "Uploading {} parts ({} MB each)...\n",
184 total_parts, part_size_mb
185 );
186
187 for part_num in 1..=total_parts {
188 let progress_percent = (part_num as f64 / total_parts as f64) * 100.0;
189
190 println!(
191 " Uploading part {}/{} ({:.1}% complete)",
192 part_num, total_parts, progress_percent
193 );
194
195 }
212
213 println!("\n All {} parts uploaded successfully", total_parts);
214
215 println!("\n");
217 println!(" Example 3: Complete Upload");
218 println!("\n");
219
220 println!("Completing upload: {}\n", upload_id);
221
222 println!(" Demo: Would finalize the upload and create a file object");
237 println!(" File ID: file-abc123");
238 println!(" Filename: {}", filename);
239 println!(" Status: ready");
240
241 println!("\n");
243 println!(" Example 4: Upload Smaller File");
244 println!("\n");
245
246 let small_filename = "training_data.jsonl";
247 let small_size_mb = 10;
248 let small_size_bytes = small_size_mb * 1024 * 1024;
249
250 println!("Creating upload for smaller file...");
251 println!(" Filename: {}", small_filename);
252 println!(" Size: {} MB", small_size_mb);
253 println!(" Purpose: assistants");
254
255 let small_builder = client.uploads().builder(
256 small_filename,
257 UploadPurpose::Assistants,
258 small_size_bytes,
259 "application/jsonl",
260 );
261
262 println!("\n Note: For files < 512 MB, consider using the regular Files API");
263 println!(" The Uploads API is optimized for large files.");
264
265 println!("\n");
267 println!(" Example 5: Error Handling & Retry");
268 println!("\n");
269
270 println!("Demonstrating retry logic for failed part uploads...\n");
271
272 let max_retries = 3;
273 let failed_part = 5;
274
275 for retry in 1..=max_retries {
276 println!(" Attempt {} to upload part {}", retry, failed_part);
277
278 }
295
296 println!("\n Tip: Implement exponential backoff for retry logic");
297
298 println!("\n");
300 println!(" Example 6: Progress Tracking");
301 println!("\n");
302
303 struct UploadProgress {
304 total_bytes: i32,
305 uploaded_bytes: i32,
306 total_parts: i32,
307 uploaded_parts: i32,
308 }
309
310 impl UploadProgress {
311 fn percentage(&self) -> f64 {
312 (self.uploaded_bytes as f64 / self.total_bytes as f64) * 100.0
313 }
314
315 fn eta_seconds(&self, bytes_per_second: f64) -> i32 {
316 let remaining_bytes = self.total_bytes - self.uploaded_bytes;
317 (remaining_bytes as f64 / bytes_per_second) as i32
318 }
319
320 fn display(&self, bytes_per_second: f64) {
321 let progress_bar_width = 40;
322 let filled = ((self.percentage() / 100.0) * progress_bar_width as f64) as usize;
323 let empty = progress_bar_width - filled;
324
325 print!(" [");
326 print!("{}", "".repeat(filled));
327 print!("{}", "".repeat(empty));
328 print!("] ");
329
330 println!(
331 "{:.1}% ({}/{} parts, {} MB/s, ETA: {}s)",
332 self.percentage(),
333 self.uploaded_parts,
334 self.total_parts,
335 bytes_per_second / (1024.0 * 1024.0),
336 self.eta_seconds(bytes_per_second)
337 );
338 }
339 }
340
341 let progress = UploadProgress {
342 total_bytes: file_size_bytes,
343 uploaded_bytes: (file_size_bytes as f64 * 0.65) as i32,
344 total_parts,
345 uploaded_parts: (total_parts as f64 * 0.65) as i32,
346 };
347
348 println!("Current upload progress:");
349 progress.display(10.0 * 1024.0 * 1024.0); println!("\n");
353 println!(" Summary");
354 println!("\n");
355
356 println!(" Uploads API examples completed!");
357 println!("\n Key Takeaways:");
358 println!(" • Uploads API is designed for large files (>512 MB)");
359 println!(" • Files are uploaded in parts for reliability");
360 println!(" • Each part can be retried independently");
361 println!(" • Progress can be tracked during upload");
362 println!(" • Upload must be completed after all parts are uploaded");
363
364 println!("\n Best Practices:");
365 println!(" 1. Use appropriate part sizes (typically 64 MB)");
366 println!(" 2. Implement retry logic with exponential backoff");
367 println!(" 3. Track progress and provide user feedback");
368 println!(" 4. Handle upload cancellation gracefully");
369 println!(" 5. Verify file integrity after upload");
370
371 println!("\n When to Use:");
372 println!(" • Large training datasets for fine-tuning");
373 println!(" • Big files for assistants (>512 MB)");
374 println!(" • Batch processing input files");
375 println!(" • Any file where reliability is critical");
376
377 println!("\n Example completed successfully!");
378
379 Ok(())
380}