assistant2: Order agent profiles in the order they are defined in settings (#27255)

Marshall Bowers created

This PR updates the ordering of the agent profiles in the tool selector
to respect the order they are defined in in the settings instead of
sorting them alphabetically.

This gives the user more control, and allows them to order the profiles
as they desire.

Release Notes:

- N/A

Change summary

Cargo.lock                                          |  3 ++-
crates/assistant2/Cargo.toml                        |  1 +
crates/assistant2/src/tool_selector.rs              |  8 ++++----
crates/assistant_settings/Cargo.toml                |  2 +-
crates/assistant_settings/src/agent_profile.rs      |  8 ++++----
crates/assistant_settings/src/assistant_settings.rs | 10 +++++-----
6 files changed, 17 insertions(+), 15 deletions(-)

Detailed changes

Cargo.lock 🔗

@@ -472,6 +472,7 @@ dependencies = [
  "heed",
  "html_to_markdown",
  "http_client",
+ "indexmap",
  "indoc",
  "itertools 0.14.0",
  "language",
@@ -608,11 +609,11 @@ version = "0.1.0"
 dependencies = [
  "anthropic",
  "anyhow",
- "collections",
  "deepseek",
  "feature_flags",
  "fs",
  "gpui",
+ "indexmap",
  "language_model",
  "lmstudio",
  "log",

crates/assistant2/Cargo.toml 🔗

@@ -44,6 +44,7 @@ gpui.workspace = true
 heed.workspace = true
 html_to_markdown.workspace = true
 http_client.workspace = true
+indexmap.workspace = true
 itertools.workspace = true
 language.workspace = true
 language_model.workspace = true

crates/assistant2/src/tool_selector.rs 🔗

@@ -2,14 +2,14 @@ use std::sync::Arc;
 
 use assistant_settings::{AgentProfile, AssistantSettings};
 use assistant_tool::{ToolSource, ToolWorkingSet};
-use collections::BTreeMap;
 use gpui::{Entity, Subscription};
+use indexmap::IndexMap;
 use scripting_tool::ScriptingTool;
 use settings::{Settings as _, SettingsStore};
 use ui::{prelude::*, ContextMenu, PopoverMenu, Tooltip};
 
 pub struct ToolSelector {
-    profiles: BTreeMap<Arc<str>, AgentProfile>,
+    profiles: IndexMap<Arc<str>, AgentProfile>,
     tools: Arc<ToolWorkingSet>,
     _subscriptions: Vec<Subscription>,
 }
@@ -21,7 +21,7 @@ impl ToolSelector {
         });
 
         let mut this = Self {
-            profiles: BTreeMap::default(),
+            profiles: IndexMap::default(),
             tools,
             _subscriptions: vec![settings_subscription],
         };
@@ -33,7 +33,7 @@ impl ToolSelector {
     fn refresh_profiles(&mut self, cx: &mut Context<Self>) {
         let settings = AssistantSettings::get_global(cx);
 
-        self.profiles = BTreeMap::from_iter(settings.profiles.clone());
+        self.profiles = settings.profiles.clone();
     }
 
     fn build_context_menu(

crates/assistant_settings/Cargo.toml 🔗

@@ -14,9 +14,9 @@ path = "src/assistant_settings.rs"
 [dependencies]
 anthropic = { workspace = true, features = ["schemars"] }
 anyhow.workspace = true
-collections.workspace = true
 feature_flags.workspace = true
 gpui.workspace = true
+indexmap.workspace = true
 language_model.workspace = true
 lmstudio = { workspace = true, features = ["schemars"] }
 log.workspace = true

crates/assistant_settings/src/agent_profile.rs 🔗

@@ -1,20 +1,20 @@
 use std::sync::Arc;
 
-use collections::HashMap;
 use gpui::SharedString;
+use indexmap::IndexMap;
 
 /// A profile for the Zed Agent that controls its behavior.
 #[derive(Debug, Clone)]
 pub struct AgentProfile {
     /// The name of the profile.
     pub name: SharedString,
-    pub tools: HashMap<Arc<str>, bool>,
+    pub tools: IndexMap<Arc<str>, bool>,
     #[allow(dead_code)]
-    pub context_servers: HashMap<Arc<str>, ContextServerPreset>,
+    pub context_servers: IndexMap<Arc<str>, ContextServerPreset>,
 }
 
 #[derive(Debug, Clone)]
 pub struct ContextServerPreset {
     #[allow(dead_code)]
-    pub tools: HashMap<Arc<str>, bool>,
+    pub tools: IndexMap<Arc<str>, bool>,
 }

crates/assistant_settings/src/assistant_settings.rs 🔗

@@ -4,10 +4,10 @@ use std::sync::Arc;
 
 use ::open_ai::Model as OpenAiModel;
 use anthropic::Model as AnthropicModel;
-use collections::HashMap;
 use deepseek::Model as DeepseekModel;
 use feature_flags::FeatureFlagAppExt;
 use gpui::{App, Pixels};
+use indexmap::IndexMap;
 use language_model::{CloudModel, LanguageModel};
 use lmstudio::Model as LmStudioModel;
 use ollama::Model as OllamaModel;
@@ -71,7 +71,7 @@ pub struct AssistantSettings {
     pub inline_alternatives: Vec<LanguageModelSelection>,
     pub using_outdated_settings_version: bool,
     pub enable_experimental_live_diffs: bool,
-    pub profiles: HashMap<Arc<str>, AgentProfile>,
+    pub profiles: IndexMap<Arc<str>, AgentProfile>,
 }
 
 impl AssistantSettings {
@@ -362,7 +362,7 @@ pub struct AssistantSettingsContentV2 {
     /// Default: false
     enable_experimental_live_diffs: Option<bool>,
     #[schemars(skip)]
-    profiles: Option<HashMap<Arc<str>, AgentProfileContent>>,
+    profiles: Option<IndexMap<Arc<str>, AgentProfileContent>>,
 }
 
 #[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq)]
@@ -402,7 +402,7 @@ impl Default for LanguageModelSelection {
 #[derive(Debug, PartialEq, Clone, Serialize, Deserialize, JsonSchema)]
 pub struct AgentProfileContent {
     pub name: Arc<str>,
-    pub tools: HashMap<Arc<str>, bool>,
+    pub tools: IndexMap<Arc<str>, bool>,
 }
 
 #[derive(Clone, Serialize, Deserialize, JsonSchema, Debug)]
@@ -509,7 +509,7 @@ impl Settings for AssistantSettings {
                             AgentProfile {
                                 name: profile.name.into(),
                                 tools: profile.tools,
-                                context_servers: HashMap::default(),
+                                context_servers: IndexMap::default(),
                             },
                         )
                     }));