agent_ui.rs

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