use std::borrow::Cow;
use aither_core::llm::model::Ability;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum ModelMode {
Chat,
Embedding,
ImageGeneration,
AudioTranscription,
AudioSpeech,
Rerank,
Moderation,
Completion,
VideoGeneration,
Search,
Ocr,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum Provider {
OpenAI,
Anthropic,
Google,
DeepSeek,
XAI,
Mistral,
Meta,
Alibaba,
Bedrock,
Azure,
VertexAI,
Groq,
Together,
Fireworks,
Replicate,
Cohere,
Perplexity,
Copilot,
Other(Cow<'static, str>),
}
impl Provider {
#[must_use]
pub fn as_str(&self) -> &str {
match self {
Self::OpenAI => "openai",
Self::Anthropic => "anthropic",
Self::Google => "gemini",
Self::DeepSeek => "deepseek",
Self::XAI => "xai",
Self::Mistral => "mistral",
Self::Meta => "meta",
Self::Alibaba => "alibaba",
Self::Bedrock => "bedrock",
Self::Azure => "azure",
Self::VertexAI => "vertex_ai",
Self::Groq => "groq",
Self::Together => "together_ai",
Self::Fireworks => "fireworks_ai",
Self::Replicate => "replicate",
Self::Cohere => "cohere",
Self::Perplexity => "perplexity",
Self::Copilot => "github_copilot",
Self::Other(s) => s,
}
}
}
impl core::fmt::Display for Provider {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_str(self.as_str())
}
}
#[allow(clippy::struct_field_names)]
#[derive(Debug, Clone, PartialEq, Default)]
pub struct Pricing {
input_per_token: f64,
output_per_token: f64,
cache_read_per_token: Option<f64>,
cache_write_per_token: Option<f64>,
reasoning_per_token: Option<f64>,
image_per_token: Option<f64>,
}
impl Pricing {
#[must_use]
pub const fn new(input_per_token: f64, output_per_token: f64) -> Self {
Self {
input_per_token,
output_per_token,
cache_read_per_token: None,
cache_write_per_token: None,
reasoning_per_token: None,
image_per_token: None,
}
}
#[must_use]
pub const fn input_per_token(&self) -> f64 {
self.input_per_token
}
#[must_use]
pub const fn output_per_token(&self) -> f64 {
self.output_per_token
}
#[must_use]
pub const fn cache_read_per_token(&self) -> Option<f64> {
self.cache_read_per_token
}
#[must_use]
pub const fn cache_write_per_token(&self) -> Option<f64> {
self.cache_write_per_token
}
#[must_use]
pub const fn reasoning_per_token(&self) -> Option<f64> {
self.reasoning_per_token
}
#[must_use]
pub const fn image_per_token(&self) -> Option<f64> {
self.image_per_token
}
#[must_use]
pub const fn with_cache_read(mut self, cost: f64) -> Self {
self.cache_read_per_token = Some(cost);
self
}
#[must_use]
pub const fn with_cache_write(mut self, cost: f64) -> Self {
self.cache_write_per_token = Some(cost);
self
}
#[must_use]
pub const fn with_reasoning(mut self, cost: f64) -> Self {
self.reasoning_per_token = Some(cost);
self
}
#[must_use]
pub const fn with_image(mut self, cost: f64) -> Self {
self.image_per_token = Some(cost);
self
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct ModelEntry {
litellm_id: Cow<'static, str>,
canonical_id: Cow<'static, str>,
provider: Provider,
mode: ModelMode,
max_input_tokens: Option<u32>,
max_output_tokens: Option<u32>,
pricing: Pricing,
abilities: Cow<'static, [Ability]>,
deprecation_date: Option<Cow<'static, str>>,
}
impl ModelEntry {
#[must_use]
pub fn new(
litellm_id: String,
canonical_id: String,
provider: Provider,
mode: ModelMode,
) -> Self {
Self {
litellm_id: Cow::Owned(litellm_id),
canonical_id: Cow::Owned(canonical_id),
provider,
mode,
max_input_tokens: None,
max_output_tokens: None,
pricing: Pricing::default(),
abilities: Cow::Borrowed(&[]),
deprecation_date: None,
}
}
#[must_use]
#[doc(hidden)]
#[allow(clippy::too_many_arguments)]
pub const fn new_static(
litellm_id: &'static str,
canonical_id: &'static str,
provider: Provider,
mode: ModelMode,
max_input_tokens: Option<u32>,
max_output_tokens: Option<u32>,
pricing: Pricing,
abilities: &'static [Ability],
deprecation_date: Option<&'static str>,
) -> Self {
Self {
litellm_id: Cow::Borrowed(litellm_id),
canonical_id: Cow::Borrowed(canonical_id),
provider,
mode,
max_input_tokens,
max_output_tokens,
pricing,
abilities: Cow::Borrowed(abilities),
deprecation_date: match deprecation_date {
Some(s) => Some(Cow::Borrowed(s)),
None => None,
},
}
}
#[must_use]
pub fn id(&self) -> &str {
&self.canonical_id
}
#[must_use]
pub fn litellm_id(&self) -> &str {
&self.litellm_id
}
#[must_use]
pub const fn provider(&self) -> &Provider {
&self.provider
}
#[must_use]
pub const fn mode(&self) -> ModelMode {
self.mode
}
#[must_use]
pub const fn max_input_tokens(&self) -> Option<u32> {
self.max_input_tokens
}
#[must_use]
pub const fn max_output_tokens(&self) -> Option<u32> {
self.max_output_tokens
}
#[must_use]
pub const fn pricing(&self) -> &Pricing {
&self.pricing
}
#[must_use]
pub fn abilities(&self) -> &[Ability] {
&self.abilities
}
#[must_use]
pub fn has_ability(&self, ability: Ability) -> bool {
self.abilities.contains(&ability)
}
#[must_use]
pub fn deprecation_date(&self) -> Option<&str> {
self.deprecation_date.as_deref()
}
#[must_use]
pub fn is_deprecated(&self) -> bool {
self.deprecation_date.as_deref().is_some_and(|date| {
let now = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_secs();
let days = now / 86400;
let (year, month, day) = days_to_ymd(days);
let today = format!("{year:04}-{month:02}-{day:02}");
date <= today.as_str()
})
}
#[must_use]
pub const fn with_max_input_tokens(mut self, tokens: u32) -> Self {
self.max_input_tokens = Some(tokens);
self
}
#[must_use]
pub const fn with_max_output_tokens(mut self, tokens: u32) -> Self {
self.max_output_tokens = Some(tokens);
self
}
#[must_use]
pub const fn with_pricing(mut self, pricing: Pricing) -> Self {
self.pricing = pricing;
self
}
#[must_use]
pub fn with_abilities(mut self, abilities: Vec<Ability>) -> Self {
self.abilities = Cow::Owned(abilities);
self
}
#[must_use]
pub fn with_deprecation_date(mut self, date: String) -> Self {
self.deprecation_date = Some(Cow::Owned(date));
self
}
}
const fn days_to_ymd(days: u64) -> (u64, u64, u64) {
let z = days + 719_468;
let era = z / 146_097;
let doe = z - era * 146_097;
let yoe = (doe - doe / 1460 + doe / 36524 - doe / 146_096) / 365;
let y = yoe + era * 400;
let doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
let mp = (5 * doy + 2) / 153;
let d = doy - (153 * mp + 2) / 5 + 1;
let m = if mp < 10 { mp + 3 } else { mp - 9 };
let y = if m <= 2 { y + 1 } else { y };
(y, m, d)
}