language_models: Clear cached credentials when OpenAI and OpenAI Compatible provider api_url change (#37610)
Closes #37093 Also check this: #37099. So currently in zed for both OpenAI and OpenAI Compatible provider when the url is changed from settings the api_key stored in the provider state is not cleared and it is still used. But if you restart zed the api_key is cleared. Currently zed uses the api_url to store and fetch the api key from credential provider. The behaviour is not changed overall, it's just that we have made it consistent it with the zed restart logic where it re-authenticates and fetches the api_key again. I have attached the video below to show case before and after of this. So all in all the problem was we were not re-authenticating the in case api_url change while zed is still running. Now we trigger a re-authentication and clear the state in case authentication fails. OpenAI Compatible Provider: | Before | After | |--------|--------| | <video src="https://github.com/user-attachments/assets/324d2707-ea72-4119-8981-6b596a9f40a3" /> | <video src="https://github.com/user-attachments/assets/cc7fdb73-8975-4aaf-a642-809bb03ce319" /> | OpenAI Provider: | Before | After | |--------|--------| | <video src="https://github.com/user-attachments/assets/a1c07d1b-1909-4b49-b33c-fc05123e92e7" /> | <video src="https://github.com/user-attachments/assets/d78aeccd-5cd3-4d0c-8b9f-6f98e499d7c8" /> | Release Notes: - Fixed OpenAI and OpenAI Compatible provide API keys being persisted when changing the API URL setting. Authentication is now properly revalidated when settings change. --------- Signed-off-by: Umesh Yadav <git@umesh.dev>
This commit is contained in:
@@ -56,13 +56,13 @@ pub struct OpenAiLanguageModelProvider {
|
||||
pub struct State {
|
||||
api_key: Option<String>,
|
||||
api_key_from_env: bool,
|
||||
last_api_url: String,
|
||||
_subscription: Subscription,
|
||||
}
|
||||
|
||||
const OPENAI_API_KEY_VAR: &str = "OPENAI_API_KEY";
|
||||
|
||||
impl State {
|
||||
//
|
||||
fn is_authenticated(&self) -> bool {
|
||||
self.api_key.is_some()
|
||||
}
|
||||
@@ -104,11 +104,7 @@ impl State {
|
||||
})
|
||||
}
|
||||
|
||||
fn authenticate(&self, cx: &mut Context<Self>) -> Task<Result<(), AuthenticateError>> {
|
||||
if self.is_authenticated() {
|
||||
return Task::ready(Ok(()));
|
||||
}
|
||||
|
||||
fn get_api_key(&self, cx: &mut Context<Self>) -> Task<Result<(), AuthenticateError>> {
|
||||
let credentials_provider = <dyn CredentialsProvider>::global(cx);
|
||||
let api_url = AllLanguageModelSettings::get_global(cx)
|
||||
.openai
|
||||
@@ -136,14 +132,52 @@ impl State {
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
fn authenticate(&self, cx: &mut Context<Self>) -> Task<Result<(), AuthenticateError>> {
|
||||
if self.is_authenticated() {
|
||||
return Task::ready(Ok(()));
|
||||
}
|
||||
|
||||
self.get_api_key(cx)
|
||||
}
|
||||
}
|
||||
|
||||
impl OpenAiLanguageModelProvider {
|
||||
pub fn new(http_client: Arc<dyn HttpClient>, cx: &mut App) -> Self {
|
||||
let initial_api_url = AllLanguageModelSettings::get_global(cx)
|
||||
.openai
|
||||
.api_url
|
||||
.clone();
|
||||
|
||||
let state = cx.new(|cx| State {
|
||||
api_key: None,
|
||||
api_key_from_env: false,
|
||||
_subscription: cx.observe_global::<SettingsStore>(|_this: &mut State, cx| {
|
||||
last_api_url: initial_api_url.clone(),
|
||||
_subscription: cx.observe_global::<SettingsStore>(|this: &mut State, cx| {
|
||||
let current_api_url = AllLanguageModelSettings::get_global(cx)
|
||||
.openai
|
||||
.api_url
|
||||
.clone();
|
||||
|
||||
if this.last_api_url != current_api_url {
|
||||
this.last_api_url = current_api_url;
|
||||
if !this.api_key_from_env {
|
||||
this.api_key = None;
|
||||
let spawn_task = cx.spawn(async move |handle, cx| {
|
||||
if let Ok(task) = handle.update(cx, |this, cx| this.get_api_key(cx)) {
|
||||
if let Err(_) = task.await {
|
||||
handle
|
||||
.update(cx, |this, _| {
|
||||
this.api_key = None;
|
||||
this.api_key_from_env = false;
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
}
|
||||
});
|
||||
spawn_task.detach();
|
||||
}
|
||||
}
|
||||
cx.notify();
|
||||
}),
|
||||
});
|
||||
|
||||
@@ -113,11 +113,7 @@ impl State {
|
||||
})
|
||||
}
|
||||
|
||||
fn authenticate(&self, cx: &mut Context<Self>) -> Task<Result<(), AuthenticateError>> {
|
||||
if self.is_authenticated() {
|
||||
return Task::ready(Ok(()));
|
||||
}
|
||||
|
||||
fn get_api_key(&self, cx: &mut Context<Self>) -> Task<Result<(), AuthenticateError>> {
|
||||
let credentials_provider = <dyn CredentialsProvider>::global(cx);
|
||||
let env_var_name = self.env_var_name.clone();
|
||||
let api_url = self.settings.api_url.clone();
|
||||
@@ -143,6 +139,14 @@ impl State {
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
fn authenticate(&self, cx: &mut Context<Self>) -> Task<Result<(), AuthenticateError>> {
|
||||
if self.is_authenticated() {
|
||||
return Task::ready(Ok(()));
|
||||
}
|
||||
|
||||
self.get_api_key(cx)
|
||||
}
|
||||
}
|
||||
|
||||
impl OpenAiCompatibleLanguageModelProvider {
|
||||
@@ -160,11 +164,27 @@ impl OpenAiCompatibleLanguageModelProvider {
|
||||
api_key: None,
|
||||
api_key_from_env: false,
|
||||
_subscription: cx.observe_global::<SettingsStore>(|this: &mut State, cx| {
|
||||
let Some(settings) = resolve_settings(&this.id, cx) else {
|
||||
let Some(settings) = resolve_settings(&this.id, cx).cloned() else {
|
||||
return;
|
||||
};
|
||||
if &this.settings != settings {
|
||||
this.settings = settings.clone();
|
||||
if &this.settings != &settings {
|
||||
if settings.api_url != this.settings.api_url && !this.api_key_from_env {
|
||||
let spawn_task = cx.spawn(async move |handle, cx| {
|
||||
if let Ok(task) = handle.update(cx, |this, cx| this.get_api_key(cx)) {
|
||||
if let Err(_) = task.await {
|
||||
handle
|
||||
.update(cx, |this, _| {
|
||||
this.api_key = None;
|
||||
this.api_key_from_env = false;
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
}
|
||||
});
|
||||
spawn_task.detach();
|
||||
}
|
||||
|
||||
this.settings = settings;
|
||||
cx.notify();
|
||||
}
|
||||
}),
|
||||
|
||||
Reference in New Issue
Block a user