#![allow(missing_docs)]
use serde::Deserialize;
use std::time::Duration;
#[derive(Debug, Default, Deserialize)]
pub struct ProviderConfig {
#[serde(default)]
pub name: String,
#[serde(default)]
pub model: String,
#[serde(default)]
pub base_url: Option<String>,
#[serde(default)]
pub api_key: Option<String>,
pub retry: Option<RetryProviderConfig>,
#[serde(default)]
pub prompt_caching: bool,
pub cascade: Option<CascadeConfig>,
#[serde(default)]
pub circuit: ProviderCircuitConfig,
}
#[derive(Debug, Clone, Deserialize)]
pub struct CascadeConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default)]
pub tiers: Vec<CascadeTierConfig>,
#[serde(default)]
pub gate: CascadeGateConfig,
}
#[derive(Debug, Clone, Deserialize)]
pub struct CascadeTierConfig {
pub model: String,
}
#[derive(Debug, Clone, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum CascadeGateConfig {
Heuristic {
#[serde(default = "default_min_output_tokens")]
min_output_tokens: u32,
#[serde(default = "super::default_true")]
accept_tool_calls: bool,
#[serde(default = "super::default_true")]
escalate_on_max_tokens: bool,
},
}
impl Default for CascadeGateConfig {
fn default() -> Self {
Self::Heuristic {
min_output_tokens: default_min_output_tokens(),
accept_tool_calls: true,
escalate_on_max_tokens: true,
}
}
}
fn default_min_output_tokens() -> u32 {
5
}
#[derive(Debug, Clone, Default, serde::Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct ProviderCircuitConfig {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub failure_threshold: Option<u32>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub initial_open_duration_seconds: Option<u32>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub max_open_duration_seconds: Option<u32>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub backoff_multiplier: Option<f64>,
}
impl From<&ProviderCircuitConfig> for crate::llm::circuit::CircuitConfig {
fn from(c: &ProviderCircuitConfig) -> Self {
let default = crate::llm::circuit::CircuitConfig::default();
Self {
failure_threshold: c.failure_threshold.unwrap_or(default.failure_threshold),
initial_open_duration: c
.initial_open_duration_seconds
.map(|s| std::time::Duration::from_secs(u64::from(s)))
.unwrap_or(default.initial_open_duration),
max_open_duration: c
.max_open_duration_seconds
.map(|s| std::time::Duration::from_secs(u64::from(s)))
.unwrap_or(default.max_open_duration),
backoff_multiplier: c.backoff_multiplier.unwrap_or(default.backoff_multiplier),
}
}
}
#[derive(Debug, Deserialize)]
pub struct RetryProviderConfig {
#[serde(default = "default_max_retries")]
pub max_retries: u32,
#[serde(default = "default_base_delay_ms")]
pub base_delay_ms: u64,
#[serde(default = "default_max_delay_ms")]
pub max_delay_ms: u64,
}
fn default_max_retries() -> u32 {
3
}
fn default_base_delay_ms() -> u64 {
500
}
fn default_max_delay_ms() -> u64 {
30_000
}
impl From<&RetryProviderConfig> for crate::llm::retry::RetryConfig {
fn from(r: &RetryProviderConfig) -> Self {
Self {
max_retries: r.max_retries,
base_delay: Duration::from_millis(r.base_delay_ms),
max_delay: Duration::from_millis(r.max_delay_ms),
}
}
}