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)
}
}