use serde::{Deserialize, Serialize};
use zeph_common::ProviderName;
use super::default_true;
fn default_cascade_quality_threshold() -> f64 {
0.5
}
fn default_cascade_max_escalations() -> u8 {
2
}
fn default_cascade_window_size() -> usize {
50
}
fn default_cascade_judge_timeout_ms() -> u64 {
5_000
}
fn default_reputation_decay_factor() -> f64 {
0.95
}
fn default_reputation_weight() -> f64 {
0.3
}
fn default_reputation_min_observations() -> u64 {
5
}
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Deserialize, Serialize)]
#[serde(rename_all = "lowercase")]
pub enum RouterStrategyConfig {
#[default]
Ema,
Thompson,
Cascade,
Bandit,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct AsiConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default = "default_asi_window")]
pub window: usize,
#[serde(default = "default_asi_coherence_threshold")]
pub coherence_threshold: f32,
#[serde(default = "default_asi_penalty_weight")]
pub penalty_weight: f32,
}
fn default_asi_window() -> usize {
5
}
fn default_asi_coherence_threshold() -> f32 {
0.7
}
fn default_asi_penalty_weight() -> f32 {
0.3
}
impl Default for AsiConfig {
fn default() -> Self {
Self {
enabled: false,
window: default_asi_window(),
coherence_threshold: default_asi_coherence_threshold(),
penalty_weight: default_asi_penalty_weight(),
}
}
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct RouterConfig {
#[serde(default)]
pub strategy: RouterStrategyConfig,
#[serde(default)]
pub thompson_state_path: Option<String>,
#[serde(default)]
pub cascade: Option<CascadeConfig>,
#[serde(default)]
pub reputation: Option<ReputationConfig>,
#[serde(default)]
pub bandit: Option<BanditConfig>,
#[serde(default)]
pub quality_gate: Option<f32>,
#[serde(default)]
pub asi: Option<AsiConfig>,
#[serde(default = "default_embed_concurrency")]
pub embed_concurrency: usize,
}
fn default_embed_concurrency() -> usize {
4
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct ReputationConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default = "default_reputation_decay_factor")]
pub decay_factor: f64,
#[serde(default = "default_reputation_weight")]
pub weight: f64,
#[serde(default = "default_reputation_min_observations")]
pub min_observations: u64,
#[serde(default)]
pub state_path: Option<String>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct CascadeConfig {
#[serde(default = "default_cascade_quality_threshold")]
pub quality_threshold: f64,
#[serde(default = "default_cascade_max_escalations")]
pub max_escalations: u8,
#[serde(default)]
pub classifier_mode: CascadeClassifierMode,
#[serde(default = "default_cascade_window_size")]
pub window_size: usize,
#[serde(default)]
pub max_cascade_tokens: Option<u32>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub cost_tiers: Option<Vec<String>>,
#[serde(default = "default_cascade_judge_timeout_ms")]
pub judge_timeout_ms: u64,
}
impl Default for CascadeConfig {
fn default() -> Self {
Self {
quality_threshold: default_cascade_quality_threshold(),
max_escalations: default_cascade_max_escalations(),
classifier_mode: CascadeClassifierMode::default(),
window_size: default_cascade_window_size(),
max_cascade_tokens: None,
cost_tiers: None,
judge_timeout_ms: default_cascade_judge_timeout_ms(),
}
}
}
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Deserialize, Serialize)]
#[serde(rename_all = "lowercase")]
pub enum CascadeClassifierMode {
#[default]
Heuristic,
Judge,
}
fn default_bandit_alpha() -> f32 {
1.0
}
fn default_bandit_dim() -> usize {
32
}
fn default_bandit_cost_weight() -> f32 {
0.1
}
fn default_bandit_decay_factor() -> f32 {
1.0
}
fn default_bandit_embedding_timeout_ms() -> u64 {
50
}
fn default_bandit_cache_size() -> usize {
512
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct BanditConfig {
#[serde(default = "default_bandit_alpha")]
pub alpha: f32,
#[serde(default = "default_bandit_dim")]
pub dim: usize,
#[serde(default = "default_bandit_cost_weight")]
pub cost_weight: f32,
#[serde(default = "default_bandit_decay_factor")]
pub decay_factor: f32,
#[serde(default)]
pub embedding_provider: ProviderName,
#[serde(default = "default_bandit_embedding_timeout_ms")]
pub embedding_timeout_ms: u64,
#[serde(default = "default_bandit_cache_size")]
pub cache_size: usize,
#[serde(default)]
pub state_path: Option<String>,
#[serde(default = "default_bandit_memory_confidence_threshold")]
pub memory_confidence_threshold: f32,
#[serde(default)]
pub warmup_queries: Option<u64>,
}
fn default_bandit_memory_confidence_threshold() -> f32 {
0.9
}
impl Default for BanditConfig {
fn default() -> Self {
Self {
alpha: default_bandit_alpha(),
dim: default_bandit_dim(),
cost_weight: default_bandit_cost_weight(),
decay_factor: default_bandit_decay_factor(),
embedding_provider: ProviderName::default(),
embedding_timeout_ms: default_bandit_embedding_timeout_ms(),
cache_size: default_bandit_cache_size(),
state_path: None,
memory_confidence_threshold: default_bandit_memory_confidence_threshold(),
warmup_queries: None,
}
}
}
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Deserialize, Serialize)]
#[serde(rename_all = "lowercase")]
pub enum LlmRoutingStrategy {
#[default]
None,
Ema,
Thompson,
Cascade,
Triage,
Bandit,
}
fn default_triage_timeout_secs() -> u64 {
5
}
fn default_max_triage_tokens() -> u32 {
50
}
#[derive(Debug, Clone, Default, Deserialize, Serialize)]
pub struct TierMapping {
pub simple: Option<String>,
pub medium: Option<String>,
pub complex: Option<String>,
pub expert: Option<String>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct ComplexityRoutingConfig {
#[serde(default)]
pub triage_provider: Option<ProviderName>,
#[serde(default = "default_true")]
pub bypass_single_provider: bool,
#[serde(default)]
pub tiers: TierMapping,
#[serde(default = "default_max_triage_tokens")]
pub max_triage_tokens: u32,
#[serde(default = "default_triage_timeout_secs")]
pub triage_timeout_secs: u64,
#[serde(default)]
pub fallback_strategy: Option<String>,
}
impl Default for ComplexityRoutingConfig {
fn default() -> Self {
Self {
triage_provider: None,
bypass_single_provider: true,
tiers: TierMapping::default(),
max_triage_tokens: default_max_triage_tokens(),
triage_timeout_secs: default_triage_timeout_secs(),
fallback_strategy: None,
}
}
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(default)]
pub struct CoeConfig {
pub enabled: bool,
pub intra_threshold: f64,
pub inter_threshold: f64,
pub shadow_sample_rate: f64,
pub secondary_provider: ProviderName,
pub embedding_provider: ProviderName,
}
impl Default for CoeConfig {
fn default() -> Self {
Self {
enabled: false,
intra_threshold: 0.8,
inter_threshold: 0.20,
shadow_sample_rate: 0.1,
secondary_provider: ProviderName::default(),
embedding_provider: ProviderName::default(),
}
}
}