ai: Show "API key configured for {URL}" for non-default urls (#38170)

Followup to #38163, also makes some changes intended to be included in
that PR.

Release Notes:

- N/A
This commit is contained in:
Michael Sloan
2025-09-15 05:49:25 +00:00
committed by GitHub
parent 634ae72cad
commit a598fbaa73
9 changed files with 100 additions and 43 deletions
@@ -1,8 +1,8 @@
use crate::api_key::ApiKeyState;
use crate::ui::InstructionListItem;
use anthropic::{
AnthropicError, AnthropicModelMode, ContentDelta, Event, ResponseContent, ToolResultContent,
ToolResultPart, Usage,
ANTHROPIC_API_URL, AnthropicError, AnthropicModelMode, ContentDelta, Event, ResponseContent,
ToolResultContent, ToolResultPart, Usage,
};
use anyhow::{Result, anyhow};
use collections::{BTreeMap, HashMap};
@@ -27,7 +27,7 @@ use std::sync::{Arc, LazyLock};
use strum::IntoEnumIterator;
use theme::ThemeSettings;
use ui::{Icon, IconName, List, Tooltip, prelude::*};
use util::ResultExt;
use util::{ResultExt, truncate_and_trailoff};
use zed_env_vars::{EnvVar, env_var};
const PROVIDER_ID: LanguageModelProviderId = language_model::ANTHROPIC_PROVIDER_ID;
@@ -162,7 +162,7 @@ impl AnthropicLanguageModelProvider {
fn api_url(cx: &App) -> SharedString {
let api_url = &Self::settings(cx).api_url;
if api_url.is_empty() {
anthropic::ANTHROPIC_API_URL.into()
ANTHROPIC_API_URL.into()
} else {
SharedString::new(api_url.as_str())
}
@@ -1045,9 +1045,14 @@ impl Render for ConfigurationView {
.gap_1()
.child(Icon::new(IconName::Check).color(Color::Success))
.child(Label::new(if env_var_set {
format!("API key set in {API_KEY_ENV_VAR_NAME} environment variable.")
format!("API key set in {API_KEY_ENV_VAR_NAME} environment variable")
} else {
"API key configured.".to_string()
let api_url = AnthropicLanguageModelProvider::api_url(cx);
if api_url == ANTHROPIC_API_URL {
"API key configured".to_string()
} else {
format!("API key configured for {}", truncate_and_trailoff(&api_url, 32))
}
})),
)
.child(
+24 -10
View File
@@ -1,5 +1,6 @@
use anyhow::{Result, anyhow};
use collections::{BTreeMap, HashMap};
use deepseek::DEEPSEEK_API_URL;
use editor::{Editor, EditorElement, EditorStyle};
use futures::Stream;
use futures::{FutureExt, StreamExt, future, future::BoxFuture, stream::BoxStream};
@@ -23,7 +24,7 @@ use std::str::FromStr;
use std::sync::{Arc, LazyLock};
use theme::ThemeSettings;
use ui::{Icon, IconName, List, prelude::*};
use util::ResultExt;
use util::{ResultExt, truncate_and_trailoff};
use zed_env_vars::{EnvVar, env_var};
use crate::{api_key::ApiKeyState, ui::InstructionListItem};
@@ -70,13 +71,13 @@ impl State {
}
fn set_api_key(&mut self, api_key: Option<String>, cx: &mut Context<Self>) -> Task<Result<()>> {
let api_url = SharedString::new(DeepSeekLanguageModelProvider::api_url(cx));
let api_url = DeepSeekLanguageModelProvider::api_url(cx);
self.api_key_state
.store(api_url, api_key, |this| &mut this.api_key_state, cx)
}
fn authenticate(&mut self, cx: &mut Context<Self>) -> Task<Result<(), AuthenticateError>> {
let api_url = SharedString::new(DeepSeekLanguageModelProvider::api_url(cx));
let api_url = DeepSeekLanguageModelProvider::api_url(cx);
self.api_key_state.load_if_needed(
api_url,
&API_KEY_ENV_VAR,
@@ -90,7 +91,7 @@ impl DeepSeekLanguageModelProvider {
pub fn new(http_client: Arc<dyn HttpClient>, cx: &mut App) -> Self {
let state = cx.new(|cx| {
cx.observe_global::<SettingsStore>(|this: &mut State, cx| {
let api_url = SharedString::new(Self::api_url(cx));
let api_url = Self::api_url(cx);
this.api_key_state.handle_url_change(
api_url,
&API_KEY_ENV_VAR,
@@ -101,7 +102,7 @@ impl DeepSeekLanguageModelProvider {
})
.detach();
State {
api_key_state: ApiKeyState::new(SharedString::new(Self::api_url(cx))),
api_key_state: ApiKeyState::new(Self::api_url(cx)),
}
});
@@ -122,8 +123,13 @@ impl DeepSeekLanguageModelProvider {
&crate::AllLanguageModelSettings::get_global(cx).deepseek
}
fn api_url(cx: &App) -> &str {
&Self::settings(cx).api_url
fn api_url(cx: &App) -> SharedString {
let api_url = &Self::settings(cx).api_url;
if api_url.is_empty() {
DEEPSEEK_API_URL.into()
} else {
SharedString::new(api_url.as_str())
}
}
}
@@ -222,7 +228,7 @@ impl DeepSeekLanguageModel {
let Ok((api_key, api_url)) = self.state.read_with(cx, |state, cx| {
let api_url = DeepSeekLanguageModelProvider::api_url(cx);
(state.api_key_state.key(api_url), api_url.to_string())
(state.api_key_state.key(&api_url), api_url)
}) else {
return future::ready(Err(anyhow!("App state dropped"))).boxed();
};
@@ -691,9 +697,17 @@ impl Render for ConfigurationView {
.gap_1()
.child(Icon::new(IconName::Check).color(Color::Success))
.child(Label::new(if env_var_set {
format!("API key set in {API_KEY_ENV_VAR_NAME}")
format!("API key set in {API_KEY_ENV_VAR_NAME} environment variable")
} else {
"API key configured".to_string()
let api_url = DeepSeekLanguageModelProvider::api_url(cx);
if api_url == DEEPSEEK_API_URL {
"API key configured".to_string()
} else {
format!(
"API key configured for {}",
truncate_and_trailoff(&api_url, 32)
)
}
})),
)
.child(
@@ -33,7 +33,7 @@ use std::sync::{
use strum::IntoEnumIterator;
use theme::ThemeSettings;
use ui::{Icon, IconName, List, Tooltip, prelude::*};
use util::ResultExt;
use util::{ResultExt, truncate_and_trailoff};
use zed_env_vars::EnvVar;
use crate::api_key::ApiKey;
@@ -930,9 +930,14 @@ impl Render for ConfigurationView {
.gap_1()
.child(Icon::new(IconName::Check).color(Color::Success))
.child(Label::new(if env_var_set {
format!("API key set in {GEMINI_API_KEY_VAR_NAME} environment variable.")
format!("API key set in {} environment variable", API_KEY_ENV_VAR.name)
} else {
"API key configured.".to_string()
let api_url = GoogleLanguageModelProvider::api_url(cx);
if api_url == google_ai::API_URL {
"API key configured".to_string()
} else {
format!("API key configured for {}", truncate_and_trailoff(&api_url, 32))
}
})),
)
.child(
@@ -14,7 +14,7 @@ use language_model::{
LanguageModelToolChoice, LanguageModelToolResultContent, LanguageModelToolUse, MessageContent,
RateLimiter, Role, StopReason, TokenUsage,
};
use mistral::StreamResponse;
use mistral::{MISTRAL_API_URL, StreamResponse};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use settings::{Settings, SettingsStore};
@@ -25,7 +25,7 @@ use std::sync::{Arc, LazyLock};
use strum::IntoEnumIterator;
use theme::ThemeSettings;
use ui::{Icon, IconName, List, Tooltip, prelude::*};
use util::ResultExt;
use util::{ResultExt, truncate_and_trailoff};
use zed_env_vars::{EnvVar, env_var};
use crate::{api_key::ApiKeyState, ui::InstructionListItem};
@@ -871,9 +871,14 @@ impl Render for ConfigurationView {
.gap_1()
.child(Icon::new(IconName::Check).color(Color::Success))
.child(Label::new(if env_var_set {
format!("API key set in {API_KEY_ENV_VAR_NAME} environment variable.")
format!("API key set in {API_KEY_ENV_VAR_NAME} environment variable")
} else {
"API key configured.".to_string()
let api_url = MistralLanguageModelProvider::api_url(cx);
if api_url == MISTRAL_API_URL {
"API key configured".to_string()
} else {
format!("API key configured for {}", truncate_and_trailoff(&api_url, 32))
}
})),
)
.child(
+11 -4
View File
@@ -12,7 +12,9 @@ use language_model::{
RateLimiter, Role, StopReason, TokenUsage,
};
use menu;
use open_ai::{ImageUrl, Model, ReasoningEffort, ResponseStreamEvent, stream_completion};
use open_ai::{
ImageUrl, Model, OPEN_AI_API_URL, ReasoningEffort, ResponseStreamEvent, stream_completion,
};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use settings::{Settings, SettingsStore};
@@ -22,7 +24,7 @@ use std::sync::{Arc, LazyLock};
use strum::IntoEnumIterator;
use ui::{ElevationIndex, List, Tooltip, prelude::*};
use ui_input::SingleLineInput;
use util::ResultExt;
use util::{ResultExt, truncate_and_trailoff};
use zed_env_vars::{EnvVar, env_var};
use crate::{api_key::ApiKeyState, ui::InstructionListItem};
@@ -818,9 +820,14 @@ impl Render for ConfigurationView {
.gap_1()
.child(Icon::new(IconName::Check).color(Color::Success))
.child(Label::new(if env_var_set {
format!("API key set in {API_KEY_ENV_VAR_NAME} environment variable.")
format!("API key set in {API_KEY_ENV_VAR_NAME} environment variable")
} else {
"API key configured.".to_string()
let api_url = OpenAiLanguageModelProvider::api_url(cx);
if api_url == OPEN_AI_API_URL {
"API key configured".to_string()
} else {
format!("API key configured for {}", truncate_and_trailoff(&api_url, 32))
}
})),
)
.child(
@@ -17,7 +17,7 @@ use settings::{Settings, SettingsStore};
use std::sync::Arc;
use ui::{ElevationIndex, Tooltip, prelude::*};
use ui_input::SingleLineInput;
use util::ResultExt;
use util::{ResultExt, truncate_and_trailoff};
use zed_env_vars::EnvVar;
use crate::api_key::ApiKeyState;
@@ -488,9 +488,9 @@ impl Render for ConfigurationView {
.gap_1()
.child(Icon::new(IconName::Check).color(Color::Success))
.child(Label::new(if env_var_set {
format!("API key set in {env_var_name} environment variable.")
format!("API key set in {env_var_name} environment variable")
} else {
"API key configured.".to_string()
format!("API key configured for {}", truncate_and_trailoff(&state.settings.api_url, 32))
})),
)
.child(
@@ -14,7 +14,8 @@ use language_model::{
LanguageModelToolUse, MessageContent, RateLimiter, Role, StopReason, TokenUsage,
};
use open_router::{
Model, ModelMode as OpenRouterModelMode, Provider, ResponseStreamEvent, list_models,
Model, ModelMode as OpenRouterModelMode, OPEN_ROUTER_API_URL, Provider, ResponseStreamEvent,
list_models,
};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
@@ -24,7 +25,7 @@ use std::str::FromStr as _;
use std::sync::{Arc, LazyLock};
use theme::ThemeSettings;
use ui::{Icon, IconName, List, Tooltip, prelude::*};
use util::ResultExt;
use util::{ResultExt, truncate_and_trailoff};
use zed_env_vars::{EnvVar, env_var};
use crate::{api_key::ApiKeyState, ui::InstructionListItem};
@@ -199,7 +200,12 @@ impl OpenRouterLanguageModelProvider {
}
fn api_url(cx: &App) -> SharedString {
SharedString::new(Self::settings(cx).api_url.as_str())
let api_url = &Self::settings(cx).api_url;
if api_url.is_empty() {
OPEN_ROUTER_API_URL.into()
} else {
SharedString::new(api_url.as_str())
}
}
fn create_language_model(&self, model: open_router::Model) -> Arc<dyn LanguageModel> {
@@ -904,9 +910,14 @@ impl Render for ConfigurationView {
.gap_1()
.child(Icon::new(IconName::Check).color(Color::Success))
.child(Label::new(if env_var_set {
format!("API key set in {API_KEY_ENV_VAR_NAME} environment variable.")
format!("API key set in {API_KEY_ENV_VAR_NAME} environment variable")
} else {
"API key configured.".to_string()
let api_url = OpenRouterLanguageModelProvider::api_url(cx);
if api_url == OPEN_ROUTER_API_URL {
"API key configured".to_string()
} else {
format!("API key configured for {}", truncate_and_trailoff(&api_url, 32))
}
})),
)
.child(
+10 -5
View File
@@ -17,8 +17,8 @@ use std::sync::{Arc, LazyLock};
use strum::IntoEnumIterator;
use ui::{ElevationIndex, List, Tooltip, prelude::*};
use ui_input::SingleLineInput;
use util::ResultExt;
use vercel::Model;
use util::{ResultExt, truncate_and_trailoff};
use vercel::{Model, VERCEL_API_URL};
use zed_env_vars::{EnvVar, env_var};
use crate::{api_key::ApiKeyState, ui::InstructionListItem};
@@ -114,7 +114,7 @@ impl VercelLanguageModelProvider {
fn api_url(cx: &App) -> SharedString {
let api_url = &Self::settings(cx).api_url;
if api_url.is_empty() {
vercel::VERCEL_API_URL.into()
VERCEL_API_URL.into()
} else {
SharedString::new(api_url.as_str())
}
@@ -502,9 +502,14 @@ impl Render for ConfigurationView {
.gap_1()
.child(Icon::new(IconName::Check).color(Color::Success))
.child(Label::new(if env_var_set {
format!("API key set in {API_KEY_ENV_VAR_NAME} environment variable.")
format!("API key set in {API_KEY_ENV_VAR_NAME} environment variable")
} else {
"API key configured.".to_string()
let api_url = VercelLanguageModelProvider::api_url(cx);
if api_url == VERCEL_API_URL {
"API key configured".to_string()
} else {
format!("API key configured for {}", truncate_and_trailoff(&api_url, 32))
}
})),
)
.child(
+8 -3
View File
@@ -17,7 +17,7 @@ use std::sync::{Arc, LazyLock};
use strum::IntoEnumIterator;
use ui::{ElevationIndex, List, Tooltip, prelude::*};
use ui_input::SingleLineInput;
use util::ResultExt;
use util::{ResultExt, truncate_and_trailoff};
use x_ai::{Model, XAI_API_URL};
use zed_env_vars::{EnvVar, env_var};
@@ -496,9 +496,14 @@ impl Render for ConfigurationView {
.gap_1()
.child(Icon::new(IconName::Check).color(Color::Success))
.child(Label::new(if env_var_set {
format!("API key set in {API_KEY_ENV_VAR_NAME} environment variable.")
format!("API key set in {API_KEY_ENV_VAR_NAME} environment variable")
} else {
"API key configured.".to_string()
let api_url = XAiLanguageModelProvider::api_url(cx);
if api_url == XAI_API_URL {
"API key configured".to_string()
} else {
format!("API key configured for {}", truncate_and_trailoff(&api_url, 32))
}
})),
)
.child(