openrouter-rust 0.1.0

A modular, type-safe Rust client for the OpenRouter API
Documentation
use crate::{
    client::OpenRouterClient,
    error::{OpenRouterError, Result},
};
use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GenerationResponse {
    pub data: GenerationData,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GenerationData {
    pub id: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub upstream_id: Option<String>,
    pub total_cost: f64,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub cache_discount: Option<f64>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub upstream_inference_cost: Option<f64>,
    pub created_at: String,
    pub model: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub app_id: Option<f64>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub streamed: Option<bool>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub cancelled: Option<bool>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub provider_name: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub latency: Option<f64>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub moderation_latency: Option<f64>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub generation_time: Option<f64>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub finish_reason: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub tokens_prompt: Option<f64>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub tokens_completion: Option<f64>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub native_tokens_prompt: Option<f64>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub native_tokens_completion: Option<f64>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub native_tokens_completion_images: Option<f64>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub native_tokens_reasoning: Option<f64>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub native_tokens_cached: Option<f64>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub num_media_prompt: Option<f64>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub num_input_audio_prompt: Option<f64>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub num_media_completion: Option<f64>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub num_search_results: Option<f64>,
    pub origin: String,
    pub usage: f64,
    pub is_byok: bool,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub native_finish_reason: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub external_user: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub api_type: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub router: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub provider_responses: Option<Vec<ProviderResponse>>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ProviderResponse {
    pub id: String,
    pub endpoint_id: String,
    pub model_permaslug: String,
    pub provider_name: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub status: Option<f64>,
    pub latency: f64,
    pub is_byok: bool,
}

impl OpenRouterClient {
    pub async fn get_generation(&self, generation_id: impl Into<String>) -> Result<GenerationResponse> {
        let url = format!("{}/generation?id={}", self.base_url, generation_id.into());
        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::<GenerationResponse>()
            .await
            .map_err(OpenRouterError::HttpError)?;

        Ok(result)
    }
}