Skip to main content

vtcode_llm/
http_client.rs

1//! Centralized HTTP client factory for LLM providers.
2//!
3//! Provides consistent timeout configuration and client creation
4//! across all providers based on the application's TimeoutsConfig.
5
6use reqwest::Client;
7use std::time::Duration;
8
9use vtcode_config::TimeoutsConfig;
10
11/// Factory for creating pre-configured HTTP clients.
12pub struct HttpClientFactory;
13
14impl HttpClientFactory {
15    /// Create an HTTP client for standard LLM API requests.
16    ///
17    /// Uses the `default_ceiling_seconds` from TimeoutsConfig for the request timeout.
18    /// Includes a 30-second connect timeout.
19    pub fn for_llm(config: &TimeoutsConfig) -> Client {
20        vtcode_commons::http::create_client_with_timeouts(Duration::from_secs(30), Self::llm_timeout(config))
21    }
22
23    pub(crate) fn for_llm_without_redirects(config: &TimeoutsConfig) -> Result<Client, reqwest::Error> {
24        vtcode_commons::http::create_client_without_redirects(Duration::from_secs(30), Self::llm_timeout(config))
25    }
26
27    fn llm_timeout(config: &TimeoutsConfig) -> Duration {
28        let timeout_secs = if config.default_ceiling_seconds > 0 {
29            config.default_ceiling_seconds
30        } else {
31            180 // Fallback if disabled
32        };
33
34        Duration::from_secs(timeout_secs)
35    }
36
37    /// Create an HTTP client optimized for streaming requests.
38    ///
39    /// Uses the `streaming_ceiling_seconds` from TimeoutsConfig.
40    /// Streaming requests typically take longer as they wait for incremental output.
41    pub fn for_streaming(config: &TimeoutsConfig) -> Client {
42        let timeout_secs = if config.streaming_ceiling_seconds > 0 {
43            config.streaming_ceiling_seconds
44        } else {
45            600 // 10 minutes fallback for streaming
46        };
47
48        Client::builder()
49            .timeout(Duration::from_secs(timeout_secs))
50            .connect_timeout(Duration::from_secs(30))
51            .pool_idle_timeout(Some(Duration::from_secs(90)))
52            .pool_max_idle_per_host(1)
53            .build()
54            .unwrap_or_else(|_| Client::new())
55    }
56
57    /// Create an HTTP client with explicit timeout values.
58    ///
59    /// Use this when providers need custom timeout handling.
60    pub(crate) fn with_timeouts(request_timeout: Duration, connect_timeout: Duration) -> Client {
61        vtcode_commons::http::create_client_with_timeouts(connect_timeout, request_timeout)
62    }
63
64    /// Create a default HTTP client with reasonable defaults.
65    ///
66    /// Uses 180s request timeout and 30s connect timeout.
67    pub fn default_client() -> Client {
68        vtcode_commons::http::create_client_with_timeouts(Duration::from_secs(30), Duration::from_secs(180))
69    }
70}
71
72#[cfg(test)]
73mod tests {
74    use super::*;
75
76    #[test]
77    fn default_client_is_created() {
78        let client = HttpClientFactory::default_client();
79        // Just verify it doesn't panic
80        drop(client);
81    }
82
83    #[test]
84    fn for_llm_uses_config_timeout() {
85        let config = TimeoutsConfig::default();
86        let client = HttpClientFactory::for_llm(&config);
87        drop(client);
88    }
89
90    #[test]
91    fn for_streaming_uses_longer_timeout() {
92        let config = TimeoutsConfig::default();
93        let client = HttpClientFactory::for_streaming(&config);
94        drop(client);
95    }
96}