diff --git a/crates/language_models/src/provider/anthropic.rs b/crates/language_models/src/provider/anthropic.rs index e35787b501d74016ce214ac6823ca147e22f6c14..f928dcbcb5c598b6cd3f5ba9def9da29311ebdf6 100644 --- a/crates/language_models/src/provider/anthropic.rs +++ b/crates/language_models/src/provider/anthropic.rs @@ -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( diff --git a/crates/language_models/src/provider/deepseek.rs b/crates/language_models/src/provider/deepseek.rs index 8bd8f4d2ed811e3a481d35b27f32bffc77539c31..0775f26b0bcaf0cd88bb80934baf8710c5c996ef 100644 --- a/crates/language_models/src/provider/deepseek.rs +++ b/crates/language_models/src/provider/deepseek.rs @@ -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, cx: &mut Context) -> Task> { - 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) -> Task> { - 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, cx: &mut App) -> Self { let state = cx.new(|cx| { cx.observe_global::(|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( diff --git a/crates/language_models/src/provider/google.rs b/crates/language_models/src/provider/google.rs index 70b028bfecaeef38468ced2814f40dfe8cb4103a..140968b917db497a04c4185857d6a44da0003a65 100644 --- a/crates/language_models/src/provider/google.rs +++ b/crates/language_models/src/provider/google.rs @@ -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( diff --git a/crates/language_models/src/provider/mistral.rs b/crates/language_models/src/provider/mistral.rs index 260e438efacb1d9db165f8a67107dc412394c8de..682d3549b8ae720ca28629293beb720798d9b583 100644 --- a/crates/language_models/src/provider/mistral.rs +++ b/crates/language_models/src/provider/mistral.rs @@ -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( diff --git a/crates/language_models/src/provider/open_ai.rs b/crates/language_models/src/provider/open_ai.rs index 050ddd09ab9da5f93cedec8ad204b0b8d6d726c3..ceb8d1144cdb73d4dde3e1cffdfdb780e6ff888e 100644 --- a/crates/language_models/src/provider/open_ai.rs +++ b/crates/language_models/src/provider/open_ai.rs @@ -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( diff --git a/crates/language_models/src/provider/open_ai_compatible.rs b/crates/language_models/src/provider/open_ai_compatible.rs index 83cbaddb7076c6665dc82300cc71b8b8d037f1fb..9a64a6d340b500d651dc88d3138e75f7c5d98528 100644 --- a/crates/language_models/src/provider/open_ai_compatible.rs +++ b/crates/language_models/src/provider/open_ai_compatible.rs @@ -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( diff --git a/crates/language_models/src/provider/open_router.rs b/crates/language_models/src/provider/open_router.rs index 116f40e3ad4490f63e27d82ea08abec2407f407a..170beab5dae84423501127835a7f560d142fe00a 100644 --- a/crates/language_models/src/provider/open_router.rs +++ b/crates/language_models/src/provider/open_router.rs @@ -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 { @@ -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( diff --git a/crates/language_models/src/provider/vercel.rs b/crates/language_models/src/provider/vercel.rs index abf63bd8e27b4ba08df8b7e2549fffc0578c138d..6fce79fd85462ed8a8e45d342b43ee83270adc25 100644 --- a/crates/language_models/src/provider/vercel.rs +++ b/crates/language_models/src/provider/vercel.rs @@ -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( diff --git a/crates/language_models/src/provider/x_ai.rs b/crates/language_models/src/provider/x_ai.rs index eba95d1c146045b3e4482ffe2db34c8a166530ef..51b59a04b3eea21e45926a3383893cdde09a7b8f 100644 --- a/crates/language_models/src/provider/x_ai.rs +++ b/crates/language_models/src/provider/x_ai.rs @@ -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(