From dd9057d0696016b16e6f155cc257cd4b3850d776 Mon Sep 17 00:00:00 2001 From: "ilblackdragon@gmail.com" Date: Sat, 21 Mar 2026 20:58:57 -0700 Subject: [PATCH] fix(llm): support smart routing cheap model for gemini_oauth backend Add explicit gemini_oauth handling in create_cheap_provider_for_backend() to create a GeminiOauthProvider with the cheap model swapped in. Without this, setting LLM_CHEAP_MODEL with gemini_oauth backend would fail with a confusing "no registry provider config available" error. Co-Authored-By: Claude Opus 4.6 (1M context) --- src/llm/mod.rs | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/src/llm/mod.rs b/src/llm/mod.rs index 9b8f8dd4..141cedf0 100644 --- a/src/llm/mod.rs +++ b/src/llm/mod.rs @@ -496,6 +496,19 @@ fn create_cheap_provider_for_backend( }); } + if config.backend == "gemini_oauth" { + let Some(ref gemini_config) = config.gemini_oauth else { + return Err(LlmError::RequestFailed { + provider: "gemini_oauth".to_string(), + reason: "Gemini OAuth config not available for cheap model".to_string(), + }); + }; + let mut cheap_gemini_config = gemini_config.clone(); + cheap_gemini_config.model = cheap_model.to_string(); + let provider = GeminiOauthProvider::new(cheap_gemini_config)?; + return Ok(Some(Arc::new(provider))); + } + // Registry-based provider: clone config and swap model let reg_config = config.provider.as_ref().ok_or_else(|| LlmError::RequestFailed { provider: config.backend.clone(), @@ -804,6 +817,30 @@ mod tests { ); } + #[test] + fn test_create_cheap_llm_provider_gemini_oauth_creates_provider() { + let mut config = test_llm_config(); + config.backend = "gemini_oauth".to_string(); + config.cheap_model = Some("gemini-2.5-flash-lite".to_string()); + config.gemini_oauth = Some(crate::config::GeminiOauthConfig { + model: "gemini-2.5-pro".to_string(), + credentials_path: std::path::PathBuf::from("/tmp/nonexistent-creds.json"), + }); + + let session = Arc::new(SessionManager::new(SessionConfig::default())); + let result = create_cheap_llm_provider(&config, session); + + // Should succeed and return a provider (credentials validation is deferred + // until the first LLM call, not at construction time). + let provider = result.expect("gemini_oauth cheap provider should succeed"); + assert!(provider.is_some(), "Should return Some(provider)"); + assert_eq!( + provider.unwrap().model_name(), + "gemini-2.5-flash-lite", + "Cheap provider should use the overridden model name" + ); + } + #[test] fn test_cheap_model_name_resolution() { // Generic takes priority