responses_comprehensive/
responses_comprehensive.rs

1#![allow(clippy::uninlined_format_args)]
2//! Comprehensive Responses API example demonstrating modern `OpenAI` usage patterns.
3//!
4//! This example showcases the Responses API, which is `OpenAI`'s recommended modern interface
5//! for chat completions, function calling, web search, and structured outputs.
6//!
7//! ## Features Demonstrated
8//!
9//! - Basic chat completions using the Responses API
10//! - Function calling with custom tools
11//! - Web search integration (if supported)
12//! - Structured JSON outputs with schemas
13//! - Comprehensive error handling patterns
14//! - Multiple message types (system, user, assistant)
15//! - Different model configurations
16//!
17//! ## Prerequisites
18//!
19//! Set your `OpenAI` API key:
20//! ```bash
21//! export OPENAI_API_KEY="your-key-here"
22//! ```
23//!
24//! ## Usage
25//!
26//! ```bash
27//! cargo run --example responses_comprehensive
28//! ```
29
30use openai_ergonomic::{
31    responses::{tool_function, tool_web_search, Response, ToolChoiceHelper},
32    Client, Error, ToolCallExt,
33};
34use serde_json::json;
35
36#[tokio::main]
37async fn main() -> Result<(), Box<dyn std::error::Error>> {
38    println!(" OpenAI Ergonomic - Comprehensive Responses API Example\n");
39
40    // Initialize client from environment variables
41    let client = match Client::from_env() {
42        Ok(client_builder) => {
43            println!(" Client initialized successfully");
44            client_builder.build()
45        }
46        Err(e) => {
47            eprintln!(" Failed to initialize client: {e}");
48            eprintln!(" Make sure OPENAI_API_KEY is set in your environment");
49            return Err(e.into());
50        }
51    };
52
53    // Example 1: Basic Responses API Usage
54    println!("\n Example 1: Basic Responses API Usage");
55    println!("=====================================");
56
57    match basic_responses_example(&client).await {
58        Ok(()) => println!(" Basic responses example completed"),
59        Err(e) => {
60            eprintln!(" Basic responses example failed: {e}");
61            handle_api_error(&e);
62        }
63    }
64
65    // Example 2: Function Calling
66    println!("\n Example 2: Function Calling");
67    println!("===============================");
68
69    match function_calling_example(&client).await {
70        Ok(()) => println!(" Function calling example completed"),
71        Err(e) => {
72            eprintln!(" Function calling example failed: {e}");
73            handle_api_error(&e);
74        }
75    }
76
77    // Example 3: Web Search Integration
78    println!("\n Example 3: Web Search Integration");
79    println!("====================================");
80
81    match web_search_example(&client).await {
82        Ok(()) => println!(" Web search example completed"),
83        Err(e) => {
84            eprintln!(" Web search example failed: {e}");
85            handle_api_error(&e);
86        }
87    }
88
89    // Example 4: Structured Outputs
90    println!("\n Example 4: Structured JSON Outputs");
91    println!("======================================");
92
93    match structured_output_example(&client).await {
94        Ok(()) => println!(" Structured output example completed"),
95        Err(e) => {
96            eprintln!(" Structured output example failed: {e}");
97            handle_api_error(&e);
98        }
99    }
100
101    // Example 5: Advanced Configuration
102    println!("\n  Example 5: Advanced Configuration");
103    println!("====================================");
104
105    match advanced_configuration_example(&client).await {
106        Ok(()) => println!(" Advanced configuration example completed"),
107        Err(e) => {
108            eprintln!(" Advanced configuration example failed: {e}");
109            handle_api_error(&e);
110        }
111    }
112
113    println!("\n All examples completed! Check the console output above for results.");
114    Ok(())
115}
116
117/// Example 1: Basic Responses API usage with system and user messages
118async fn basic_responses_example(client: &Client) -> Result<(), Error> {
119    println!("Creating a basic response with system context...");
120
121    // Build a simple request with system and user messages
122    let builder = client
123        .responses()
124        .system("You are a helpful assistant who provides concise, accurate answers.")
125        .user("What is the capital of France?")
126        .temperature(0.7)
127        .max_completion_tokens(100);
128
129    let response = client.send_responses(builder).await?;
130
131    // Extract and display the response
132    if let Some(content) = response.content() {
133        println!(" Assistant: {content}");
134    } else {
135        println!("  No content in response");
136    }
137
138    // Show response metadata
139    println!(" Response metadata:");
140    println!("   - Model: {}", response.model().unwrap_or("unknown"));
141    println!(
142        "   - Finish reason: {}",
143        response
144            .finish_reason()
145            .unwrap_or_else(|| "unknown".to_string())
146    );
147
148    if let Some(usage) = response.usage() {
149        println!(
150            "   - Tokens used: {} prompt + {} completion = {} total",
151            usage.prompt_tokens, usage.completion_tokens, usage.total_tokens
152        );
153    }
154
155    Ok(())
156}
157
158/// Example 2: Function calling with custom tools
159async fn function_calling_example(client: &Client) -> Result<(), Error> {
160    println!("Setting up function calling with custom tools...");
161
162    // Define a weather function tool
163    let weather_tool = tool_function(
164        "get_weather",
165        "Get the current weather information for a specific location",
166        json!({
167            "type": "object",
168            "properties": {
169                "location": {
170                    "type": "string",
171                    "description": "The city name, e.g., 'San Francisco, CA'"
172                },
173                "unit": {
174                    "type": "string",
175                    "enum": ["celsius", "fahrenheit"],
176                    "description": "Temperature unit preference"
177                }
178            },
179            "required": ["location"],
180            "additionalProperties": false
181        }),
182    );
183
184    // Define a time function tool
185    let time_tool = tool_function(
186        "get_current_time",
187        "Get the current time in a specific timezone",
188        json!({
189            "type": "object",
190            "properties": {
191                "timezone": {
192                    "type": "string",
193                    "description": "Timezone name, e.g., 'America/New_York'"
194                }
195            },
196            "required": ["timezone"],
197            "additionalProperties": false
198        }),
199    );
200
201    // Make a request that should trigger function calling
202    let builder = client
203        .responses()
204        .system("You are a helpful assistant with access to weather and time information. Use the provided tools when users ask about weather or time.")
205        .user("What's the weather like in London and what time is it there?")
206        .tool(weather_tool)
207        .tool(time_tool)
208        .tool_choice(ToolChoiceHelper::auto())
209        .temperature(0.3);
210
211    let response = client.send_responses(builder).await?;
212
213    // Check if the model wants to call functions
214    let tool_calls = response.tool_calls();
215    if !tool_calls.is_empty() {
216        println!(" Model requested {} tool call(s):", tool_calls.len());
217
218        for (i, tool_call) in tool_calls.iter().enumerate() {
219            println!("   {}. Function: {}", i + 1, tool_call.function_name());
220            println!("      Arguments: {}", tool_call.function_arguments());
221
222            // In a real application, you would:
223            // 1. Parse the arguments
224            // 2. Execute the actual function
225            // 3. Send the results back to the model
226            println!("      [Simulated] Executing function call...");
227            match tool_call.function_name() {
228                "get_weather" => {
229                    println!("      [Simulated] Weather: 22°C, partly cloudy");
230                }
231                "get_current_time" => {
232                    println!("      [Simulated] Time: 14:30 GMT");
233                }
234                _ => {
235                    println!("      [Simulated] Unknown function");
236                }
237            }
238        }
239    } else if let Some(content) = response.content() {
240        println!(" Assistant response: {content}");
241    }
242
243    Ok(())
244}
245
246/// Example 3: Web search integration
247async fn web_search_example(client: &Client) -> Result<(), Error> {
248    println!("Demonstrating web search tool integration...");
249
250    // Create a web search tool
251    let web_search_tool = tool_web_search();
252
253    // Ask a question that would benefit from current information
254    let builder = client
255        .responses()
256        .system("You are a helpful assistant with access to web search. When users ask about current events, recent information, or real-time data, use the web search tool to find accurate, up-to-date information.")
257        .user("What are the latest developments in artificial intelligence this week?")
258        .tool(web_search_tool)
259        .tool_choice(ToolChoiceHelper::auto())
260        .temperature(0.3)
261        .max_completion_tokens(200);
262
263    let response = client.send_responses(builder).await?;
264
265    // Handle the response
266    let tool_calls = response.tool_calls();
267    if !tool_calls.is_empty() {
268        println!(" Model requested web search:");
269
270        for tool_call in &tool_calls {
271            if tool_call.function_name() == "web_search" {
272                println!("   Search query: {}", tool_call.function_arguments());
273                println!("   [Simulated] Performing web search...");
274                println!("   [Simulated] Found recent AI news and developments");
275
276                // In a real implementation:
277                // 1. Parse the search query from arguments
278                // 2. Perform actual web search
279                // 3. Return results to the model
280                // 4. Get final response with search results
281            }
282        }
283    } else if let Some(content) = response.content() {
284        println!(" Assistant response: {content}");
285    }
286
287    println!(" Note: Web search requires additional implementation to execute actual searches");
288
289    Ok(())
290}
291
292/// Example 4: Structured JSON outputs with schemas
293async fn structured_output_example(client: &Client) -> Result<(), Error> {
294    println!("Demonstrating structured JSON outputs...");
295
296    // Define a schema for recipe information
297    let recipe_schema = json!({
298        "type": "object",
299        "properties": {
300            "name": {
301                "type": "string",
302                "description": "Name of the recipe"
303            },
304            "ingredients": {
305                "type": "array",
306                "items": {
307                    "type": "object",
308                    "properties": {
309                        "name": {
310                            "type": "string",
311                            "description": "Ingredient name"
312                        },
313                        "amount": {
314                            "type": "string",
315                            "description": "Amount needed"
316                        }
317                    },
318                    "required": ["name", "amount"],
319                    "additionalProperties": false
320                },
321                "description": "List of ingredients"
322            },
323            "instructions": {
324                "type": "array",
325                "items": {
326                    "type": "string"
327                },
328                "description": "Step-by-step cooking instructions"
329            },
330            "prep_time_minutes": {
331                "type": "integer",
332                "description": "Preparation time in minutes"
333            },
334            "difficulty": {
335                "type": "string",
336                "enum": ["easy", "medium", "hard"],
337                "description": "Recipe difficulty level"
338            }
339        },
340        "required": ["name", "ingredients", "instructions", "prep_time_minutes", "difficulty"],
341        "additionalProperties": false
342    });
343
344    // Request a recipe in structured JSON format
345    let builder = client
346        .responses()
347        .system("You are a cooking expert. Provide recipes in the exact JSON format specified.")
348        .user("Give me a simple recipe for chocolate chip cookies")
349        .json_schema("recipe", recipe_schema)
350        .temperature(0.5);
351
352    let response = client.send_responses(builder).await?;
353
354    if let Some(content) = response.content() {
355        println!(" Structured recipe output:");
356
357        // Try to parse and pretty-print the JSON
358        match serde_json::from_str::<serde_json::Value>(content) {
359            Ok(json) => {
360                println!("{}", serde_json::to_string_pretty(&json)?);
361            }
362            Err(_) => {
363                println!("Raw response: {content}");
364            }
365        }
366    }
367
368    // Example of simple JSON mode (without schema)
369    println!("\n Simple JSON mode example:");
370    let simple_builder = client
371        .responses()
372        .system("Respond in valid JSON format with keys: summary, key_points, sentiment")
373        .user("Analyze this text: 'The new product launch exceeded expectations with great customer feedback.'")
374        .json_mode()
375        .temperature(0.3);
376
377    let simple_response = client.send_responses(simple_builder).await?;
378
379    if let Some(content) = simple_response.content() {
380        println!(" Analysis result: {content}");
381    }
382
383    Ok(())
384}
385
386/// Example 5: Advanced configuration and parameters
387async fn advanced_configuration_example(client: &Client) -> Result<(), Error> {
388    println!("Demonstrating advanced response configuration...");
389
390    // Example with multiple completions and various parameters
391    let builder = client
392        .responses()
393        .system("You are a creative writing assistant. Write in different styles when asked.")
394        .user("Write a short tagline for a futuristic coffee shop")
395        .temperature(0.9)  // High creativity
396        .max_completion_tokens(50)
397        .n(1)  // Generate 1 completion
398        .top_p(0.9)
399        .frequency_penalty(0.1)
400        .presence_penalty(0.1)
401        .stop(vec!["\n".to_string(), ".".to_string()])
402        .seed(42)  // For reproducible results
403        .user_id("example_user_123");
404
405    let response = client.send_responses(builder).await?;
406
407    println!(" Creative tagline generation:");
408    if let Some(content) = response.content() {
409        println!("   Result: {content}");
410    }
411
412    // Example with reasoning effort (for o3 models)
413    println!("\n Example with reasoning effort (o3 models):");
414    let reasoning_builder = client
415        .responses()
416        .system("You are a logic puzzle solver. Think through problems step by step.")
417        .user("If a train leaves Station A at 2 PM going 60 mph, and another train leaves Station B at 3 PM going 80 mph, and the stations are 280 miles apart, when do they meet?")
418        .reasoning_effort("medium")
419        .temperature(0.1); // Low temperature for accuracy
420
421    let reasoning_response = client.send_responses(reasoning_builder).await?;
422
423    if let Some(content) = reasoning_response.content() {
424        println!("   Solution: {content}");
425    } else {
426        println!("   Note: Reasoning effort requires compatible model (e.g., o3)");
427    }
428
429    // Show model information
430    println!("\n Model and usage information:");
431    println!("   Model used: {}", response.model().unwrap_or("unknown"));
432    if let Some(usage) = response.usage() {
433        println!(
434            "   Token usage: {} total ({} prompt + {} completion)",
435            usage.total_tokens, usage.prompt_tokens, usage.completion_tokens
436        );
437    }
438
439    Ok(())
440}
441
442/// Comprehensive error handling helper
443fn handle_api_error(error: &Error) {
444    match error {
445        Error::Api {
446            status,
447            message,
448            error_type,
449            error_code,
450        } => {
451            eprintln!(" API Error [{status}]: {message}");
452            if let Some(error_type) = error_type {
453                eprintln!("   Type: {error_type}");
454            }
455            if let Some(error_code) = error_code {
456                eprintln!("   Code: {error_code}");
457            }
458
459            // Provide specific guidance based on error type
460            match *status {
461                401 => eprintln!(" Check your API key: export OPENAI_API_KEY=\"your-key\""),
462                429 => eprintln!(" Rate limited - try again in a moment"),
463                500..=599 => eprintln!(" Server error - try again later"),
464                _ => {}
465            }
466        }
467        Error::InvalidRequest(msg) => {
468            eprintln!(" Invalid Request: {msg}");
469            eprintln!(" Check your request parameters");
470        }
471        Error::Config(msg) => {
472            eprintln!(" Configuration Error: {msg}");
473            eprintln!(" Check your client configuration");
474        }
475        Error::Http(err) => {
476            eprintln!(" HTTP Error: {err}");
477            eprintln!(" Check your network connection");
478        }
479        Error::HttpMiddleware(err) => {
480            eprintln!(" HTTP Middleware Error: {err}");
481            eprintln!(" Check your network connection and middleware configuration");
482        }
483        Error::Json(err) => {
484            eprintln!(" JSON Error: {err}");
485            eprintln!(" Response parsing failed - may be a temporary issue");
486        }
487        Error::Authentication(msg) => {
488            eprintln!(" Authentication Error: {msg}");
489            eprintln!(" Check your API key");
490        }
491        Error::RateLimit(msg) => {
492            eprintln!(" Rate Limit Error: {msg}");
493            eprintln!(" Try again in a moment");
494        }
495        Error::Stream(msg) => {
496            eprintln!(" Stream Error: {msg}");
497            eprintln!(" Connection issue with streaming");
498        }
499        Error::File(err) => {
500            eprintln!(" File Error: {err}");
501            eprintln!(" Check file permissions and paths");
502        }
503        Error::Builder(msg) => {
504            eprintln!(" Builder Error: {msg}");
505            eprintln!(" Check your request builder configuration");
506        }
507        Error::Internal(msg) => {
508            eprintln!(" Internal Error: {msg}");
509            eprintln!(" This may be a bug, please report it");
510        }
511        Error::StreamConnection { message } => {
512            eprintln!(" Stream Connection Error: {message}");
513            eprintln!(" Check your network connection");
514        }
515        Error::StreamParsing { message, chunk } => {
516            eprintln!(" Stream Parsing Error: {message}");
517            eprintln!("   Problematic chunk: {chunk}");
518            eprintln!(" The response stream may be corrupted");
519        }
520        Error::StreamBuffer { message } => {
521            eprintln!(" Stream Buffer Error: {message}");
522            eprintln!(" The stream buffer encountered an issue");
523        }
524    }
525}