agent.rs

  1mod active_thread;
  2mod agent_configuration;
  3mod agent_diff;
  4mod agent_model_selector;
  5mod agent_panel;
  6mod agent_profile;
  7mod buffer_codegen;
  8mod context;
  9mod context_picker;
 10mod context_server_configuration;
 11mod context_server_tool;
 12mod context_store;
 13mod context_strip;
 14mod debug;
 15mod history_store;
 16mod inline_assistant;
 17mod inline_prompt_editor;
 18mod message_editor;
 19mod profile_selector;
 20mod slash_command_settings;
 21mod terminal_codegen;
 22mod terminal_inline_assistant;
 23mod thread;
 24mod thread_history;
 25mod thread_store;
 26mod tool_compatibility;
 27mod tool_use;
 28mod ui;
 29
 30use std::sync::Arc;
 31
 32use agent_settings::{AgentProfileId, AgentSettings, LanguageModelSelection};
 33use assistant_slash_command::SlashCommandRegistry;
 34use client::Client;
 35use feature_flags::FeatureFlagAppExt as _;
 36use fs::Fs;
 37use gpui::{App, Entity, actions, impl_actions};
 38use language::LanguageRegistry;
 39use language_model::{
 40    ConfiguredModel, LanguageModel, LanguageModelId, LanguageModelProviderId, LanguageModelRegistry,
 41};
 42use prompt_store::PromptBuilder;
 43use schemars::JsonSchema;
 44use serde::Deserialize;
 45use settings::{Settings as _, SettingsStore};
 46use thread::ThreadId;
 47
 48pub use crate::active_thread::ActiveThread;
 49use crate::agent_configuration::{ConfigureContextServerModal, ManageProfilesModal};
 50pub use crate::agent_panel::{AgentPanel, ConcreteAssistantPanelDelegate};
 51pub use crate::context::{ContextLoadResult, LoadedContext};
 52pub use crate::inline_assistant::InlineAssistant;
 53use crate::slash_command_settings::SlashCommandSettings;
 54pub use crate::thread::{Message, MessageSegment, Thread, ThreadEvent};
 55pub use crate::thread_store::{SerializedThread, TextThreadStore, ThreadStore};
 56pub use agent_diff::{AgentDiffPane, AgentDiffToolbar};
 57pub use context_store::ContextStore;
 58pub use ui::preview::{all_agent_previews, get_agent_preview};
 59
 60actions!(
 61    agent,
 62    [
 63        NewTextThread,
 64        ToggleContextPicker,
 65        ToggleNavigationMenu,
 66        ToggleOptionsMenu,
 67        DeleteRecentlyOpenThread,
 68        ToggleProfileSelector,
 69        RemoveAllContext,
 70        ExpandMessageEditor,
 71        OpenHistory,
 72        AddContextServer,
 73        RemoveSelectedThread,
 74        Chat,
 75        ChatWithFollow,
 76        CycleNextInlineAssist,
 77        CyclePreviousInlineAssist,
 78        FocusUp,
 79        FocusDown,
 80        FocusLeft,
 81        FocusRight,
 82        RemoveFocusedContext,
 83        AcceptSuggestedContext,
 84        OpenActiveThreadAsMarkdown,
 85        OpenAgentDiff,
 86        Keep,
 87        Reject,
 88        RejectAll,
 89        KeepAll,
 90        Follow,
 91        ResetTrialUpsell,
 92        ResetTrialEndUpsell,
 93        ContinueThread,
 94        ContinueWithBurnMode,
 95        ToggleBurnMode,
 96    ]
 97);
 98
 99#[derive(Default, Clone, PartialEq, Deserialize, JsonSchema)]
100pub struct NewThread {
101    #[serde(default)]
102    from_thread_id: Option<ThreadId>,
103}
104
105#[derive(PartialEq, Clone, Default, Debug, Deserialize, JsonSchema)]
106pub struct ManageProfiles {
107    #[serde(default)]
108    pub customize_tools: Option<AgentProfileId>,
109}
110
111impl ManageProfiles {
112    pub fn customize_tools(profile_id: AgentProfileId) -> Self {
113        Self {
114            customize_tools: Some(profile_id),
115        }
116    }
117}
118
119impl_actions!(agent, [NewThread, ManageProfiles]);
120
121#[derive(Clone)]
122pub(crate) enum ModelUsageContext {
123    Thread(Entity<Thread>),
124    InlineAssistant,
125}
126
127impl ModelUsageContext {
128    pub fn configured_model(&self, cx: &App) -> Option<ConfiguredModel> {
129        match self {
130            Self::Thread(thread) => thread.read(cx).configured_model(),
131            Self::InlineAssistant => {
132                LanguageModelRegistry::read_global(cx).inline_assistant_model()
133            }
134        }
135    }
136
137    pub fn language_model(&self, cx: &App) -> Option<Arc<dyn LanguageModel>> {
138        self.configured_model(cx)
139            .map(|configured_model| configured_model.model)
140    }
141}
142
143/// Initializes the `agent` crate.
144pub fn init(
145    fs: Arc<dyn Fs>,
146    client: Arc<Client>,
147    prompt_builder: Arc<PromptBuilder>,
148    language_registry: Arc<LanguageRegistry>,
149    is_eval: bool,
150    cx: &mut App,
151) {
152    AgentSettings::register(cx);
153    SlashCommandSettings::register(cx);
154
155    assistant_context_editor::init(client.clone(), cx);
156    rules_library::init(cx);
157    if !is_eval {
158        // Initializing the language model from the user settings messes with the eval, so we only initialize them when
159        // we're not running inside of the eval.
160        init_language_model_settings(cx);
161    }
162    assistant_slash_command::init(cx);
163    thread_store::init(cx);
164    agent_panel::init(cx);
165    context_server_configuration::init(language_registry.clone(), fs.clone(), cx);
166
167    register_slash_commands(cx);
168    inline_assistant::init(
169        fs.clone(),
170        prompt_builder.clone(),
171        client.telemetry().clone(),
172        cx,
173    );
174    terminal_inline_assistant::init(
175        fs.clone(),
176        prompt_builder.clone(),
177        client.telemetry().clone(),
178        cx,
179    );
180    indexed_docs::init(cx);
181    cx.observe_new(move |workspace, window, cx| {
182        ConfigureContextServerModal::register(workspace, language_registry.clone(), window, cx)
183    })
184    .detach();
185    cx.observe_new(ManageProfilesModal::register).detach();
186}
187
188fn init_language_model_settings(cx: &mut App) {
189    update_active_language_model_from_settings(cx);
190
191    cx.observe_global::<SettingsStore>(update_active_language_model_from_settings)
192        .detach();
193    cx.subscribe(
194        &LanguageModelRegistry::global(cx),
195        |_, event: &language_model::Event, cx| match event {
196            language_model::Event::ProviderStateChanged
197            | language_model::Event::AddedProvider(_)
198            | language_model::Event::RemovedProvider(_) => {
199                update_active_language_model_from_settings(cx);
200            }
201            _ => {}
202        },
203    )
204    .detach();
205}
206
207fn update_active_language_model_from_settings(cx: &mut App) {
208    let settings = AgentSettings::get_global(cx);
209
210    fn to_selected_model(selection: &LanguageModelSelection) -> language_model::SelectedModel {
211        language_model::SelectedModel {
212            provider: LanguageModelProviderId::from(selection.provider.0.clone()),
213            model: LanguageModelId::from(selection.model.clone()),
214        }
215    }
216
217    let default = to_selected_model(&settings.default_model);
218    let inline_assistant = settings
219        .inline_assistant_model
220        .as_ref()
221        .map(to_selected_model);
222    let commit_message = settings
223        .commit_message_model
224        .as_ref()
225        .map(to_selected_model);
226    let thread_summary = settings
227        .thread_summary_model
228        .as_ref()
229        .map(to_selected_model);
230    let inline_alternatives = settings
231        .inline_alternatives
232        .iter()
233        .map(to_selected_model)
234        .collect::<Vec<_>>();
235
236    LanguageModelRegistry::global(cx).update(cx, |registry, cx| {
237        registry.select_default_model(Some(&default), cx);
238        registry.select_inline_assistant_model(inline_assistant.as_ref(), cx);
239        registry.select_commit_message_model(commit_message.as_ref(), cx);
240        registry.select_thread_summary_model(thread_summary.as_ref(), cx);
241        registry.select_inline_alternative_models(inline_alternatives, cx);
242    });
243}
244
245fn register_slash_commands(cx: &mut App) {
246    let slash_command_registry = SlashCommandRegistry::global(cx);
247
248    slash_command_registry.register_command(assistant_slash_commands::FileSlashCommand, true);
249    slash_command_registry.register_command(assistant_slash_commands::DeltaSlashCommand, true);
250    slash_command_registry.register_command(assistant_slash_commands::OutlineSlashCommand, true);
251    slash_command_registry.register_command(assistant_slash_commands::TabSlashCommand, true);
252    slash_command_registry
253        .register_command(assistant_slash_commands::CargoWorkspaceSlashCommand, true);
254    slash_command_registry.register_command(assistant_slash_commands::PromptSlashCommand, true);
255    slash_command_registry.register_command(assistant_slash_commands::SelectionCommand, true);
256    slash_command_registry.register_command(assistant_slash_commands::DefaultSlashCommand, false);
257    slash_command_registry.register_command(assistant_slash_commands::NowSlashCommand, false);
258    slash_command_registry
259        .register_command(assistant_slash_commands::DiagnosticsSlashCommand, true);
260    slash_command_registry.register_command(assistant_slash_commands::FetchSlashCommand, true);
261
262    cx.observe_flag::<assistant_slash_commands::StreamingExampleSlashCommandFeatureFlag, _>({
263        let slash_command_registry = slash_command_registry.clone();
264        move |is_enabled, _cx| {
265            if is_enabled {
266                slash_command_registry.register_command(
267                    assistant_slash_commands::StreamingExampleSlashCommand,
268                    false,
269                );
270            }
271        }
272    })
273    .detach();
274
275    update_slash_commands_from_settings(cx);
276    cx.observe_global::<SettingsStore>(update_slash_commands_from_settings)
277        .detach();
278}
279
280fn update_slash_commands_from_settings(cx: &mut App) {
281    let slash_command_registry = SlashCommandRegistry::global(cx);
282    let settings = SlashCommandSettings::get_global(cx);
283
284    if settings.docs.enabled {
285        slash_command_registry.register_command(assistant_slash_commands::DocsSlashCommand, true);
286    } else {
287        slash_command_registry.unregister_command(assistant_slash_commands::DocsSlashCommand);
288    }
289
290    if settings.cargo_workspace.enabled {
291        slash_command_registry
292            .register_command(assistant_slash_commands::CargoWorkspaceSlashCommand, true);
293    } else {
294        slash_command_registry
295            .unregister_command(assistant_slash_commands::CargoWorkspaceSlashCommand);
296    }
297}