use anyhow::{Result, bail};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct Provider {
pub name: String,
pub id: String,
#[serde(rename = "type")]
pub provider_type: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub api_key: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub api_endpoint: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub default_large_model_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub default_small_model_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub default_headers: Option<HashMap<String, String>>,
#[serde(default)]
pub models: Vec<Model>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct Model {
pub id: String,
pub name: String,
pub cost_per_1m_in: f64,
pub cost_per_1m_out: f64,
#[serde(skip_serializing_if = "Option::is_none")]
pub cost_per_1m_in_cached: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub cost_per_1m_out_cached: Option<f64>,
pub context_window: u64,
pub default_max_tokens: u64,
#[serde(default)]
pub can_reason: bool,
#[serde(default)]
pub has_reasoning_efforts: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub default_reasoning_effort: Option<String>,
#[serde(default)]
pub supports_attachments: bool,
}
#[derive(Debug, Clone)]
pub struct RegistryClient {
base_url: String,
http: reqwest::Client,
}
impl RegistryClient {
pub fn new(base_url: impl Into<String>) -> Self {
Self {
base_url: base_url.into(),
http: reqwest::Client::new(),
}
}
fn url(&self, path: &str) -> String {
format!("{}/{}", self.base_url.trim_end_matches('/'), path)
}
pub async fn get_providers(&self) -> Result<Vec<Provider>> {
let response = self.http.get(self.url("providers")).send().await?;
let status = response.status();
if !status.is_success() {
bail!("Failed to get providers: HTTP {status}");
}
Ok(response.json().await?)
}
pub async fn health_check(&self) -> Result<bool> {
match self.http.get(self.url("health")).send().await {
Ok(response) => Ok(response.status().is_success()),
Err(_) => Ok(false),
}
}
}