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, LanguageModelParameters, LanguageModelSelection, NotifyWhenAgentWaiting,
12 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(Default, 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}
54
55impl AgentSettings {
56 pub fn temperature_for_model(model: &Arc<dyn LanguageModel>, cx: &App) -> Option<f32> {
57 let settings = Self::get_global(cx);
58 for setting in settings.model_parameters.iter().rev() {
59 if let Some(provider) = &setting.provider
60 && provider.0 != model.provider_id().0
61 {
62 continue;
63 }
64 if let Some(setting_model) = &setting.model
65 && *setting_model != model.id().0
66 {
67 continue;
68 }
69 return setting.temperature;
70 }
71 return None;
72 }
73
74 pub fn set_inline_assistant_model(&mut self, provider: String, model: String) {
75 self.inline_assistant_model = Some(LanguageModelSelection {
76 provider: provider.into(),
77 model,
78 });
79 }
80
81 pub fn set_commit_message_model(&mut self, provider: String, model: String) {
82 self.commit_message_model = Some(LanguageModelSelection {
83 provider: provider.into(),
84 model,
85 });
86 }
87
88 pub fn set_thread_summary_model(&mut self, provider: String, model: String) {
89 self.thread_summary_model = Some(LanguageModelSelection {
90 provider: provider.into(),
91 model,
92 });
93 }
94}
95
96#[derive(Clone, Copy, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Default)]
97#[serde(rename_all = "snake_case")]
98pub enum CompletionMode {
99 #[default]
100 Normal,
101 #[serde(alias = "max")]
102 Burn,
103}
104
105impl From<CompletionMode> for cloud_llm_client::CompletionMode {
106 fn from(value: CompletionMode) -> Self {
107 match value {
108 CompletionMode::Normal => cloud_llm_client::CompletionMode::Normal,
109 CompletionMode::Burn => cloud_llm_client::CompletionMode::Max,
110 }
111 }
112}
113
114impl From<settings::CompletionMode> for CompletionMode {
115 fn from(value: settings::CompletionMode) -> Self {
116 match value {
117 settings::CompletionMode::Normal => CompletionMode::Normal,
118 settings::CompletionMode::Burn => CompletionMode::Burn,
119 }
120 }
121}
122
123#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, JsonSchema)]
124pub struct AgentProfileId(pub Arc<str>);
125
126impl AgentProfileId {
127 pub fn as_str(&self) -> &str {
128 &self.0
129 }
130}
131
132impl std::fmt::Display for AgentProfileId {
133 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
134 write!(f, "{}", self.0)
135 }
136}
137
138impl Default for AgentProfileId {
139 fn default() -> Self {
140 Self("write".into())
141 }
142}
143
144impl Settings for AgentSettings {
145 // todo!() test preserved keys logic
146 fn from_defaults(content: &settings::SettingsContent, _cx: &mut App) -> Self {
147 let agent = content.agent.clone().unwrap();
148 Self {
149 enabled: agent.enabled.unwrap(),
150 button: agent.button.unwrap(),
151 dock: agent.dock.unwrap(),
152 default_width: px(agent.default_width.unwrap()),
153 default_height: px(agent.default_height.unwrap()),
154 default_model: Some(agent.default_model.unwrap()),
155 inline_assistant_model: agent.inline_assistant_model,
156 commit_message_model: agent.commit_message_model,
157 thread_summary_model: agent.thread_summary_model,
158 inline_alternatives: agent.inline_alternatives.unwrap_or_default(),
159 default_profile: AgentProfileId(agent.default_profile.unwrap()),
160 default_view: agent.default_view.unwrap(),
161 profiles: agent
162 .profiles
163 .unwrap()
164 .into_iter()
165 .map(|(key, val)| (AgentProfileId(key), val.into()))
166 .collect(),
167 always_allow_tool_actions: agent.always_allow_tool_actions.unwrap(),
168 notify_when_agent_waiting: agent.notify_when_agent_waiting.unwrap(),
169 play_sound_when_agent_done: agent.play_sound_when_agent_done.unwrap(),
170 stream_edits: agent.stream_edits.unwrap(),
171 single_file_review: agent.single_file_review.unwrap(),
172 model_parameters: agent.model_parameters,
173 preferred_completion_mode: agent.preferred_completion_mode.unwrap().into(),
174 enable_feedback: agent.enable_feedback.unwrap(),
175 expand_edit_card: agent.expand_edit_card.unwrap(),
176 expand_terminal_card: agent.expand_terminal_card.unwrap(),
177 use_modifier_to_send: agent.use_modifier_to_send.unwrap(),
178 }
179 }
180
181 fn refine(&mut self, content: &settings::SettingsContent, _: &mut App) {
182 let Some(value) = &content.agent else { return };
183 self.enabled.merge_from(&value.enabled);
184 self.button.merge_from(&value.button);
185 self.dock.merge_from(&value.dock);
186 self.default_width
187 .merge_from(&value.default_width.map(Into::into));
188 self.default_height
189 .merge_from(&value.default_height.map(Into::into));
190 self.default_model = value.default_model.clone().or(self.default_model.take());
191
192 self.inline_assistant_model = value
193 .inline_assistant_model
194 .clone()
195 .or(self.inline_assistant_model.take());
196 self.commit_message_model = value
197 .clone()
198 .commit_message_model
199 .or(self.commit_message_model.take());
200 self.thread_summary_model = value
201 .clone()
202 .thread_summary_model
203 .or(self.thread_summary_model.take());
204 self.inline_alternatives
205 .merge_from(&value.inline_alternatives.clone());
206 self.notify_when_agent_waiting
207 .merge_from(&value.notify_when_agent_waiting);
208 self.play_sound_when_agent_done
209 .merge_from(&value.play_sound_when_agent_done);
210 self.stream_edits.merge_from(&value.stream_edits);
211 self.single_file_review
212 .merge_from(&value.single_file_review);
213 self.default_profile
214 .merge_from(&value.default_profile.clone().map(AgentProfileId));
215 self.default_view.merge_from(&value.default_view);
216 self.preferred_completion_mode
217 .merge_from(&value.preferred_completion_mode.map(Into::into));
218 self.enable_feedback.merge_from(&value.enable_feedback);
219 self.expand_edit_card.merge_from(&value.expand_edit_card);
220 self.expand_terminal_card
221 .merge_from(&value.expand_terminal_card);
222 self.use_modifier_to_send
223 .merge_from(&value.use_modifier_to_send);
224
225 self.model_parameters
226 .extend_from_slice(&value.model_parameters);
227
228 if let Some(profiles) = value.profiles.as_ref() {
229 self.profiles.extend(
230 profiles
231 .into_iter()
232 .map(|(id, profile)| (AgentProfileId(id.clone()), profile.clone().into())),
233 );
234 }
235 }
236
237 fn import_from_vscode(vscode: &settings::VsCodeSettings, current: &mut SettingsContent) {
238 if let Some(b) = vscode
239 .read_value("chat.agent.enabled")
240 .and_then(|b| b.as_bool())
241 {
242 current.agent.get_or_insert_default().enabled = Some(b);
243 current.agent.get_or_insert_default().button = Some(b);
244 }
245 }
246}