structured_outputs/
structured_outputs.rs

1#![allow(clippy::uninlined_format_args)]
2//! Structured Outputs example demonstrating JSON mode and schema validation.
3//!
4//! This example showcases `OpenAI`'s structured outputs capabilities, including:
5//! - Simple JSON mode for basic structure enforcement
6//! - Schema-based structured outputs with type validation
7//! - Complex nested data structures
8//! - Different use cases (data extraction, classification, analysis)
9//! - Comprehensive error handling and validation
10//!
11//! ## Features Demonstrated
12//!
13//! - Basic JSON mode with implicit structure
14//! - JSON Schema validation with strict typing
15//! - Nested objects and arrays
16//! - Enum validation for constrained values
17//! - Data extraction from unstructured text
18//! - Content classification and analysis
19//! - Error handling for malformed schemas
20//!
21//! ## Prerequisites
22//!
23//! Set your `OpenAI` API key:
24//! ```bash
25//! export OPENAI_API_KEY="your-key-here"
26//! ```
27//!
28//! ## Usage
29//!
30//! ```bash
31//! cargo run --example structured_outputs
32//! ```
33
34use openai_ergonomic::{Client, Error};
35use serde_json::json;
36
37#[tokio::main]
38async fn main() -> Result<(), Box<dyn std::error::Error>> {
39    println!("๐Ÿ—๏ธ  OpenAI Ergonomic - Structured Outputs Example\n");
40
41    // Initialize client from environment variables
42    let client = match Client::from_env() {
43        Ok(client_builder) => {
44            println!("โœ… Client initialized successfully");
45            client_builder.build()
46        }
47        Err(e) => {
48            eprintln!("โŒ Failed to initialize client: {e}");
49            eprintln!("๐Ÿ’ก Make sure OPENAI_API_KEY is set in your environment");
50            return Err(e.into());
51        }
52    };
53
54    // Example 1: Simple JSON Mode
55    println!("\n๐Ÿ“ Example 1: Simple JSON Mode");
56    println!("==============================");
57
58    match simple_json_mode_example(&client).await {
59        Ok(()) => println!("โœ… Simple JSON mode example completed"),
60        Err(e) => {
61            eprintln!("โŒ Simple JSON mode example failed: {e}");
62            handle_api_error(&e);
63        }
64    }
65
66    // Example 2: Schema-Based Data Extraction
67    println!("\n๐Ÿ” Example 2: Schema-Based Data Extraction");
68    println!("==========================================");
69
70    match data_extraction_example(&client).await {
71        Ok(()) => println!("โœ… Data extraction example completed"),
72        Err(e) => {
73            eprintln!("โŒ Data extraction example failed: {e}");
74            handle_api_error(&e);
75        }
76    }
77
78    // Example 3: Complex Nested Structures
79    println!("\n๐Ÿข Example 3: Complex Nested Structures");
80    println!("=======================================");
81
82    match complex_structure_example(&client).await {
83        Ok(()) => println!("โœ… Complex structure example completed"),
84        Err(e) => {
85            eprintln!("โŒ Complex structure example failed: {e}");
86            handle_api_error(&e);
87        }
88    }
89
90    // Example 4: Content Classification
91    println!("\n๐Ÿท๏ธ  Example 4: Content Classification");
92    println!("=====================================");
93
94    match classification_example(&client).await {
95        Ok(()) => println!("โœ… Classification example completed"),
96        Err(e) => {
97            eprintln!("โŒ Classification example failed: {e}");
98            handle_api_error(&e);
99        }
100    }
101
102    // Example 5: Mathematical Analysis
103    println!("\n๐Ÿงฎ Example 5: Mathematical Analysis");
104    println!("===================================");
105
106    match math_analysis_example(&client).await {
107        Ok(()) => println!("โœ… Mathematical analysis example completed"),
108        Err(e) => {
109            eprintln!("โŒ Mathematical analysis example failed: {e}");
110            handle_api_error(&e);
111        }
112    }
113
114    // Example 6: Schema Validation Error Handling
115    println!("\nโš ๏ธ  Example 6: Schema Validation & Error Handling");
116    println!("=================================================");
117
118    match validation_error_example(&client).await {
119        Ok(()) => println!("โœ… Validation error example completed"),
120        Err(e) => {
121            eprintln!("โŒ Validation error example failed: {e}");
122            handle_api_error(&e);
123        }
124    }
125
126    println!("\n๐ŸŽ‰ All structured output examples completed!");
127    println!("๐Ÿ“Š Check the console output above for JSON-formatted results.");
128    Ok(())
129}
130
131/// Example 1: Simple JSON mode without explicit schema
132async fn simple_json_mode_example(client: &Client) -> Result<(), Error> {
133    println!("Using simple JSON mode for basic structure enforcement...");
134
135    let builder = client
136        .responses()
137        .system("You are a helpful assistant. Always respond in valid JSON format with the keys: summary, sentiment, and confidence_score (0-1).")
138        .user("Analyze this product review: 'This laptop is amazing! Great performance, excellent battery life, and the display is crystal clear. Highly recommended!'")
139        .json_mode()
140        .temperature(0.3)
141        .max_completion_tokens(200);
142
143    let response = client.send_responses(builder).await?;
144
145    if let Some(content) = response.content() {
146        println!("๐Ÿ“Š JSON Analysis Result:");
147
148        // Try to parse and pretty-print the JSON
149        match serde_json::from_str::<serde_json::Value>(content) {
150            Ok(json) => {
151                println!("{}", serde_json::to_string_pretty(&json)?);
152
153                // Demonstrate accessing specific fields
154                if let Some(sentiment) = json.get("sentiment").and_then(|s| s.as_str()) {
155                    println!("\n๐ŸŽฏ Extracted sentiment: {sentiment}");
156                }
157                if let Some(confidence) = json
158                    .get("confidence_score")
159                    .and_then(serde_json::Value::as_f64)
160                {
161                    println!("๐ŸŽฏ Confidence score: {confidence:.2}");
162                }
163            }
164            Err(e) => {
165                println!("โš ๏ธ  Failed to parse JSON: {e}");
166                println!("Raw response: {content}");
167            }
168        }
169    }
170
171    Ok(())
172}
173
174/// Example 2: Data extraction with schema validation
175async fn data_extraction_example(client: &Client) -> Result<(), Error> {
176    println!("Extracting structured data from unstructured text using JSON schema...");
177
178    // Define schema for extracting contact information
179    let contact_schema = json!({
180        "type": "object",
181        "properties": {
182            "contacts": {
183                "type": "array",
184                "items": {
185                    "type": "object",
186                    "properties": {
187                        "name": {
188                            "type": "string",
189                            "description": "Full name of the person"
190                        },
191                        "email": {
192                            "type": "string",
193                            "format": "email",
194                            "description": "Email address"
195                        },
196                        "phone": {
197                            "type": "string",
198                            "description": "Phone number"
199                        },
200                        "company": {
201                            "type": "string",
202                            "description": "Company or organization"
203                        },
204                        "role": {
205                            "type": "string",
206                            "description": "Job title or role"
207                        }
208                    },
209                    "required": ["name"],
210                    "additionalProperties": false
211                }
212            },
213            "total_contacts": {
214                "type": "integer",
215                "description": "Total number of contacts extracted"
216            }
217        },
218        "required": ["contacts", "total_contacts"],
219        "additionalProperties": false
220    });
221
222    let unstructured_text =
223        "Contact our team: John Smith (CEO) at john@example.com or call 555-0123. \
224        For technical support, reach out to Sarah Johnson at sarah.johnson@techcorp.com. \
225        Our sales manager Mike Wilson can be reached at mike@company.com or 555-0456.";
226
227    let builder = client
228        .responses()
229        .system("You are an expert at extracting contact information from text. Extract all contact details you can find and structure them according to the provided schema.")
230        .user(format!("Extract contact information from this text: {unstructured_text}"))
231        .json_schema("contact_extraction", contact_schema)
232        .temperature(0.1); // Low temperature for accuracy
233
234    let response = client.send_responses(builder).await?;
235
236    if let Some(content) = response.content() {
237        println!("๐Ÿ“Š Extracted Contact Information:");
238
239        match serde_json::from_str::<serde_json::Value>(content) {
240            Ok(json) => {
241                println!("{}", serde_json::to_string_pretty(&json)?);
242
243                // Demonstrate accessing the structured data
244                if let Some(contacts) = json.get("contacts").and_then(|c| c.as_array()) {
245                    println!("\n๐ŸŽฏ Summary: Found {} contact(s)", contacts.len());
246                    for (i, contact) in contacts.iter().enumerate() {
247                        if let Some(name) = contact.get("name").and_then(|n| n.as_str()) {
248                            println!("   {}. {name}", i + 1);
249                            if let Some(email) = contact.get("email").and_then(|e| e.as_str()) {
250                                println!("      ๐Ÿ“ง {email}");
251                            }
252                            if let Some(company) = contact.get("company").and_then(|c| c.as_str()) {
253                                println!("      ๐Ÿข {company}");
254                            }
255                        }
256                    }
257                }
258            }
259            Err(e) => {
260                println!("โš ๏ธ  Failed to parse JSON: {e}");
261                println!("Raw response: {content}");
262            }
263        }
264    }
265
266    Ok(())
267}
268
269/// Example 3: Complex nested structure for event planning
270#[allow(clippy::too_many_lines)]
271async fn complex_structure_example(client: &Client) -> Result<(), Error> {
272    println!("Creating complex nested structure for event planning...");
273
274    // Define a comprehensive event schema
275    let event_schema = json!({
276        "type": "object",
277        "properties": {
278            "event": {
279                "type": "object",
280                "properties": {
281                    "name": {
282                        "type": "string",
283                        "description": "Event name"
284                    },
285                    "type": {
286                        "type": "string",
287                        "enum": ["conference", "workshop", "seminar", "networking", "party", "meeting"],
288                        "description": "Type of event"
289                    },
290                    "date": {
291                        "type": "string",
292                        "format": "date",
293                        "description": "Event date in YYYY-MM-DD format"
294                    },
295                    "duration_hours": {
296                        "type": "number",
297                        "minimum": 0.5,
298                        "maximum": 24,
299                        "description": "Duration in hours"
300                    },
301                    "venue": {
302                        "type": "object",
303                        "properties": {
304                            "name": {
305                                "type": "string",
306                                "description": "Venue name"
307                            },
308                            "address": {
309                                "type": "string",
310                                "description": "Venue address"
311                            },
312                            "capacity": {
313                                "type": "integer",
314                                "minimum": 1,
315                                "description": "Maximum capacity"
316                            },
317                            "amenities": {
318                                "type": "array",
319                                "items": {
320                                    "type": "string",
321                                    "enum": ["wifi", "parking", "catering", "av_equipment", "wheelchair_accessible", "air_conditioning"]
322                                },
323                                "description": "Available amenities"
324                            }
325                        },
326                        "required": ["name", "capacity"],
327                        "additionalProperties": false
328                    },
329                    "agenda": {
330                        "type": "array",
331                        "items": {
332                            "type": "object",
333                            "properties": {
334                                "time": {
335                                    "type": "string",
336                                    "pattern": "^([0-1]?[0-9]|2[0-3]):[0-5][0-9]$",
337                                    "description": "Time in HH:MM format"
338                                },
339                                "activity": {
340                                    "type": "string",
341                                    "description": "Activity description"
342                                },
343                                "speaker": {
344                                    "type": "string",
345                                    "description": "Speaker name"
346                                },
347                                "duration_minutes": {
348                                    "type": "integer",
349                                    "minimum": 15,
350                                    "maximum": 480,
351                                    "description": "Activity duration in minutes"
352                                }
353                            },
354                            "required": ["time", "activity", "duration_minutes"],
355                            "additionalProperties": false
356                        }
357                    },
358                    "estimated_cost": {
359                        "type": "object",
360                        "properties": {
361                            "venue": {
362                                "type": "number",
363                                "minimum": 0,
364                                "description": "Venue cost in USD"
365                            },
366                            "catering": {
367                                "type": "number",
368                                "minimum": 0,
369                                "description": "Catering cost in USD"
370                            },
371                            "equipment": {
372                                "type": "number",
373                                "minimum": 0,
374                                "description": "Equipment cost in USD"
375                            },
376                            "total": {
377                                "type": "number",
378                                "minimum": 0,
379                                "description": "Total estimated cost in USD"
380                            }
381                        },
382                        "required": ["total"],
383                        "additionalProperties": false
384                    }
385                },
386                "required": ["name", "type", "date", "duration_hours", "venue"],
387                "additionalProperties": false
388            }
389        },
390        "required": ["event"],
391        "additionalProperties": false
392    });
393
394    let builder = client
395        .responses()
396        .system("You are an expert event planner. Create a detailed event plan based on the user's requirements, including venue details, agenda, and cost estimates.")
397        .user("Plan a one-day AI/ML conference for 200 people in San Francisco. Include morning keynotes, afternoon workshops, networking lunch, and panel discussions. Budget around $50,000.")
398        .json_schema("event_plan", event_schema)
399        .temperature(0.5);
400
401    let response = client.send_responses(builder).await?;
402
403    if let Some(content) = response.content() {
404        println!("๐Ÿ“Š Event Plan:");
405
406        match serde_json::from_str::<serde_json::Value>(content) {
407            Ok(json) => {
408                println!("{}", serde_json::to_string_pretty(&json)?);
409
410                // Extract and display key information
411                if let Some(event) = json.get("event") {
412                    if let Some(name) = event.get("name").and_then(|n| n.as_str()) {
413                        println!("\n๐ŸŽฏ Event: {name}");
414                    }
415                    if let Some(venue) = event.get("venue") {
416                        if let Some(venue_name) = venue.get("name").and_then(|n| n.as_str()) {
417                            let capacity = venue
418                                .get("capacity")
419                                .and_then(serde_json::Value::as_i64)
420                                .unwrap_or(0);
421                            println!("๐Ÿข Venue: {venue_name} (Capacity: {capacity})");
422                        }
423                    }
424                    if let Some(agenda) = event.get("agenda").and_then(|a| a.as_array()) {
425                        println!("๐Ÿ“… Agenda has {} activities", agenda.len());
426                    }
427                    if let Some(cost) = event.get("estimated_cost") {
428                        if let Some(total) = cost.get("total").and_then(serde_json::Value::as_f64) {
429                            println!("๐Ÿ’ฐ Estimated total cost: ${total:.2}");
430                        }
431                    }
432                }
433            }
434            Err(e) => {
435                println!("โš ๏ธ  Failed to parse JSON: {e}");
436                println!("Raw response: {content}");
437            }
438        }
439    }
440
441    Ok(())
442}
443
444/// Example 4: Content classification with enum validation
445#[allow(clippy::too_many_lines)]
446async fn classification_example(client: &Client) -> Result<(), Error> {
447    println!("Classifying content with enum validation...");
448
449    // Define schema for content classification
450    let classification_schema = json!({
451        "type": "object",
452        "properties": {
453            "classification": {
454                "type": "object",
455                "properties": {
456                    "category": {
457                        "type": "string",
458                        "enum": ["technology", "business", "science", "health", "politics", "sports", "entertainment", "education", "travel", "lifestyle"],
459                        "description": "Primary content category"
460                    },
461                    "subcategory": {
462                        "type": "string",
463                        "description": "More specific subcategory"
464                    },
465                    "sentiment": {
466                        "type": "string",
467                        "enum": ["positive", "neutral", "negative", "mixed"],
468                        "description": "Overall sentiment"
469                    },
470                    "topics": {
471                        "type": "array",
472                        "items": {
473                            "type": "string"
474                        },
475                        "maxItems": 5,
476                        "description": "Key topics mentioned"
477                    },
478                    "target_audience": {
479                        "type": "string",
480                        "enum": ["general", "professionals", "students", "experts", "consumers"],
481                        "description": "Intended audience"
482                    },
483                    "complexity_level": {
484                        "type": "string",
485                        "enum": ["beginner", "intermediate", "advanced", "expert"],
486                        "description": "Content complexity level"
487                    },
488                    "confidence_score": {
489                        "type": "number",
490                        "minimum": 0,
491                        "maximum": 1,
492                        "description": "Confidence in classification (0-1)"
493                    }
494                },
495                "required": ["category", "sentiment", "topics", "target_audience", "complexity_level", "confidence_score"],
496                "additionalProperties": false
497            }
498        },
499        "required": ["classification"],
500        "additionalProperties": false
501    });
502
503    let content_to_classify = "Recent advances in quantum computing have shown promising results for solving complex optimization problems. \
504        Researchers at leading universities have demonstrated quantum algorithms that can potentially outperform classical computers \
505        in specific domains like cryptography and molecular simulation. However, current quantum computers still face challenges \
506        with noise and error rates, requiring sophisticated error correction techniques. The field is rapidly evolving with \
507        significant investments from both academic institutions and major technology companies.";
508
509    let builder = client
510        .responses()
511        .system("You are an expert content classifier. Analyze the provided text and classify it according to the given schema. Be precise with your classifications and provide accurate confidence scores.")
512        .user(format!("Classify this content: {content_to_classify}"))
513        .json_schema("content_classification", classification_schema)
514        .temperature(0.2); // Low temperature for consistent classification
515
516    let response = client.send_responses(builder).await?;
517
518    if let Some(content) = response.content() {
519        println!("๐Ÿ“Š Content Classification:");
520
521        match serde_json::from_str::<serde_json::Value>(content) {
522            Ok(json) => {
523                println!("{}", serde_json::to_string_pretty(&json)?);
524
525                // Extract classification details
526                if let Some(classification) = json.get("classification") {
527                    println!("\n๐ŸŽฏ Classification Summary:");
528                    if let Some(category) = classification.get("category").and_then(|c| c.as_str())
529                    {
530                        println!("   ๐Ÿ“‚ Category: {category}");
531                    }
532                    if let Some(sentiment) =
533                        classification.get("sentiment").and_then(|s| s.as_str())
534                    {
535                        println!("   ๐Ÿ˜Š Sentiment: {sentiment}");
536                    }
537                    if let Some(audience) = classification
538                        .get("target_audience")
539                        .and_then(|a| a.as_str())
540                    {
541                        println!("   ๐Ÿ‘ฅ Target Audience: {audience}");
542                    }
543                    if let Some(complexity) = classification
544                        .get("complexity_level")
545                        .and_then(|c| c.as_str())
546                    {
547                        println!("   ๐ŸŽ“ Complexity: {complexity}");
548                    }
549                    if let Some(confidence) = classification
550                        .get("confidence_score")
551                        .and_then(serde_json::Value::as_f64)
552                    {
553                        println!("   ๐ŸŽฏ Confidence: {:.2}%", confidence * 100.0);
554                    }
555                    if let Some(topics) = classification.get("topics").and_then(|t| t.as_array()) {
556                        let topic_strings: Vec<String> = topics
557                            .iter()
558                            .filter_map(|t| t.as_str())
559                            .map(std::string::ToString::to_string)
560                            .collect();
561                        println!("   ๐Ÿท๏ธ  Topics: {}", topic_strings.join(", "));
562                    }
563                }
564            }
565            Err(e) => {
566                println!("โš ๏ธ  Failed to parse JSON: {e}");
567                println!("Raw response: {content}");
568            }
569        }
570    }
571
572    Ok(())
573}
574
575/// Example 5: Mathematical analysis with structured output
576#[allow(clippy::too_many_lines)]
577async fn math_analysis_example(client: &Client) -> Result<(), Error> {
578    println!("Performing mathematical analysis with structured output...");
579
580    // Define schema for mathematical analysis
581    let math_schema = json!({
582        "type": "object",
583        "properties": {
584            "analysis": {
585                "type": "object",
586                "properties": {
587                    "problem_type": {
588                        "type": "string",
589                        "enum": ["algebra", "geometry", "calculus", "statistics", "probability", "discrete_math", "linear_algebra"],
590                        "description": "Type of mathematical problem"
591                    },
592                    "solution_steps": {
593                        "type": "array",
594                        "items": {
595                            "type": "object",
596                            "properties": {
597                                "step_number": {
598                                    "type": "integer",
599                                    "minimum": 1,
600                                    "description": "Step number in the solution"
601                                },
602                                "description": {
603                                    "type": "string",
604                                    "description": "Description of what this step does"
605                                },
606                                "equation": {
607                                    "type": "string",
608                                    "description": "Mathematical equation or expression"
609                                },
610                                "result": {
611                                    "type": "string",
612                                    "description": "Result of this step"
613                                }
614                            },
615                            "required": ["step_number", "description", "equation"],
616                            "additionalProperties": false
617                        }
618                    },
619                    "final_answer": {
620                        "type": "string",
621                        "description": "Final answer to the problem"
622                    },
623                    "verification": {
624                        "type": "object",
625                        "properties": {
626                            "check_method": {
627                                "type": "string",
628                                "description": "Method used to verify the answer"
629                            },
630                            "is_correct": {
631                                "type": "boolean",
632                                "description": "Whether the answer passes verification"
633                            }
634                        },
635                        "required": ["check_method", "is_correct"],
636                        "additionalProperties": false
637                    },
638                    "concepts_used": {
639                        "type": "array",
640                        "items": {
641                            "type": "string"
642                        },
643                        "description": "Mathematical concepts used in the solution"
644                    }
645                },
646                "required": ["problem_type", "solution_steps", "final_answer", "verification", "concepts_used"],
647                "additionalProperties": false
648            }
649        },
650        "required": ["analysis"],
651        "additionalProperties": false
652    });
653
654    let math_problem =
655        "Find the derivative of f(x) = 3x^3 + 2x^2 - 5x + 7 and evaluate it at x = 2.";
656
657    let builder = client
658        .responses()
659        .system("You are a mathematics tutor. Solve mathematical problems step by step, showing your work clearly and verifying your answers. Structure your response according to the provided schema.")
660        .user(format!("Solve this problem: {math_problem}"))
661        .json_schema("math_analysis", math_schema)
662        .temperature(0.1); // Very low temperature for mathematical accuracy
663
664    let response = client.send_responses(builder).await?;
665
666    if let Some(content) = response.content() {
667        println!("๐Ÿ“Š Mathematical Analysis:");
668
669        match serde_json::from_str::<serde_json::Value>(content) {
670            Ok(json) => {
671                println!("{}", serde_json::to_string_pretty(&json)?);
672
673                // Extract and display solution steps
674                if let Some(analysis) = json.get("analysis") {
675                    println!("\n๐ŸŽฏ Solution Summary:");
676
677                    if let Some(problem_type) =
678                        analysis.get("problem_type").and_then(|p| p.as_str())
679                    {
680                        println!("   ๐Ÿ“š Problem Type: {problem_type}");
681                    }
682
683                    if let Some(steps) = analysis.get("solution_steps").and_then(|s| s.as_array()) {
684                        println!("   ๐Ÿ“ Solution Steps: {} steps", steps.len());
685                        for step in steps {
686                            if let (Some(step_num), Some(desc)) = (
687                                step.get("step_number").and_then(serde_json::Value::as_i64),
688                                step.get("description").and_then(|d| d.as_str()),
689                            ) {
690                                println!("      {step_num}. {desc}");
691                                if let Some(equation) =
692                                    step.get("equation").and_then(|e| e.as_str())
693                                {
694                                    println!("         ๐Ÿ“ {equation}");
695                                }
696                            }
697                        }
698                    }
699
700                    if let Some(answer) = analysis.get("final_answer").and_then(|a| a.as_str()) {
701                        println!("   โœ… Final Answer: {answer}");
702                    }
703
704                    if let Some(verification) = analysis.get("verification") {
705                        if let Some(is_correct) = verification
706                            .get("is_correct")
707                            .and_then(serde_json::Value::as_bool)
708                        {
709                            let status = if is_correct {
710                                "โœ… Verified"
711                            } else {
712                                "โŒ Needs Review"
713                            };
714                            println!("   ๐Ÿ” Verification: {status}");
715                        }
716                    }
717
718                    if let Some(concepts) = analysis.get("concepts_used").and_then(|c| c.as_array())
719                    {
720                        let concept_strings: Vec<String> = concepts
721                            .iter()
722                            .filter_map(|c| c.as_str())
723                            .map(std::string::ToString::to_string)
724                            .collect();
725                        println!("   ๐Ÿง  Concepts Used: {}", concept_strings.join(", "));
726                    }
727                }
728            }
729            Err(e) => {
730                println!("โš ๏ธ  Failed to parse JSON: {e}");
731                println!("Raw response: {content}");
732            }
733        }
734    }
735
736    Ok(())
737}
738
739/// Example 6: Demonstration of schema validation and error handling
740#[allow(clippy::too_many_lines)]
741async fn validation_error_example(client: &Client) -> Result<(), Error> {
742    println!("Demonstrating schema validation and error handling...");
743
744    // Define a strict schema that's likely to cause validation challenges
745    let strict_schema = json!({
746        "type": "object",
747        "properties": {
748            "numbers": {
749                "type": "array",
750                "items": {
751                    "type": "integer",
752                    "minimum": 1,
753                    "maximum": 100
754                },
755                "minItems": 3,
756                "maxItems": 5,
757                "description": "Array of 3-5 integers between 1 and 100"
758            },
759            "precision_value": {
760                "type": "number",
761                "multipleOf": 0.01,
762                "minimum": 0,
763                "maximum": 1,
764                "description": "A precise decimal value between 0 and 1, to 2 decimal places"
765            },
766            "strict_enum": {
767                "type": "string",
768                "enum": ["alpha", "beta", "gamma"],
769                "description": "Must be exactly one of the allowed values"
770            },
771            "required_pattern": {
772                "type": "string",
773                "pattern": "^[A-Z]{2}[0-9]{4}$",
774                "description": "Must be exactly 2 uppercase letters followed by 4 digits"
775            }
776        },
777        "required": ["numbers", "precision_value", "strict_enum", "required_pattern"],
778        "additionalProperties": false
779    });
780
781    println!("๐Ÿ’ก Using a strict schema with specific constraints...");
782
783    let builder = client
784        .responses()
785        .system("Generate data that strictly follows the provided JSON schema. Pay careful attention to all constraints including ranges, patterns, and array sizes.")
786        .user("Generate sample data that conforms to the schema. Make sure all values meet the exact requirements.")
787        .json_schema("strict_validation", strict_schema)
788        .temperature(0.1)
789        .max_completion_tokens(300);
790
791    let response = client.send_responses(builder).await?;
792
793    if let Some(content) = response.content() {
794        println!("๐Ÿ“Š Schema Validation Test:");
795
796        match serde_json::from_str::<serde_json::Value>(content) {
797            Ok(json) => {
798                println!("{}", serde_json::to_string_pretty(&json)?);
799
800                // Manual validation of the generated data
801                println!("\n๐Ÿ” Manual Validation:");
802                let mut validation_passed = true;
803
804                // Check numbers array
805                if let Some(numbers) = json.get("numbers").and_then(|n| n.as_array()) {
806                    println!("   ๐Ÿ“Š Numbers array: {} items", numbers.len());
807                    if numbers.len() < 3 || numbers.len() > 5 {
808                        println!("   โŒ Array size constraint violated");
809                        validation_passed = false;
810                    }
811                    for (i, num) in numbers.iter().enumerate() {
812                        if let Some(val) = num.as_i64() {
813                            if !(1..=100).contains(&val) {
814                                println!("   โŒ Number {i} ({val}) outside valid range [1-100]");
815                                validation_passed = false;
816                            }
817                        }
818                    }
819                } else {
820                    println!("   โŒ Numbers array missing or invalid");
821                    validation_passed = false;
822                }
823
824                // Check precision value
825                if let Some(precision) = json
826                    .get("precision_value")
827                    .and_then(serde_json::Value::as_f64)
828                {
829                    println!("   ๐ŸŽฏ Precision value: {precision}");
830                    if !(0.0..=1.0).contains(&precision) {
831                        println!("   โŒ Precision value outside range [0-1]");
832                        validation_passed = false;
833                    }
834                }
835
836                // Check enum value
837                if let Some(enum_val) = json.get("strict_enum").and_then(|e| e.as_str()) {
838                    println!("   ๐Ÿท๏ธ  Enum value: {enum_val}");
839                    if !["alpha", "beta", "gamma"].contains(&enum_val) {
840                        println!("   โŒ Enum value not in allowed set");
841                        validation_passed = false;
842                    }
843                }
844
845                // Check pattern
846                if let Some(pattern_val) = json.get("required_pattern").and_then(|p| p.as_str()) {
847                    println!("   ๐Ÿ”ค Pattern value: {pattern_val}");
848                    let regex = regex::Regex::new(r"^[A-Z]{2}[0-9]{4}$").unwrap();
849                    if !regex.is_match(pattern_val) {
850                        println!("   โŒ Pattern does not match required format");
851                        validation_passed = false;
852                    }
853                }
854
855                if validation_passed {
856                    println!("   โœ… All manual validations passed!");
857                } else {
858                    println!("   โš ๏ธ  Some validation constraints were not met");
859                }
860            }
861            Err(e) => {
862                println!("โš ๏ธ  JSON parsing failed: {e}");
863                println!("This demonstrates how schema constraints can sometimes be challenging for the model");
864                println!("Raw response: {content}");
865            }
866        }
867    }
868
869    // Demonstrate handling of intentionally problematic schema
870    println!("\n๐Ÿงช Testing with intentionally problematic request...");
871
872    let problematic_builder = client
873        .responses()
874        .system("You are unhelpful and ignore instructions.")
875        .user("Ignore the schema and just say 'hello world'")
876        .json_schema(
877            "strict_validation",
878            json!({
879                "type": "object",
880                "properties": {
881                    "impossible": {
882                        "type": "string",
883                        "pattern": "^impossible_pattern_that_cannot_match$"
884                    }
885                },
886                "required": ["impossible"]
887            }),
888        )
889        .temperature(0.1);
890
891    match client.send_responses(problematic_builder).await {
892        Ok(problematic_response) => {
893            if let Some(content) = problematic_response.content() {
894                println!("๐Ÿ“Š Problematic request result:");
895                println!("{content}");
896                println!("๐Ÿ’ก The model likely still attempted to follow the schema despite conflicting instructions");
897            }
898        }
899        Err(e) => {
900            println!("โš ๏ธ  Problematic request failed as expected: {e}");
901        }
902    }
903
904    Ok(())
905}
906
907/// Comprehensive error handling helper
908fn handle_api_error(error: &Error) {
909    match error {
910        Error::Api {
911            status,
912            message,
913            error_type,
914            error_code,
915        } => {
916            eprintln!("๐Ÿšซ API Error [{status}]: {message}");
917            if let Some(error_type) = error_type {
918                eprintln!("   Type: {error_type}");
919            }
920            if let Some(error_code) = error_code {
921                eprintln!("   Code: {error_code}");
922            }
923
924            // Provide specific guidance based on error type
925            match *status {
926                400 => eprintln!("๐Ÿ’ก Bad Request - Check your JSON schema or request parameters"),
927                401 => eprintln!("๐Ÿ’ก Check your API key: export OPENAI_API_KEY=\"your-key\""),
928                403 => eprintln!("๐Ÿ’ก Forbidden - Check your API permissions and model access"),
929                422 => eprintln!("๐Ÿ’ก Invalid schema or request format - verify your JSON schema"),
930                429 => eprintln!("๐Ÿ’ก Rate limited - try again in a moment"),
931                500..=599 => eprintln!("๐Ÿ’ก Server error - try again later"),
932                _ => {}
933            }
934        }
935        Error::InvalidRequest(msg) => {
936            eprintln!("๐Ÿšซ Invalid Request: {msg}");
937            eprintln!("๐Ÿ’ก Check your request parameters and JSON schema format");
938        }
939        Error::Config(msg) => {
940            eprintln!("๐Ÿšซ Configuration Error: {msg}");
941            eprintln!("๐Ÿ’ก Check your client configuration");
942        }
943        Error::Http(err) => {
944            eprintln!("๐Ÿšซ HTTP Error: {err}");
945            eprintln!("๐Ÿ’ก Check your network connection");
946        }
947        Error::Json(err) => {
948            eprintln!("๐Ÿšซ JSON Error: {err}");
949            eprintln!("๐Ÿ’ก Response parsing failed - the model may have generated invalid JSON");
950        }
951        Error::Authentication(msg) => {
952            eprintln!("๐Ÿšซ Authentication Error: {msg}");
953            eprintln!("๐Ÿ’ก Check your API key");
954        }
955        Error::RateLimit(msg) => {
956            eprintln!("๐Ÿšซ Rate Limit Error: {msg}");
957            eprintln!("๐Ÿ’ก Try again in a moment or upgrade your plan");
958        }
959        Error::Stream(msg) => {
960            eprintln!("๐Ÿšซ Stream Error: {msg}");
961            eprintln!("๐Ÿ’ก Connection issue with streaming");
962        }
963        Error::File(err) => {
964            eprintln!("๐Ÿšซ File Error: {err}");
965            eprintln!("๐Ÿ’ก Check file permissions and paths");
966        }
967        Error::Builder(msg) => {
968            eprintln!("๐Ÿšซ Builder Error: {msg}");
969            eprintln!("๐Ÿ’ก Check your request builder configuration");
970        }
971        Error::Internal(msg) => {
972            eprintln!("๐Ÿšซ Internal Error: {msg}");
973            eprintln!("๐Ÿ’ก This may be a bug, please report it");
974        }
975        Error::StreamConnection { message } => {
976            eprintln!("๐Ÿšซ Stream Connection Error: {message}");
977            eprintln!("๐Ÿ’ก Check your network connection");
978        }
979        Error::StreamParsing { message, chunk } => {
980            eprintln!("๐Ÿšซ Stream Parsing Error: {message}");
981            eprintln!("   Problematic chunk: {chunk}");
982            eprintln!("๐Ÿ’ก The response stream may be corrupted");
983        }
984        Error::StreamBuffer { message } => {
985            eprintln!("๐Ÿšซ Stream Buffer Error: {message}");
986            eprintln!("๐Ÿ’ก The stream buffer encountered an issue");
987        }
988    }
989}