diff --git a/Cargo.lock b/Cargo.lock index 4684bec47e..16ccb89fc6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -110,18 +110,11 @@ dependencies = [ name = "agent_settings" version = "0.1.0" dependencies = [ - "anthropic", "anyhow", "collections", - "deepseek", "fs", "gpui", "language_model", - "lmstudio", - "log", - "mistral", - "ollama", - "open_ai", "paths", "schemars", "serde", diff --git a/crates/agent_settings/Cargo.toml b/crates/agent_settings/Cargo.toml index c6a4bedbb5..3afe5ae547 100644 --- a/crates/agent_settings/Cargo.toml +++ b/crates/agent_settings/Cargo.toml @@ -12,17 +12,10 @@ workspace = true path = "src/agent_settings.rs" [dependencies] -anthropic = { workspace = true, features = ["schemars"] } anyhow.workspace = true collections.workspace = true gpui.workspace = true language_model.workspace = true -lmstudio = { workspace = true, features = ["schemars"] } -log.workspace = true -ollama = { workspace = true, features = ["schemars"] } -open_ai = { workspace = true, features = ["schemars"] } -deepseek = { workspace = true, features = ["schemars"] } -mistral = { workspace = true, features = ["schemars"] } schemars.workspace = true serde.workspace = true settings.workspace = true diff --git a/crates/agent_settings/src/agent_settings.rs b/crates/agent_settings/src/agent_settings.rs index a1162b8066..294d793e79 100644 --- a/crates/agent_settings/src/agent_settings.rs +++ b/crates/agent_settings/src/agent_settings.rs @@ -2,16 +2,10 @@ mod agent_profile; use std::sync::Arc; -use ::open_ai::Model as OpenAiModel; -use anthropic::Model as AnthropicModel; use anyhow::{Result, bail}; use collections::IndexMap; -use deepseek::Model as DeepseekModel; use gpui::{App, Pixels, SharedString}; use language_model::LanguageModel; -use lmstudio::Model as LmStudioModel; -use mistral::Model as MistralModel; -use ollama::Model as OllamaModel; use schemars::{JsonSchema, schema::Schema}; use serde::{Deserialize, Serialize}; use settings::{Settings, SettingsSources}; @@ -48,45 +42,6 @@ pub enum NotifyWhenAgentWaiting { Never, } -#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq)] -#[serde(tag = "name", rename_all = "snake_case")] -#[schemars(deny_unknown_fields)] -pub enum AgentProviderContentV1 { - #[serde(rename = "zed.dev")] - ZedDotDev { default_model: Option }, - #[serde(rename = "openai")] - OpenAi { - default_model: Option, - api_url: Option, - available_models: Option>, - }, - #[serde(rename = "anthropic")] - Anthropic { - default_model: Option, - api_url: Option, - }, - #[serde(rename = "ollama")] - Ollama { - default_model: Option, - api_url: Option, - }, - #[serde(rename = "lmstudio")] - LmStudio { - default_model: Option, - api_url: Option, - }, - #[serde(rename = "deepseek")] - DeepSeek { - default_model: Option, - api_url: Option, - }, - #[serde(rename = "mistral")] - Mistral { - default_model: Option, - api_url: Option, - }, -} - #[derive(Default, Clone, Debug)] pub struct AgentSettings { pub enabled: bool, @@ -168,366 +123,56 @@ impl LanguageModelParameters { } } -/// Agent panel settings -#[derive(Clone, Serialize, Deserialize, Debug, Default)] -pub struct AgentSettingsContent { - #[serde(flatten)] - pub inner: Option, -} - -#[derive(Clone, Serialize, Deserialize, Debug)] -#[serde(untagged)] -pub enum AgentSettingsContentInner { - Versioned(Box), - Legacy(LegacyAgentSettingsContent), -} - -impl AgentSettingsContentInner { - fn for_v2(content: AgentSettingsContentV2) -> Self { - AgentSettingsContentInner::Versioned(Box::new(VersionedAgentSettingsContent::V2(content))) - } -} - -impl JsonSchema for AgentSettingsContent { - fn schema_name() -> String { - VersionedAgentSettingsContent::schema_name() - } - - fn json_schema(r#gen: &mut schemars::r#gen::SchemaGenerator) -> Schema { - VersionedAgentSettingsContent::json_schema(r#gen) - } - - fn is_referenceable() -> bool { - VersionedAgentSettingsContent::is_referenceable() - } -} - impl AgentSettingsContent { - pub fn is_version_outdated(&self) -> bool { - match &self.inner { - Some(AgentSettingsContentInner::Versioned(settings)) => match **settings { - VersionedAgentSettingsContent::V1(_) => true, - VersionedAgentSettingsContent::V2(_) => false, - }, - Some(AgentSettingsContentInner::Legacy(_)) => true, - None => false, - } - } - - fn upgrade(&self) -> AgentSettingsContentV2 { - match &self.inner { - Some(AgentSettingsContentInner::Versioned(settings)) => match **settings { - VersionedAgentSettingsContent::V1(ref settings) => AgentSettingsContentV2 { - enabled: settings.enabled, - button: settings.button, - dock: settings.dock, - default_width: settings.default_width, - default_height: settings.default_width, - default_model: settings - .provider - .clone() - .and_then(|provider| match provider { - AgentProviderContentV1::ZedDotDev { default_model } => default_model - .map(|model| LanguageModelSelection { - provider: "zed.dev".into(), - model, - }), - AgentProviderContentV1::OpenAi { default_model, .. } => default_model - .map(|model| LanguageModelSelection { - provider: "openai".into(), - model: model.id().to_string(), - }), - AgentProviderContentV1::Anthropic { default_model, .. } => { - default_model.map(|model| LanguageModelSelection { - provider: "anthropic".into(), - model: model.id().to_string(), - }) - } - AgentProviderContentV1::Ollama { default_model, .. } => default_model - .map(|model| LanguageModelSelection { - provider: "ollama".into(), - model: model.id().to_string(), - }), - AgentProviderContentV1::LmStudio { default_model, .. } => default_model - .map(|model| LanguageModelSelection { - provider: "lmstudio".into(), - model: model.id().to_string(), - }), - AgentProviderContentV1::DeepSeek { default_model, .. } => default_model - .map(|model| LanguageModelSelection { - provider: "deepseek".into(), - model: model.id().to_string(), - }), - AgentProviderContentV1::Mistral { default_model, .. } => default_model - .map(|model| LanguageModelSelection { - provider: "mistral".into(), - model: model.id().to_string(), - }), - }), - inline_assistant_model: None, - commit_message_model: None, - thread_summary_model: None, - inline_alternatives: None, - default_profile: None, - default_view: None, - profiles: None, - always_allow_tool_actions: None, - notify_when_agent_waiting: None, - stream_edits: None, - single_file_review: None, - model_parameters: Vec::new(), - preferred_completion_mode: None, - enable_feedback: None, - play_sound_when_agent_done: None, - }, - VersionedAgentSettingsContent::V2(ref settings) => settings.clone(), - }, - Some(AgentSettingsContentInner::Legacy(settings)) => AgentSettingsContentV2 { - enabled: None, - button: settings.button, - dock: settings.dock, - default_width: settings.default_width, - default_height: settings.default_height, - default_model: Some(LanguageModelSelection { - provider: "openai".into(), - model: settings - .default_open_ai_model - .clone() - .unwrap_or_default() - .id() - .to_string(), - }), - inline_assistant_model: None, - commit_message_model: None, - thread_summary_model: None, - inline_alternatives: None, - default_profile: None, - default_view: None, - profiles: None, - always_allow_tool_actions: None, - notify_when_agent_waiting: None, - stream_edits: None, - single_file_review: None, - model_parameters: Vec::new(), - preferred_completion_mode: None, - enable_feedback: None, - play_sound_when_agent_done: None, - }, - None => AgentSettingsContentV2::default(), - } - } - pub fn set_dock(&mut self, dock: AgentDockPosition) { - match &mut self.inner { - Some(AgentSettingsContentInner::Versioned(settings)) => match **settings { - VersionedAgentSettingsContent::V1(ref mut settings) => { - settings.dock = Some(dock); - } - VersionedAgentSettingsContent::V2(ref mut settings) => { - settings.dock = Some(dock); - } - }, - Some(AgentSettingsContentInner::Legacy(settings)) => { - settings.dock = Some(dock); - } - None => { - self.inner = Some(AgentSettingsContentInner::for_v2(AgentSettingsContentV2 { - dock: Some(dock), - ..Default::default() - })) - } - } + self.dock = Some(dock); } pub fn set_model(&mut self, language_model: Arc) { let model = language_model.id().0.to_string(); let provider = language_model.provider_id().0.to_string(); - match &mut self.inner { - Some(AgentSettingsContentInner::Versioned(settings)) => match **settings { - VersionedAgentSettingsContent::V1(ref mut settings) => match provider.as_ref() { - "zed.dev" => { - log::warn!("attempted to set zed.dev model on outdated settings"); - } - "anthropic" => { - let api_url = match &settings.provider { - Some(AgentProviderContentV1::Anthropic { api_url, .. }) => { - api_url.clone() - } - _ => None, - }; - settings.provider = Some(AgentProviderContentV1::Anthropic { - default_model: AnthropicModel::from_id(&model).ok(), - api_url, - }); - } - "ollama" => { - let api_url = match &settings.provider { - Some(AgentProviderContentV1::Ollama { api_url, .. }) => api_url.clone(), - _ => None, - }; - settings.provider = Some(AgentProviderContentV1::Ollama { - default_model: Some(ollama::Model::new( - &model, - None, - None, - Some(language_model.supports_tools()), - Some(language_model.supports_images()), - None, - )), - api_url, - }); - } - "lmstudio" => { - let api_url = match &settings.provider { - Some(AgentProviderContentV1::LmStudio { api_url, .. }) => { - api_url.clone() - } - _ => None, - }; - settings.provider = Some(AgentProviderContentV1::LmStudio { - default_model: Some(lmstudio::Model::new( - &model, None, None, false, false, - )), - api_url, - }); - } - "openai" => { - let (api_url, available_models) = match &settings.provider { - Some(AgentProviderContentV1::OpenAi { - api_url, - available_models, - .. - }) => (api_url.clone(), available_models.clone()), - _ => (None, None), - }; - settings.provider = Some(AgentProviderContentV1::OpenAi { - default_model: OpenAiModel::from_id(&model).ok(), - api_url, - available_models, - }); - } - "deepseek" => { - let api_url = match &settings.provider { - Some(AgentProviderContentV1::DeepSeek { api_url, .. }) => { - api_url.clone() - } - _ => None, - }; - settings.provider = Some(AgentProviderContentV1::DeepSeek { - default_model: DeepseekModel::from_id(&model).ok(), - api_url, - }); - } - _ => {} - }, - VersionedAgentSettingsContent::V2(ref mut settings) => { - settings.default_model = Some(LanguageModelSelection { - provider: provider.into(), - model, - }); - } - }, - Some(AgentSettingsContentInner::Legacy(settings)) => { - if let Ok(model) = OpenAiModel::from_id(&language_model.id().0) { - settings.default_open_ai_model = Some(model); - } - } - None => { - self.inner = Some(AgentSettingsContentInner::for_v2(AgentSettingsContentV2 { - default_model: Some(LanguageModelSelection { - provider: provider.into(), - model, - }), - ..Default::default() - })); - } - } + self.default_model = Some(LanguageModelSelection { + provider: provider.into(), + model, + }); } pub fn set_inline_assistant_model(&mut self, provider: String, model: String) { - self.v2_setting(|setting| { - setting.inline_assistant_model = Some(LanguageModelSelection { - provider: provider.into(), - model, - }); - Ok(()) - }) - .ok(); + self.inline_assistant_model = Some(LanguageModelSelection { + provider: provider.into(), + model, + }); } pub fn set_commit_message_model(&mut self, provider: String, model: String) { - self.v2_setting(|setting| { - setting.commit_message_model = Some(LanguageModelSelection { - provider: provider.into(), - model, - }); - Ok(()) - }) - .ok(); - } - - pub fn v2_setting( - &mut self, - f: impl FnOnce(&mut AgentSettingsContentV2) -> anyhow::Result<()>, - ) -> anyhow::Result<()> { - match self.inner.get_or_insert_with(|| { - AgentSettingsContentInner::for_v2(AgentSettingsContentV2 { - ..Default::default() - }) - }) { - AgentSettingsContentInner::Versioned(boxed) => { - if let VersionedAgentSettingsContent::V2(ref mut settings) = **boxed { - f(settings) - } else { - Ok(()) - } - } - _ => Ok(()), - } + self.commit_message_model = Some(LanguageModelSelection { + provider: provider.into(), + model, + }); } pub fn set_thread_summary_model(&mut self, provider: String, model: String) { - self.v2_setting(|setting| { - setting.thread_summary_model = Some(LanguageModelSelection { - provider: provider.into(), - model, - }); - Ok(()) - }) - .ok(); + self.thread_summary_model = Some(LanguageModelSelection { + provider: provider.into(), + model, + }); } pub fn set_always_allow_tool_actions(&mut self, allow: bool) { - self.v2_setting(|setting| { - setting.always_allow_tool_actions = Some(allow); - Ok(()) - }) - .ok(); + self.always_allow_tool_actions = Some(allow); } pub fn set_play_sound_when_agent_done(&mut self, allow: bool) { - self.v2_setting(|setting| { - setting.play_sound_when_agent_done = Some(allow); - Ok(()) - }) - .ok(); + self.play_sound_when_agent_done = Some(allow); } pub fn set_single_file_review(&mut self, allow: bool) { - self.v2_setting(|setting| { - setting.single_file_review = Some(allow); - Ok(()) - }) - .ok(); + self.single_file_review = Some(allow); } pub fn set_profile(&mut self, profile_id: AgentProfileId) { - self.v2_setting(|setting| { - setting.default_profile = Some(profile_id); - Ok(()) - }) - .ok(); + self.default_profile = Some(profile_id); } pub fn create_profile( @@ -535,79 +180,39 @@ impl AgentSettingsContent { profile_id: AgentProfileId, profile_settings: AgentProfileSettings, ) -> Result<()> { - self.v2_setting(|settings| { - let profiles = settings.profiles.get_or_insert_default(); - if profiles.contains_key(&profile_id) { - bail!("profile with ID '{profile_id}' already exists"); - } + let profiles = self.profiles.get_or_insert_default(); + if profiles.contains_key(&profile_id) { + bail!("profile with ID '{profile_id}' already exists"); + } - profiles.insert( - profile_id, - AgentProfileContent { - name: profile_settings.name.into(), - tools: profile_settings.tools, - enable_all_context_servers: Some(profile_settings.enable_all_context_servers), - context_servers: profile_settings - .context_servers - .into_iter() - .map(|(server_id, preset)| { - ( - server_id, - ContextServerPresetContent { - tools: preset.tools, - }, - ) - }) - .collect(), - }, - ); + profiles.insert( + profile_id, + AgentProfileContent { + name: profile_settings.name.into(), + tools: profile_settings.tools, + enable_all_context_servers: Some(profile_settings.enable_all_context_servers), + context_servers: profile_settings + .context_servers + .into_iter() + .map(|(server_id, preset)| { + ( + server_id, + ContextServerPresetContent { + tools: preset.tools, + }, + ) + }) + .collect(), + }, + ); - Ok(()) - }) - } -} - -#[derive(Clone, Serialize, Deserialize, JsonSchema, Debug)] -#[serde(tag = "version")] -#[schemars(deny_unknown_fields)] -pub enum VersionedAgentSettingsContent { - #[serde(rename = "1")] - V1(AgentSettingsContentV1), - #[serde(rename = "2")] - V2(AgentSettingsContentV2), -} - -impl Default for VersionedAgentSettingsContent { - fn default() -> Self { - Self::V2(AgentSettingsContentV2 { - enabled: None, - button: None, - dock: None, - default_width: None, - default_height: None, - default_model: None, - inline_assistant_model: None, - commit_message_model: None, - thread_summary_model: None, - inline_alternatives: None, - default_profile: None, - default_view: None, - profiles: None, - always_allow_tool_actions: None, - notify_when_agent_waiting: None, - stream_edits: None, - single_file_review: None, - model_parameters: Vec::new(), - preferred_completion_mode: None, - enable_feedback: None, - play_sound_when_agent_done: None, - }) + Ok(()) } } #[derive(Clone, Serialize, Deserialize, JsonSchema, Debug, Default)] #[schemars(deny_unknown_fields)] -pub struct AgentSettingsContentV2 { +pub struct AgentSettingsContent { /// Whether the Agent is enabled. /// /// Default: true @@ -779,65 +384,6 @@ pub struct ContextServerPresetContent { pub tools: IndexMap, bool>, } -#[derive(Clone, Serialize, Deserialize, JsonSchema, Debug)] -#[schemars(deny_unknown_fields)] -pub struct AgentSettingsContentV1 { - /// Whether the Agent is enabled. - /// - /// Default: true - enabled: Option, - /// Whether to show the Agent panel button in the status bar. - /// - /// Default: true - button: Option, - /// Where to dock the Agent. - /// - /// Default: right - dock: Option, - /// Default width in pixels when the Agent is docked to the left or right. - /// - /// Default: 640 - default_width: Option, - /// Default height in pixels when the Agent is docked to the bottom. - /// - /// Default: 320 - default_height: Option, - /// The provider of the Agent service. - /// - /// This can be "openai", "anthropic", "ollama", "lmstudio", "deepseek", "zed.dev" - /// each with their respective default models and configurations. - provider: Option, -} - -#[derive(Clone, Serialize, Deserialize, JsonSchema, Debug)] -#[schemars(deny_unknown_fields)] -pub struct LegacyAgentSettingsContent { - /// Whether to show the Agent panel button in the status bar. - /// - /// Default: true - pub button: Option, - /// Where to dock the Agent. - /// - /// Default: right - pub dock: Option, - /// Default width in pixels when the Agent is docked to the left or right. - /// - /// Default: 640 - pub default_width: Option, - /// Default height in pixels when the Agent is docked to the bottom. - /// - /// Default: 320 - pub default_height: Option, - /// The default OpenAI model to use when creating new chats. - /// - /// Default: gpt-4-1106-preview - pub default_open_ai_model: Option, - /// OpenAI API base URL to use when creating new chats. - /// - /// Default: - pub openai_api_url: Option, -} - impl Settings for AgentSettings { const KEY: Option<&'static str> = Some("agent"); @@ -854,11 +400,6 @@ impl Settings for AgentSettings { let mut settings = AgentSettings::default(); for value in sources.defaults_and_customizations() { - if value.is_version_outdated() { - settings.using_outdated_settings_version = true; - } - - let value = value.upgrade(); merge(&mut settings.enabled, value.enabled); merge(&mut settings.button, value.button); merge(&mut settings.dock, value.dock); @@ -870,17 +411,23 @@ impl Settings for AgentSettings { &mut settings.default_height, value.default_height.map(Into::into), ); - merge(&mut settings.default_model, value.default_model); + merge(&mut settings.default_model, value.default_model.clone()); settings.inline_assistant_model = value .inline_assistant_model + .clone() .or(settings.inline_assistant_model.take()); settings.commit_message_model = value + .clone() .commit_message_model .or(settings.commit_message_model.take()); settings.thread_summary_model = value + .clone() .thread_summary_model .or(settings.thread_summary_model.take()); - merge(&mut settings.inline_alternatives, value.inline_alternatives); + merge( + &mut settings.inline_alternatives, + value.inline_alternatives.clone(), + ); merge( &mut settings.always_allow_tool_actions, value.always_allow_tool_actions, @@ -895,7 +442,7 @@ impl Settings for AgentSettings { ); merge(&mut settings.stream_edits, value.stream_edits); merge(&mut settings.single_file_review, value.single_file_review); - merge(&mut settings.default_profile, value.default_profile); + merge(&mut settings.default_profile, value.default_profile.clone()); merge(&mut settings.default_view, value.default_view); merge( &mut settings.preferred_completion_mode, @@ -907,24 +454,24 @@ impl Settings for AgentSettings { .model_parameters .extend_from_slice(&value.model_parameters); - if let Some(profiles) = value.profiles { + if let Some(profiles) = value.profiles.as_ref() { settings .profiles .extend(profiles.into_iter().map(|(id, profile)| { ( - id, + id.clone(), AgentProfileSettings { - name: profile.name.into(), - tools: profile.tools, + name: profile.name.clone().into(), + tools: profile.tools.clone(), enable_all_context_servers: profile .enable_all_context_servers .unwrap_or_default(), context_servers: profile .context_servers - .into_iter() + .iter() .map(|(context_server_id, preset)| { ( - context_server_id, + context_server_id.clone(), ContextServerPreset { tools: preset.tools.clone(), }, @@ -945,28 +492,8 @@ impl Settings for AgentSettings { .read_value("chat.agent.enabled") .and_then(|b| b.as_bool()) { - match &mut current.inner { - Some(AgentSettingsContentInner::Versioned(versioned)) => match versioned.as_mut() { - VersionedAgentSettingsContent::V1(setting) => { - setting.enabled = Some(b); - setting.button = Some(b); - } - - VersionedAgentSettingsContent::V2(setting) => { - setting.enabled = Some(b); - setting.button = Some(b); - } - }, - Some(AgentSettingsContentInner::Legacy(setting)) => setting.button = Some(b), - None => { - current.inner = - Some(AgentSettingsContentInner::for_v2(AgentSettingsContentV2 { - enabled: Some(b), - button: Some(b), - ..Default::default() - })); - } - } + current.enabled = Some(b); + current.button = Some(b); } } } @@ -976,149 +503,3 @@ fn merge(target: &mut T, value: Option) { *target = value; } } - -#[cfg(test)] -mod tests { - use fs::Fs; - use gpui::{ReadGlobal, TestAppContext}; - use settings::SettingsStore; - - use super::*; - - #[gpui::test] - async fn test_deserialize_agent_settings_with_version(cx: &mut TestAppContext) { - let fs = fs::FakeFs::new(cx.executor().clone()); - fs.create_dir(paths::settings_file().parent().unwrap()) - .await - .unwrap(); - - cx.update(|cx| { - let test_settings = settings::SettingsStore::test(cx); - cx.set_global(test_settings); - AgentSettings::register(cx); - }); - - cx.update(|cx| { - assert!(!AgentSettings::get_global(cx).using_outdated_settings_version); - assert_eq!( - AgentSettings::get_global(cx).default_model, - LanguageModelSelection { - provider: "zed.dev".into(), - model: "claude-sonnet-4".into(), - } - ); - }); - - cx.update(|cx| { - settings::SettingsStore::global(cx).update_settings_file::( - fs.clone(), - |settings, _| { - *settings = AgentSettingsContent { - inner: Some(AgentSettingsContentInner::for_v2(AgentSettingsContentV2 { - default_model: Some(LanguageModelSelection { - provider: "test-provider".into(), - model: "gpt-99".into(), - }), - inline_assistant_model: None, - commit_message_model: None, - thread_summary_model: None, - inline_alternatives: None, - enabled: None, - button: None, - dock: None, - default_width: None, - default_height: None, - default_profile: None, - default_view: None, - profiles: None, - always_allow_tool_actions: None, - play_sound_when_agent_done: None, - notify_when_agent_waiting: None, - stream_edits: None, - single_file_review: None, - enable_feedback: None, - model_parameters: Vec::new(), - preferred_completion_mode: None, - })), - } - }, - ); - }); - - cx.run_until_parked(); - - let raw_settings_value = fs.load(paths::settings_file()).await.unwrap(); - assert!(raw_settings_value.contains(r#""version": "2""#)); - - #[derive(Debug, Deserialize)] - struct AgentSettingsTest { - agent: AgentSettingsContent, - } - - let agent_settings: AgentSettingsTest = - serde_json_lenient::from_str(&raw_settings_value).unwrap(); - - assert!(!agent_settings.agent.is_version_outdated()); - } - - #[gpui::test] - async fn test_load_settings_from_old_key(cx: &mut TestAppContext) { - let fs = fs::FakeFs::new(cx.executor().clone()); - fs.create_dir(paths::settings_file().parent().unwrap()) - .await - .unwrap(); - - cx.update(|cx| { - let mut test_settings = settings::SettingsStore::test(cx); - let user_settings_content = r#"{ - "assistant": { - "enabled": true, - "version": "2", - "default_model": { - "provider": "zed.dev", - "model": "gpt-99" - }, - }}"#; - test_settings - .set_user_settings(user_settings_content, cx) - .unwrap(); - cx.set_global(test_settings); - AgentSettings::register(cx); - }); - - cx.run_until_parked(); - - let agent_settings = cx.update(|cx| AgentSettings::get_global(cx).clone()); - assert!(agent_settings.enabled); - assert!(!agent_settings.using_outdated_settings_version); - assert_eq!(agent_settings.default_model.model, "gpt-99"); - - cx.update_global::(|settings_store, cx| { - settings_store.update_user_settings::(cx, |settings| { - *settings = AgentSettingsContent { - inner: Some(AgentSettingsContentInner::for_v2(AgentSettingsContentV2 { - enabled: Some(false), - default_model: Some(LanguageModelSelection { - provider: "xai".to_owned().into(), - model: "grok".to_owned(), - }), - ..Default::default() - })), - }; - }); - }); - - cx.run_until_parked(); - - let settings = cx.update(|cx| SettingsStore::global(cx).raw_user_settings().clone()); - - #[derive(Debug, Deserialize)] - struct AgentSettingsTest { - assistant: AgentSettingsContent, - agent: Option, - } - - let agent_settings: AgentSettingsTest = serde_json::from_value(settings).unwrap(); - assert!(agent_settings.agent.is_none()); - } -} diff --git a/crates/agent_ui/src/agent_configuration/tool_picker.rs b/crates/agent_ui/src/agent_configuration/tool_picker.rs index 7c3d20457e..8f1e0d71c0 100644 --- a/crates/agent_ui/src/agent_configuration/tool_picker.rs +++ b/crates/agent_ui/src/agent_configuration/tool_picker.rs @@ -272,42 +272,35 @@ impl PickerDelegate for ToolPickerDelegate { let server_id = server_id.clone(); let tool_name = tool_name.clone(); move |settings: &mut AgentSettingsContent, _cx| { - settings - .v2_setting(|v2_settings| { - let profiles = v2_settings.profiles.get_or_insert_default(); - let profile = - profiles - .entry(profile_id) - .or_insert_with(|| AgentProfileContent { - name: default_profile.name.into(), - tools: default_profile.tools, - enable_all_context_servers: Some( - default_profile.enable_all_context_servers, - ), - context_servers: default_profile - .context_servers - .into_iter() - .map(|(server_id, preset)| { - ( - server_id, - ContextServerPresetContent { - tools: preset.tools, - }, - ) - }) - .collect(), - }); + let profiles = settings.profiles.get_or_insert_default(); + let profile = profiles + .entry(profile_id) + .or_insert_with(|| AgentProfileContent { + name: default_profile.name.into(), + tools: default_profile.tools, + enable_all_context_servers: Some( + default_profile.enable_all_context_servers, + ), + context_servers: default_profile + .context_servers + .into_iter() + .map(|(server_id, preset)| { + ( + server_id, + ContextServerPresetContent { + tools: preset.tools, + }, + ) + }) + .collect(), + }); - if let Some(server_id) = server_id { - let preset = profile.context_servers.entry(server_id).or_default(); - *preset.tools.entry(tool_name).or_default() = !is_currently_enabled; - } else { - *profile.tools.entry(tool_name).or_default() = !is_currently_enabled; - } - - Ok(()) - }) - .ok(); + if let Some(server_id) = server_id { + let preset = profile.context_servers.entry(server_id).or_default(); + *preset.tools.entry(tool_name).or_default() = !is_currently_enabled; + } else { + *profile.tools.entry(tool_name).or_default() = !is_currently_enabled; + } } }); } diff --git a/crates/assistant_tools/src/edit_agent/evals.rs b/crates/assistant_tools/src/edit_agent/evals.rs index 116654e382..7beb2ec919 100644 --- a/crates/assistant_tools/src/edit_agent/evals.rs +++ b/crates/assistant_tools/src/edit_agent/evals.rs @@ -1470,7 +1470,7 @@ impl EditAgentTest { Project::init_settings(cx); language::init(cx); language_model::init(client.clone(), cx); - language_models::init(user_store.clone(), client.clone(), fs.clone(), cx); + language_models::init(user_store.clone(), client.clone(), cx); crate::init(client.http_client(), cx); }); diff --git a/crates/eval/src/eval.rs b/crates/eval/src/eval.rs index e5132b0f33..5e8dd8961c 100644 --- a/crates/eval/src/eval.rs +++ b/crates/eval/src/eval.rs @@ -417,7 +417,7 @@ pub fn init(cx: &mut App) -> Arc { debug_adapter_extension::init(extension_host_proxy.clone(), cx); language_extension::init(extension_host_proxy.clone(), languages.clone()); language_model::init(client.clone(), cx); - language_models::init(user_store.clone(), client.clone(), fs.clone(), cx); + language_models::init(user_store.clone(), client.clone(), cx); languages::init(languages.clone(), node_runtime.clone(), cx); prompt_store::init(cx); terminal_view::init(cx); diff --git a/crates/language_models/Cargo.toml b/crates/language_models/Cargo.toml index 80412cb5d2..d6aff380aa 100644 --- a/crates/language_models/Cargo.toml +++ b/crates/language_models/Cargo.toml @@ -42,7 +42,6 @@ open_ai = { workspace = true, features = ["schemars"] } open_router = { workspace = true, features = ["schemars"] } vercel = { workspace = true, features = ["schemars"] } partial-json-fixer.workspace = true -project.workspace = true proto.workspace = true release_channel.workspace = true schemars.workspace = true diff --git a/crates/language_models/src/language_models.rs b/crates/language_models/src/language_models.rs index 78dbc33c51..c7324732c9 100644 --- a/crates/language_models/src/language_models.rs +++ b/crates/language_models/src/language_models.rs @@ -1,7 +1,6 @@ use std::sync::Arc; use client::{Client, UserStore}; -use fs::Fs; use gpui::{App, Context, Entity}; use language_model::LanguageModelRegistry; use provider::deepseek::DeepSeekLanguageModelProvider; @@ -23,8 +22,8 @@ use crate::provider::open_router::OpenRouterLanguageModelProvider; use crate::provider::vercel::VercelLanguageModelProvider; pub use crate::settings::*; -pub fn init(user_store: Entity, client: Arc, fs: Arc, cx: &mut App) { - crate::settings::init(fs, cx); +pub fn init(user_store: Entity, client: Arc, cx: &mut App) { + crate::settings::init(cx); let registry = LanguageModelRegistry::global(cx); registry.update(cx, |registry, cx| { register_language_model_providers(registry, user_store, client, cx); diff --git a/crates/language_models/src/provider/anthropic.rs b/crates/language_models/src/provider/anthropic.rs index d19348eed6..48bea47fec 100644 --- a/crates/language_models/src/provider/anthropic.rs +++ b/crates/language_models/src/provider/anthropic.rs @@ -41,7 +41,6 @@ pub struct AnthropicSettings { pub api_url: String, /// Extend Zed's list of Anthropic models. pub available_models: Vec, - pub needs_setting_migration: bool, } #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema)] diff --git a/crates/language_models/src/provider/mistral.rs b/crates/language_models/src/provider/mistral.rs index 5e46c41746..171ce05896 100644 --- a/crates/language_models/src/provider/mistral.rs +++ b/crates/language_models/src/provider/mistral.rs @@ -36,7 +36,6 @@ const PROVIDER_NAME: &str = "Mistral"; pub struct MistralSettings { pub api_url: String, pub available_models: Vec, - pub needs_setting_migration: bool, } #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema)] diff --git a/crates/language_models/src/provider/open_ai.rs b/crates/language_models/src/provider/open_ai.rs index 56a81d36e9..ad4203ff81 100644 --- a/crates/language_models/src/provider/open_ai.rs +++ b/crates/language_models/src/provider/open_ai.rs @@ -28,6 +28,7 @@ use ui::{ElevationIndex, List, Tooltip, prelude::*}; use ui_input::SingleLineInput; use util::ResultExt; +use crate::OpenAiSettingsContent; use crate::{AllLanguageModelSettings, ui::InstructionListItem}; const PROVIDER_ID: &str = "openai"; @@ -37,7 +38,6 @@ const PROVIDER_NAME: &str = "OpenAI"; pub struct OpenAiSettings { pub api_url: String, pub available_models: Vec, - pub needs_setting_migration: bool, } #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema)] @@ -803,30 +803,13 @@ impl ConfigurationView { if !api_url.is_empty() && api_url != effective_current_url { let fs = ::global(cx); update_settings_file::(fs, cx, move |settings, _| { - use crate::settings::{OpenAiSettingsContent, VersionedOpenAiSettingsContent}; - - if settings.openai.is_none() { - settings.openai = Some(OpenAiSettingsContent::Versioned( - VersionedOpenAiSettingsContent::V1( - crate::settings::OpenAiSettingsContentV1 { - api_url: Some(api_url.clone()), - available_models: None, - }, - ), - )); + if let Some(settings) = settings.openai.as_mut() { + settings.api_url = Some(api_url.clone()); } else { - if let Some(openai) = settings.openai.as_mut() { - match openai { - OpenAiSettingsContent::Versioned(versioned) => match versioned { - VersionedOpenAiSettingsContent::V1(v1) => { - v1.api_url = Some(api_url.clone()); - } - }, - OpenAiSettingsContent::Legacy(legacy) => { - legacy.api_url = Some(api_url.clone()); - } - } - } + settings.openai = Some(OpenAiSettingsContent { + api_url: Some(api_url.clone()), + available_models: None, + }); } }); } @@ -840,19 +823,8 @@ impl ConfigurationView { }); let fs = ::global(cx); update_settings_file::(fs, cx, |settings, _cx| { - use crate::settings::{OpenAiSettingsContent, VersionedOpenAiSettingsContent}; - - if let Some(openai) = settings.openai.as_mut() { - match openai { - OpenAiSettingsContent::Versioned(versioned) => match versioned { - VersionedOpenAiSettingsContent::V1(v1) => { - v1.api_url = None; - } - }, - OpenAiSettingsContent::Legacy(legacy) => { - legacy.api_url = None; - } - } + if let Some(settings) = settings.openai.as_mut() { + settings.api_url = None; } }); cx.notify(); diff --git a/crates/language_models/src/provider/vercel.rs b/crates/language_models/src/provider/vercel.rs index c86902fe76..2f64115d20 100644 --- a/crates/language_models/src/provider/vercel.rs +++ b/crates/language_models/src/provider/vercel.rs @@ -32,7 +32,6 @@ const PROVIDER_NAME: &str = "Vercel"; pub struct VercelSettings { pub api_url: String, pub available_models: Vec, - pub needs_setting_migration: bool, } #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema)] diff --git a/crates/language_models/src/settings.rs b/crates/language_models/src/settings.rs index 644e59d397..f96a2c0a66 100644 --- a/crates/language_models/src/settings.rs +++ b/crates/language_models/src/settings.rs @@ -1,12 +1,8 @@ -use std::sync::Arc; - use anyhow::Result; use gpui::App; -use language_model::LanguageModelCacheConfiguration; -use project::Fs; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use settings::{Settings, SettingsSources, update_settings_file}; +use settings::{Settings, SettingsSources}; use crate::provider::{ self, @@ -24,36 +20,8 @@ use crate::provider::{ }; /// Initializes the language model settings. -pub fn init(fs: Arc, cx: &mut App) { +pub fn init(cx: &mut App) { AllLanguageModelSettings::register(cx); - - if AllLanguageModelSettings::get_global(cx) - .openai - .needs_setting_migration - { - update_settings_file::(fs.clone(), cx, move |setting, _| { - if let Some(settings) = setting.openai.clone() { - let (newest_version, _) = settings.upgrade(); - setting.openai = Some(OpenAiSettingsContent::Versioned( - VersionedOpenAiSettingsContent::V1(newest_version), - )); - } - }); - } - - if AllLanguageModelSettings::get_global(cx) - .anthropic - .needs_setting_migration - { - update_settings_file::(fs, cx, move |setting, _| { - if let Some(settings) = setting.anthropic.clone() { - let (newest_version, _) = settings.upgrade(); - setting.anthropic = Some(AnthropicSettingsContent::Versioned( - VersionedAnthropicSettingsContent::V1(newest_version), - )); - } - }); - } } #[derive(Default)] @@ -90,78 +58,7 @@ pub struct AllLanguageModelSettingsContent { } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema)] -#[serde(untagged)] -pub enum AnthropicSettingsContent { - Versioned(VersionedAnthropicSettingsContent), - Legacy(LegacyAnthropicSettingsContent), -} - -impl AnthropicSettingsContent { - pub fn upgrade(self) -> (AnthropicSettingsContentV1, bool) { - match self { - AnthropicSettingsContent::Legacy(content) => ( - AnthropicSettingsContentV1 { - api_url: content.api_url, - available_models: content.available_models.map(|models| { - models - .into_iter() - .filter_map(|model| match model { - anthropic::Model::Custom { - name, - display_name, - max_tokens, - tool_override, - cache_configuration, - max_output_tokens, - default_temperature, - extra_beta_headers, - mode, - } => Some(provider::anthropic::AvailableModel { - name, - display_name, - max_tokens, - tool_override, - cache_configuration: cache_configuration.as_ref().map( - |config| LanguageModelCacheConfiguration { - max_cache_anchors: config.max_cache_anchors, - should_speculate: config.should_speculate, - min_total_token: config.min_total_token, - }, - ), - max_output_tokens, - default_temperature, - extra_beta_headers, - mode: Some(mode.into()), - }), - _ => None, - }) - .collect() - }), - }, - true, - ), - AnthropicSettingsContent::Versioned(content) => match content { - VersionedAnthropicSettingsContent::V1(content) => (content, false), - }, - } - } -} - -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema)] -pub struct LegacyAnthropicSettingsContent { - pub api_url: Option, - pub available_models: Option>, -} - -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema)] -#[serde(tag = "version")] -pub enum VersionedAnthropicSettingsContent { - #[serde(rename = "1")] - V1(AnthropicSettingsContentV1), -} - -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema)] -pub struct AnthropicSettingsContentV1 { +pub struct AnthropicSettingsContent { pub api_url: Option, pub available_models: Option>, } @@ -200,64 +97,7 @@ pub struct MistralSettingsContent { } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema)] -#[serde(untagged)] -pub enum OpenAiSettingsContent { - Versioned(VersionedOpenAiSettingsContent), - Legacy(LegacyOpenAiSettingsContent), -} - -impl OpenAiSettingsContent { - pub fn upgrade(self) -> (OpenAiSettingsContentV1, bool) { - match self { - OpenAiSettingsContent::Legacy(content) => ( - OpenAiSettingsContentV1 { - api_url: content.api_url, - available_models: content.available_models.map(|models| { - models - .into_iter() - .filter_map(|model| match model { - open_ai::Model::Custom { - name, - display_name, - max_tokens, - max_output_tokens, - max_completion_tokens, - } => Some(provider::open_ai::AvailableModel { - name, - max_tokens, - max_output_tokens, - display_name, - max_completion_tokens, - }), - _ => None, - }) - .collect() - }), - }, - true, - ), - OpenAiSettingsContent::Versioned(content) => match content { - VersionedOpenAiSettingsContent::V1(content) => (content, false), - }, - } - } -} - -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema)] -pub struct LegacyOpenAiSettingsContent { - pub api_url: Option, - pub available_models: Option>, -} - -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema)] -#[serde(tag = "version")] -pub enum VersionedOpenAiSettingsContent { - #[serde(rename = "1")] - V1(OpenAiSettingsContentV1), -} - -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema)] -pub struct OpenAiSettingsContentV1 { +pub struct OpenAiSettingsContent { pub api_url: Option, pub available_models: Option>, } @@ -303,15 +143,7 @@ impl settings::Settings for AllLanguageModelSettings { for value in sources.defaults_and_customizations() { // Anthropic - let (anthropic, upgraded) = match value.anthropic.clone().map(|s| s.upgrade()) { - Some((content, upgraded)) => (Some(content), upgraded), - None => (None, false), - }; - - if upgraded { - settings.anthropic.needs_setting_migration = true; - } - + let anthropic = value.anthropic.clone(); merge( &mut settings.anthropic.api_url, anthropic.as_ref().and_then(|s| s.api_url.clone()), @@ -377,15 +209,7 @@ impl settings::Settings for AllLanguageModelSettings { ); // OpenAI - let (openai, upgraded) = match value.openai.clone().map(|s| s.upgrade()) { - Some((content, upgraded)) => (Some(content), upgraded), - None => (None, false), - }; - - if upgraded { - settings.openai.needs_setting_migration = true; - } - + let openai = value.openai.clone(); merge( &mut settings.openai.api_url, openai.as_ref().and_then(|s| s.api_url.clone()), diff --git a/crates/migrator/src/migrations.rs b/crates/migrator/src/migrations.rs index 281ae93123..d43521faa9 100644 --- a/crates/migrator/src/migrations.rs +++ b/crates/migrator/src/migrations.rs @@ -81,3 +81,9 @@ pub(crate) mod m_2025_06_16 { pub(crate) use settings::SETTINGS_PATTERNS; } + +pub(crate) mod m_2025_06_25 { + mod settings; + + pub(crate) use settings::SETTINGS_PATTERNS; +} diff --git a/crates/migrator/src/migrations/m_2025_06_25/settings.rs b/crates/migrator/src/migrations/m_2025_06_25/settings.rs new file mode 100644 index 0000000000..5dd6c3093a --- /dev/null +++ b/crates/migrator/src/migrations/m_2025_06_25/settings.rs @@ -0,0 +1,133 @@ +use std::ops::Range; +use tree_sitter::{Query, QueryMatch}; + +use crate::MigrationPatterns; + +pub const SETTINGS_PATTERNS: MigrationPatterns = &[ + (SETTINGS_VERSION_PATTERN, remove_version_fields), + ( + SETTINGS_NESTED_VERSION_PATTERN, + remove_nested_version_fields, + ), +]; + +const SETTINGS_VERSION_PATTERN: &str = r#"(document + (object + (pair + key: (string (string_content) @key) + value: (object + (pair + key: (string (string_content) @version_key) + value: (_) @version_value + ) @version_pair + ) + ) + ) + (#eq? @key "agent") + (#eq? @version_key "version") +)"#; + +const SETTINGS_NESTED_VERSION_PATTERN: &str = r#"(document + (object + (pair + key: (string (string_content) @language_models) + value: (object + (pair + key: (string (string_content) @provider) + value: (object + (pair + key: (string (string_content) @version_key) + value: (_) @version_value + ) @version_pair + ) + ) + ) + ) + ) + (#eq? @language_models "language_models") + (#match? @provider "^(anthropic|openai)$") + (#eq? @version_key "version") +)"#; + +fn remove_version_fields( + contents: &str, + mat: &QueryMatch, + query: &Query, +) -> Option<(Range, String)> { + let version_pair_ix = query.capture_index_for_name("version_pair")?; + let version_pair_node = mat.nodes_for_capture_index(version_pair_ix).next()?; + + remove_pair_with_whitespace(contents, version_pair_node) +} + +fn remove_nested_version_fields( + contents: &str, + mat: &QueryMatch, + query: &Query, +) -> Option<(Range, String)> { + let version_pair_ix = query.capture_index_for_name("version_pair")?; + let version_pair_node = mat.nodes_for_capture_index(version_pair_ix).next()?; + + remove_pair_with_whitespace(contents, version_pair_node) +} + +fn remove_pair_with_whitespace( + contents: &str, + pair_node: tree_sitter::Node, +) -> Option<(Range, String)> { + let mut range_to_remove = pair_node.byte_range(); + + // Check if there's a comma after this pair + if let Some(next_sibling) = pair_node.next_sibling() { + if next_sibling.kind() == "," { + range_to_remove.end = next_sibling.end_byte(); + } + } else { + // If no next sibling, check if there's a comma before + if let Some(prev_sibling) = pair_node.prev_sibling() { + if prev_sibling.kind() == "," { + range_to_remove.start = prev_sibling.start_byte(); + } + } + } + + // Include any leading whitespace/newline, including comments + let text_before = &contents[..range_to_remove.start]; + if let Some(last_newline) = text_before.rfind('\n') { + let whitespace_start = last_newline + 1; + let potential_whitespace = &contents[whitespace_start..range_to_remove.start]; + + // Check if it's only whitespace or comments + let mut is_whitespace_or_comment = true; + let mut in_comment = false; + let mut chars = potential_whitespace.chars().peekable(); + + while let Some(ch) = chars.next() { + if in_comment { + if ch == '\n' { + in_comment = false; + } + } else if ch == '/' && chars.peek() == Some(&'/') { + in_comment = true; + chars.next(); // Skip the second '/' + } else if !ch.is_whitespace() { + is_whitespace_or_comment = false; + break; + } + } + + if is_whitespace_or_comment { + range_to_remove.start = whitespace_start; + } + } + + // Also check if we need to include trailing whitespace up to the next line + let text_after = &contents[range_to_remove.end..]; + if let Some(newline_pos) = text_after.find('\n') { + if text_after[..newline_pos].chars().all(|c| c.is_whitespace()) { + range_to_remove.end += newline_pos + 1; + } + } + + Some((range_to_remove, String::new())) +} diff --git a/crates/migrator/src/migrator.rs b/crates/migrator/src/migrator.rs index b45744b766..bcd41836e6 100644 --- a/crates/migrator/src/migrator.rs +++ b/crates/migrator/src/migrator.rs @@ -152,6 +152,10 @@ pub fn migrate_settings(text: &str) -> Result> { migrations::m_2025_06_16::SETTINGS_PATTERNS, &SETTINGS_QUERY_2025_06_16, ), + ( + migrations::m_2025_06_25::SETTINGS_PATTERNS, + &SETTINGS_QUERY_2025_06_25, + ), ]; run_migrations(text, migrations) } @@ -254,6 +258,10 @@ define_query!( SETTINGS_QUERY_2025_06_16, migrations::m_2025_06_16::SETTINGS_PATTERNS ); +define_query!( + SETTINGS_QUERY_2025_06_25, + migrations::m_2025_06_25::SETTINGS_PATTERNS +); // custom query static EDIT_PREDICTION_SETTINGS_MIGRATION_QUERY: LazyLock = LazyLock::new(|| { @@ -1052,4 +1060,75 @@ mod tests { }"#; assert_migrate_settings(settings, None); } + + #[test] + fn test_remove_version_fields() { + assert_migrate_settings( + r#"{ + "language_models": { + "anthropic": { + "version": "1", + "api_url": "https://api.anthropic.com" + }, + "openai": { + "version": "1", + "api_url": "https://api.openai.com/v1" + } + }, + "agent": { + "version": "2", + "enabled": true, + "preferred_completion_mode": "normal", + "button": true, + "dock": "right", + "default_width": 640, + "default_height": 320, + "default_model": { + "provider": "zed.dev", + "model": "claude-sonnet-4" + } + } +}"#, + Some( + r#"{ + "language_models": { + "anthropic": { + "api_url": "https://api.anthropic.com" + }, + "openai": { + "api_url": "https://api.openai.com/v1" + } + }, + "agent": { + "enabled": true, + "preferred_completion_mode": "normal", + "button": true, + "dock": "right", + "default_width": 640, + "default_height": 320, + "default_model": { + "provider": "zed.dev", + "model": "claude-sonnet-4" + } + } +}"#, + ), + ); + + // Test that version fields in other contexts are not removed + assert_migrate_settings( + r#"{ + "language_models": { + "other_provider": { + "version": "1", + "api_url": "https://api.example.com" + } + }, + "other_section": { + "version": "1" + } +}"#, + None, + ); + } } diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index 1b8b1d697d..0e08b304f7 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -516,12 +516,7 @@ pub fn main() { ); supermaven::init(app_state.client.clone(), cx); language_model::init(app_state.client.clone(), cx); - language_models::init( - app_state.user_store.clone(), - app_state.client.clone(), - app_state.fs.clone(), - cx, - ); + language_models::init(app_state.user_store.clone(), app_state.client.clone(), cx); web_search::init(cx); web_search_providers::init(app_state.client.clone(), cx); snippet_provider::init(cx); diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index 510cdb2b46..c57a9b576a 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -4441,12 +4441,7 @@ mod tests { ); image_viewer::init(cx); language_model::init(app_state.client.clone(), cx); - language_models::init( - app_state.user_store.clone(), - app_state.client.clone(), - app_state.fs.clone(), - cx, - ); + language_models::init(app_state.user_store.clone(), app_state.client.clone(), cx); web_search::init(cx); web_search_providers::init(app_state.client.clone(), cx); let prompt_builder = PromptBuilder::load(app_state.fs.clone(), false, cx);