diff --git a/crates/extension_host/src/extension_host.rs b/crates/extension_host/src/extension_host.rs index f2feb4e8657b056580d790057a099847282d09f9..3b2f1586a62ea854464bc2e73618ebdf815807f6 100644 --- a/crates/extension_host/src/extension_host.rs +++ b/crates/extension_host/src/extension_host.rs @@ -80,6 +80,80 @@ pub use extension_settings::ExtensionSettings; pub const RELOAD_DEBOUNCE_DURATION: Duration = Duration::from_millis(200); const FS_WATCH_LATENCY: Duration = Duration::from_millis(100); +/// Extension IDs that are being migrated from hardcoded LLM providers. +/// For backwards compatibility, if the user has the corresponding env var set, +/// we automatically enable env var reading for these extensions. +const LEGACY_LLM_EXTENSION_IDS: &[&str] = &[ + "anthropic", + "copilot_chat", + "google-ai", + "open_router", + "openai", +]; + +/// Migrates legacy LLM provider extensions by auto-enabling env var reading +/// if the env var is currently present in the environment. +fn migrate_legacy_llm_provider_env_var(manifest: &ExtensionManifest, cx: &mut App) { + // Only apply migration to known legacy LLM extensions + if !LEGACY_LLM_EXTENSION_IDS.contains(&manifest.id.as_ref()) { + return; + } + + // Check each provider in the manifest + for (provider_id, provider_entry) in &manifest.language_model_providers { + let Some(auth_config) = &provider_entry.auth else { + continue; + }; + let Some(env_var_name) = &auth_config.env_var else { + continue; + }; + + // Check if the env var is present and non-empty + let env_var_is_set = std::env::var(env_var_name) + .map(|v| !v.is_empty()) + .unwrap_or(false); + + if !env_var_is_set { + continue; + } + + let full_provider_id: Arc = format!("{}:{}", manifest.id, provider_id).into(); + + // Check if already in settings + let already_allowed = ExtensionSettings::get_global(cx) + .allowed_env_var_providers + .contains(full_provider_id.as_ref()); + + if already_allowed { + continue; + } + + // Auto-enable env var reading for this provider + log::info!( + "Migrating legacy LLM provider {}: auto-enabling {} env var reading", + full_provider_id, + env_var_name + ); + + settings::update_settings_file(::global(cx), cx, { + let full_provider_id = full_provider_id.clone(); + move |settings, _| { + let providers = settings + .extension + .allowed_env_var_providers + .get_or_insert_with(Vec::new); + + if !providers + .iter() + .any(|id| id.as_ref() == full_provider_id.as_ref()) + { + providers.push(full_provider_id); + } + } + }); + } +} + /// The current extension [`SchemaVersion`] supported by Zed. const CURRENT_SCHEMA_VERSION: SchemaVersion = SchemaVersion(1); @@ -781,6 +855,11 @@ impl ExtensionStore { if let ExtensionOperation::Install = operation { this.update(cx, |this, cx| { + // Check for legacy LLM provider migration + if let Some(manifest) = this.extension_manifest_for_id(&extension_id) { + migrate_legacy_llm_provider_env_var(&manifest, cx); + } + cx.emit(Event::ExtensionInstalled(extension_id.clone())); if let Some(events) = ExtensionEvents::try_global(cx) && let Some(manifest) = this.extension_manifest_for_id(&extension_id) diff --git a/extensions/anthropic/extension.toml b/extensions/anthropic/extension.toml index ab27ecda7869c2a3d2d22c6351ae26aaa2c73bef..c37b8aca34f6cfcf2d70715eb24383077e207366 100644 --- a/extensions/anthropic/extension.toml +++ b/extensions/anthropic/extension.toml @@ -8,3 +8,6 @@ repository = "https://github.com/zed-industries/zed" [language_model_providers.anthropic] name = "Anthropic" + +[language_model_providers.anthropic.auth] +env_var = "ANTHROPIC_API_KEY" \ No newline at end of file diff --git a/extensions/copilot_chat/extension.toml b/extensions/copilot_chat/extension.toml index 321a17df003e02c44442f25c5864e0e30082d1ea..9afa188462fcb632fe1e162964997d957c567fbf 100644 --- a/extensions/copilot_chat/extension.toml +++ b/extensions/copilot_chat/extension.toml @@ -8,3 +8,6 @@ repository = "https://github.com/zed-industries/zed" [language_model_providers.copilot_chat] name = "Copilot Chat" + +[language_model_providers.copilot_chat.auth] +env_var = "GH_COPILOT_TOKEN" \ No newline at end of file diff --git a/extensions/google-ai/extension.toml b/extensions/google-ai/extension.toml index a9ed706c1f4d5e26a983e6c47ee3604008a87a9f..1b1cb382a7835d79adf93af225b19ced9a5f551c 100644 --- a/extensions/google-ai/extension.toml +++ b/extensions/google-ai/extension.toml @@ -8,3 +8,6 @@ repository = "https://github.com/zed-industries/zed" [language_model_providers.google-ai] name = "Google AI" + +[language_model_providers.google-ai.auth] +env_var = "GEMINI_API_KEY" \ No newline at end of file diff --git a/extensions/open_router/extension.toml b/extensions/open_router/extension.toml index 41c8c8458ae016b2f34b98db64c9982a03be9612..d321b3d9620c23ca8206af247468162d8f875d0f 100644 --- a/extensions/open_router/extension.toml +++ b/extensions/open_router/extension.toml @@ -8,3 +8,6 @@ repository = "https://github.com/zed-industries/zed" [language_model_providers.open_router] name = "OpenRouter" + +[language_model_providers.open_router.auth] +env_var = "OPENROUTER_API_KEY" \ No newline at end of file diff --git a/extensions/openai/extension.toml b/extensions/openai/extension.toml index b688cd108e91b4e43ad6d2d96200ed2151cf1213..94788688716f1d8a6534684b0cb39febf98585ef 100644 --- a/extensions/openai/extension.toml +++ b/extensions/openai/extension.toml @@ -8,3 +8,6 @@ repository = "https://github.com/zed-industries/zed" [language_model_providers.openai] name = "OpenAI" + +[language_model_providers.openai.auth] +env_var = "OPENAI_API_KEY" \ No newline at end of file