1#![allow(clippy::uninlined_format_args)]
19#![allow(clippy::no_effect_underscore_binding)]
20#![allow(clippy::cast_sign_loss)]
21#![allow(clippy::unused_async)]
22
23use openai_ergonomic::Client;
24use std::fs;
25use std::io::{self, Write};
26use std::path::PathBuf;
27
28#[derive(Debug)]
30struct ImageManager {
31 output_dir: PathBuf,
32 generated_images: Vec<PathBuf>,
33}
34
35impl ImageManager {
36 fn new(output_dir: impl Into<PathBuf>) -> Result<Self, Box<dyn std::error::Error>> {
38 let output_dir = output_dir.into();
39
40 if !output_dir.exists() {
42 fs::create_dir_all(&output_dir)?;
43 println!(" Created output directory: {}", output_dir.display());
44 }
45
46 Ok(Self {
47 output_dir,
48 generated_images: Vec::new(),
49 })
50 }
51
52 fn save_image(
54 &mut self,
55 image_data: &[u8],
56 filename: &str,
57 ) -> Result<PathBuf, Box<dyn std::error::Error>> {
58 let image_path = self.output_dir.join(filename);
59 fs::write(&image_path, image_data)?;
60 self.generated_images.push(image_path.clone());
61 println!(" Saved image: {}", image_path.display());
62 Ok(image_path)
63 }
64
65 #[allow(dead_code)]
67 fn get_image_path(&self, index: usize) -> Option<&PathBuf> {
68 self.generated_images.get(index)
69 }
70
71 fn list_images(&self) {
73 println!("\n Generated Images:");
74 for (i, path) in self.generated_images.iter().enumerate() {
75 println!(" {}. {}", i + 1, path.display());
76 }
77 }
78
79 fn cleanup(&self) -> Result<(), Box<dyn std::error::Error>> {
81 println!("\n Cleaning up generated images...");
82 for path in &self.generated_images {
83 if path.exists() {
84 fs::remove_file(path)?;
85 println!(" Removed: {}", path.display());
86 }
87 }
88 Ok(())
89 }
90}
91
92#[tokio::main]
93async fn main() -> Result<(), Box<dyn std::error::Error>> {
94 println!("OpenAI Ergonomic - Comprehensive Images Example");
95 println!("==============================================");
96 println!();
97
98 let client = match Client::from_env() {
100 Ok(client_builder) => {
101 println!(" Client initialized successfully");
102 client_builder.build()
103 }
104 Err(e) => {
105 eprintln!(" Failed to initialize client: {e}");
106 eprintln!("Make sure OPENAI_API_KEY environment variable is set");
107 return Err(e.into());
108 }
109 };
110
111 let mut image_manager = ImageManager::new("./generated_images")?;
113
114 demonstrate_basic_generation(&client, &mut image_manager).await?;
116 demonstrate_advanced_generation(&client, &mut image_manager).await?;
117 demonstrate_image_editing(&client, &mut image_manager).await?;
118 demonstrate_image_variations(&client, &mut image_manager).await?;
119 demonstrate_streaming_generation(&client, &mut image_manager).await?;
120 demonstrate_error_handling(&client).await?;
121
122 image_manager.list_images();
124
125 println!("\n Images comprehensive example completed!");
126 println!("This example demonstrated:");
127 println!(" • Image generation with multiple models");
128 println!(" • Different sizes, qualities, and formats");
129 println!(" • Image editing with masks and backgrounds");
130 println!(" • Creating variations of existing images");
131 println!(" • File handling for inputs and outputs");
132 println!(" • Streaming image generation");
133 println!(" • Comprehensive error handling");
134
135 print!("\nKeep generated images? (y/N): ");
137 io::stdout().flush()?;
138 let mut input = String::new();
139 io::stdin().read_line(&mut input)?;
140
141 if !input.trim().to_lowercase().starts_with('y') {
142 image_manager.cleanup()?;
143 }
144
145 Ok(())
146}
147
148async fn demonstrate_basic_generation(
150 _client: &Client,
151 image_manager: &mut ImageManager,
152) -> Result<(), Box<dyn std::error::Error>> {
153 println!(" Example 1: Basic Image Generation");
154 println!("----------------------------------");
155
156 println!("\n1.1: DALL-E 3 Generation");
158 let _dalle3_prompt = "A serene mountain landscape at sunset with a crystal clear lake reflecting the orange and pink sky";
159
160 println!(" DALL-E 3 generation would be implemented like this:");
162 println!("```rust");
163 println!("let response = client.images()");
164 println!(" .generate()");
165 println!(" .model(\"dall-e-3\")");
166 println!(" .prompt(\"A serene mountain landscape at sunset...\")");
167 println!(" .size(\"1024x1024\")");
168 println!(" .quality(\"hd\")");
169 println!(" .response_format(\"b64_json\")");
170 println!(" .await?;");
171 println!("```");
172
173 let simulated_image_data = b"simulated_dalle3_image_data";
175 image_manager.save_image(simulated_image_data, "dalle3_landscape.png")?;
176
177 println!("\n1.2: DALL-E 2 Generation (Multiple Images)");
179 let _dalle2_prompt = "A cute robot reading a book in a cozy library";
180
181 println!(" DALL-E 2 multiple image generation would be:");
182 println!("```rust");
183 println!("let response = client.images()");
184 println!(" .generate()");
185 println!(" .model(\"dall-e-2\")");
186 println!(" .prompt(\"A cute robot reading a book...\")");
187 println!(" .n(3) // Generate 3 images");
188 println!(" .size(\"512x512\")");
189 println!(" .await?;");
190 println!("```");
191
192 for i in 1..=3 {
194 let simulated_data = format!("simulated_dalle2_image_{}", i).into_bytes();
195 image_manager.save_image(&simulated_data, &format!("dalle2_robot_{}.png", i))?;
196 }
197
198 println!("\n1.3: GPT-Image-1 Generation");
200 let _gpt_image_prompt = "A futuristic cityscape with flying cars and neon lights";
201
202 println!(" GPT-Image-1 generation would be:");
203 println!("```rust");
204 println!("let response = client.images()");
205 println!(" .generate()");
206 println!(" .model(\"gpt-image-1\")");
207 println!(" .prompt(\"A futuristic cityscape with flying cars...\")");
208 println!(" .size(\"1536x1024\") // Landscape");
209 println!(" .quality(\"high\")");
210 println!(" .output_format(\"webp\")");
211 println!(" .output_compression(85)");
212 println!(" .await?;");
213 println!("```");
214
215 let simulated_gpt_data = b"simulated_gpt_image_data";
216 image_manager.save_image(simulated_gpt_data, "gpt_image_cityscape.webp")?;
217
218 println!("\n Basic generation examples completed");
219 Ok(())
220}
221
222async fn demonstrate_advanced_generation(
224 _client: &Client,
225 image_manager: &mut ImageManager,
226) -> Result<(), Box<dyn std::error::Error>> {
227 println!("\n Example 2: Advanced Generation Options");
228 println!("---------------------------------------");
229
230 println!("\n2.1: Different Sizes and Aspect Ratios");
232
233 let sizes_demo = vec![
234 ("1024x1024", "Square format - perfect for social media"),
235 ("1792x1024", "Landscape format - great for wallpapers"),
236 ("1024x1792", "Portrait format - ideal for phone backgrounds"),
237 ];
238
239 for (size, description) in sizes_demo {
240 println!(" • {} - {}", size, description);
241 println!(" Implementation: .size(\"{}\")", size);
242
243 let simulated_data = format!("simulated_{}_{}", size.replace('x', "_"), "art").into_bytes();
244 let filename = format!("size_demo_{}.png", size.replace('x', "_"));
245 image_manager.save_image(&simulated_data, &filename)?;
246 }
247
248 println!("\n2.2: Quality Options");
250 let quality_demo = vec![
251 ("low", "Faster generation, lower detail"),
252 ("medium", "Balanced speed and quality"),
253 ("high", "Slower generation, maximum detail"),
254 ("hd", "DALL-E 3 high definition mode"),
255 ];
256
257 for (quality, description) in quality_demo {
258 println!(" • {} - {}", quality, description);
259 println!(" Implementation: .quality(\"{}\")", quality);
260 }
261
262 println!("\n2.3: Output Formats (GPT-Image-1)");
264 let format_demo = vec![
265 ("png", "Best quality, larger file size"),
266 ("jpeg", "Good quality, medium file size"),
267 ("webp", "Modern format, smallest file size"),
268 ];
269
270 for (format, description) in format_demo {
271 println!(" • {} - {}", format, description);
272 println!(" Implementation: .output_format(\"{}\")", format);
273
274 let simulated_data = format!("simulated_format_demo_{}", format).into_bytes();
275 let filename = format!("format_demo.{}", format);
276 image_manager.save_image(&simulated_data, &filename)?;
277 }
278
279 println!("\n2.4: Content Moderation (GPT-Image-1)");
281 println!(" • auto - Default moderation level");
282 println!(" • low - Less restrictive filtering");
283 println!(" Implementation: .content_filter(\"low\")");
284
285 println!("\n Advanced generation options demonstrated");
286 Ok(())
287}
288
289async fn demonstrate_image_editing(
291 _client: &Client,
292 image_manager: &mut ImageManager,
293) -> Result<(), Box<dyn std::error::Error>> {
294 println!("\n Example 3: Image Editing");
295 println!("---------------------------");
296
297 println!("\n3.1: Creating Sample Image for Editing");
299 let sample_image_data = b"simulated_original_image_for_editing";
300 let original_path = image_manager.save_image(sample_image_data, "original_for_editing.png")?;
301
302 println!("\n3.2: Basic Image Editing");
304 println!("Original image: {}", original_path.display());
305 println!("Edit prompt: Add a rainbow in the sky");
306
307 println!(" Image editing would be implemented like this:");
308 println!("```rust");
309 println!("let response = client.images()");
310 println!(" .edit()");
311 println!(" .image(PathBuf::from(\"{}\"))", original_path.display());
312 println!(" .prompt(\"Add a rainbow in the sky\")");
313 println!(" .model(\"dall-e-2\") // Only DALL-E 2 supports editing");
314 println!(" .size(\"1024x1024\")");
315 println!(" .await?;");
316 println!("```");
317
318 let edited_data = b"simulated_edited_image_with_rainbow";
319 image_manager.save_image(edited_data, "edited_with_rainbow.png")?;
320
321 println!("\n3.3: Editing with Mask");
323 let mask_data = b"simulated_mask_data";
324 let mask_path = image_manager.save_image(mask_data, "edit_mask.png")?;
325
326 println!("Original: {}", original_path.display());
327 println!("Mask: {}", mask_path.display());
328 println!("Edit prompt: Replace the masked area with a beautiful garden");
329
330 println!(" Masked editing would be:");
331 println!("```rust");
332 println!("let response = client.images()");
333 println!(" .edit()");
334 println!(" .image(PathBuf::from(\"{}\"))", original_path.display());
335 println!(" .mask(PathBuf::from(\"{}\"))", mask_path.display());
336 println!(" .prompt(\"Replace the masked area with a beautiful garden\")");
337 println!(" .n(2) // Generate 2 variations");
338 println!(" .await?;");
339 println!("```");
340
341 for i in 1..=2 {
342 let masked_edit_data = format!("simulated_masked_edit_{}", i).into_bytes();
343 image_manager.save_image(&masked_edit_data, &format!("masked_edit_{}.png", i))?;
344 }
345
346 println!("\n3.4: Background Replacement (GPT-Image-1)");
348 println!(" Background replacement would be:");
349 println!("```rust");
350 println!("let response = client.images()");
351 println!(" .edit()");
352 println!(" .image(PathBuf::from(\"{}\"))", original_path.display());
353 println!(" .background(\"transparent\")");
354 println!(" .prompt(\"Professional headshot with clean background\")");
355 println!(" .model(\"gpt-image-1\")");
356 println!(" .await?;");
357 println!("```");
358
359 let background_edit_data = b"simulated_background_replaced";
360 image_manager.save_image(background_edit_data, "background_replaced.png")?;
361
362 println!("\n Image editing examples completed");
363 Ok(())
364}
365
366async fn demonstrate_image_variations(
368 _client: &Client,
369 image_manager: &mut ImageManager,
370) -> Result<(), Box<dyn std::error::Error>> {
371 println!("\n Example 4: Image Variations");
372 println!("-----------------------------");
373
374 let base_image_data = b"simulated_base_image_for_variations";
376 let base_path = image_manager.save_image(base_image_data, "base_for_variations.png")?;
377
378 println!("\n4.1: Creating Variations");
379 println!("Base image: {}", base_path.display());
380
381 println!(" Image variations would be implemented like this:");
382 println!("```rust");
383 println!("let response = client.images()");
384 println!(" .variations()");
385 println!(" .image(PathBuf::from(\"{}\"))", base_path.display());
386 println!(" .model(\"dall-e-2\") // Only DALL-E 2 supports variations");
387 println!(" .n(4) // Generate 4 variations");
388 println!(" .size(\"512x512\")");
389 println!(" .response_format(\"url\")");
390 println!(" .await?;");
391 println!("```");
392
393 for i in 1..=4 {
395 let variation_data = format!("simulated_variation_{}", i).into_bytes();
396 image_manager.save_image(&variation_data, &format!("variation_{}.png", i))?;
397 println!(" Generated variation {}", i);
398 }
399
400 println!("\n4.2: Variations with Different Sizes");
401 let sizes = ["256x256", "512x512", "1024x1024"];
402
403 for size in sizes {
404 println!("Creating variation with size: {}", size);
405 println!(" Implementation: .size(\"{}\")", size);
406
407 let size_variation_data =
408 format!("simulated_variation_{}", size.replace('x', "_")).into_bytes();
409 let filename = format!("variation_{}.png", size.replace('x', "_"));
410 image_manager.save_image(&size_variation_data, &filename)?;
411 }
412
413 println!("\n Image variations examples completed");
414 Ok(())
415}
416
417async fn demonstrate_streaming_generation(
419 _client: &Client,
420 image_manager: &mut ImageManager,
421) -> Result<(), Box<dyn std::error::Error>> {
422 println!("\n Example 5: Streaming Image Generation");
423 println!("--------------------------------------");
424
425 println!("\n5.1: Streaming with Partial Images (GPT-Image-1)");
426 println!("Prompt: A detailed architectural drawing of a modern house");
427
428 println!(" Streaming image generation would be:");
429 println!("```rust");
430 println!("let mut stream = client.images()");
431 println!(" .generate()");
432 println!(" .model(\"gpt-image-1\")");
433 println!(" .prompt(\"A detailed architectural drawing of a modern house\")");
434 println!(" .stream(true)");
435 println!(" .partial_images(2) // Receive 2 partial images during generation");
436 println!(" .stream()");
437 println!(" .await?;");
438 println!();
439 println!("while let Some(chunk) = stream.next().await {{");
440 println!(" match chunk? {{");
441 println!(" ImageChunk::Partial {{ data, progress }} => {{");
442 println!(" println!(\"Partial image received: {{}}% complete\", progress);");
443 println!(" // Save partial image if desired");
444 println!(" }}");
445 println!(" ImageChunk::Final {{ data }} => {{");
446 println!(" println!(\"Final image received!\");");
447 println!(" // Save final image");
448 println!(" break;");
449 println!(" }}");
450 println!(" }}");
451 println!("}}");
452 println!("```");
453
454 println!("\n Simulating streaming progress:");
456 let progress_steps = [25, 50, 75, 100];
457
458 for progress in &progress_steps {
459 tokio::time::sleep(tokio::time::Duration::from_millis(800)).await;
460
461 if *progress < 100 {
462 println!(" Partial image received: {}% complete", progress);
463 let partial_data = format!("simulated_partial_image_{}", progress).into_bytes();
464 let filename = format!("streaming_partial_{}.png", progress);
465 image_manager.save_image(&partial_data, &filename)?;
466 } else {
467 println!(" Final image received: 100% complete");
468 let final_data = b"simulated_final_streaming_image";
469 image_manager.save_image(final_data, "streaming_final.png")?;
470 }
471
472 let filled = (*progress as usize) / 4;
474 let empty = 25 - filled;
475 println!(
476 " [{}{}] {}%",
477 "".repeat(filled),
478 "".repeat(empty),
479 progress
480 );
481 }
482
483 println!("\n5.2: Streaming Benefits");
484 println!("• Real-time feedback during image generation");
485 println!("• Ability to cancel long-running generations");
486 println!("• Progressive image refinement visibility");
487 println!("• Better user experience for complex prompts");
488
489 println!("\n Streaming image generation demonstrated");
490 Ok(())
491}
492
493async fn demonstrate_error_handling(_client: &Client) -> Result<(), Box<dyn std::error::Error>> {
495 println!("\n Example 6: Error Handling Patterns");
496 println!("------------------------------------");
497
498 println!("\n6.1: Common Error Scenarios");
499
500 println!("\nTest 1: Invalid model name");
502 println!(" Error handling would look like:");
503 println!("```rust");
504 println!("match client.images().generate()");
505 println!(" .model(\"invalid-model\")");
506 println!(" .prompt(\"Test image\")");
507 println!(" .await");
508 println!("{{");
509 println!(" Ok(_) => println!(\"Success\"),");
510 println!(" Err(Error::Api {{ status, message, .. }}) => {{");
511 println!(" match status {{");
512 println!(" 400 => println!(\"Bad request: {{}}\", message),");
513 println!(" 404 => println!(\"Model not found: {{}}\", message),");
514 println!(" _ => println!(\"API error {{}}: {{}}\", status, message),");
515 println!(" }}");
516 println!(" }}");
517 println!(" Err(e) => println!(\"Other error: {{}}\", e),");
518 println!("}}");
519 println!("```");
520
521 println!("\nTest 2: Invalid image file for editing/variations");
523 println!(" File validation would be:");
524 println!("```rust");
525 println!("let image_path = PathBuf::from(\"nonexistent.png\");");
526 println!("match client.images().edit()");
527 println!(" .image(image_path)");
528 println!(" .prompt(\"Edit this\")");
529 println!(" .await");
530 println!("{{");
531 println!(" Ok(_) => println!(\"Success\"),");
532 println!(" Err(Error::InvalidRequest(msg)) => {{");
533 println!(" println!(\"Invalid request: {{}}\", msg);");
534 println!(" }}");
535 println!(" Err(Error::Io(e)) => {{");
536 println!(" println!(\"File error: {{}}\", e);");
537 println!(" }}");
538 println!(" Err(e) => println!(\"Other error: {{}}\", e),");
539 println!("}}");
540 println!("```");
541
542 println!("\nTest 3: Rate limiting");
544 println!(" Rate limit handling would be:");
545 println!("```rust");
546 println!("match client.images().generate()");
547 println!(" .prompt(\"High-resolution detailed artwork\")");
548 println!(" .await");
549 println!("{{");
550 println!(" Err(Error::RateLimit {{ retry_after, .. }}) => {{");
551 println!(" println!(\"Rate limited. Retry after {{}} seconds\", retry_after);");
552 println!(" tokio::time::sleep(Duration::from_secs(retry_after)).await;");
553 println!(" // Retry the request");
554 println!(" }}");
555 println!(" Ok(response) => println!(\"Image generated successfully\"),");
556 println!(" Err(e) => println!(\"Error: {{}}\", e),");
557 println!("}}");
558 println!("```");
559
560 println!("\nTest 4: Content policy violations");
562 println!(" Content policy error handling:");
563 println!("```rust");
564 println!("match client.images().generate()");
565 println!(" .prompt(\"inappropriate content\")");
566 println!(" .await");
567 println!("{{");
568 println!(" Err(Error::Api {{ status: 400, message, .. }}) ");
569 println!(" if message.contains(\"content_policy\") => {{");
570 println!(" println!(\"Content policy violation: {{}}\", message);");
571 println!(
572 " println!(\"Please revise your prompt to comply with OpenAI's usage policies\");"
573 );
574 println!(" }}");
575 println!(" Ok(response) => println!(\"Image generated\"),");
576 println!(" Err(e) => println!(\"Error: {{}}\", e),");
577 println!("}}");
578 println!("```");
579
580 println!("\n6.2: Error Recovery Strategies");
581 println!("• Automatic retry with exponential backoff for transient errors");
582 println!("• Prompt modification suggestions for content policy violations");
583 println!("• Fallback to different models when one is unavailable");
584 println!("• Graceful degradation of quality/size when limits are exceeded");
585
586 println!("\n6.3: Best Practices");
587 println!("• Always validate input files before sending requests");
588 println!("• Implement proper timeout handling for long-running generations");
589 println!("• Cache successful responses to avoid redundant API calls");
590 println!("• Monitor API usage to stay within rate limits");
591
592 println!("\n Error handling patterns demonstrated");
593 Ok(())
594}