From 0e901697677534c4bb5f6b42f71d11a774b1b876 Mon Sep 17 00:00:00 2001 From: "zed-zippy[bot]" <234243425+zed-zippy[bot]@users.noreply.github.com> Date: Fri, 17 Apr 2026 15:14:35 +0000 Subject: [PATCH] Fix RefCell panic in cloud model token counting (#54188) (cherry-pick to preview) (#54191) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cherry-pick of #54188 to preview ---- Fixes #54140 When `RulesLibrary::count_tokens` calls `CloudLanguageModel::count_tokens` for Google cloud models, it does so inside a `cx.update` closure, which holds a mutable borrow on the global `AppCell`. The Google provider branch then called `token_provider.auth_context(&cx.to_async())`, which created a new `AsyncApp` handle and tried to take a shared borrow on the same `RefCell` — causing a "RefCell already mutably borrowed" panic. This only affects Google models because they are the only provider that counts tokens server-side via an HTTP request (requiring authentication). The other providers (Anthropic, OpenAI, xAI) count tokens locally using tiktoken, so they never call `auth_context` during `count_tokens`. The fix makes `CloudLlmTokenProvider::auth_context` generic over `impl AppContext` instead of requiring `&AsyncApp`. This allows the `count_tokens` call site to pass `&App` directly (which reads entities without re-borrowing the `RefCell`), while all other call sites that already pass `&AsyncApp` (e.g. `stream_completion`, `refresh_models`) continue to work unchanged. Release Notes: - Fixed a crash ("RefCell already mutably borrowed") that could occur when counting tokens with Google cloud language models. Co-authored-by: Richard Feldman --- crates/language_models/src/provider/cloud.rs | 5 ++--- crates/language_models_cloud/src/language_models_cloud.rs | 4 ++-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/crates/language_models/src/provider/cloud.rs b/crates/language_models/src/provider/cloud.rs index 294b44ecae9941481e26c2341018ce584d68b3ec..9fef05e7555bc55a0ea6a3081280ea85e201dca9 100644 --- a/crates/language_models/src/provider/cloud.rs +++ b/crates/language_models/src/provider/cloud.rs @@ -6,8 +6,7 @@ use cloud_api_types::OrganizationId; use cloud_api_types::Plan; use futures::StreamExt; use futures::future::BoxFuture; -use gpui::AsyncApp; -use gpui::{AnyElement, AnyView, App, Context, Entity, Subscription, Task}; +use gpui::{AnyElement, AnyView, App, AppContext, Context, Entity, Subscription, Task}; use language_model::{ AuthenticateError, IconOrSvg, LanguageModel, LanguageModelProvider, LanguageModelProviderId, LanguageModelProviderName, LanguageModelProviderState, ZED_CLOUD_PROVIDER_ID, @@ -34,7 +33,7 @@ struct ClientTokenProvider { impl CloudLlmTokenProvider for ClientTokenProvider { type AuthContext = Option; - fn auth_context(&self, cx: &AsyncApp) -> Self::AuthContext { + fn auth_context(&self, cx: &impl AppContext) -> Self::AuthContext { self.user_store.read_with(cx, |user_store, _| { user_store .current_organization() diff --git a/crates/language_models_cloud/src/language_models_cloud.rs b/crates/language_models_cloud/src/language_models_cloud.rs index 95b94e146b374f8a5772239086616f3144efce03..1300fd42e60f0be5c135a94dde3b0503d77eb7a9 100644 --- a/crates/language_models_cloud/src/language_models_cloud.rs +++ b/crates/language_models_cloud/src/language_models_cloud.rs @@ -57,7 +57,7 @@ const PROVIDER_NAME: LanguageModelProviderName = ZED_CLOUD_PROVIDER_NAME; pub trait CloudLlmTokenProvider: Send + Sync { type AuthContext: Clone + Send + 'static; - fn auth_context(&self, cx: &AsyncApp) -> Self::AuthContext; + fn auth_context(&self, cx: &impl AppContext) -> Self::AuthContext; fn acquire_token(&self, auth_context: Self::AuthContext) -> BoxFuture<'static, Result>; fn refresh_token(&self, auth_context: Self::AuthContext) -> BoxFuture<'static, Result>; } @@ -405,7 +405,7 @@ impl LanguageModel for CloudLanguageModel