images_comprehensive/
images_comprehensive.rs

1//! Comprehensive images example.
2//!
3//! This example demonstrates all image-related operations including:
4//! - Image generation with different models (DALL-E 2, DALL-E 3, GPT-Image-1)
5//! - Different sizes, qualities, and formats
6//! - Image editing with masks and backgrounds
7//! - Creating variations of existing images
8//! - Proper file handling for inputs and outputs
9//! - Streaming image generation (for supported models)
10//! - Error handling patterns
11//!
12//! Run with: `cargo run --example images_comprehensive`
13//!
14//! Note: This example demonstrates the intended API design. The actual images API
15//! implementation is still in development. Many of these features will be available
16//! once the builders and response types are implemented.
17
18#![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/// Manages image outputs and file operations.
29#[derive(Debug)]
30struct ImageManager {
31    output_dir: PathBuf,
32    generated_images: Vec<PathBuf>,
33}
34
35impl ImageManager {
36    /// Create a new image manager with an output directory.
37    fn new(output_dir: impl Into<PathBuf>) -> Result<Self, Box<dyn std::error::Error>> {
38        let output_dir = output_dir.into();
39
40        // Create output directory if it doesn't exist
41        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    /// Save an image to the output directory.
53    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    /// Get the path to a generated image by index.
66    #[allow(dead_code)]
67    fn get_image_path(&self, index: usize) -> Option<&PathBuf> {
68        self.generated_images.get(index)
69    }
70
71    /// List all generated images.
72    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    /// Clean up generated images.
80    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    // Create client from environment variables
99    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    // Initialize image manager
112    let mut image_manager = ImageManager::new("./generated_images")?;
113
114    // Demonstrate various image operations
115    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    // Summary and cleanup
123    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    // Ask user if they want to keep the generated images
136    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
148/// Demonstrate basic image generation with different models.
149async 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    // DALL-E 3 generation (high quality, single image)
157    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    // Note: This demonstrates the intended API once images builders are implemented
161    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    // Simulate saving the image
174    let simulated_image_data = b"simulated_dalle3_image_data";
175    image_manager.save_image(simulated_image_data, "dalle3_landscape.png")?;
176
177    // DALL-E 2 generation (multiple images, standard quality)
178    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    // Simulate multiple images
193    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    // GPT-Image-1 generation (newest model with streaming support)
199    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
222/// Demonstrate advanced generation options.
223async 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    // Different sizes and aspect ratios
231    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    // Quality options
249    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    // Output formats (GPT-Image-1)
263    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    // Content moderation levels
280    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
289/// Demonstrate image editing capabilities.
290async 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    // Create a sample image file for editing demonstrations
298    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    // Basic image editing
303    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    // Editing with mask
322    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    // Background replacement (GPT-Image-1)
347    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
366/// Demonstrate creating variations of existing images.
367async 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    // Use an existing image for variations
375    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    // Simulate generating variations
394    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
417/// Demonstrate streaming image generation.
418async 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    // Simulate streaming with partial images
455    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        // Show progress bar
473        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
493/// Demonstrate error handling patterns for images API.
494async 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    // Test 1: Invalid model
501    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    // Test 2: Invalid image file
522    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    // Test 3: Rate limiting
543    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    // Test 4: Content policy violations
561    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}