From 4e70abf407aa66f635365c3dcb36c41eba107521 Mon Sep 17 00:00:00 2001 From: argenis de la rosa Date: Sat, 28 Feb 2026 23:45:42 -0500 Subject: [PATCH] fix(cost): validate route_down hint against model routes --- src/config/schema.rs | 62 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/src/config/schema.rs b/src/config/schema.rs index b00538eaa..c99c31779 100644 --- a/src/config/schema.rs +++ b/src/config/schema.rs @@ -7829,6 +7829,36 @@ impl Config { if self.cost.enforcement.reserve_percent > 100 { anyhow::bail!("cost.enforcement.reserve_percent must be between 0 and 100"); } + if matches!(self.cost.enforcement.mode, CostEnforcementMode::RouteDown) { + let route_down_model = self + .cost + .enforcement + .route_down_model + .as_deref() + .map(str::trim) + .filter(|model| !model.is_empty()) + .ok_or_else(|| { + anyhow::anyhow!( + "cost.enforcement.route_down_model must be set when mode is route_down" + ) + })?; + + if let Some(route_hint) = route_down_model + .strip_prefix("hint:") + .map(str::trim) + .filter(|hint| !hint.is_empty()) + { + if !self + .model_routes + .iter() + .any(|route| route.hint.trim() == route_hint) + { + anyhow::bail!( + "cost.enforcement.route_down_model uses hint '{route_hint}', but no matching [[model_routes]] entry exists" + ); + } + } + } // Scheduler if self.scheduler.max_concurrent == 0 { @@ -13848,4 +13878,36 @@ reserve_percent = 15 .expect_err("expected cost.enforcement.reserve_percent validation failure"); assert!(err.to_string().contains("cost.enforcement.reserve_percent")); } + + #[test] + async fn validation_rejects_route_down_hint_without_matching_route() { + let mut config = Config::default(); + config.cost.enforcement.mode = CostEnforcementMode::RouteDown; + config.cost.enforcement.route_down_model = Some("hint:fast".to_string()); + let err = config + .validate() + .expect_err("route_down hint should require a matching model route"); + assert!(err + .to_string() + .contains("cost.enforcement.route_down_model uses hint 'fast'")); + } + + #[test] + async fn validation_accepts_route_down_hint_with_matching_route() { + let mut config = Config::default(); + config.cost.enforcement.mode = CostEnforcementMode::RouteDown; + config.cost.enforcement.route_down_model = Some("hint:fast".to_string()); + config.model_routes = vec![ModelRouteConfig { + hint: "fast".to_string(), + provider: "openrouter".to_string(), + model: "openai/gpt-4.1-mini".to_string(), + api_key: None, + max_tokens: None, + transport: None, + }]; + + config + .validate() + .expect("matching route_down hint route should validate"); + } }