From abb9ea09aedf359ac6cbe282fad6392d765217ac Mon Sep 17 00:00:00 2001 From: Abhijay Jain Date: Thu, 23 Jul 2026 01:34:47 +0530 Subject: [PATCH 1/6] fix(providers): enable inventory refresh for OpenRouter model picker Register OpenRouterProvider with refresh_only inventory so the model picker fetches all models dynamically from the OpenRouter API instead of falling back to the 10-entry KNOWN_MODELS static list. Also add skip_canonical_filtering so all models returned by the OpenRouter API are surfaced without being filtered against the canonical registry (mirroring the LiteLLM fix from #10489). Fixes #10631 --- crates/goose/src/providers/init.rs | 19 ++++++++++++----- crates/goose/src/providers/openrouter.rs | 27 +++++++++++++++--------- 2 files changed, 31 insertions(+), 15 deletions(-) diff --git a/crates/goose/src/providers/init.rs b/crates/goose/src/providers/init.rs index bb19a1f9a04b..9b0ca09c569f 100644 --- a/crates/goose/src/providers/init.rs +++ b/crates/goose/src/providers/init.rs @@ -137,7 +137,15 @@ async fn init_registry() -> RwLock { true, Some(registrations::openai_inventory()), ); - registry.register::(true); + registry.register_with_inventory::( + true, + Some(registrations::refresh_only().with_configured(|| { + let config = crate::config::Config::global(); + config + .get_secret::("OPENROUTER_API_KEY") + .is_ok() + })), + ); registry.register_with_inventory::( false, Some(registrations::pi_acp_inventory()), @@ -296,10 +304,11 @@ mod tests { assert_eq!(huggingface.provider_type(), ProviderType::Preferred); assert_eq!(meta.display_name, "Hugging Face"); assert_eq!(meta.default_model, "Qwen/Qwen3-Coder-480B-A35B-Instruct"); - assert!(meta - .config_keys - .iter() - .any(|key| key.name == "HF_TOKEN" && key.secret)); + assert!( + meta.config_keys + .iter() + .any(|key| key.name == "HF_TOKEN" && key.secret) + ); } #[tokio::test] diff --git a/crates/goose/src/providers/openrouter.rs b/crates/goose/src/providers/openrouter.rs index d9d5088dafdf..340146ccc1df 100644 --- a/crates/goose/src/providers/openrouter.rs +++ b/crates/goose/src/providers/openrouter.rs @@ -1,8 +1,8 @@ -use anyhow::{bail, Result}; +use anyhow::{Result, bail}; use async_trait::async_trait; use futures::future::BoxFuture; use goose_providers::images::ImageFormat; -use serde_json::{json, Value}; +use serde_json::{Value, json}; use std::collections::HashMap; use super::api_client::{ApiClient, AuthMethod}; @@ -14,7 +14,7 @@ use crate::providers::formats::openrouter as openrouter_format; use goose_providers::errors::ProviderError; use goose_providers::formats::openai::create_request; use goose_providers::model::ModelConfig; -use goose_providers::request_log::{start_log, LoggerHandleExt}; +use goose_providers::request_log::{LoggerHandleExt, start_log}; use rmcp::model::Tool; pub const OPENROUTER_PROVIDER_NAME: &str = "openrouter"; @@ -233,6 +233,10 @@ impl Provider for OpenRouterProvider { &self.name } + fn skip_canonical_filtering(&self) -> bool { + true + } + /// Fetch supported models from OpenRouter API (only models with tool support) async fn fetch_supported_models(&self) -> Result, ProviderError> { let response = self @@ -379,10 +383,12 @@ mod tests { fn metadata_includes_openrouter_parameters_config_key() { let metadata = OpenRouterProvider::metadata(); - assert!(metadata - .config_keys - .iter() - .any(|key| key.name == OPENROUTER_PARAMETERS_CONFIG_KEY)); + assert!( + metadata + .config_keys + .iter() + .any(|key| key.name == OPENROUTER_PARAMETERS_CONFIG_KEY) + ); } #[test] @@ -412,9 +418,10 @@ mod tests { fn parse_openrouter_parameters_rejects_non_object_json_string() { let err = parse_openrouter_parameters(json!(r#"["web"]"#)).unwrap_err(); - assert!(err - .to_string() - .contains("OPENROUTER_PARAMETERS must be a JSON object")); + assert!( + err.to_string() + .contains("OPENROUTER_PARAMETERS must be a JSON object") + ); } #[test] From 03b97ae68078419fc3a243ceecb19123d33c7df6 Mon Sep 17 00:00:00 2001 From: Abhijay Jain Date: Thu, 23 Jul 2026 01:45:42 +0530 Subject: [PATCH 2/6] fix(providers): filter OpenRouter models to tool-capable only The fetch_supported_models comment always said "only models with tool support" but the filter was never implemented. Now checks supported_parameters contains "tools" per OpenRouter API docs before including a model in the inventory list. --- crates/goose/src/providers/openrouter.rs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/crates/goose/src/providers/openrouter.rs b/crates/goose/src/providers/openrouter.rs index 340146ccc1df..5187bccd1ad8 100644 --- a/crates/goose/src/providers/openrouter.rs +++ b/crates/goose/src/providers/openrouter.rs @@ -277,7 +277,17 @@ impl Provider for OpenRouterProvider { .iter() .filter_map(|model| { let id = model.get("id").and_then(|v| v.as_str())?; - Some(id.to_string()) + let supports_tools = model + .get("supported_parameters") + .and_then(|v| v.as_array()) + .map_or(false, |params| { + params.iter().any(|p| p.as_str() == Some("tools")) + }); + if supports_tools { + Some(id.to_string()) + } else { + None + } }) .collect(); From 37dd89e7865a6a72dcb5ea52fbb42cf32fa6603e Mon Sep 17 00:00:00 2001 From: Abhijay Jain Date: Thu, 23 Jul 2026 01:47:44 +0530 Subject: [PATCH 3/6] chore: apply cargo fmt to openrouter and init --- crates/goose/src/providers/init.rs | 9 ++++----- crates/goose/src/providers/openrouter.rs | 23 ++++++++++------------- 2 files changed, 14 insertions(+), 18 deletions(-) diff --git a/crates/goose/src/providers/init.rs b/crates/goose/src/providers/init.rs index 9b0ca09c569f..6105fc5626a5 100644 --- a/crates/goose/src/providers/init.rs +++ b/crates/goose/src/providers/init.rs @@ -304,11 +304,10 @@ mod tests { assert_eq!(huggingface.provider_type(), ProviderType::Preferred); assert_eq!(meta.display_name, "Hugging Face"); assert_eq!(meta.default_model, "Qwen/Qwen3-Coder-480B-A35B-Instruct"); - assert!( - meta.config_keys - .iter() - .any(|key| key.name == "HF_TOKEN" && key.secret) - ); + assert!(meta + .config_keys + .iter() + .any(|key| key.name == "HF_TOKEN" && key.secret)); } #[tokio::test] diff --git a/crates/goose/src/providers/openrouter.rs b/crates/goose/src/providers/openrouter.rs index 5187bccd1ad8..6eff8a725b44 100644 --- a/crates/goose/src/providers/openrouter.rs +++ b/crates/goose/src/providers/openrouter.rs @@ -1,8 +1,8 @@ -use anyhow::{Result, bail}; +use anyhow::{bail, Result}; use async_trait::async_trait; use futures::future::BoxFuture; use goose_providers::images::ImageFormat; -use serde_json::{Value, json}; +use serde_json::{json, Value}; use std::collections::HashMap; use super::api_client::{ApiClient, AuthMethod}; @@ -14,7 +14,7 @@ use crate::providers::formats::openrouter as openrouter_format; use goose_providers::errors::ProviderError; use goose_providers::formats::openai::create_request; use goose_providers::model::ModelConfig; -use goose_providers::request_log::{LoggerHandleExt, start_log}; +use goose_providers::request_log::{start_log, LoggerHandleExt}; use rmcp::model::Tool; pub const OPENROUTER_PROVIDER_NAME: &str = "openrouter"; @@ -393,12 +393,10 @@ mod tests { fn metadata_includes_openrouter_parameters_config_key() { let metadata = OpenRouterProvider::metadata(); - assert!( - metadata - .config_keys - .iter() - .any(|key| key.name == OPENROUTER_PARAMETERS_CONFIG_KEY) - ); + assert!(metadata + .config_keys + .iter() + .any(|key| key.name == OPENROUTER_PARAMETERS_CONFIG_KEY)); } #[test] @@ -428,10 +426,9 @@ mod tests { fn parse_openrouter_parameters_rejects_non_object_json_string() { let err = parse_openrouter_parameters(json!(r#"["web"]"#)).unwrap_err(); - assert!( - err.to_string() - .contains("OPENROUTER_PARAMETERS must be a JSON object") - ); + assert!(err + .to_string() + .contains("OPENROUTER_PARAMETERS must be a JSON object")); } #[test] From 36ae8ff745854580df049dc6704bf320cfe4014f Mon Sep 17 00:00:00 2001 From: Abhijay Jain Date: Thu, 23 Jul 2026 02:04:14 +0530 Subject: [PATCH 4/6] fix(providers): respect toolshim when filtering OpenRouter models Extract fetch_model_entries helper and move tool-support filtering into a fetch_recommended_models override that receives the toolshim flag. When toolshim is enabled all models are included; when disabled only models with supported_parameters containing tools are returned. --- crates/goose/src/providers/openrouter.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/crates/goose/src/providers/openrouter.rs b/crates/goose/src/providers/openrouter.rs index 6eff8a725b44..e6f9b9db60c9 100644 --- a/crates/goose/src/providers/openrouter.rs +++ b/crates/goose/src/providers/openrouter.rs @@ -237,8 +237,7 @@ impl Provider for OpenRouterProvider { true } - /// Fetch supported models from OpenRouter API (only models with tool support) - async fn fetch_supported_models(&self) -> Result, ProviderError> { + async fn fetch_recommended_models(&self, toolshim: bool) -> Result, ProviderError> { let response = self .api_client .request("api/v1/models") @@ -277,6 +276,9 @@ impl Provider for OpenRouterProvider { .iter() .filter_map(|model| { let id = model.get("id").and_then(|v| v.as_str())?; + if toolshim { + return Some(id.to_string()); + } let supports_tools = model .get("supported_parameters") .and_then(|v| v.as_array()) @@ -290,7 +292,6 @@ impl Provider for OpenRouterProvider { } }) .collect(); - models.sort(); Ok(models) } From 44fbeab40148f5edf7c0a9aff25ab1dfc3c3f440 Mon Sep 17 00:00:00 2001 From: Abhijay Jain Date: Thu, 23 Jul 2026 02:09:57 +0530 Subject: [PATCH 5/6] fix(providers): use is_some_and instead of map_or for clippy --- crates/goose/src/providers/openrouter.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/goose/src/providers/openrouter.rs b/crates/goose/src/providers/openrouter.rs index e6f9b9db60c9..1d54a701eb85 100644 --- a/crates/goose/src/providers/openrouter.rs +++ b/crates/goose/src/providers/openrouter.rs @@ -282,7 +282,7 @@ impl Provider for OpenRouterProvider { let supports_tools = model .get("supported_parameters") .and_then(|v| v.as_array()) - .map_or(false, |params| { + .is_some_and(|params| { params.iter().any(|p| p.as_str() == Some("tools")) }); if supports_tools { From 2784cb9bf7d52d1bbb733f7dc98f47eb777f9b27 Mon Sep 17 00:00:00 2001 From: Abhijay Jain Date: Thu, 23 Jul 2026 02:14:09 +0530 Subject: [PATCH 6/6] chore: apply cargo fmt to openrouter is_some_and --- crates/goose/src/providers/openrouter.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/crates/goose/src/providers/openrouter.rs b/crates/goose/src/providers/openrouter.rs index 1d54a701eb85..631745ccfe49 100644 --- a/crates/goose/src/providers/openrouter.rs +++ b/crates/goose/src/providers/openrouter.rs @@ -282,9 +282,7 @@ impl Provider for OpenRouterProvider { let supports_tools = model .get("supported_parameters") .and_then(|v| v.as_array()) - .is_some_and(|params| { - params.iter().any(|p| p.as_str() == Some("tools")) - }); + .is_some_and(|params| params.iter().any(|p| p.as_str() == Some("tools"))); if supports_tools { Some(id.to_string()) } else {