Skip to main content

zeph_llm/router/
config.rs

1// SPDX-FileCopyrightText: 2026 Andrei G <bug-ops>
2// SPDX-License-Identifier: MIT OR Apache-2.0
3
4//! Routing strategy selector and per-strategy configuration structs for
5//! [`RouterProvider`](super::RouterProvider).
6//!
7//! These types form the public configuration surface of the router and are
8//! re-exported from [`crate::router`] to preserve their original import paths.
9
10use std::sync::Arc;
11
12use super::cascade::ClassifierMode;
13
14/// Routing strategy used by [`RouterProvider`](super::RouterProvider).
15#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
16#[non_exhaustive]
17pub enum RouterStrategy {
18    /// Exponential moving average-based latency-aware ordering.
19    #[default]
20    Ema,
21    /// Thompson Sampling with Beta distributions.
22    Thompson,
23    /// Cascade: try cheapest provider first, escalate on degenerate output.
24    Cascade,
25    /// PILOT: `LinUCB` contextual bandit with online learning and budget-aware selection.
26    Bandit,
27}
28
29/// Configuration for PILOT bandit routing in `RouterProvider`.
30///
31/// See [`bandit`](super::bandit) module for the algorithm details and trade-offs.
32#[derive(Debug, Clone)]
33#[allow(clippy::doc_markdown)] // PILOT, LinUCB, Thompson are proper nouns/acronyms
34pub struct BanditRouterConfig {
35    /// `LinUCB` exploration parameter. Higher = more exploration. Default: 1.0.
36    pub alpha: f32,
37    /// Feature vector dimension (first `dim` components of embedding). Default: 32.
38    pub dim: usize,
39    /// Cost penalty weight in the reward signal: `reward = quality - cost_weight * cost_fraction`.
40    /// Default: 0.1. Increase to penalise expensive providers more aggressively.
41    pub cost_weight: f32,
42    /// Session-level decay factor: values < 1.0 cause re-exploration over time. Default: 1.0.
43    pub decay_factor: f32,
44    /// Minimum total updates before `LinUCB` takes over from Thompson fallback.
45    /// Default: `10 * num_providers` (computed at construction time from provider count).
46    pub warmup_queries: u64,
47    /// Hard timeout for the embedding call (milliseconds). If exceeded, falls back
48    /// to Thompson/uniform selection. Default: 50.
49    pub embedding_timeout_ms: u64,
50    /// Maximum number of cached embeddings (keyed by query string hash). Default: 512.
51    pub cache_size: usize,
52    /// MAR threshold: when `memory_hit_confidence >= this`, bias toward cheap providers.
53    /// Default: 0.9. Set to 1.0 to disable MAR.
54    pub memory_confidence_threshold: f32,
55}
56
57impl Default for BanditRouterConfig {
58    fn default() -> Self {
59        Self {
60            alpha: 1.0,
61            dim: 32,
62            cost_weight: 0.1,
63            decay_factor: 1.0,
64            warmup_queries: 0, // overridden by with_bandit() based on provider count
65            embedding_timeout_ms: 50,
66            cache_size: 512,
67            memory_confidence_threshold: 0.9,
68        }
69    }
70}
71
72/// Runtime ASI configuration passed to [`RouterProvider::with_asi`](super::RouterProvider::with_asi).
73///
74/// Mirrors `AsiRouterConfig` but lives in `zeph-llm` to avoid
75/// a dependency on `zeph-config`. The bootstrap layer maps config → this struct.
76#[derive(Debug, Clone)]
77pub struct AsiRouterConfig {
78    /// Sliding window size. Default: 5.
79    pub window: usize,
80    /// Coherence score threshold below which the provider is penalized. Default: 0.7.
81    pub coherence_threshold: f32,
82    /// Penalty weight added to Thompson beta on low coherence. Default: 0.3.
83    pub penalty_weight: f32,
84}
85
86impl Default for AsiRouterConfig {
87    fn default() -> Self {
88        Self {
89            window: 5,
90            coherence_threshold: 0.7,
91            penalty_weight: 0.3,
92        }
93    }
94}
95
96/// Configuration for cascade routing in `RouterProvider`.
97#[derive(Debug, Clone)]
98pub struct CascadeRouterConfig {
99    pub quality_threshold: f64,
100    pub max_escalations: u8,
101    pub classifier_mode: ClassifierMode,
102    pub window_size: usize,
103    pub max_cascade_tokens: Option<u32>,
104    /// LLM provider used for judge-mode quality scoring.
105    /// Required when `classifier_mode = Judge`; falls back to heuristic if `None`.
106    pub summary_provider: Option<Arc<dyn crate::provider_dyn::LlmProviderDyn>>,
107    /// Explicit cost ordering of provider names (cheapest first).
108    /// When set, providers are sorted by their position in this list at construction time.
109    /// Providers not listed are appended after listed ones in original chain order.
110    pub cost_tiers: Option<Vec<String>>,
111    /// Hard timeout for the judge LLM call (milliseconds). Default: 5000.
112    pub judge_timeout_ms: u64,
113}
114
115impl Default for CascadeRouterConfig {
116    fn default() -> Self {
117        Self {
118            quality_threshold: 0.5,
119            max_escalations: 2,
120            classifier_mode: ClassifierMode::Heuristic,
121            window_size: 50,
122            max_cascade_tokens: None,
123            summary_provider: None,
124            cost_tiers: None,
125            judge_timeout_ms: 5_000,
126        }
127    }
128}