use reqwest::Client as HttpClient;
use serde::{Deserialize, Serialize};
use urlencoding::encode;
use crate::{
error::OpenRouterError,
transport::{request as transport_request, response as transport_response},
types::{ApiResponse, ModelCategory, SupportedParameters},
};
#[derive(Serialize, Deserialize, Debug)]
pub struct Model {
pub id: String,
pub name: String,
pub created: f64,
pub description: String,
pub context_length: f64,
pub architecture: Architecture,
pub top_provider: TopProvider,
pub pricing: Pricing,
pub per_request_limits: Option<std::collections::HashMap<String, String>>,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct Architecture {
pub modality: String,
pub tokenizer: String,
pub instruct_type: Option<String>,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct TopProvider {
pub context_length: Option<f64>,
pub max_completion_tokens: Option<f64>,
pub is_moderated: bool,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct Pricing {
pub prompt: String,
pub completion: String,
pub image: Option<String>,
pub request: Option<String>,
pub input_cache_read: Option<String>,
pub input_cache_write: Option<String>,
pub web_search: Option<String>,
pub internal_reasoning: Option<String>,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct Endpoint {
pub name: String,
pub context_length: f64,
pub pricing: EndpointPricing,
pub provider_name: String,
pub supported_parameters: Vec<String>,
pub quantization: Option<String>,
pub max_completion_tokens: Option<f64>,
pub max_prompt_tokens: Option<f64>,
pub status: Option<serde_json::Value>,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct EndpointPricing {
#[serde(skip_serializing_if = "Option::is_none")]
pub request: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub image: Option<String>,
pub prompt: String,
pub completion: String,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct EndpointData {
pub id: String,
pub name: String,
pub created: f64,
pub description: String,
pub architecture: EndpointArchitecture,
pub endpoints: Vec<Endpoint>,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct EndpointArchitecture {
pub tokenizer: Option<String>,
pub instruct_type: Option<String>,
pub modality: Option<String>,
}
pub async fn list_models(
base_url: &str,
api_key: &str,
category: Option<ModelCategory>,
supported_parameters: Option<SupportedParameters>,
) -> Result<Vec<Model>, OpenRouterError> {
let http_client = crate::transport::new_client()?;
list_models_with_client(
&http_client,
base_url,
api_key,
category,
supported_parameters,
)
.await
}
pub(crate) async fn list_models_with_client(
http_client: &HttpClient,
base_url: &str,
api_key: &str,
category: Option<ModelCategory>,
supported_parameters: Option<SupportedParameters>,
) -> Result<Vec<Model>, OpenRouterError> {
#[derive(Serialize)]
struct ListModelsQuery {
#[serde(skip_serializing_if = "Option::is_none")]
category: Option<ModelCategory>,
#[serde(skip_serializing_if = "Option::is_none")]
supported_parameters: Option<SupportedParameters>,
}
let url = format!("{base_url}/models");
let query = ListModelsQuery {
category,
supported_parameters,
};
let req =
transport_request::with_bearer_auth(transport_request::get(http_client, &url), api_key);
let response = if query.category.is_none() && query.supported_parameters.is_none() {
req.send().await?
} else {
req.query(&query).send().await?
};
if response.status().is_success() {
let model_list_response: ApiResponse<_> =
transport_response::parse_json_response(response, "model list").await?;
Ok(model_list_response.data)
} else {
transport_response::handle_error(response).await?;
unreachable!()
}
}
pub async fn list_model_endpoints(
base_url: &str,
api_key: &str,
author: &str,
slug: &str,
) -> Result<EndpointData, OpenRouterError> {
let http_client = crate::transport::new_client()?;
list_model_endpoints_with_client(&http_client, base_url, api_key, author, slug).await
}
pub(crate) async fn list_model_endpoints_with_client(
http_client: &HttpClient,
base_url: &str,
api_key: &str,
author: &str,
slug: &str,
) -> Result<EndpointData, OpenRouterError> {
let encoded_author = encode(author);
let encoded_slug = encode(slug);
let url = format!("{base_url}/models/{encoded_author}/{encoded_slug}/endpoints");
let response =
transport_request::with_bearer_auth(transport_request::get(http_client, &url), api_key)
.send()
.await?;
if response.status().is_success() {
let endpoint_list_response: ApiResponse<_> =
transport_response::parse_json_response(response, "model endpoint list").await?;
Ok(endpoint_list_response.data)
} else {
transport_response::handle_error(response).await?;
unreachable!()
}
}