From b28990e4719a46b4f0fd97590a2e8ca9acef4a5d Mon Sep 17 00:00:00 2001 From: Ben Brandt Date: Tue, 24 Mar 2026 12:08:44 +0100 Subject: [PATCH] language_models: Use weak entities in subscribe/observes around the language model registry (#52312) ## Context I was getting some leak detection failures in evals and tracked it down to these entities getting passed into observe/subscribe callbacks and causing cycles. Release Notes: - N/A Co-authored-by: Lukas Wirth --- .../language_model/src/model/cloud_model.rs | 6 +- crates/language_models/src/language_models.rs | 62 +++++++++++-------- 2 files changed, 40 insertions(+), 28 deletions(-) diff --git a/crates/language_model/src/model/cloud_model.rs b/crates/language_model/src/model/cloud_model.rs index f6ad907483e5946652752895d0a48ec129660b0b..a1362d78292082522f4e883efe42b2ca1e0a0300 100644 --- a/crates/language_model/src/model/cloud_model.rs +++ b/crates/language_model/src/model/cloud_model.rs @@ -154,9 +154,11 @@ impl RefreshLlmTokenListener { fn new(client: Arc, user_store: Entity, cx: &mut Context) -> Self { client.add_message_to_client_handler({ - let this = cx.entity(); + let this = cx.weak_entity(); move |message, cx| { - Self::handle_refresh_llm_token(this.clone(), message, cx); + if let Some(this) = this.upgrade() { + Self::handle_refresh_llm_token(this, message, cx); + } } }); diff --git a/crates/language_models/src/language_models.rs b/crates/language_models/src/language_models.rs index 55624ed9d52d5dbb9cf8b724e0ea9ca2ef5a894a..4db1db8fa6ce5afb9d77a6685bfc0861d0fb8885 100644 --- a/crates/language_models/src/language_models.rs +++ b/crates/language_models/src/language_models.rs @@ -39,37 +39,43 @@ pub fn init(user_store: Entity, client: Arc, cx: &mut App) { // Subscribe to extension store events to track LLM extension installations if let Some(extension_store) = extension_host::ExtensionStore::try_global(cx) { cx.subscribe(&extension_store, { - let registry = registry.clone(); - move |extension_store, event, cx| match event { - extension_host::Event::ExtensionInstalled(extension_id) => { - if let Some(manifest) = extension_store - .read(cx) - .extension_manifest_for_id(extension_id) - { - if !manifest.language_model_providers.is_empty() { - registry.update(cx, |registry, cx| { - registry.extension_installed(extension_id.clone(), cx); - }); + let registry = registry.downgrade(); + move |extension_store, event, cx| { + let Some(registry) = registry.upgrade() else { + return; + }; + match event { + extension_host::Event::ExtensionInstalled(extension_id) => { + if let Some(manifest) = extension_store + .read(cx) + .extension_manifest_for_id(extension_id) + { + if !manifest.language_model_providers.is_empty() { + registry.update(cx, |registry, cx| { + registry.extension_installed(extension_id.clone(), cx); + }); + } } } - } - extension_host::Event::ExtensionUninstalled(extension_id) => { - registry.update(cx, |registry, cx| { - registry.extension_uninstalled(extension_id, cx); - }); - } - extension_host::Event::ExtensionsUpdated => { - let mut new_ids = HashSet::default(); - for (extension_id, entry) in extension_store.read(cx).installed_extensions() { - if !entry.manifest.language_model_providers.is_empty() { - new_ids.insert(extension_id.clone()); + extension_host::Event::ExtensionUninstalled(extension_id) => { + registry.update(cx, |registry, cx| { + registry.extension_uninstalled(extension_id, cx); + }); + } + extension_host::Event::ExtensionsUpdated => { + let mut new_ids = HashSet::default(); + for (extension_id, entry) in extension_store.read(cx).installed_extensions() + { + if !entry.manifest.language_model_providers.is_empty() { + new_ids.insert(extension_id.clone()); + } } + registry.update(cx, |registry, cx| { + registry.sync_installed_llm_extensions(new_ids, cx); + }); } - registry.update(cx, |registry, cx| { - registry.sync_installed_llm_extensions(new_ids, cx); - }); + _ => {} } - _ => {} } }) .detach(); @@ -101,7 +107,11 @@ pub fn init(user_store: Entity, client: Arc, cx: &mut App) { cx, ); }); + let registry = registry.downgrade(); cx.observe_global::(move |cx| { + let Some(registry) = registry.upgrade() else { + return; + }; let openai_compatible_providers_new = AllLanguageModelSettings::get_global(cx) .openai_compatible .keys()