assistant panel: Avoid auth prompt on provider switch (#15478)

Thorsten Ball and Bennet created

Previously, the following lead to a bug:

1. Set OpenAI key
2. Switch to Anthropic
3. Restart Zed
4. Switch provider to OpenAI -> get prompted for authentication prompt

With this change, you won't get prompted for the OpenAI key again.

Release Notes:

- N/A

Co-authored-by: Bennet <bennet@zed.dev>

Change summary

crates/assistant/src/assistant_panel.rs | 46 +++++++++++++++++++++-----
1 file changed, 36 insertions(+), 10 deletions(-)

Detailed changes

crates/assistant/src/assistant_panel.rs 🔗

@@ -43,7 +43,7 @@ use language::{
     language_settings::SoftWrap, Buffer, Capability, LanguageRegistry, LspAdapterDelegate, Point,
     ToOffset,
 };
-use language_model::Role;
+use language_model::{LanguageModelProviderId, Role};
 use multi_buffer::MultiBufferRow;
 use picker::{Picker, PickerDelegate};
 use project::{Project, ProjectLspAdapterDelegate};
@@ -139,6 +139,7 @@ pub struct AssistantPanel {
     authentication_prompt: Option<AnyView>,
     model_selector_menu_handle: PopoverMenuHandle<ContextMenu>,
     model_summary_editor: View<Editor>,
+    authentificate_provider_task: Option<(LanguageModelProviderId, Task<()>)>,
 }
 
 #[derive(Clone)]
@@ -412,6 +413,7 @@ impl AssistantPanel {
             authentication_prompt: None,
             model_selector_menu_handle,
             model_summary_editor,
+            authentificate_provider_task: None,
         }
     }
 
@@ -558,18 +560,42 @@ impl AssistantPanel {
             })
         }
 
-        if self.active_context_editor(cx).is_none() {
-            self.new_context(cx);
-        }
+        let Some(new_provider_id) = LanguageModelCompletionProvider::read_global(cx)
+            .active_provider()
+            .map(|p| p.id())
+        else {
+            return;
+        };
+
+        if self
+            .authentificate_provider_task
+            .as_ref()
+            .map_or(true, |(old_provider_id, _)| {
+                *old_provider_id != new_provider_id
+            })
+        {
+            let load_credentials = self.authenticate(cx);
+            let task = cx.spawn(|this, mut cx| async move {
+                let _ = load_credentials.await;
+                this.update(&mut cx, |this, cx| {
+                    if this.active_context_editor(cx).is_none() {
+                        this.new_context(cx);
+                    }
+
+                    let authentication_prompt = Self::authentication_prompt(cx);
+                    for context_editor in this.context_editors(cx) {
+                        context_editor.update(cx, |editor, cx| {
+                            editor.set_authentication_prompt(authentication_prompt.clone(), cx);
+                        });
+                    }
 
-        let authentication_prompt = Self::authentication_prompt(cx);
-        for context_editor in self.context_editors(cx) {
-            context_editor.update(cx, |editor, cx| {
-                editor.set_authentication_prompt(authentication_prompt.clone(), cx);
+                    cx.notify();
+                })
+                .log_err();
             });
-        }
 
-        cx.notify();
+            self.authentificate_provider_task = Some((new_provider_id, task));
+        }
     }
 
     fn authentication_prompt(cx: &mut WindowContext) -> Option<AnyView> {