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}