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;
 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    prompt_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    let active_model_provider_name =
165        LanguageModelProviderId::from(settings.default_model.provider.clone());
166    let active_model_id = LanguageModelId::from(settings.default_model.model.clone());
167    let editor_provider_name =
168        LanguageModelProviderId::from(settings.editor_model.provider.clone());
169    let editor_model_id = LanguageModelId::from(settings.editor_model.model.clone());
170    let inline_alternatives = settings
171        .inline_alternatives
172        .iter()
173        .map(|alternative| {
174            (
175                LanguageModelProviderId::from(alternative.provider.clone()),
176                LanguageModelId::from(alternative.model.clone()),
177            )
178        })
179        .collect::<Vec<_>>();
180    LanguageModelRegistry::global(cx).update(cx, |registry, cx| {
181        registry.select_active_model(&active_model_provider_name, &active_model_id, cx);
182        registry.select_editor_model(&editor_provider_name, &editor_model_id, cx);
183        registry.select_inline_alternative_models(inline_alternatives, cx);
184    });
185}
186
187fn register_slash_commands(cx: &mut App) {
188    let slash_command_registry = SlashCommandRegistry::global(cx);
189
190    slash_command_registry.register_command(assistant_slash_commands::FileSlashCommand, true);
191    slash_command_registry.register_command(assistant_slash_commands::DeltaSlashCommand, true);
192    slash_command_registry.register_command(assistant_slash_commands::OutlineSlashCommand, true);
193    slash_command_registry.register_command(assistant_slash_commands::TabSlashCommand, true);
194    slash_command_registry
195        .register_command(assistant_slash_commands::CargoWorkspaceSlashCommand, true);
196    slash_command_registry.register_command(assistant_slash_commands::PromptSlashCommand, true);
197    slash_command_registry.register_command(assistant_slash_commands::SelectionCommand, true);
198    slash_command_registry.register_command(assistant_slash_commands::DefaultSlashCommand, false);
199    slash_command_registry.register_command(assistant_slash_commands::TerminalSlashCommand, true);
200    slash_command_registry.register_command(assistant_slash_commands::NowSlashCommand, false);
201    slash_command_registry
202        .register_command(assistant_slash_commands::DiagnosticsSlashCommand, true);
203    slash_command_registry.register_command(assistant_slash_commands::FetchSlashCommand, true);
204
205    cx.observe_flag::<assistant_slash_commands::StreamingExampleSlashCommandFeatureFlag, _>({
206        let slash_command_registry = slash_command_registry.clone();
207        move |is_enabled, _cx| {
208            if is_enabled {
209                slash_command_registry.register_command(
210                    assistant_slash_commands::StreamingExampleSlashCommand,
211                    false,
212                );
213            }
214        }
215    })
216    .detach();
217
218    update_slash_commands_from_settings(cx);
219    cx.observe_global::<SettingsStore>(update_slash_commands_from_settings)
220        .detach();
221}
222
223fn update_slash_commands_from_settings(cx: &mut App) {
224    let slash_command_registry = SlashCommandRegistry::global(cx);
225    let settings = SlashCommandSettings::get_global(cx);
226
227    if settings.docs.enabled {
228        slash_command_registry.register_command(assistant_slash_commands::DocsSlashCommand, true);
229    } else {
230        slash_command_registry.unregister_command(assistant_slash_commands::DocsSlashCommand);
231    }
232
233    if settings.cargo_workspace.enabled {
234        slash_command_registry
235            .register_command(assistant_slash_commands::CargoWorkspaceSlashCommand, true);
236    } else {
237        slash_command_registry
238            .unregister_command(assistant_slash_commands::CargoWorkspaceSlashCommand);
239    }
240}
241
242#[cfg(test)]
243#[ctor::ctor]
244fn init_logger() {
245    if std::env::var("RUST_LOG").is_ok() {
246        env_logger::init();
247    }
248}