assistant: Show only configured models in the model picker (#20392)
Closes https://github.com/zed-industries/zed/issues/16568 This PR introduces some changes to how we display models in the model selector within the assistant panel. Basically, it comes down to this: - If you don't have any provider configured, you should see _all_ available models in the picker - But, once you've configured some, you should _only_ see models from them in the picker Visually, nothing's changed much aside from the added "Configured Models" label at the top to ensure the understanding that that's a list of, well, configured models only. 😬 <img width="700" alt="Screenshot 2024-11-07 at 23 42 41" src="https://github.com/user-attachments/assets/219ed386-2318-43a6-abea-1de0cda8dc53"> Release Notes: - Change model selector in the assistant panel to only show configured models
This commit is contained in:
parent
435708b615
commit
187356ab9b
3 changed files with 88 additions and 53 deletions
|
@ -4507,7 +4507,6 @@ impl Render for ContextEditorToolbarItem {
|
||||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
|
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
|
||||||
let left_side = h_flex()
|
let left_side = h_flex()
|
||||||
.group("chat-title-group")
|
.group("chat-title-group")
|
||||||
.pl_0p5()
|
|
||||||
.gap_1()
|
.gap_1()
|
||||||
.items_center()
|
.items_center()
|
||||||
.flex_grow()
|
.flex_grow()
|
||||||
|
@ -4598,6 +4597,7 @@ impl Render for ContextEditorToolbarItem {
|
||||||
.children(self.render_remaining_tokens(cx));
|
.children(self.render_remaining_tokens(cx));
|
||||||
|
|
||||||
h_flex()
|
h_flex()
|
||||||
|
.px_0p5()
|
||||||
.size_full()
|
.size_full()
|
||||||
.gap_2()
|
.gap_2()
|
||||||
.justify_between()
|
.justify_between()
|
||||||
|
|
|
@ -1,21 +1,17 @@
|
||||||
use feature_flags::ZedPro;
|
use feature_flags::ZedPro;
|
||||||
use gpui::Action;
|
|
||||||
use gpui::DismissEvent;
|
|
||||||
|
|
||||||
use language_model::{LanguageModel, LanguageModelAvailability, LanguageModelRegistry};
|
use language_model::{LanguageModel, LanguageModelAvailability, LanguageModelRegistry};
|
||||||
use proto::Plan;
|
use proto::Plan;
|
||||||
use workspace::ShowConfiguration;
|
use workspace::ShowConfiguration;
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use ui::ListItemSpacing;
|
|
||||||
|
|
||||||
use crate::assistant_settings::AssistantSettings;
|
use crate::assistant_settings::AssistantSettings;
|
||||||
use fs::Fs;
|
use fs::Fs;
|
||||||
use gpui::SharedString;
|
use gpui::{Action, AnyElement, DismissEvent, SharedString, Task};
|
||||||
use gpui::Task;
|
|
||||||
use picker::{Picker, PickerDelegate};
|
use picker::{Picker, PickerDelegate};
|
||||||
use settings::update_settings_file;
|
use settings::update_settings_file;
|
||||||
use ui::{prelude::*, ListItem, PopoverMenu, PopoverMenuHandle, PopoverTrigger};
|
use ui::{prelude::*, ListItem, ListItemSpacing, PopoverMenu, PopoverMenuHandle, PopoverTrigger};
|
||||||
|
|
||||||
const TRY_ZED_PRO_URL: &str = "https://zed.dev/pro";
|
const TRY_ZED_PRO_URL: &str = "https://zed.dev/pro";
|
||||||
|
|
||||||
|
@ -85,14 +81,36 @@ impl PickerDelegate for ModelPickerDelegate {
|
||||||
|
|
||||||
fn update_matches(&mut self, query: String, cx: &mut ViewContext<Picker<Self>>) -> Task<()> {
|
fn update_matches(&mut self, query: String, cx: &mut ViewContext<Picker<Self>>) -> Task<()> {
|
||||||
let all_models = self.all_models.clone();
|
let all_models = self.all_models.clone();
|
||||||
|
|
||||||
|
let llm_registry = LanguageModelRegistry::global(cx);
|
||||||
|
|
||||||
|
let configured_models: Vec<_> = llm_registry
|
||||||
|
.read(cx)
|
||||||
|
.providers()
|
||||||
|
.iter()
|
||||||
|
.filter(|provider| provider.is_authenticated(cx))
|
||||||
|
.map(|provider| provider.id())
|
||||||
|
.collect();
|
||||||
|
|
||||||
cx.spawn(|this, mut cx| async move {
|
cx.spawn(|this, mut cx| async move {
|
||||||
let filtered_models = cx
|
let filtered_models = cx
|
||||||
.background_executor()
|
.background_executor()
|
||||||
.spawn(async move {
|
.spawn(async move {
|
||||||
if query.is_empty() {
|
let displayed_models = if configured_models.is_empty() {
|
||||||
all_models
|
all_models
|
||||||
} else {
|
} else {
|
||||||
all_models
|
all_models
|
||||||
|
.into_iter()
|
||||||
|
.filter(|model_info| {
|
||||||
|
configured_models.contains(&model_info.model.provider_id())
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
};
|
||||||
|
|
||||||
|
if query.is_empty() {
|
||||||
|
displayed_models
|
||||||
|
} else {
|
||||||
|
displayed_models
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter(|model_info| {
|
.filter(|model_info| {
|
||||||
model_info
|
model_info
|
||||||
|
@ -141,6 +159,29 @@ impl PickerDelegate for ModelPickerDelegate {
|
||||||
|
|
||||||
fn dismissed(&mut self, _cx: &mut ViewContext<Picker<Self>>) {}
|
fn dismissed(&mut self, _cx: &mut ViewContext<Picker<Self>>) {}
|
||||||
|
|
||||||
|
fn render_header(&self, cx: &mut ViewContext<Picker<Self>>) -> Option<AnyElement> {
|
||||||
|
let configured_models_count = LanguageModelRegistry::global(cx)
|
||||||
|
.read(cx)
|
||||||
|
.providers()
|
||||||
|
.iter()
|
||||||
|
.filter(|provider| provider.is_authenticated(cx))
|
||||||
|
.count();
|
||||||
|
|
||||||
|
if configured_models_count > 0 {
|
||||||
|
Some(
|
||||||
|
Label::new("Configured Models")
|
||||||
|
.size(LabelSize::Small)
|
||||||
|
.color(Color::Muted)
|
||||||
|
.mt_1()
|
||||||
|
.mb_0p5()
|
||||||
|
.ml_3()
|
||||||
|
.into_any_element(),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn render_match(
|
fn render_match(
|
||||||
&self,
|
&self,
|
||||||
ix: usize,
|
ix: usize,
|
||||||
|
@ -148,9 +189,10 @@ impl PickerDelegate for ModelPickerDelegate {
|
||||||
cx: &mut ViewContext<Picker<Self>>,
|
cx: &mut ViewContext<Picker<Self>>,
|
||||||
) -> Option<Self::ListItem> {
|
) -> Option<Self::ListItem> {
|
||||||
use feature_flags::FeatureFlagAppExt;
|
use feature_flags::FeatureFlagAppExt;
|
||||||
let model_info = self.filtered_models.get(ix)?;
|
|
||||||
let show_badges = cx.has_flag::<ZedPro>();
|
let show_badges = cx.has_flag::<ZedPro>();
|
||||||
let provider_name: String = model_info.model.provider_name().0.into();
|
|
||||||
|
let model_info = self.filtered_models.get(ix)?;
|
||||||
|
let provider_name: String = model_info.model.provider_name().0.clone().into();
|
||||||
|
|
||||||
Some(
|
Some(
|
||||||
ListItem::new(ix)
|
ListItem::new(ix)
|
||||||
|
@ -165,27 +207,32 @@ impl PickerDelegate for ModelPickerDelegate {
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.child(
|
.child(
|
||||||
h_flex().w_full().justify_between().min_w(px(200.)).child(
|
h_flex()
|
||||||
h_flex()
|
.w_full()
|
||||||
.gap_1p5()
|
.items_center()
|
||||||
.child(Label::new(model_info.model.name().0.clone()))
|
.gap_1p5()
|
||||||
.child(
|
.min_w(px(200.))
|
||||||
Label::new(provider_name)
|
.child(Label::new(model_info.model.name().0.clone()))
|
||||||
.size(LabelSize::XSmall)
|
.child(
|
||||||
.color(Color::Muted),
|
h_flex()
|
||||||
)
|
.gap_0p5()
|
||||||
.children(match model_info.availability {
|
.child(
|
||||||
LanguageModelAvailability::Public => None,
|
Label::new(provider_name)
|
||||||
LanguageModelAvailability::RequiresPlan(Plan::Free) => None,
|
.size(LabelSize::XSmall)
|
||||||
LanguageModelAvailability::RequiresPlan(Plan::ZedPro) => {
|
.color(Color::Muted),
|
||||||
show_badges.then(|| {
|
)
|
||||||
Label::new("Pro")
|
.children(match model_info.availability {
|
||||||
.size(LabelSize::XSmall)
|
LanguageModelAvailability::Public => None,
|
||||||
.color(Color::Muted)
|
LanguageModelAvailability::RequiresPlan(Plan::Free) => None,
|
||||||
})
|
LanguageModelAvailability::RequiresPlan(Plan::ZedPro) => {
|
||||||
}
|
show_badges.then(|| {
|
||||||
}),
|
Label::new("Pro")
|
||||||
),
|
.size(LabelSize::XSmall)
|
||||||
|
.color(Color::Muted)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
.end_slot(div().when(model_info.is_selected, |this| {
|
.end_slot(div().when(model_info.is_selected, |this| {
|
||||||
this.child(
|
this.child(
|
||||||
|
@ -213,7 +260,7 @@ impl PickerDelegate for ModelPickerDelegate {
|
||||||
.justify_between()
|
.justify_between()
|
||||||
.when(cx.has_flag::<ZedPro>(), |this| {
|
.when(cx.has_flag::<ZedPro>(), |this| {
|
||||||
this.child(match plan {
|
this.child(match plan {
|
||||||
// Already a zed pro subscriber
|
// Already a Zed Pro subscriber
|
||||||
Plan::ZedPro => Button::new("zed-pro", "Zed Pro")
|
Plan::ZedPro => Button::new("zed-pro", "Zed Pro")
|
||||||
.icon(IconName::ZedAssistant)
|
.icon(IconName::ZedAssistant)
|
||||||
.icon_size(IconSize::Small)
|
.icon_size(IconSize::Small)
|
||||||
|
@ -254,6 +301,7 @@ impl<T: PopoverTrigger> RenderOnce for ModelSelector<T> {
|
||||||
let selected_provider = LanguageModelRegistry::read_global(cx)
|
let selected_provider = LanguageModelRegistry::read_global(cx)
|
||||||
.active_provider()
|
.active_provider()
|
||||||
.map(|m| m.id());
|
.map(|m| m.id());
|
||||||
|
|
||||||
let selected_model = LanguageModelRegistry::read_global(cx)
|
let selected_model = LanguageModelRegistry::read_global(cx)
|
||||||
.active_model()
|
.active_model()
|
||||||
.map(|m| m.id());
|
.map(|m| m.id());
|
||||||
|
|
|
@ -912,7 +912,7 @@ impl Render for ConfigurationView {
|
||||||
|
|
||||||
let is_pro = plan == Some(proto::Plan::ZedPro);
|
let is_pro = plan == Some(proto::Plan::ZedPro);
|
||||||
let subscription_text = Label::new(if is_pro {
|
let subscription_text = Label::new(if is_pro {
|
||||||
"You have full access to Zed's hosted models from Anthropic, OpenAI, Google with faster speeds and higher limits through Zed Pro."
|
"You have full access to Zed's hosted LLMs, which include models from Anthropic, OpenAI, and Google. They come with faster speeds and higher limits through Zed Pro."
|
||||||
} else {
|
} else {
|
||||||
"You have basic access to models from Anthropic through the Zed AI Free plan."
|
"You have basic access to models from Anthropic through the Zed AI Free plan."
|
||||||
});
|
});
|
||||||
|
@ -957,27 +957,14 @@ impl Render for ConfigurationView {
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
v_flex()
|
v_flex()
|
||||||
.gap_6()
|
.gap_2()
|
||||||
.child(Label::new("Use the zed.dev to access language models."))
|
.child(Label::new("Use Zed AI to access hosted language models."))
|
||||||
.child(
|
.child(
|
||||||
v_flex()
|
Button::new("sign_in", "Sign In")
|
||||||
.gap_2()
|
.icon_color(Color::Muted)
|
||||||
.child(
|
.icon(IconName::Github)
|
||||||
Button::new("sign_in", "Sign in")
|
.icon_position(IconPosition::Start)
|
||||||
.icon_color(Color::Muted)
|
.on_click(cx.listener(move |this, _, cx| this.authenticate(cx))),
|
||||||
.icon(IconName::Github)
|
|
||||||
.icon_position(IconPosition::Start)
|
|
||||||
.style(ButtonStyle::Filled)
|
|
||||||
.full_width()
|
|
||||||
.on_click(cx.listener(move |this, _, cx| this.authenticate(cx))),
|
|
||||||
)
|
|
||||||
.child(
|
|
||||||
div().flex().w_full().items_center().child(
|
|
||||||
Label::new("Sign in to enable collaboration.")
|
|
||||||
.color(Color::Muted)
|
|
||||||
.size(LabelSize::Small),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue