agent_settings.rs

  1mod agent_profile;
  2
  3use std::sync::Arc;
  4
  5use collections::IndexMap;
  6use gpui::{App, Pixels, px};
  7use language_model::LanguageModel;
  8use schemars::JsonSchema;
  9use serde::{Deserialize, Serialize};
 10use settings::{
 11    DefaultAgentView, DockPosition, LanguageModelParameters, LanguageModelSelection,
 12    NotifyWhenAgentWaiting, Settings, SettingsContent,
 13};
 14use util::MergeFrom;
 15
 16pub use crate::agent_profile::*;
 17
 18pub const SUMMARIZE_THREAD_PROMPT: &str =
 19    include_str!("../../agent/src/prompts/summarize_thread_prompt.txt");
 20pub const SUMMARIZE_THREAD_DETAILED_PROMPT: &str =
 21    include_str!("../../agent/src/prompts/summarize_thread_detailed_prompt.txt");
 22
 23pub fn init(cx: &mut App) {
 24    AgentSettings::register(cx);
 25}
 26
 27#[derive(Clone, Debug)]
 28pub struct AgentSettings {
 29    pub enabled: bool,
 30    pub button: bool,
 31    pub dock: DockPosition,
 32    pub default_width: Pixels,
 33    pub default_height: Pixels,
 34    pub default_model: Option<LanguageModelSelection>,
 35    pub inline_assistant_model: Option<LanguageModelSelection>,
 36    pub commit_message_model: Option<LanguageModelSelection>,
 37    pub thread_summary_model: Option<LanguageModelSelection>,
 38    pub inline_alternatives: Vec<LanguageModelSelection>,
 39    pub default_profile: AgentProfileId,
 40    pub default_view: DefaultAgentView,
 41    pub profiles: IndexMap<AgentProfileId, AgentProfileSettings>,
 42    pub always_allow_tool_actions: bool,
 43    pub notify_when_agent_waiting: NotifyWhenAgentWaiting,
 44    pub play_sound_when_agent_done: bool,
 45    pub stream_edits: bool,
 46    pub single_file_review: bool,
 47    pub model_parameters: Vec<LanguageModelParameters>,
 48    pub preferred_completion_mode: CompletionMode,
 49    pub enable_feedback: bool,
 50    pub expand_edit_card: bool,
 51    pub expand_terminal_card: bool,
 52    pub use_modifier_to_send: bool,
 53    pub message_editor_min_lines: usize,
 54}
 55
 56impl AgentSettings {
 57    pub fn temperature_for_model(model: &Arc<dyn LanguageModel>, cx: &App) -> Option<f32> {
 58        let settings = Self::get_global(cx);
 59        for setting in settings.model_parameters.iter().rev() {
 60            if let Some(provider) = &setting.provider
 61                && provider.0 != model.provider_id().0
 62            {
 63                continue;
 64            }
 65            if let Some(setting_model) = &setting.model
 66                && *setting_model != model.id().0
 67            {
 68                continue;
 69            }
 70            return setting.temperature;
 71        }
 72        return None;
 73    }
 74
 75    pub fn set_inline_assistant_model(&mut self, provider: String, model: String) {
 76        self.inline_assistant_model = Some(LanguageModelSelection {
 77            provider: provider.into(),
 78            model,
 79        });
 80    }
 81
 82    pub fn set_commit_message_model(&mut self, provider: String, model: String) {
 83        self.commit_message_model = Some(LanguageModelSelection {
 84            provider: provider.into(),
 85            model,
 86        });
 87    }
 88
 89    pub fn set_thread_summary_model(&mut self, provider: String, model: String) {
 90        self.thread_summary_model = Some(LanguageModelSelection {
 91            provider: provider.into(),
 92            model,
 93        });
 94    }
 95
 96    pub fn set_message_editor_max_lines(&self) -> usize {
 97        self.message_editor_min_lines * 2
 98    }
 99}
100
101#[derive(Clone, Copy, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Default)]
102#[serde(rename_all = "snake_case")]
103pub enum CompletionMode {
104    #[default]
105    Normal,
106    #[serde(alias = "max")]
107    Burn,
108}
109
110impl From<CompletionMode> for cloud_llm_client::CompletionMode {
111    fn from(value: CompletionMode) -> Self {
112        match value {
113            CompletionMode::Normal => cloud_llm_client::CompletionMode::Normal,
114            CompletionMode::Burn => cloud_llm_client::CompletionMode::Max,
115        }
116    }
117}
118
119impl From<settings::CompletionMode> for CompletionMode {
120    fn from(value: settings::CompletionMode) -> Self {
121        match value {
122            settings::CompletionMode::Normal => CompletionMode::Normal,
123            settings::CompletionMode::Burn => CompletionMode::Burn,
124        }
125    }
126}
127
128#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, JsonSchema)]
129pub struct AgentProfileId(pub Arc<str>);
130
131impl AgentProfileId {
132    pub fn as_str(&self) -> &str {
133        &self.0
134    }
135}
136
137impl std::fmt::Display for AgentProfileId {
138    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
139        write!(f, "{}", self.0)
140    }
141}
142
143impl Default for AgentProfileId {
144    fn default() -> Self {
145        Self("write".into())
146    }
147}
148
149impl Settings for AgentSettings {
150    fn from_defaults(content: &settings::SettingsContent, _cx: &mut App) -> Self {
151        let agent = content.agent.clone().unwrap();
152        Self {
153            enabled: agent.enabled.unwrap(),
154            button: agent.button.unwrap(),
155            dock: agent.dock.unwrap(),
156            default_width: px(agent.default_width.unwrap()),
157            default_height: px(agent.default_height.unwrap()),
158            default_model: Some(agent.default_model.unwrap()),
159            inline_assistant_model: agent.inline_assistant_model,
160            commit_message_model: agent.commit_message_model,
161            thread_summary_model: agent.thread_summary_model,
162            inline_alternatives: agent.inline_alternatives.unwrap_or_default(),
163            default_profile: AgentProfileId(agent.default_profile.unwrap()),
164            default_view: agent.default_view.unwrap(),
165            profiles: agent
166                .profiles
167                .unwrap()
168                .into_iter()
169                .map(|(key, val)| (AgentProfileId(key), val.into()))
170                .collect(),
171            always_allow_tool_actions: agent.always_allow_tool_actions.unwrap(),
172            notify_when_agent_waiting: agent.notify_when_agent_waiting.unwrap(),
173            play_sound_when_agent_done: agent.play_sound_when_agent_done.unwrap(),
174            stream_edits: agent.stream_edits.unwrap(),
175            single_file_review: agent.single_file_review.unwrap(),
176            model_parameters: agent.model_parameters,
177            preferred_completion_mode: agent.preferred_completion_mode.unwrap().into(),
178            enable_feedback: agent.enable_feedback.unwrap(),
179            expand_edit_card: agent.expand_edit_card.unwrap(),
180            expand_terminal_card: agent.expand_terminal_card.unwrap(),
181            use_modifier_to_send: agent.use_modifier_to_send.unwrap(),
182            message_editor_min_lines: agent.message_editor_min_lines.unwrap(),
183        }
184    }
185
186    fn refine(&mut self, content: &settings::SettingsContent, _: &mut App) {
187        let Some(value) = &content.agent else { return };
188        self.enabled.merge_from(&value.enabled);
189        self.button.merge_from(&value.button);
190        self.dock.merge_from(&value.dock);
191        self.default_width
192            .merge_from(&value.default_width.map(Into::into));
193        self.default_height
194            .merge_from(&value.default_height.map(Into::into));
195        self.default_model = value.default_model.clone().or(self.default_model.take());
196
197        self.inline_assistant_model = value
198            .inline_assistant_model
199            .clone()
200            .or(self.inline_assistant_model.take());
201        self.commit_message_model = value
202            .clone()
203            .commit_message_model
204            .or(self.commit_message_model.take());
205        self.thread_summary_model = value
206            .clone()
207            .thread_summary_model
208            .or(self.thread_summary_model.take());
209        self.inline_alternatives
210            .merge_from(&value.inline_alternatives.clone());
211        self.default_profile
212            .merge_from(&value.default_profile.clone().map(AgentProfileId));
213        self.default_view.merge_from(&value.default_view);
214        self.always_allow_tool_actions
215            .merge_from(&value.always_allow_tool_actions);
216        self.notify_when_agent_waiting
217            .merge_from(&value.notify_when_agent_waiting);
218        self.play_sound_when_agent_done
219            .merge_from(&value.play_sound_when_agent_done);
220        self.stream_edits.merge_from(&value.stream_edits);
221        self.single_file_review
222            .merge_from(&value.single_file_review);
223        self.preferred_completion_mode
224            .merge_from(&value.preferred_completion_mode.map(Into::into));
225        self.enable_feedback.merge_from(&value.enable_feedback);
226        self.expand_edit_card.merge_from(&value.expand_edit_card);
227        self.expand_terminal_card
228            .merge_from(&value.expand_terminal_card);
229        self.use_modifier_to_send
230            .merge_from(&value.use_modifier_to_send);
231
232        self.model_parameters
233            .extend_from_slice(&value.model_parameters);
234        self.message_editor_min_lines
235            .merge_from(&value.message_editor_min_lines);
236
237        if let Some(profiles) = value.profiles.as_ref() {
238            self.profiles.extend(
239                profiles
240                    .into_iter()
241                    .map(|(id, profile)| (AgentProfileId(id.clone()), profile.clone().into())),
242            );
243        }
244    }
245
246    fn import_from_vscode(vscode: &settings::VsCodeSettings, current: &mut SettingsContent) {
247        if let Some(b) = vscode
248            .read_value("chat.agent.enabled")
249            .and_then(|b| b.as_bool())
250        {
251            current.agent.get_or_insert_default().enabled = Some(b);
252            current.agent.get_or_insert_default().button = Some(b);
253        }
254    }
255}