From 0ace570e28080f0c5d54a76728245bcf82572967 Mon Sep 17 00:00:00 2001 From: Lifei Zhou Date: Sat, 21 Mar 2026 09:48:30 +1100 Subject: [PATCH] fix: use default provider and model when provider in session no longer exists (#8035) --- crates/goose-server/src/routes/agent.rs | 21 ++++++-- crates/goose/src/agents/agent.rs | 69 +++++++++++++++++++++++-- crates/goose/src/providers/init.rs | 2 +- crates/goose/src/providers/mod.rs | 4 +- 4 files changed, 85 insertions(+), 11 deletions(-) diff --git a/crates/goose-server/src/routes/agent.rs b/crates/goose-server/src/routes/agent.rs index 392a00e3db..9314b206b7 100644 --- a/crates/goose-server/src/routes/agent.rs +++ b/crates/goose-server/src/routes/agent.rs @@ -385,7 +385,7 @@ async fn resume_agent( } })?; - let extension_results = if payload.load_model_and_extensions { + let (extension_results, session) = if payload.load_model_and_extensions { let agent = state .get_agent_for_route(payload.session_id.clone()) .await @@ -394,7 +394,7 @@ async fn resume_agent( status: code, })?; - agent + let provider_changed = agent .restore_provider_from_session(&session) .await .map_err(|e| ErrorResponse { @@ -402,6 +402,19 @@ async fn resume_agent( status: StatusCode::INTERNAL_SERVER_ERROR, })?; + let session = if provider_changed { + state + .session_manager() + .get_session(&payload.session_id, true) + .await + .map_err(|err| ErrorResponse { + message: format!("Failed to re-fetch session: {}", err), + status: StatusCode::INTERNAL_SERVER_ERROR, + })? + } else { + session + }; + let extension_results = if let Some(results) = state.take_extension_loading_task(&payload.session_id).await { tracing::debug!( @@ -420,9 +433,9 @@ async fn resume_agent( agent.load_extensions_from_session(&session).await }; - Some(extension_results) + (Some(extension_results), session) } else { - None + (None, session) }; Ok(Json(ResumeAgentResponse { diff --git a/crates/goose/src/agents/agent.rs b/crates/goose/src/agents/agent.rs index ee31d401e1..3b30b31cdb 100644 --- a/crates/goose/src/agents/agent.rs +++ b/crates/goose/src/agents/agent.rs @@ -1759,7 +1759,8 @@ impl Agent { /// Restore the provider from session data or fall back to global config /// This is used when resuming a session to restore the provider state - pub async fn restore_provider_from_session(&self, session: &Session) -> Result<()> { + /// Returns true if the session's provider was replaced with a fallback. + pub async fn restore_provider_from_session(&self, session: &Session) -> Result { let config = Config::global(); let provider_name = session @@ -1784,9 +1785,69 @@ impl Agent { let extensions = EnabledExtensionsState::extensions_or_default(Some(&session.extension_data), config); - let provider = crate::providers::create(&provider_name, model_config, extensions) + let (provider, provider_changed) = if crate::providers::get_from_registry(&provider_name) .await - .map_err(|e| anyhow!("Could not create provider: {}", e))?; + .is_ok() + { + let p = crate::providers::create(&provider_name, model_config, extensions) + .await + .map_err(|e| anyhow!("Could not create provider: {}", e))?; + (p, false) + } else { + let fallback_provider_name = config + .get_goose_provider() + .ok() + .filter(|name| name != &provider_name) + .ok_or_else(|| { + anyhow!( + "Could not create provider: provider '{}' not found", + provider_name + ) + })?; + + tracing::warn!( + "Session provider '{}' unavailable, falling back to '{}'", + provider_name, + fallback_provider_name + ); + + let fallback_model_name = config + .get_goose_model() + .ok() + .ok_or_else(|| anyhow!("Could not configure fallback provider: missing model"))?; + let fallback_model_config = crate::model::ModelConfig::new(&fallback_model_name) + .map_err(|e| anyhow!("Could not configure fallback provider: invalid model {}", e))? + .with_canonical_limits(&fallback_provider_name); + + let fallback_provider = crate::providers::create( + &fallback_provider_name, + fallback_model_config.clone(), + extensions, + ) + .await + .map_err(|e| { + anyhow!( + "Could not create provider '{}' or fallback '{}': {}", + provider_name, + fallback_provider_name, + e + ) + })?; + + if let Err(e) = self + .config + .session_manager + .update(&session.id) + .provider_name(&fallback_provider_name) + .model_config(fallback_model_config) + .apply() + .await + { + tracing::warn!("Failed to update session provider: {}", e); + } + + (fallback_provider, true) + }; self.update_provider(provider, &session.id).await?; // Propagate session mode to the new provider @@ -1797,7 +1858,7 @@ impl Agent { .map_err(|e| anyhow!("Failed to propagate mode to provider: {}", e))?; } *self.current_goose_mode.lock().await = session.goose_mode; - Ok(()) + Ok(provider_changed) } /// Override the system prompt with a custom template diff --git a/crates/goose/src/providers/init.rs b/crates/goose/src/providers/init.rs index 4c02ba7406..930112986a 100644 --- a/crates/goose/src/providers/init.rs +++ b/crates/goose/src/providers/init.rs @@ -118,7 +118,7 @@ pub async fn refresh_custom_providers() -> Result<()> { Ok(()) } -async fn get_from_registry(name: &str) -> Result { +pub async fn get_from_registry(name: &str) -> Result { let guard = get_registry().await.read().unwrap(); guard .entries diff --git a/crates/goose/src/providers/mod.rs b/crates/goose/src/providers/mod.rs index b1b564f4fa..e9e4b44db2 100644 --- a/crates/goose/src/providers/mod.rs +++ b/crates/goose/src/providers/mod.rs @@ -48,7 +48,7 @@ pub mod venice; pub mod xai; pub use init::{ - cleanup_provider, create, create_with_default_model, create_with_named_model, providers, - refresh_custom_providers, + cleanup_provider, create, create_with_default_model, create_with_named_model, + get_from_registry, providers, refresh_custom_providers, }; pub use retry::{retry_operation, RetryConfig};