1use crate::{
2 ModelUsageContext,
3 language_model_selector::{LanguageModelSelector, language_model_selector},
4};
5use agent_settings::AgentSettings;
6use fs::Fs;
7use gpui::{Entity, FocusHandle, SharedString};
8use language_model::{ConfiguredModel, LanguageModelRegistry};
9use picker::popover_menu::PickerPopoverMenu;
10use settings::update_settings_file;
11use std::sync::Arc;
12use ui::{ButtonLike, PopoverMenuHandle, Tooltip, prelude::*};
13use zed_actions::agent::ToggleModelSelector;
14
15pub struct AgentModelSelector {
16 selector: Entity<LanguageModelSelector>,
17 menu_handle: PopoverMenuHandle<LanguageModelSelector>,
18 focus_handle: FocusHandle,
19}
20
21impl AgentModelSelector {
22 pub(crate) fn new(
23 fs: Arc<dyn Fs>,
24 menu_handle: PopoverMenuHandle<LanguageModelSelector>,
25 focus_handle: FocusHandle,
26 model_usage_context: ModelUsageContext,
27 window: &mut Window,
28 cx: &mut Context<Self>,
29 ) -> Self {
30 Self {
31 selector: cx.new(move |cx| {
32 let fs = fs.clone();
33 language_model_selector(
34 {
35 let model_context = model_usage_context.clone();
36 move |cx| model_context.configured_model(cx)
37 },
38 move |model, cx| {
39 let provider = model.provider_id().0.to_string();
40 let model_id = model.id().0.to_string();
41 match &model_usage_context {
42 ModelUsageContext::Thread(thread) => {
43 thread.update(cx, |thread, cx| {
44 let registry = LanguageModelRegistry::read_global(cx);
45 if let Some(provider) = registry.provider(&model.provider_id())
46 {
47 thread.set_configured_model(
48 Some(ConfiguredModel {
49 provider,
50 model: model.clone(),
51 }),
52 cx,
53 );
54 }
55 });
56 update_settings_file::<AgentSettings>(
57 fs.clone(),
58 cx,
59 move |settings, _cx| {
60 settings.set_model(model.clone());
61 },
62 );
63 }
64 ModelUsageContext::InlineAssistant => {
65 update_settings_file::<AgentSettings>(
66 fs.clone(),
67 cx,
68 move |settings, _cx| {
69 settings.set_inline_assistant_model(
70 provider.clone(),
71 model_id.clone(),
72 );
73 },
74 );
75 }
76 }
77 },
78 window,
79 cx,
80 )
81 }),
82 menu_handle,
83 focus_handle,
84 }
85 }
86
87 pub fn toggle(&self, window: &mut Window, cx: &mut Context<Self>) {
88 self.menu_handle.toggle(window, cx);
89 }
90}
91
92impl Render for AgentModelSelector {
93 fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
94 let model = self.selector.read(cx).delegate.active_model(cx);
95 let model_name = model
96 .as_ref()
97 .map(|model| model.model.name().0)
98 .unwrap_or_else(|| SharedString::from("Select a Model"));
99
100 let provider_icon = model.as_ref().map(|model| model.provider.icon());
101
102 let focus_handle = self.focus_handle.clone();
103
104 PickerPopoverMenu::new(
105 self.selector.clone(),
106 ButtonLike::new("active-model")
107 .when_some(provider_icon, |this, icon| {
108 this.child(Icon::new(icon).color(Color::Muted).size(IconSize::XSmall))
109 })
110 .child(
111 Label::new(model_name)
112 .color(Color::Muted)
113 .size(LabelSize::Small)
114 .ml_0p5(),
115 )
116 .child(
117 Icon::new(IconName::ChevronDown)
118 .color(Color::Muted)
119 .size(IconSize::XSmall),
120 ),
121 move |window, cx| {
122 Tooltip::for_action_in(
123 "Change Model",
124 &ToggleModelSelector,
125 &focus_handle,
126 window,
127 cx,
128 )
129 },
130 gpui::Corner::BottomRight,
131 cx,
132 )
133 .with_handle(self.menu_handle.clone())
134 .render(window, cx)
135 }
136}