agent_profile.rs

  1use std::sync::Arc;
  2
  3use collections::IndexMap;
  4use convert_case::{Case, Casing as _};
  5use fs::Fs;
  6use gpui::{App, SharedString};
  7use schemars::JsonSchema;
  8use serde::{Deserialize, Serialize};
  9use settings::{Settings as _, update_settings_file};
 10use util::ResultExt as _;
 11
 12use crate::AgentSettings;
 13
 14pub mod builtin_profiles {
 15    use super::AgentProfileId;
 16
 17    pub const WRITE: &str = "write";
 18    pub const ASK: &str = "ask";
 19    pub const MINIMAL: &str = "minimal";
 20
 21    pub fn is_builtin(profile_id: &AgentProfileId) -> bool {
 22        profile_id.as_str() == WRITE || profile_id.as_str() == ASK || profile_id.as_str() == MINIMAL
 23    }
 24}
 25
 26#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, JsonSchema)]
 27pub struct AgentProfileId(pub Arc<str>);
 28
 29impl AgentProfileId {
 30    pub fn as_str(&self) -> &str {
 31        &self.0
 32    }
 33}
 34
 35impl std::fmt::Display for AgentProfileId {
 36    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 37        write!(f, "{}", self.0)
 38    }
 39}
 40
 41impl Default for AgentProfileId {
 42    fn default() -> Self {
 43        Self("write".into())
 44    }
 45}
 46
 47#[derive(Clone, Debug, Eq, PartialEq)]
 48pub struct AgentProfile {
 49    id: AgentProfileId,
 50}
 51
 52pub type AvailableProfiles = IndexMap<AgentProfileId, SharedString>;
 53
 54impl AgentProfile {
 55    pub fn new(id: AgentProfileId) -> Self {
 56        Self { id }
 57    }
 58
 59    pub fn id(&self) -> &AgentProfileId {
 60        &self.id
 61    }
 62
 63    /// Saves a new profile to the settings.
 64    pub fn create(
 65        name: String,
 66        base_profile_id: Option<AgentProfileId>,
 67        fs: Arc<dyn Fs>,
 68        cx: &App,
 69    ) -> AgentProfileId {
 70        let id = AgentProfileId(name.to_case(Case::Kebab).into());
 71
 72        let base_profile =
 73            base_profile_id.and_then(|id| AgentSettings::get_global(cx).profiles.get(&id).cloned());
 74
 75        let profile_settings = AgentProfileSettings {
 76            name: name.into(),
 77            tools: base_profile
 78                .as_ref()
 79                .map(|profile| profile.tools.clone())
 80                .unwrap_or_default(),
 81            enable_all_context_servers: base_profile
 82                .as_ref()
 83                .map(|profile| profile.enable_all_context_servers)
 84                .unwrap_or_default(),
 85            context_servers: base_profile
 86                .map(|profile| profile.context_servers)
 87                .unwrap_or_default(),
 88        };
 89
 90        update_settings_file::<AgentSettings>(fs, cx, {
 91            let id = id.clone();
 92            move |settings, _cx| {
 93                settings.create_profile(id, profile_settings).log_err();
 94            }
 95        });
 96
 97        id
 98    }
 99
100    /// Returns a map of AgentProfileIds to their names
101    pub fn available_profiles(cx: &App) -> AvailableProfiles {
102        let mut profiles = AvailableProfiles::default();
103        for (id, profile) in AgentSettings::get_global(cx).profiles.iter() {
104            profiles.insert(id.clone(), profile.name.clone());
105        }
106        profiles
107    }
108}
109
110/// A profile for the Zed Agent that controls its behavior.
111#[derive(Debug, Clone)]
112pub struct AgentProfileSettings {
113    /// The name of the profile.
114    pub name: SharedString,
115    pub tools: IndexMap<Arc<str>, bool>,
116    pub enable_all_context_servers: bool,
117    pub context_servers: IndexMap<Arc<str>, ContextServerPreset>,
118}
119
120impl AgentProfileSettings {
121    pub fn is_tool_enabled(&self, tool_name: &str) -> bool {
122        self.tools.get(tool_name) == Some(&true)
123    }
124
125    pub fn is_context_server_tool_enabled(&self, server_id: &str, tool_name: &str) -> bool {
126        self.enable_all_context_servers
127            || self
128                .context_servers
129                .get(server_id)
130                .is_some_and(|preset| preset.tools.get(tool_name) == Some(&true))
131    }
132}
133
134#[derive(Debug, Clone, Default)]
135pub struct ContextServerPreset {
136    pub tools: IndexMap<Arc<str>, bool>,
137}