assistant2: Allow creating agent profiles via settings (#27216)

This PR adds support for creating new agent profiles via the settings:

```json
{
  "assistant": {
    "profiles": {
      "lua": {
        "name": "Lua",
        "tools": {
          "lua-interpreter": true
        }
      },
      "lua-thinking": {
        "name": "Lua + Thinking",
        "tools": {
          "lua-interpreter": true,
          "thinking": true
        }
      }
    }
  }
}
```

Release Notes:

- N/A
This commit is contained in:
Marshall Bowers 2025-03-20 16:30:07 -04:00 committed by GitHub
parent 48b1a43f5e
commit 4b5df2189b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 61 additions and 9 deletions

1
Cargo.lock generated
View file

@ -607,6 +607,7 @@ version = "0.1.0"
dependencies = [ dependencies = [
"anthropic", "anthropic",
"anyhow", "anyhow",
"collections",
"deepseek", "deepseek",
"feature_flags", "feature_flags",
"fs", "fs",

View file

@ -1,5 +1,4 @@
mod active_thread; mod active_thread;
mod agent_profile;
mod assistant_configuration; mod assistant_configuration;
mod assistant_model_selector; mod assistant_model_selector;
mod assistant_panel; mod assistant_panel;

View file

@ -1,23 +1,34 @@
use std::sync::Arc; use std::sync::Arc;
use assistant_settings::{AgentProfile, AssistantSettings};
use assistant_tool::{ToolSource, ToolWorkingSet}; use assistant_tool::{ToolSource, ToolWorkingSet};
use collections::HashMap;
use gpui::Entity; use gpui::Entity;
use scripting_tool::ScriptingTool; use scripting_tool::ScriptingTool;
use settings::Settings as _;
use ui::{prelude::*, ContextMenu, PopoverMenu, Tooltip}; use ui::{prelude::*, ContextMenu, PopoverMenu, Tooltip};
use crate::agent_profile::AgentProfile;
pub struct ToolSelector { pub struct ToolSelector {
profiles: Vec<AgentProfile>, profiles: HashMap<Arc<str>, AgentProfile>,
tools: Arc<ToolWorkingSet>, tools: Arc<ToolWorkingSet>,
} }
impl ToolSelector { impl ToolSelector {
pub fn new(tools: Arc<ToolWorkingSet>, _cx: &mut Context<Self>) -> Self { pub fn new(tools: Arc<ToolWorkingSet>, cx: &mut Context<Self>) -> Self {
Self { let settings = AssistantSettings::get_global(cx);
profiles: vec![AgentProfile::read_only(), AgentProfile::code_writer()], let mut profiles = settings.profiles.clone();
tools,
let read_only = AgentProfile::read_only();
if !profiles.contains_key(read_only.name.as_ref()) {
profiles.insert(read_only.name.clone().into(), read_only);
} }
let code_writer = AgentProfile::code_writer();
if !profiles.contains_key(code_writer.name.as_ref()) {
profiles.insert(code_writer.name.clone().into(), code_writer);
}
Self { profiles, tools }
} }
fn build_context_menu( fn build_context_menu(
@ -31,7 +42,7 @@ impl ToolSelector {
let icon_position = IconPosition::End; let icon_position = IconPosition::End;
menu = menu.header("Profiles"); menu = menu.header("Profiles");
for profile in profiles.clone() { for (_id, profile) in profiles.clone() {
menu = menu.toggleable_entry(profile.name.clone(), false, icon_position, None, { menu = menu.toggleable_entry(profile.name.clone(), false, icon_position, None, {
let tools = tool_set.clone(); let tools = tool_set.clone();
move |_window, cx| { move |_window, cx| {
@ -44,6 +55,10 @@ impl ToolSelector {
.filter_map(|(tool, enabled)| enabled.then(|| tool.clone())) .filter_map(|(tool, enabled)| enabled.then(|| tool.clone()))
.collect::<Vec<_>>(), .collect::<Vec<_>>(),
); );
if profile.tools.contains_key(ScriptingTool::NAME) {
tools.enable_scripting_tool();
}
} }
}); });
} }

View file

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

View file

@ -1,7 +1,10 @@
mod agent_profile;
use std::sync::Arc; use std::sync::Arc;
use ::open_ai::Model as OpenAiModel; use ::open_ai::Model as OpenAiModel;
use anthropic::Model as AnthropicModel; use anthropic::Model as AnthropicModel;
use collections::HashMap;
use deepseek::Model as DeepseekModel; use deepseek::Model as DeepseekModel;
use feature_flags::FeatureFlagAppExt; use feature_flags::FeatureFlagAppExt;
use gpui::{App, Pixels}; use gpui::{App, Pixels};
@ -12,6 +15,8 @@ use schemars::{schema::Schema, JsonSchema};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use settings::{Settings, SettingsSources}; use settings::{Settings, SettingsSources};
pub use crate::agent_profile::*;
#[derive(Copy, Clone, Default, Debug, Serialize, Deserialize, JsonSchema)] #[derive(Copy, Clone, Default, Debug, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "snake_case")] #[serde(rename_all = "snake_case")]
pub enum AssistantDockPosition { pub enum AssistantDockPosition {
@ -66,6 +71,7 @@ pub struct AssistantSettings {
pub inline_alternatives: Vec<LanguageModelSelection>, pub inline_alternatives: Vec<LanguageModelSelection>,
pub using_outdated_settings_version: bool, pub using_outdated_settings_version: bool,
pub enable_experimental_live_diffs: bool, pub enable_experimental_live_diffs: bool,
pub profiles: HashMap<Arc<str>, AgentProfile>,
} }
impl AssistantSettings { impl AssistantSettings {
@ -166,6 +172,7 @@ impl AssistantSettingsContent {
editor_model: None, editor_model: None,
inline_alternatives: None, inline_alternatives: None,
enable_experimental_live_diffs: None, enable_experimental_live_diffs: None,
profiles: None,
}, },
VersionedAssistantSettingsContent::V2(settings) => settings.clone(), VersionedAssistantSettingsContent::V2(settings) => settings.clone(),
}, },
@ -187,6 +194,7 @@ impl AssistantSettingsContent {
editor_model: None, editor_model: None,
inline_alternatives: None, inline_alternatives: None,
enable_experimental_live_diffs: None, enable_experimental_live_diffs: None,
profiles: None,
}, },
} }
} }
@ -316,6 +324,7 @@ impl Default for VersionedAssistantSettingsContent {
editor_model: None, editor_model: None,
inline_alternatives: None, inline_alternatives: None,
enable_experimental_live_diffs: None, enable_experimental_live_diffs: None,
profiles: None,
}) })
} }
} }
@ -352,6 +361,8 @@ pub struct AssistantSettingsContentV2 {
/// ///
/// Default: false /// Default: false
enable_experimental_live_diffs: Option<bool>, enable_experimental_live_diffs: Option<bool>,
#[schemars(skip)]
profiles: Option<HashMap<Arc<str>, AgentProfileContent>>,
} }
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq)] #[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq)]
@ -388,6 +399,12 @@ impl Default for LanguageModelSelection {
} }
} }
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, JsonSchema)]
pub struct AgentProfileContent {
pub name: Arc<str>,
pub tools: HashMap<Arc<str>, bool>,
}
#[derive(Clone, Serialize, Deserialize, JsonSchema, Debug)] #[derive(Clone, Serialize, Deserialize, JsonSchema, Debug)]
pub struct AssistantSettingsContentV1 { pub struct AssistantSettingsContentV1 {
/// Whether the Assistant is enabled. /// Whether the Assistant is enabled.
@ -482,6 +499,24 @@ impl Settings for AssistantSettings {
&mut settings.enable_experimental_live_diffs, &mut settings.enable_experimental_live_diffs,
value.enable_experimental_live_diffs, value.enable_experimental_live_diffs,
); );
merge(
&mut settings.profiles,
value.profiles.map(|profiles| {
profiles
.into_iter()
.map(|(id, profile)| {
(
id,
AgentProfile {
name: profile.name.into(),
tools: profile.tools,
context_servers: HashMap::default(),
},
)
})
.collect()
}),
);
} }
Ok(settings) Ok(settings)
@ -546,6 +581,7 @@ mod tests {
default_width: None, default_width: None,
default_height: None, default_height: None,
enable_experimental_live_diffs: None, enable_experimental_live_diffs: None,
profiles: None,
}), }),
) )
}, },