use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct ListModelsResponse {
pub data: Vec<Model>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct Model {
pub id: String,
pub name: String,
pub created: i64,
pub input_modalities: Vec<InputModality>,
pub output_modalities: Vec<OutputModality>,
pub quantization: Quantization,
pub context_length: u64,
pub max_output_length: u64,
pub pricing: Pricing,
pub supported_sampling_parameters: Vec<SamplingParameter>,
pub supported_features: Vec<Feature>,
#[serde(skip_serializing_if = "Option::is_none")]
pub openrouter: Option<OpenRouterInfo>,
#[serde(skip_serializing_if = "Option::is_none")]
pub hugging_face_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub datacenters: Option<Vec<Datacenter>>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct OpenRouterInfo {
pub slug: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct Pricing {
pub prompt: String,
pub completion: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub image: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub request: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub input_cache_read: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub input_cache_write: Option<String>,
}
impl Pricing {
pub fn new(prompt: impl Into<String>, completion: impl Into<String>) -> Self {
Self {
prompt: prompt.into(),
completion: completion.into(),
image: None,
request: None,
input_cache_read: None,
input_cache_write: None,
}
}
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum InputModality {
Text,
File,
Image,
Audio,
Video,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum OutputModality {
Text,
Image,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum Quantization {
Int4,
Int8,
Fp4,
Fp6,
Fp8,
Fp16,
Bf16,
Fp32,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum SamplingParameter {
Temperature,
TopP,
TopK,
RepetitionPenalty,
FrequencyPenalty,
PresencePenalty,
Stop,
Seed,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum Feature {
Tools,
JsonMode,
StructuredOutputs,
WebSearch,
Reasoning,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct Datacenter {
pub country_code: String,
}
impl Datacenter {
pub fn new(country_code: impl Into<String>) -> Self {
Self {
country_code: country_code.into(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_round_trip() {
let original = ListModelsResponse {
data: vec![Model {
id: "org/model".to_string(),
name: "Model".to_string(),
created: 1700000000,
input_modalities: vec![InputModality::Text, InputModality::Image],
output_modalities: vec![OutputModality::Text],
quantization: Quantization::Fp16,
context_length: 128000,
max_output_length: 4096,
pricing: Pricing::new("0.001", "0.002"),
supported_sampling_parameters: vec![SamplingParameter::Temperature],
supported_features: vec![Feature::Tools],
openrouter: None,
hugging_face_id: None,
description: Some("Test".to_string()),
datacenters: Some(vec![Datacenter::new("US")]),
}],
};
let json = serde_json::to_string(&original).unwrap();
let parsed: ListModelsResponse = serde_json::from_str(&json).unwrap();
assert_eq!(original, parsed);
}
#[test]
fn test_quantization_serialization() {
assert_eq!(
serde_json::to_string(&Quantization::Int4).unwrap(),
"\"int4\""
);
assert_eq!(
serde_json::to_string(&Quantization::Bf16).unwrap(),
"\"bf16\""
);
assert_eq!(
serde_json::to_string(&Quantization::Fp32).unwrap(),
"\"fp32\""
);
}
#[test]
fn test_feature_serialization() {
assert_eq!(
serde_json::to_string(&Feature::JsonMode).unwrap(),
"\"json_mode\""
);
assert_eq!(
serde_json::to_string(&Feature::StructuredOutputs).unwrap(),
"\"structured_outputs\""
);
}
#[test]
fn test_sampling_parameter_serialization() {
assert_eq!(
serde_json::to_string(&SamplingParameter::TopP).unwrap(),
"\"top_p\""
);
assert_eq!(
serde_json::to_string(&SamplingParameter::RepetitionPenalty).unwrap(),
"\"repetition_penalty\""
);
}
}