http_middleware_retry/
http_middleware_retry.rs

1//! HTTP middleware with retry support using reqwest-middleware.
2//!
3//! This example demonstrates how to use `reqwest-middleware` with `reqwest-retry`
4//! to add automatic retry capabilities with exponential backoff to the `OpenAI` client.
5//!
6//! The middleware approach allows you to:
7//! - Automatically retry transient errors (network failures, timeouts, 5xx errors)
8//! - Configure exponential backoff strategies
9//! - Add custom middleware for logging, metrics, or other cross-cutting concerns
10//! - Compose multiple middleware layers
11//!
12//! Run with: `cargo run --example http_middleware_retry`
13
14use openai_ergonomic::{Client, Config, Response, Result};
15use reqwest_middleware::ClientBuilder;
16use reqwest_retry::{policies::ExponentialBackoff, RetryTransientMiddleware};
17
18#[tokio::main]
19async fn main() -> Result<()> {
20    println!("=== HTTP Middleware with Retry Example ===\n");
21
22    // Example 1: Basic client with retry middleware
23    println!("1. Creating client with retry middleware");
24
25    // Create a retry policy with exponential backoff
26    // This will retry transient errors up to 3 times with exponential delays
27    let retry_policy = ExponentialBackoff::builder().build_with_max_retries(3);
28
29    // Build an HTTP client with retry middleware
30    let http_client = ClientBuilder::new(reqwest::Client::new())
31        .with(RetryTransientMiddleware::new_with_policy(retry_policy))
32        .build();
33
34    // Create OpenAI client with custom HTTP client
35    let config = Config::builder()
36        .api_key(
37            std::env::var("OPENAI_API_KEY")
38                .expect("OPENAI_API_KEY environment variable must be set"),
39        )
40        .http_client(http_client)
41        .build();
42
43    let client = Client::builder(config)?.build();
44
45    // Use the client normally - retries are handled automatically
46    println!("Sending chat completion request (retries are automatic)...");
47
48    let builder = client.chat_simple("Hello! How are you today?");
49    match client.send_chat(builder).await {
50        Ok(response) => {
51            println!("\nSuccess! Response received:");
52            if let Some(content) = response.content() {
53                println!("{content}");
54            }
55        }
56        Err(e) => {
57            eprintln!("\nError after retries: {e}");
58        }
59    }
60
61    // Example 2: Custom retry policy with more retries and custom delays
62    println!("\n2. Creating client with custom retry policy");
63
64    let custom_retry_policy = ExponentialBackoff::builder()
65        .retry_bounds(
66            std::time::Duration::from_millis(100), // minimum delay
67            std::time::Duration::from_secs(30),    // maximum delay
68        )
69        .build_with_max_retries(5); // up to 5 retries
70
71    let custom_http_client = ClientBuilder::new(
72        reqwest::Client::builder()
73            .timeout(std::time::Duration::from_secs(60))
74            .build()
75            .expect("Failed to build reqwest client"),
76    )
77    .with(RetryTransientMiddleware::new_with_policy(
78        custom_retry_policy,
79    ))
80    .build();
81
82    let custom_config = Config::builder()
83        .api_key(
84            std::env::var("OPENAI_API_KEY")
85                .expect("OPENAI_API_KEY environment variable must be set"),
86        )
87        .http_client(custom_http_client)
88        .build();
89
90    let custom_client = Client::builder(custom_config)?.build();
91
92    println!("Sending request with custom retry policy (up to 5 retries)...");
93
94    let builder = custom_client.chat_simple("Explain quantum computing in one sentence.");
95    match custom_client.send_chat(builder).await {
96        Ok(response) => {
97            println!("\nSuccess! Response received:");
98            if let Some(content) = response.content() {
99                println!("{content}");
100            }
101        }
102        Err(e) => {
103            eprintln!("\nError after all retries: {e}");
104        }
105    }
106
107    // Example 3: Using the builder pattern for more complex requests
108    println!("\n3. Using builder pattern with retry middleware");
109
110    let builder = custom_client
111        .responses()
112        .user("What are the three laws of robotics?")
113        .max_completion_tokens(200)
114        .temperature(0.7);
115
116    let response = custom_client.send_responses(builder).await?;
117
118    println!("\nResponse received:");
119    if let Some(content) = response.content() {
120        println!("{content}");
121    }
122
123    println!("\nToken usage:");
124    if let Some(usage) = response.usage() {
125        let prompt = usage.prompt_tokens;
126        let completion = usage.completion_tokens;
127        let total = usage.total_tokens;
128        println!("  Prompt tokens: {prompt}");
129        println!("  Completion tokens: {completion}");
130        println!("  Total tokens: {total}");
131    }
132
133    println!("\n=== Example completed successfully! ===");
134    println!("\nKey benefits of using reqwest-middleware:");
135    println!("  - Automatic retry of transient failures");
136    println!("  - Exponential backoff to avoid overwhelming servers");
137    println!("  - Composable middleware for logging, metrics, etc.");
138    println!("  - Transparent to application code - works with any request");
139
140    Ok(())
141}