1#![allow(clippy::uninlined_format_args)]
2use 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 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 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 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 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 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 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
117async fn basic_responses_example(client: &Client) -> Result<(), Error> {
119 println!("Creating a basic response with system context...");
120
121 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 if let Some(content) = response.content() {
133 println!(" Assistant: {content}");
134 } else {
135 println!(" No content in response");
136 }
137
138 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
158async fn function_calling_example(client: &Client) -> Result<(), Error> {
160 println!("Setting up function calling with custom tools...");
161
162 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 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 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 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 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
246async fn web_search_example(client: &Client) -> Result<(), Error> {
248 println!("Demonstrating web search tool integration...");
249
250 let web_search_tool = tool_web_search();
252
253 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 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 }
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
292async fn structured_output_example(client: &Client) -> Result<(), Error> {
294 println!("Demonstrating structured JSON outputs...");
295
296 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 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 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 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
386async fn advanced_configuration_example(client: &Client) -> Result<(), Error> {
388 println!("Demonstrating advanced response configuration...");
389
390 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) .max_completion_tokens(50)
397 .n(1) .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) .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 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); 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 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
442fn 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 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}