use crate::error::Result;
use reqwest::{header, Client};
use std::time::Duration;
const DEFAULT_BASE_URL: &str = "https://openrouter.ai/api/v1";
const DEFAULT_TIMEOUT: Duration = Duration::from_secs(60);
#[derive(Clone, Debug)]
pub struct OpenRouterClient {
pub(crate) client: Client,
pub api_key: String,
pub base_url: String,
pub http_referer: Option<String>,
pub x_title: Option<String>,
}
impl OpenRouterClient {
pub fn builder() -> OpenRouterClientBuilder {
OpenRouterClientBuilder::new()
}
pub fn build_headers(&self) -> Result<header::HeaderMap> {
let mut headers = header::HeaderMap::new();
let auth_value = format!("Bearer {}", self.api_key).parse().map_err(|e| {
crate::error::OpenRouterError::ConfigError(format!("Invalid API key: {}", e))
})?;
headers.insert(header::AUTHORIZATION, auth_value);
headers.insert(header::CONTENT_TYPE, "application/json".parse().unwrap());
if let Some(ref referer) = self.http_referer {
if let Ok(value) = referer.parse() {
headers.insert("HTTP-Referer", value);
}
}
if let Some(ref title) = self.x_title {
if let Ok(value) = title.parse() {
headers.insert("X-Title", value);
}
}
Ok(headers)
}
}
pub struct OpenRouterClientBuilder {
api_key: Option<String>,
base_url: Option<String>,
http_referer: Option<String>,
x_title: Option<String>,
timeout: Option<Duration>,
}
impl OpenRouterClientBuilder {
pub fn new() -> Self {
Self {
api_key: None,
base_url: None,
http_referer: None,
x_title: None,
timeout: None,
}
}
pub fn api_key(mut self, key: impl Into<String>) -> Self {
self.api_key = Some(key.into());
self
}
pub fn base_url(mut self, url: impl Into<String>) -> Self {
self.base_url = Some(url.into());
self
}
pub fn http_referer(mut self, referer: impl Into<String>) -> Self {
self.http_referer = Some(referer.into());
self
}
pub fn x_title(mut self, title: impl Into<String>) -> Self {
self.x_title = Some(title.into());
self
}
pub fn timeout(mut self, timeout: Duration) -> Self {
self.timeout = Some(timeout);
self
}
pub fn build(self) -> Result<OpenRouterClient> {
let api_key = self.api_key.ok_or_else(|| {
crate::error::OpenRouterError::ConfigError("API key is required".to_string())
})?;
let client = Client::builder()
.timeout(self.timeout.unwrap_or(DEFAULT_TIMEOUT))
.build()
.map_err(crate::error::OpenRouterError::HttpError)?;
Ok(OpenRouterClient {
client,
api_key,
base_url: self
.base_url
.unwrap_or_else(|| DEFAULT_BASE_URL.to_string()),
http_referer: self.http_referer,
x_title: self.x_title,
})
}
}
impl Default for OpenRouterClientBuilder {
fn default() -> Self {
Self::new()
}
}