assistant.rs

  1#![cfg_attr(target_os = "windows", allow(unused, dead_code))]
  2
  3mod assistant_configuration;
  4pub mod assistant_panel;
  5mod inline_assistant;
  6pub mod slash_command_settings;
  7mod terminal_inline_assistant;
  8
  9use std::sync::Arc;
 10
 11use assistant_settings::{AssistantSettings, LanguageModelSelection};
 12use assistant_slash_command::SlashCommandRegistry;
 13use client::Client;
 14use command_palette_hooks::CommandPaletteFilter;
 15use feature_flags::FeatureFlagAppExt;
 16use fs::Fs;
 17use gpui::{App, Global, ReadGlobal, UpdateGlobal, actions};
 18use language_model::{
 19    LanguageModelId, LanguageModelProviderId, LanguageModelRegistry, LanguageModelResponseMessage,
 20};
 21use prompt_store::PromptBuilder;
 22use serde::Deserialize;
 23use settings::{Settings, SettingsStore};
 24
 25pub use crate::assistant_panel::{AssistantPanel, AssistantPanelEvent};
 26pub(crate) use crate::inline_assistant::*;
 27use crate::slash_command_settings::SlashCommandSettings;
 28
 29actions!(
 30    assistant,
 31    [
 32        InsertActivePrompt,
 33        DeployHistory,
 34        NewChat,
 35        CycleNextInlineAssist,
 36        CyclePreviousInlineAssist
 37    ]
 38);
 39
 40const DEFAULT_CONTEXT_LINES: usize = 50;
 41
 42#[derive(Deserialize, Debug)]
 43pub struct LanguageModelUsage {
 44    pub prompt_tokens: u32,
 45    pub completion_tokens: u32,
 46    pub total_tokens: u32,
 47}
 48
 49#[derive(Deserialize, Debug)]
 50pub struct LanguageModelChoiceDelta {
 51    pub index: u32,
 52    pub delta: LanguageModelResponseMessage,
 53    pub finish_reason: Option<String>,
 54}
 55
 56/// The state pertaining to the Assistant.
 57#[derive(Default)]
 58struct Assistant {
 59    /// Whether the Assistant is enabled.
 60    enabled: bool,
 61}
 62
 63impl Global for Assistant {}
 64
 65impl Assistant {
 66    const NAMESPACE: &'static str = "assistant";
 67
 68    fn set_enabled(&mut self, enabled: bool, cx: &mut App) {
 69        if self.enabled == enabled {
 70            return;
 71        }
 72
 73        self.enabled = enabled;
 74
 75        if !enabled {
 76            CommandPaletteFilter::update_global(cx, |filter, _cx| {
 77                filter.hide_namespace(Self::NAMESPACE);
 78            });
 79
 80            return;
 81        }
 82
 83        CommandPaletteFilter::update_global(cx, |filter, _cx| {
 84            filter.show_namespace(Self::NAMESPACE);
 85        });
 86    }
 87
 88    pub fn enabled(cx: &App) -> bool {
 89        Self::global(cx).enabled
 90    }
 91}
 92
 93pub fn init(
 94    fs: Arc<dyn Fs>,
 95    client: Arc<Client>,
 96    prompt_builder: Arc<PromptBuilder>,
 97    cx: &mut App,
 98) {
 99    cx.set_global(Assistant::default());
100    AssistantSettings::register(cx);
101    SlashCommandSettings::register(cx);
102
103    assistant_context_editor::init(client.clone(), cx);
104    rules_library::init(cx);
105    init_language_model_settings(cx);
106    assistant_slash_command::init(cx);
107    assistant_tool::init(cx);
108    assistant_panel::init(cx);
109    context_server::init(cx);
110
111    register_slash_commands(cx);
112    inline_assistant::init(
113        fs.clone(),
114        prompt_builder.clone(),
115        client.telemetry().clone(),
116        cx,
117    );
118    terminal_inline_assistant::init(
119        fs.clone(),
120        prompt_builder.clone(),
121        client.telemetry().clone(),
122        cx,
123    );
124    indexed_docs::init(cx);
125
126    CommandPaletteFilter::update_global(cx, |filter, _cx| {
127        filter.hide_namespace(Assistant::NAMESPACE);
128    });
129    Assistant::update_global(cx, |assistant, cx| {
130        let settings = AssistantSettings::get_global(cx);
131
132        assistant.set_enabled(settings.enabled, cx);
133    });
134    cx.observe_global::<SettingsStore>(|cx| {
135        Assistant::update_global(cx, |assistant, cx| {
136            let settings = AssistantSettings::get_global(cx);
137            assistant.set_enabled(settings.enabled, cx);
138        });
139    })
140    .detach();
141}
142
143fn init_language_model_settings(cx: &mut App) {
144    update_active_language_model_from_settings(cx);
145
146    cx.observe_global::<SettingsStore>(update_active_language_model_from_settings)
147        .detach();
148    cx.subscribe(
149        &LanguageModelRegistry::global(cx),
150        |_, event: &language_model::Event, cx| match event {
151            language_model::Event::ProviderStateChanged
152            | language_model::Event::AddedProvider(_)
153            | language_model::Event::RemovedProvider(_) => {
154                update_active_language_model_from_settings(cx);
155            }
156            _ => {}
157        },
158    )
159    .detach();
160}
161
162fn update_active_language_model_from_settings(cx: &mut App) {
163    let settings = AssistantSettings::get_global(cx);
164
165    fn to_selected_model(selection: &LanguageModelSelection) -> language_model::SelectedModel {
166        language_model::SelectedModel {
167            provider: LanguageModelProviderId::from(selection.provider.clone()),
168            model: LanguageModelId::from(selection.model.clone()),
169        }
170    }
171
172    let default = to_selected_model(&settings.default_model);
173    let inline_assistant = settings
174        .inline_assistant_model
175        .as_ref()
176        .map(to_selected_model);
177    let commit_message = settings
178        .commit_message_model
179        .as_ref()
180        .map(to_selected_model);
181    let thread_summary = settings
182        .thread_summary_model
183        .as_ref()
184        .map(to_selected_model);
185    let inline_alternatives = settings
186        .inline_alternatives
187        .iter()
188        .map(to_selected_model)
189        .collect::<Vec<_>>();
190
191    LanguageModelRegistry::global(cx).update(cx, |registry, cx| {
192        registry.select_default_model(Some(&default), cx);
193        registry.select_inline_assistant_model(inline_assistant.as_ref(), cx);
194        registry.select_commit_message_model(commit_message.as_ref(), cx);
195        registry.select_thread_summary_model(thread_summary.as_ref(), cx);
196        registry.select_inline_alternative_models(inline_alternatives, cx);
197    });
198}
199
200fn register_slash_commands(cx: &mut App) {
201    let slash_command_registry = SlashCommandRegistry::global(cx);
202
203    slash_command_registry.register_command(assistant_slash_commands::FileSlashCommand, true);
204    slash_command_registry.register_command(assistant_slash_commands::DeltaSlashCommand, true);
205    slash_command_registry.register_command(assistant_slash_commands::OutlineSlashCommand, true);
206    slash_command_registry.register_command(assistant_slash_commands::TabSlashCommand, true);
207    slash_command_registry
208        .register_command(assistant_slash_commands::CargoWorkspaceSlashCommand, true);
209    slash_command_registry.register_command(assistant_slash_commands::PromptSlashCommand, true);
210    slash_command_registry.register_command(assistant_slash_commands::SelectionCommand, true);
211    slash_command_registry.register_command(assistant_slash_commands::DefaultSlashCommand, false);
212    slash_command_registry.register_command(assistant_slash_commands::TerminalSlashCommand, true);
213    slash_command_registry.register_command(assistant_slash_commands::NowSlashCommand, false);
214    slash_command_registry
215        .register_command(assistant_slash_commands::DiagnosticsSlashCommand, true);
216    slash_command_registry.register_command(assistant_slash_commands::FetchSlashCommand, true);
217
218    cx.observe_flag::<assistant_slash_commands::StreamingExampleSlashCommandFeatureFlag, _>({
219        let slash_command_registry = slash_command_registry.clone();
220        move |is_enabled, _cx| {
221            if is_enabled {
222                slash_command_registry.register_command(
223                    assistant_slash_commands::StreamingExampleSlashCommand,
224                    false,
225                );
226            }
227        }
228    })
229    .detach();
230
231    update_slash_commands_from_settings(cx);
232    cx.observe_global::<SettingsStore>(update_slash_commands_from_settings)
233        .detach();
234}
235
236fn update_slash_commands_from_settings(cx: &mut App) {
237    let slash_command_registry = SlashCommandRegistry::global(cx);
238    let settings = SlashCommandSettings::get_global(cx);
239
240    if settings.docs.enabled {
241        slash_command_registry.register_command(assistant_slash_commands::DocsSlashCommand, true);
242    } else {
243        slash_command_registry.unregister_command(assistant_slash_commands::DocsSlashCommand);
244    }
245
246    if settings.cargo_workspace.enabled {
247        slash_command_registry
248            .register_command(assistant_slash_commands::CargoWorkspaceSlashCommand, true);
249    } else {
250        slash_command_registry
251            .unregister_command(assistant_slash_commands::CargoWorkspaceSlashCommand);
252    }
253}
254
255#[cfg(test)]
256#[ctor::ctor]
257fn init_logger() {
258    if std::env::var("RUST_LOG").is_ok() {
259        env_logger::init();
260    }
261}