diff --git a/crates/goose-server/src/routes/config_management.rs b/crates/goose-server/src/routes/config_management.rs index 0f65ef7aa873..828caea0ffd7 100644 --- a/crates/goose-server/src/routes/config_management.rs +++ b/crates/goose-server/src/routes/config_management.rs @@ -501,10 +501,16 @@ pub async fn resolve_provider_model_info( ))); } - let model_config = goose::model_config::model_config_from_user_config(name, model)?; + let entry = goose::providers::get_from_registry(name).await?; + let model_config = entry.normalize_model_config(ModelConfig::new(model))?; let provider = goose::providers::create(name, Vec::new()).await?; match provider.fetch_model_info(model).await { - Ok(info) => Ok(info), + Ok(mut info) => { + if let Some(limit) = model_config.context_limit { + info.context_limit = limit; + } + Ok(info) + } Err(error) => { let mut info = ModelInfo::new(model, model_config.context_limit()); info.reasoning = model_config.is_reasoning_model(); diff --git a/crates/goose/src/providers/init.rs b/crates/goose/src/providers/init.rs index 41079ba8360f..19cd1f1f09b8 100644 --- a/crates/goose/src/providers/init.rs +++ b/crates/goose/src/providers/init.rs @@ -269,6 +269,7 @@ pub async fn create_with_named_model( mod tests { use super::*; use crate::config::paths::Paths; + use goose_providers::model::ModelConfig; use std::fs; #[tokio::test] @@ -407,4 +408,60 @@ mod tests { std::env::remove_var("GOOSE_PATH_ROOT"); } + + #[tokio::test] + async fn test_goose_context_limit_overrides_known_models_and_defaults() { + let _guard = env_lock::lock_env([ + ("GOOSE_PATH_ROOT", None::<&str>), + ("GOOSE_CONTEXT_LIMIT", Some("1000000")), + ("GOOSE_MAX_TOKENS", None::<&str>), + ("GOOSE_TEMPERATURE", None::<&str>), + ("GOOSE_TOOLSHIM", None::<&str>), + ("GOOSE_TOOLSHIM_OLLAMA_MODEL", None::<&str>), + ("GOOSE_THINKING_EFFORT", None::<&str>), + ]); + + let openai = get_from_registry("openai") + .await + .expect("openai provider should be registered"); + let unknown = openai + .normalize_model_config(ModelConfig::new("totally-unknown-model")) + .expect("unknown model config should normalize"); + assert_eq!(unknown.context_limit(), 1_000_000); + + let temp_dir = tempfile::tempdir().expect("tempdir should be created"); + std::env::set_var("GOOSE_PATH_ROOT", temp_dir.path()); + + let custom_dir = Paths::config_dir().join("custom_providers"); + fs::create_dir_all(&custom_dir).expect("custom providers dir should be created"); + + let custom_inf = r#"{ + "name": "custom_inf", + "engine": "openai", + "display_name": "Custom Inf", + "description": "test provider", + "api_key_env": "", + "base_url": "https://example.invalid/v1/chat/completions", + "models": [ + {"name": "kimi-k2.5", "context_limit": 256000} + ], + "requires_auth": false +}"#; + fs::write(custom_dir.join("custom_inf.json"), custom_inf) + .expect("custom_inf.json should be written"); + + refresh_custom_providers() + .await + .expect("custom providers should refresh"); + + let inf_entry = get_from_registry("custom_inf") + .await + .expect("custom_inf entry should exist"); + let inf_config = inf_entry + .normalize_model_config(ModelConfig::new("kimi-k2.5")) + .expect("custom_inf model config should normalize"); + assert_eq!(inf_config.context_limit(), 1_000_000); + + std::env::remove_var("GOOSE_PATH_ROOT"); + } }