use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash)]
#[serde(rename_all = "lowercase")]
pub enum ProviderKind {
Openai,
Anthropic,
}
impl ProviderKind {
pub fn as_str(&self) -> &'static str {
match self {
ProviderKind::Openai => "openai",
ProviderKind::Anthropic => "anthropic",
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UpstreamConfig {
pub id: String,
#[serde(default)]
pub name: Option<String>,
pub kind: ProviderKind,
pub base_url: String,
pub api_key: String,
#[serde(default)]
pub priority: i32,
#[serde(default)]
pub models: Vec<String>,
#[serde(default)]
pub weight: u32,
#[serde(default = "default_timeout_ms")]
pub timeout_ms: u64,
#[serde(default)]
pub max_concurrency: u32,
#[serde(default)]
pub rate_limit_rpm: u32,
#[serde(default)]
pub rate_limit_tpm: u32,
#[serde(default = "default_true")]
pub enabled: bool,
#[serde(default)]
pub max_budget: Option<f64>,
#[serde(default)]
pub budget_duration: Option<String>,
#[serde(default)]
pub model_info: BTreeMap<String, ModelCost>,
#[serde(default)]
pub region: Option<String>,
#[serde(default)]
pub tags: Vec<String>,
}
fn default_timeout_ms() -> u64 {
60_000
}
fn default_true() -> bool {
true
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ModelCost {
#[serde(default)]
pub input_cost_per_token: Option<f64>,
#[serde(default)]
pub output_cost_per_token: Option<f64>,
#[serde(default)]
pub max_input_tokens: Option<u32>,
#[serde(default)]
pub max_output_tokens: Option<u32>,
#[serde(default)]
pub cache_read_input_token_cost: Option<f64>,
#[serde(default)]
pub cache_creation_input_token_cost: Option<f64>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct ServerConfig {
#[serde(default = "default_bind")]
pub bind: String,
#[serde(default)]
pub admin_token: Option<String>,
#[serde(default)]
pub global_max_parallel_requests: Option<u32>,
#[serde(default)]
pub max_request_size_mb: Option<u32>,
#[serde(default)]
pub usage_retention_days: u32,
}
fn default_bind() -> String {
"0.0.0.0:8080".to_string()
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ModelAliasEntry {
pub upstream_id: String,
pub upstream_model: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ModelListEntry {
pub model_name: String,
pub upstream_id: String,
#[serde(default)]
pub upstream_model: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ApiKeyConfig {
#[serde(default)]
pub key: Option<String>,
#[serde(default)]
pub key_alias: Option<String>,
#[serde(default = "default_role_api")]
pub role: String,
#[serde(default)]
pub models: Vec<String>,
#[serde(default)]
pub allowed_providers: Vec<ProviderKind>,
#[serde(default)]
pub rpm_limit: u32,
#[serde(default)]
pub tpm_limit: u32,
#[serde(default)]
pub max_parallel_requests: u32,
#[serde(default)]
pub max_budget: Option<f64>,
#[serde(default)]
pub budget_duration: Option<String>,
#[serde(default)]
pub expires: Option<String>,
#[serde(default)]
pub soft_budget: Option<f64>,
#[serde(default)]
pub allowed_model_region: Option<String>,
#[serde(default)]
pub blocked: bool,
}
fn default_role_api() -> String {
"api".to_string()
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Config {
#[serde(default)]
pub server: ServerConfig,
#[serde(default)]
pub api_keys: Vec<ApiKeyConfig>,
#[serde(default)]
pub api_keys_legacy: Vec<String>,
#[serde(default)]
pub upstreams: Vec<UpstreamConfig>,
#[serde(default)]
pub model_list: Vec<ModelListEntry>,
#[serde(default)]
pub model_aliases: BTreeMap<String, Vec<ModelAliasEntry>>,
#[serde(default)]
pub queue: QueueConfig,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct QueueConfig {
#[serde(default)]
pub max_queue_per_provider: usize,
#[serde(default = "default_queue_wait_ms")]
pub queue_wait_timeout_ms: u64,
#[serde(default = "default_healthcheck_ms")]
pub healthcheck_interval_ms: u64,
#[serde(default = "default_healthcheck_timeout_ms")]
pub healthcheck_timeout_ms: u64,
#[serde(default = "default_healthcheck_threshold")]
pub healthcheck_failure_threshold: u32,
}
impl Default for QueueConfig {
fn default() -> Self {
Self {
max_queue_per_provider: 0,
queue_wait_timeout_ms: default_queue_wait_ms(),
healthcheck_interval_ms: default_healthcheck_ms(),
healthcheck_timeout_ms: default_healthcheck_timeout_ms(),
healthcheck_failure_threshold: default_healthcheck_threshold(),
}
}
}
fn default_queue_wait_ms() -> u64 {
30_000
}
fn default_healthcheck_ms() -> u64 {
15_000
}
fn default_healthcheck_timeout_ms() -> u64 {
5_000
}
fn default_healthcheck_threshold() -> u32 {
3
}
impl Default for Config {
fn default() -> Self {
Self {
server: ServerConfig {
bind: default_bind(),
admin_token: None,
global_max_parallel_requests: None,
max_request_size_mb: None,
usage_retention_days: 0,
},
api_keys: Vec::new(),
api_keys_legacy: Vec::new(),
upstreams: Vec::new(),
model_list: Vec::new(),
model_aliases: BTreeMap::new(),
queue: QueueConfig::default(),
}
}
}