Skip to main content

openrouter_rust/
client.rs

1use crate::error::Result;
2use reqwest::{header, Client};
3use std::time::Duration;
4
5const DEFAULT_BASE_URL: &str = "https://openrouter.ai/api/v1";
6const DEFAULT_TIMEOUT: Duration = Duration::from_secs(60);
7
8#[derive(Clone, Debug)]
9pub struct OpenRouterClient {
10    pub(crate) client: Client,
11    pub api_key: String,
12    pub base_url: String,
13    pub http_referer: Option<String>,
14    pub x_title: Option<String>,
15}
16
17impl OpenRouterClient {
18    pub fn builder() -> OpenRouterClientBuilder {
19        OpenRouterClientBuilder::new()
20    }
21
22    pub fn build_headers(&self) -> Result<header::HeaderMap> {
23        let mut headers = header::HeaderMap::new();
24
25        let auth_value = format!("Bearer {}", self.api_key).parse().map_err(|e| {
26            crate::error::OpenRouterError::ConfigError(format!("Invalid API key: {}", e))
27        })?;
28        headers.insert(header::AUTHORIZATION, auth_value);
29
30        headers.insert(header::CONTENT_TYPE, "application/json".parse().unwrap());
31
32        if let Some(ref referer) = self.http_referer {
33            if let Ok(value) = referer.parse() {
34                headers.insert("HTTP-Referer", value);
35            }
36        }
37
38        if let Some(ref title) = self.x_title {
39            if let Ok(value) = title.parse() {
40                headers.insert("X-Title", value);
41            }
42        }
43
44        Ok(headers)
45    }
46}
47
48pub struct OpenRouterClientBuilder {
49    api_key: Option<String>,
50    base_url: Option<String>,
51    http_referer: Option<String>,
52    x_title: Option<String>,
53    timeout: Option<Duration>,
54}
55
56impl OpenRouterClientBuilder {
57    pub fn new() -> Self {
58        Self {
59            api_key: None,
60            base_url: None,
61            http_referer: None,
62            x_title: None,
63            timeout: None,
64        }
65    }
66
67    pub fn api_key(mut self, key: impl Into<String>) -> Self {
68        self.api_key = Some(key.into());
69        self
70    }
71
72    pub fn base_url(mut self, url: impl Into<String>) -> Self {
73        self.base_url = Some(url.into());
74        self
75    }
76
77    pub fn http_referer(mut self, referer: impl Into<String>) -> Self {
78        self.http_referer = Some(referer.into());
79        self
80    }
81
82    pub fn x_title(mut self, title: impl Into<String>) -> Self {
83        self.x_title = Some(title.into());
84        self
85    }
86
87    pub fn timeout(mut self, timeout: Duration) -> Self {
88        self.timeout = Some(timeout);
89        self
90    }
91
92    pub fn build(self) -> Result<OpenRouterClient> {
93        let api_key = self.api_key.ok_or_else(|| {
94            crate::error::OpenRouterError::ConfigError("API key is required".to_string())
95        })?;
96
97        let client = Client::builder()
98            .timeout(self.timeout.unwrap_or(DEFAULT_TIMEOUT))
99            .build()
100            .map_err(crate::error::OpenRouterError::HttpError)?;
101
102        Ok(OpenRouterClient {
103            client,
104            api_key,
105            base_url: self
106                .base_url
107                .unwrap_or_else(|| DEFAULT_BASE_URL.to_string()),
108            http_referer: self.http_referer,
109            x_title: self.x_title,
110        })
111    }
112}
113
114impl Default for OpenRouterClientBuilder {
115    fn default() -> Self {
116        Self::new()
117    }
118}