mirror of
https://github.com/zed-industries/zed.git
synced 2024-09-20 02:47:34 +03:00
assistant panel: Show if env var with API key is set (#16453)
This makes it easier to debug why resetting a key doesn't work. We now show when the key is set via an env var and if so, we disable the reset-key button and instead give instructions. ![screenshot-2024-08-19-11 22 05@2x](https://github.com/user-attachments/assets/6c75dc82-cb61-4661-9647-f77fca8fdf41) Release Notes: - N/A Co-authored-by: Bennet <bennet@zed.dev>
This commit is contained in:
parent
14fa4abce4
commit
f651333896
@ -19,7 +19,7 @@ use settings::{Settings, SettingsStore};
|
|||||||
use std::{sync::Arc, time::Duration};
|
use std::{sync::Arc, time::Duration};
|
||||||
use strum::IntoEnumIterator;
|
use strum::IntoEnumIterator;
|
||||||
use theme::ThemeSettings;
|
use theme::ThemeSettings;
|
||||||
use ui::{prelude::*, Icon, IconName};
|
use ui::{prelude::*, Icon, IconName, Tooltip};
|
||||||
use util::ResultExt;
|
use util::ResultExt;
|
||||||
|
|
||||||
const PROVIDER_ID: &str = "anthropic";
|
const PROVIDER_ID: &str = "anthropic";
|
||||||
@ -54,8 +54,11 @@ pub struct AnthropicLanguageModelProvider {
|
|||||||
state: gpui::Model<State>,
|
state: gpui::Model<State>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const ANTHROPIC_API_KEY_VAR: &'static str = "ANTHROPIC_API_KEY";
|
||||||
|
|
||||||
pub struct State {
|
pub struct State {
|
||||||
api_key: Option<String>,
|
api_key: Option<String>,
|
||||||
|
api_key_from_env: bool,
|
||||||
_subscription: Subscription,
|
_subscription: Subscription,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -67,6 +70,7 @@ impl State {
|
|||||||
delete_credentials.await.ok();
|
delete_credentials.await.ok();
|
||||||
this.update(&mut cx, |this, cx| {
|
this.update(&mut cx, |this, cx| {
|
||||||
this.api_key = None;
|
this.api_key = None;
|
||||||
|
this.api_key_from_env = false;
|
||||||
cx.notify();
|
cx.notify();
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -105,18 +109,20 @@ impl State {
|
|||||||
.clone();
|
.clone();
|
||||||
|
|
||||||
cx.spawn(|this, mut cx| async move {
|
cx.spawn(|this, mut cx| async move {
|
||||||
let api_key = if let Ok(api_key) = std::env::var("ANTHROPIC_API_KEY") {
|
let (api_key, from_env) = if let Ok(api_key) = std::env::var(ANTHROPIC_API_KEY_VAR)
|
||||||
api_key
|
{
|
||||||
|
(api_key, true)
|
||||||
} else {
|
} else {
|
||||||
let (_, api_key) = cx
|
let (_, api_key) = cx
|
||||||
.update(|cx| cx.read_credentials(&api_url))?
|
.update(|cx| cx.read_credentials(&api_url))?
|
||||||
.await?
|
.await?
|
||||||
.ok_or_else(|| anyhow!("credentials not found"))?;
|
.ok_or_else(|| anyhow!("credentials not found"))?;
|
||||||
String::from_utf8(api_key)?
|
(String::from_utf8(api_key)?, false)
|
||||||
};
|
};
|
||||||
|
|
||||||
this.update(&mut cx, |this, cx| {
|
this.update(&mut cx, |this, cx| {
|
||||||
this.api_key = Some(api_key);
|
this.api_key = Some(api_key);
|
||||||
|
this.api_key_from_env = from_env;
|
||||||
cx.notify();
|
cx.notify();
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -128,6 +134,7 @@ impl AnthropicLanguageModelProvider {
|
|||||||
pub fn new(http_client: Arc<dyn HttpClient>, cx: &mut AppContext) -> Self {
|
pub fn new(http_client: Arc<dyn HttpClient>, cx: &mut AppContext) -> Self {
|
||||||
let state = cx.new_model(|cx| State {
|
let state = cx.new_model(|cx| State {
|
||||||
api_key: None,
|
api_key: None,
|
||||||
|
api_key_from_env: false,
|
||||||
_subscription: cx.observe_global::<SettingsStore>(|_, cx| {
|
_subscription: cx.observe_global::<SettingsStore>(|_, cx| {
|
||||||
cx.notify();
|
cx.notify();
|
||||||
}),
|
}),
|
||||||
@ -537,6 +544,8 @@ impl Render for ConfigurationView {
|
|||||||
"Paste your Anthropic API key below and hit enter to use the assistant:",
|
"Paste your Anthropic API key below and hit enter to use the assistant:",
|
||||||
];
|
];
|
||||||
|
|
||||||
|
let env_var_set = self.state.read(cx).api_key_from_env;
|
||||||
|
|
||||||
if self.load_credentials_task.is_some() {
|
if self.load_credentials_task.is_some() {
|
||||||
div().child(Label::new("Loading credentials...")).into_any()
|
div().child(Label::new("Loading credentials...")).into_any()
|
||||||
} else if self.should_render_editor(cx) {
|
} else if self.should_render_editor(cx) {
|
||||||
@ -558,7 +567,7 @@ impl Render for ConfigurationView {
|
|||||||
)
|
)
|
||||||
.child(
|
.child(
|
||||||
Label::new(
|
Label::new(
|
||||||
"You can also assign the ANTHROPIC_API_KEY environment variable and restart Zed.",
|
"You can also assign the {ANTHROPIC_API_KEY_VAR} environment variable and restart Zed.",
|
||||||
)
|
)
|
||||||
.size(LabelSize::Small),
|
.size(LabelSize::Small),
|
||||||
)
|
)
|
||||||
@ -571,13 +580,21 @@ impl Render for ConfigurationView {
|
|||||||
h_flex()
|
h_flex()
|
||||||
.gap_1()
|
.gap_1()
|
||||||
.child(Icon::new(IconName::Check).color(Color::Success))
|
.child(Icon::new(IconName::Check).color(Color::Success))
|
||||||
.child(Label::new("API key configured.")),
|
.child(Label::new(if env_var_set {
|
||||||
|
format!("API key set in {ANTHROPIC_API_KEY_VAR} environment variable.")
|
||||||
|
} else {
|
||||||
|
"API key configured.".to_string()
|
||||||
|
})),
|
||||||
)
|
)
|
||||||
.child(
|
.child(
|
||||||
Button::new("reset-key", "Reset key")
|
Button::new("reset-key", "Reset key")
|
||||||
.icon(Some(IconName::Trash))
|
.icon(Some(IconName::Trash))
|
||||||
.icon_size(IconSize::Small)
|
.icon_size(IconSize::Small)
|
||||||
.icon_position(IconPosition::Start)
|
.icon_position(IconPosition::Start)
|
||||||
|
.disabled(env_var_set)
|
||||||
|
.when(env_var_set, |this| {
|
||||||
|
this.tooltip(|cx| Tooltip::text(format!("To reset your API key, unset the {ANTHROPIC_API_KEY_VAR} environment variable."), cx))
|
||||||
|
})
|
||||||
.on_click(cx.listener(|this, _, cx| this.reset_api_key(cx))),
|
.on_click(cx.listener(|this, _, cx| this.reset_api_key(cx))),
|
||||||
)
|
)
|
||||||
.into_any()
|
.into_any()
|
||||||
|
@ -14,7 +14,7 @@ use settings::{Settings, SettingsStore};
|
|||||||
use std::{future, sync::Arc, time::Duration};
|
use std::{future, sync::Arc, time::Duration};
|
||||||
use strum::IntoEnumIterator;
|
use strum::IntoEnumIterator;
|
||||||
use theme::ThemeSettings;
|
use theme::ThemeSettings;
|
||||||
use ui::{prelude::*, Icon, IconName};
|
use ui::{prelude::*, Icon, IconName, Tooltip};
|
||||||
use util::ResultExt;
|
use util::ResultExt;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
@ -46,9 +46,12 @@ pub struct GoogleLanguageModelProvider {
|
|||||||
|
|
||||||
pub struct State {
|
pub struct State {
|
||||||
api_key: Option<String>,
|
api_key: Option<String>,
|
||||||
|
api_key_from_env: bool,
|
||||||
_subscription: Subscription,
|
_subscription: Subscription,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const GOOGLE_AI_API_KEY_VAR: &'static str = "GOOGLE_AI_API_KEY";
|
||||||
|
|
||||||
impl State {
|
impl State {
|
||||||
fn is_authenticated(&self) -> bool {
|
fn is_authenticated(&self) -> bool {
|
||||||
self.api_key.is_some()
|
self.api_key.is_some()
|
||||||
@ -61,6 +64,7 @@ impl State {
|
|||||||
delete_credentials.await.ok();
|
delete_credentials.await.ok();
|
||||||
this.update(&mut cx, |this, cx| {
|
this.update(&mut cx, |this, cx| {
|
||||||
this.api_key = None;
|
this.api_key = None;
|
||||||
|
this.api_key_from_env = false;
|
||||||
cx.notify();
|
cx.notify();
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -90,18 +94,20 @@ impl State {
|
|||||||
.clone();
|
.clone();
|
||||||
|
|
||||||
cx.spawn(|this, mut cx| async move {
|
cx.spawn(|this, mut cx| async move {
|
||||||
let api_key = if let Ok(api_key) = std::env::var("GOOGLE_AI_API_KEY") {
|
let (api_key, from_env) = if let Ok(api_key) = std::env::var(GOOGLE_AI_API_KEY_VAR)
|
||||||
api_key
|
{
|
||||||
|
(api_key, true)
|
||||||
} else {
|
} else {
|
||||||
let (_, api_key) = cx
|
let (_, api_key) = cx
|
||||||
.update(|cx| cx.read_credentials(&api_url))?
|
.update(|cx| cx.read_credentials(&api_url))?
|
||||||
.await?
|
.await?
|
||||||
.ok_or_else(|| anyhow!("credentials not found"))?;
|
.ok_or_else(|| anyhow!("credentials not found"))?;
|
||||||
String::from_utf8(api_key)?
|
(String::from_utf8(api_key)?, false)
|
||||||
};
|
};
|
||||||
|
|
||||||
this.update(&mut cx, |this, cx| {
|
this.update(&mut cx, |this, cx| {
|
||||||
this.api_key = Some(api_key);
|
this.api_key = Some(api_key);
|
||||||
|
this.api_key_from_env = from_env;
|
||||||
cx.notify();
|
cx.notify();
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -113,6 +119,7 @@ impl GoogleLanguageModelProvider {
|
|||||||
pub fn new(http_client: Arc<dyn HttpClient>, cx: &mut AppContext) -> Self {
|
pub fn new(http_client: Arc<dyn HttpClient>, cx: &mut AppContext) -> Self {
|
||||||
let state = cx.new_model(|cx| State {
|
let state = cx.new_model(|cx| State {
|
||||||
api_key: None,
|
api_key: None,
|
||||||
|
api_key_from_env: false,
|
||||||
_subscription: cx.observe_global::<SettingsStore>(|_, cx| {
|
_subscription: cx.observe_global::<SettingsStore>(|_, cx| {
|
||||||
cx.notify();
|
cx.notify();
|
||||||
}),
|
}),
|
||||||
@ -422,6 +429,8 @@ impl Render for ConfigurationView {
|
|||||||
"Paste your Google AI API key below and hit enter to use the assistant:",
|
"Paste your Google AI API key below and hit enter to use the assistant:",
|
||||||
];
|
];
|
||||||
|
|
||||||
|
let env_var_set = self.state.read(cx).api_key_from_env;
|
||||||
|
|
||||||
if self.load_credentials_task.is_some() {
|
if self.load_credentials_task.is_some() {
|
||||||
div().child(Label::new("Loading credentials...")).into_any()
|
div().child(Label::new("Loading credentials...")).into_any()
|
||||||
} else if self.should_render_editor(cx) {
|
} else if self.should_render_editor(cx) {
|
||||||
@ -443,7 +452,7 @@ impl Render for ConfigurationView {
|
|||||||
)
|
)
|
||||||
.child(
|
.child(
|
||||||
Label::new(
|
Label::new(
|
||||||
"You can also assign the GOOGLE_AI_API_KEY environment variable and restart Zed.",
|
format!("You can also assign the {GOOGLE_AI_API_KEY_VAR} environment variable and restart Zed."),
|
||||||
)
|
)
|
||||||
.size(LabelSize::Small),
|
.size(LabelSize::Small),
|
||||||
)
|
)
|
||||||
@ -456,13 +465,21 @@ impl Render for ConfigurationView {
|
|||||||
h_flex()
|
h_flex()
|
||||||
.gap_1()
|
.gap_1()
|
||||||
.child(Icon::new(IconName::Check).color(Color::Success))
|
.child(Icon::new(IconName::Check).color(Color::Success))
|
||||||
.child(Label::new("API key configured.")),
|
.child(Label::new(if env_var_set {
|
||||||
|
format!("API key set in {GOOGLE_AI_API_KEY_VAR} environment variable.")
|
||||||
|
} else {
|
||||||
|
"API key configured.".to_string()
|
||||||
|
})),
|
||||||
)
|
)
|
||||||
.child(
|
.child(
|
||||||
Button::new("reset-key", "Reset key")
|
Button::new("reset-key", "Reset key")
|
||||||
.icon(Some(IconName::Trash))
|
.icon(Some(IconName::Trash))
|
||||||
.icon_size(IconSize::Small)
|
.icon_size(IconSize::Small)
|
||||||
.icon_position(IconPosition::Start)
|
.icon_position(IconPosition::Start)
|
||||||
|
.disabled(env_var_set)
|
||||||
|
.when(env_var_set, |this| {
|
||||||
|
this.tooltip(|cx| Tooltip::text(format!("To reset your API key, unset the {GOOGLE_AI_API_KEY_VAR} environment variable."), cx))
|
||||||
|
})
|
||||||
.on_click(cx.listener(|this, _, cx| this.reset_api_key(cx))),
|
.on_click(cx.listener(|this, _, cx| this.reset_api_key(cx))),
|
||||||
)
|
)
|
||||||
.into_any()
|
.into_any()
|
||||||
|
@ -16,7 +16,7 @@ use settings::{Settings, SettingsStore};
|
|||||||
use std::{sync::Arc, time::Duration};
|
use std::{sync::Arc, time::Duration};
|
||||||
use strum::IntoEnumIterator;
|
use strum::IntoEnumIterator;
|
||||||
use theme::ThemeSettings;
|
use theme::ThemeSettings;
|
||||||
use ui::{prelude::*, Icon, IconName};
|
use ui::{prelude::*, Icon, IconName, Tooltip};
|
||||||
use util::ResultExt;
|
use util::ResultExt;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
@ -49,9 +49,12 @@ pub struct OpenAiLanguageModelProvider {
|
|||||||
|
|
||||||
pub struct State {
|
pub struct State {
|
||||||
api_key: Option<String>,
|
api_key: Option<String>,
|
||||||
|
api_key_from_env: bool,
|
||||||
_subscription: Subscription,
|
_subscription: Subscription,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const OPENAI_API_KEY_VAR: &'static str = "OPENAI_API_KEY";
|
||||||
|
|
||||||
impl State {
|
impl State {
|
||||||
fn is_authenticated(&self) -> bool {
|
fn is_authenticated(&self) -> bool {
|
||||||
self.api_key.is_some()
|
self.api_key.is_some()
|
||||||
@ -64,6 +67,7 @@ impl State {
|
|||||||
delete_credentials.await.log_err();
|
delete_credentials.await.log_err();
|
||||||
this.update(&mut cx, |this, cx| {
|
this.update(&mut cx, |this, cx| {
|
||||||
this.api_key = None;
|
this.api_key = None;
|
||||||
|
this.api_key_from_env = false;
|
||||||
cx.notify();
|
cx.notify();
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -92,17 +96,18 @@ impl State {
|
|||||||
.api_url
|
.api_url
|
||||||
.clone();
|
.clone();
|
||||||
cx.spawn(|this, mut cx| async move {
|
cx.spawn(|this, mut cx| async move {
|
||||||
let api_key = if let Ok(api_key) = std::env::var("OPENAI_API_KEY") {
|
let (api_key, from_env) = if let Ok(api_key) = std::env::var(OPENAI_API_KEY_VAR) {
|
||||||
api_key
|
(api_key, true)
|
||||||
} else {
|
} else {
|
||||||
let (_, api_key) = cx
|
let (_, api_key) = cx
|
||||||
.update(|cx| cx.read_credentials(&api_url))?
|
.update(|cx| cx.read_credentials(&api_url))?
|
||||||
.await?
|
.await?
|
||||||
.ok_or_else(|| anyhow!("credentials not found"))?;
|
.ok_or_else(|| anyhow!("credentials not found"))?;
|
||||||
String::from_utf8(api_key)?
|
(String::from_utf8(api_key)?, false)
|
||||||
};
|
};
|
||||||
this.update(&mut cx, |this, cx| {
|
this.update(&mut cx, |this, cx| {
|
||||||
this.api_key = Some(api_key);
|
this.api_key = Some(api_key);
|
||||||
|
this.api_key_from_env = from_env;
|
||||||
cx.notify();
|
cx.notify();
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -114,6 +119,7 @@ impl OpenAiLanguageModelProvider {
|
|||||||
pub fn new(http_client: Arc<dyn HttpClient>, cx: &mut AppContext) -> Self {
|
pub fn new(http_client: Arc<dyn HttpClient>, cx: &mut AppContext) -> Self {
|
||||||
let state = cx.new_model(|cx| State {
|
let state = cx.new_model(|cx| State {
|
||||||
api_key: None,
|
api_key: None,
|
||||||
|
api_key_from_env: false,
|
||||||
_subscription: cx.observe_global::<SettingsStore>(|_this: &mut State, cx| {
|
_subscription: cx.observe_global::<SettingsStore>(|_this: &mut State, cx| {
|
||||||
cx.notify();
|
cx.notify();
|
||||||
}),
|
}),
|
||||||
@ -476,6 +482,8 @@ impl Render for ConfigurationView {
|
|||||||
"Paste your OpenAI API key below and hit enter to use the assistant:",
|
"Paste your OpenAI API key below and hit enter to use the assistant:",
|
||||||
];
|
];
|
||||||
|
|
||||||
|
let env_var_set = self.state.read(cx).api_key_from_env;
|
||||||
|
|
||||||
if self.load_credentials_task.is_some() {
|
if self.load_credentials_task.is_some() {
|
||||||
div().child(Label::new("Loading credentials...")).into_any()
|
div().child(Label::new("Loading credentials...")).into_any()
|
||||||
} else if self.should_render_editor(cx) {
|
} else if self.should_render_editor(cx) {
|
||||||
@ -497,7 +505,7 @@ impl Render for ConfigurationView {
|
|||||||
)
|
)
|
||||||
.child(
|
.child(
|
||||||
Label::new(
|
Label::new(
|
||||||
"You can also assign the OPENAI_API_KEY environment variable and restart Zed.",
|
format!("You can also assign the {OPENAI_API_KEY_VAR} environment variable and restart Zed."),
|
||||||
)
|
)
|
||||||
.size(LabelSize::Small),
|
.size(LabelSize::Small),
|
||||||
)
|
)
|
||||||
@ -510,13 +518,21 @@ impl Render for ConfigurationView {
|
|||||||
h_flex()
|
h_flex()
|
||||||
.gap_1()
|
.gap_1()
|
||||||
.child(Icon::new(IconName::Check).color(Color::Success))
|
.child(Icon::new(IconName::Check).color(Color::Success))
|
||||||
.child(Label::new("API key configured.")),
|
.child(Label::new(if env_var_set {
|
||||||
|
format!("API key set in {OPENAI_API_KEY_VAR} environment variable.")
|
||||||
|
} else {
|
||||||
|
"API key configured.".to_string()
|
||||||
|
})),
|
||||||
)
|
)
|
||||||
.child(
|
.child(
|
||||||
Button::new("reset-key", "Reset key")
|
Button::new("reset-key", "Reset key")
|
||||||
.icon(Some(IconName::Trash))
|
.icon(Some(IconName::Trash))
|
||||||
.icon_size(IconSize::Small)
|
.icon_size(IconSize::Small)
|
||||||
.icon_position(IconPosition::Start)
|
.icon_position(IconPosition::Start)
|
||||||
|
.disabled(env_var_set)
|
||||||
|
.when(env_var_set, |this| {
|
||||||
|
this.tooltip(|cx| Tooltip::text(format!("To reset your API key, unset the {OPENAI_API_KEY_VAR} environment variable."), cx))
|
||||||
|
})
|
||||||
.on_click(cx.listener(|this, _, cx| this.reset_api_key(cx))),
|
.on_click(cx.listener(|this, _, cx| this.reset_api_key(cx))),
|
||||||
)
|
)
|
||||||
.into_any()
|
.into_any()
|
||||||
|
Loading…
Reference in New Issue
Block a user