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}