use crate::{
client::OpenRouterClient,
error::{OpenRouterError, Result},
};
use serde::{Deserialize, Serialize};
use serde_json::Value;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Model {
pub id: String,
pub canonical_slug: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub hugging_face_id: Option<String>,
pub name: String,
pub created: f64,
pub description: String,
pub pricing: PublicPricing,
#[serde(skip_serializing_if = "Option::is_none")]
pub context_length: Option<f64>,
pub architecture: ModelArchitecture,
pub top_provider: TopProviderInfo,
pub per_request_limits: PerRequestLimits,
pub supported_parameters: Vec<String>,
pub default_parameters: DefaultParameters,
#[serde(skip_serializing_if = "Option::is_none")]
pub expiration_date: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PublicPricing {
pub prompt: Value,
pub completion: Value,
#[serde(skip_serializing_if = "Option::is_none")]
pub request: Option<Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub image: Option<Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub image_token: Option<Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub image_output: Option<Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub audio: Option<Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub audio_output: Option<Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub input_audio_cache: Option<Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub web_search: Option<Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub internal_reasoning: Option<Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub input_cache_read: Option<Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub input_cache_write: Option<Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub discount: Option<f64>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ModelArchitecture {
#[serde(skip_serializing_if = "Option::is_none")]
pub tokenizer: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub instruct_type: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub modality: Option<String>,
pub input_modalities: Vec<String>,
pub output_modalities: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TopProviderInfo {
#[serde(skip_serializing_if = "Option::is_none")]
pub context_length: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub max_completion_tokens: Option<f64>,
pub is_moderated: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PerRequestLimits {
pub prompt_tokens: f64,
pub completion_tokens: f64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DefaultParameters {
#[serde(skip_serializing_if = "Option::is_none")]
pub temperature: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub top_p: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub frequency_penalty: Option<f64>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ModelsResponse {
pub data: Vec<Model>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ModelsCountResponse {
pub data: ModelsCountData,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ModelsCountData {
pub count: f64,
}
#[derive(Debug, Clone, Default)]
pub struct ListModelsParams {
pub category: Option<String>,
pub supported_parameters: Option<String>,
pub use_rss: Option<String>,
pub use_rss_chat_links: Option<String>,
}
impl OpenRouterClient {
pub async fn list_models(&self, params: Option<ListModelsParams>) -> Result<ModelsResponse> {
let mut url = format!("{}/models", self.base_url);
if let Some(p) = params {
let mut query_parts = Vec::new();
if let Some(cat) = p.category {
query_parts.push(format!("category={}", cat));
}
if let Some(sup) = p.supported_parameters {
query_parts.push(format!("supported_parameters={}", sup));
}
if let Some(rss) = p.use_rss {
query_parts.push(format!("use_rss={}", rss));
}
if let Some(links) = p.use_rss_chat_links {
query_parts.push(format!("use_rss_chat_links={}", links));
}
if !query_parts.is_empty() {
url = format!("{}?{}", url, query_parts.join("&"));
}
}
let headers = self.build_headers()?;
let response = self
.client
.get(&url)
.headers(headers)
.send()
.await
.map_err(OpenRouterError::HttpError)?;
let status = response.status();
if !status.is_success() {
let error_text = response.text().await.unwrap_or_default();
return Err(OpenRouterError::ApiError {
code: status.as_u16(),
message: error_text,
});
}
let result = response
.json::<ModelsResponse>()
.await
.map_err(OpenRouterError::HttpError)?;
Ok(result)
}
pub async fn get_models_count(&self) -> Result<ModelsCountResponse> {
let url = format!("{}/models/count", self.base_url);
let headers = self.build_headers()?;
let response = self
.client
.get(&url)
.headers(headers)
.send()
.await
.map_err(OpenRouterError::HttpError)?;
let status = response.status();
if !status.is_success() {
let error_text = response.text().await.unwrap_or_default();
return Err(OpenRouterError::ApiError {
code: status.as_u16(),
message: error_text,
});
}
let result = response
.json::<ModelsCountResponse>()
.await
.map_err(OpenRouterError::HttpError)?;
Ok(result)
}
pub async fn list_models_user(&self) -> Result<ModelsResponse> {
let url = format!("{}/models/user", self.base_url);
let headers = self.build_headers()?;
let response = self
.client
.get(&url)
.headers(headers)
.send()
.await
.map_err(OpenRouterError::HttpError)?;
let status = response.status();
if !status.is_success() {
let error_text = response.text().await.unwrap_or_default();
return Err(OpenRouterError::ApiError {
code: status.as_u16(),
message: error_text,
});
}
let result = response
.json::<ModelsResponse>()
.await
.map_err(OpenRouterError::HttpError)?;
Ok(result)
}
}