model_selector.rs

  1use std::sync::Arc;
  2
  3use crate::{
  4    assistant_settings::AssistantSettings, LanguageModelCompletionProvider, ToggleModelSelector,
  5};
  6use fs::Fs;
  7use language_model::LanguageModelRegistry;
  8use settings::update_settings_file;
  9use ui::{prelude::*, ButtonLike, ContextMenu, PopoverMenu, PopoverMenuHandle, Tooltip};
 10
 11#[derive(IntoElement)]
 12pub struct ModelSelector {
 13    handle: PopoverMenuHandle<ContextMenu>,
 14    fs: Arc<dyn Fs>,
 15}
 16
 17impl ModelSelector {
 18    pub fn new(handle: PopoverMenuHandle<ContextMenu>, fs: Arc<dyn Fs>) -> Self {
 19        ModelSelector { handle, fs }
 20    }
 21}
 22
 23impl RenderOnce for ModelSelector {
 24    fn render(self, cx: &mut WindowContext) -> impl IntoElement {
 25        PopoverMenu::new("model-switcher")
 26            .with_handle(self.handle)
 27            .menu(move |cx| {
 28                ContextMenu::build(cx, |mut menu, cx| {
 29                    for (provider, available_models) in LanguageModelRegistry::global(cx)
 30                        .read(cx)
 31                        .available_models_grouped_by_provider(cx)
 32                    {
 33                        menu = menu.header(provider.0.clone());
 34
 35                        if available_models.is_empty() {
 36                            menu = menu.custom_entry(
 37                                {
 38                                    move |_| {
 39                                        h_flex()
 40                                            .w_full()
 41                                            .gap_1()
 42                                            .child(Icon::new(IconName::Settings))
 43                                            .child(Label::new("Configure"))
 44                                            .into_any()
 45                                    }
 46                                },
 47                                {
 48                                    let provider = provider.clone();
 49                                    move |cx| {
 50                                        LanguageModelCompletionProvider::global(cx).update(
 51                                            cx,
 52                                            |completion_provider, cx| {
 53                                                completion_provider
 54                                                    .set_active_provider(provider.clone(), cx)
 55                                            },
 56                                        );
 57                                    }
 58                                },
 59                            );
 60                        }
 61
 62                        for available_model in available_models {
 63                            menu = menu.custom_entry(
 64                                {
 65                                    let model_name = available_model.name().0.clone();
 66                                    move |_| {
 67                                        h_flex()
 68                                            .w_full()
 69                                            .child(Label::new(model_name.clone()))
 70                                            .into_any()
 71                                    }
 72                                },
 73                                {
 74                                    let fs = self.fs.clone();
 75                                    let model = available_model.clone();
 76                                    move |cx| {
 77                                        let model = model.clone();
 78                                        update_settings_file::<AssistantSettings>(
 79                                            fs.clone(),
 80                                            cx,
 81                                            move |settings, _| settings.set_model(model),
 82                                        );
 83                                    }
 84                                },
 85                            );
 86                        }
 87                    }
 88                    menu
 89                })
 90                .into()
 91            })
 92            .trigger(
 93                ButtonLike::new("active-model")
 94                    .style(ButtonStyle::Subtle)
 95                    .child(
 96                        h_flex()
 97                            .w_full()
 98                            .gap_0p5()
 99                            .child(
100                                div()
101                                    .overflow_x_hidden()
102                                    .flex_grow()
103                                    .whitespace_nowrap()
104                                    .child(
105                                        Label::new(
106                                            LanguageModelCompletionProvider::read_global(cx)
107                                                .active_model()
108                                                .map(|model| model.name().0)
109                                                .unwrap_or_else(|| "No model selected".into()),
110                                        )
111                                        .size(LabelSize::Small)
112                                        .color(Color::Muted),
113                                    ),
114                            )
115                            .child(
116                                Icon::new(IconName::ChevronDown)
117                                    .color(Color::Muted)
118                                    .size(IconSize::XSmall),
119                            ),
120                    )
121                    .tooltip(move |cx| {
122                        Tooltip::for_action("Change Model", &ToggleModelSelector, cx)
123                    }),
124            )
125            .attach(gpui::AnchorCorner::BottomLeft)
126    }
127}