Adjust model selector popover design (#15056)
This PR mostly refines the model selector popover design by formatting the models names' and adjusting spacing/alignment in the list-related items. The list component changes could've been made in a separate PR but it was also very practical to do it here as I was already in-context. Either way, I'm happy to separate if that's better! One thing I couldn't necessarily figure out, though, is why the order changed (e.g., Anthropic at last ). I wonder if that was because of the separator logic somehow? I'd love guidance here—new to Rust! | Before | After | |--------|--------| | <img width="228" alt="Screenshot 2024-07-23 at 21 02 33" src="https://github.com/user-attachments/assets/3372c6c9-08dc-4d71-9265-26f015e2dbc2"> | <img width="228" alt="Screenshot 2024-07-23 at 21 01 45" src="https://github.com/user-attachments/assets/624cc7db-a3d9-48e3-99d7-c29829501130"> | --- Release Notes: - N/A --------- Co-authored-by: Marshall Bowers <elliott.codes@gmail.com> Co-authored-by: Bennet Bo Fenner <bennet@zed.dev> Co-authored-by: Antonio <antonio@zed.dev> Co-authored-by: Antonio Scandurra <me@as-cii.com>
This commit is contained in:
co-authored by
Marshall Bowers
Bennet Bo Fenner
Antonio
Antonio Scandurra
parent
87d93033d1
commit
912b396e58
@@ -2630,10 +2630,43 @@ impl Render for ContextEditorToolbarItem {
|
||||
});
|
||||
let right_side = h_flex()
|
||||
.gap_2()
|
||||
.child(ModelSelector::new(
|
||||
self.model_selector_menu_handle.clone(),
|
||||
self.fs.clone(),
|
||||
))
|
||||
.child(
|
||||
ModelSelector::new(
|
||||
self.fs.clone(),
|
||||
ButtonLike::new("active-model")
|
||||
.style(ButtonStyle::Subtle)
|
||||
.child(
|
||||
h_flex()
|
||||
.w_full()
|
||||
.gap_0p5()
|
||||
.child(
|
||||
div()
|
||||
.overflow_x_hidden()
|
||||
.flex_grow()
|
||||
.whitespace_nowrap()
|
||||
.child(
|
||||
Label::new(
|
||||
LanguageModelCompletionProvider::read_global(cx)
|
||||
.active_model()
|
||||
.map(|model| model.name().0)
|
||||
.unwrap_or_else(|| "No model selected".into()),
|
||||
)
|
||||
.size(LabelSize::Small)
|
||||
.color(Color::Muted),
|
||||
),
|
||||
)
|
||||
.child(
|
||||
Icon::new(IconName::ChevronDown)
|
||||
.color(Color::Muted)
|
||||
.size(IconSize::XSmall),
|
||||
),
|
||||
)
|
||||
.tooltip(move |cx| {
|
||||
Tooltip::for_action("Change Model", &ToggleModelSelector, cx)
|
||||
}),
|
||||
)
|
||||
.with_handle(self.model_selector_menu_handle.clone()),
|
||||
)
|
||||
.children(self.render_remaining_tokens(cx))
|
||||
.child(self.render_inject_context_menu(cx));
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::{
|
||||
assistant_settings::AssistantSettings, humanize_token_count, prompts::generate_content_prompt,
|
||||
AssistantPanel, AssistantPanelEvent, Hunk, LanguageModelCompletionProvider, StreamingDiff,
|
||||
humanize_token_count, prompts::generate_content_prompt, AssistantPanel, AssistantPanelEvent,
|
||||
Hunk, LanguageModelCompletionProvider, ModelSelector, StreamingDiff,
|
||||
};
|
||||
use anyhow::{anyhow, Context as _, Result};
|
||||
use client::telemetry::Telemetry;
|
||||
@@ -27,13 +27,11 @@ use gpui::{
|
||||
WindowContext,
|
||||
};
|
||||
use language::{Buffer, Point, Selection, TransactionId};
|
||||
use language_model::{
|
||||
LanguageModelRegistry, LanguageModelRequest, LanguageModelRequestMessage, Role,
|
||||
};
|
||||
use language_model::{LanguageModelRequest, LanguageModelRequestMessage, Role};
|
||||
use multi_buffer::MultiBufferRow;
|
||||
use parking_lot::Mutex;
|
||||
use rope::Rope;
|
||||
use settings::{update_settings_file, Settings};
|
||||
use settings::Settings;
|
||||
use similar::TextDiff;
|
||||
use smol::future::FutureExt;
|
||||
use std::{
|
||||
@@ -47,7 +45,7 @@ use std::{
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
use theme::ThemeSettings;
|
||||
use ui::{prelude::*, ContextMenu, IconButtonShape, PopoverMenu, Tooltip};
|
||||
use ui::{prelude::*, IconButtonShape, Tooltip};
|
||||
use util::RangeExt;
|
||||
use workspace::{notifications::NotificationId, Toast, Workspace};
|
||||
|
||||
@@ -1325,8 +1323,6 @@ impl EventEmitter<PromptEditorEvent> for PromptEditor {}
|
||||
impl Render for PromptEditor {
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
|
||||
let gutter_dimensions = *self.gutter_dimensions.lock();
|
||||
let fs = self.fs.clone();
|
||||
|
||||
let buttons = match &self.codegen.read(cx).status {
|
||||
CodegenStatus::Idle => {
|
||||
vec![
|
||||
@@ -1427,74 +1423,27 @@ impl Render for PromptEditor {
|
||||
.w(gutter_dimensions.full_width() + (gutter_dimensions.margin / 2.0))
|
||||
.justify_center()
|
||||
.gap_2()
|
||||
.child(
|
||||
PopoverMenu::new("model-switcher")
|
||||
.menu(move |cx| {
|
||||
ContextMenu::build(cx, |mut menu, cx| {
|
||||
for available_model in
|
||||
LanguageModelRegistry::read_global(cx).available_models(cx)
|
||||
{
|
||||
menu = menu.custom_entry(
|
||||
{
|
||||
let model_name = available_model.name().0.clone();
|
||||
let provider =
|
||||
available_model.provider_id().0.clone();
|
||||
move |_| {
|
||||
h_flex()
|
||||
.w_full()
|
||||
.justify_between()
|
||||
.child(Label::new(model_name.clone()))
|
||||
.child(
|
||||
div().ml_4().child(
|
||||
Label::new(provider.clone())
|
||||
.color(Color::Muted),
|
||||
),
|
||||
)
|
||||
.into_any()
|
||||
}
|
||||
},
|
||||
{
|
||||
let fs = fs.clone();
|
||||
let model = available_model.clone();
|
||||
move |cx| {
|
||||
let model = model.clone();
|
||||
update_settings_file::<AssistantSettings>(
|
||||
fs.clone(),
|
||||
cx,
|
||||
move |settings, _| {
|
||||
settings.set_model(model)
|
||||
},
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
menu
|
||||
})
|
||||
.into()
|
||||
})
|
||||
.trigger(
|
||||
IconButton::new("context", IconName::Settings)
|
||||
.shape(IconButtonShape::Square)
|
||||
.icon_size(IconSize::Small)
|
||||
.icon_color(Color::Muted)
|
||||
.tooltip(move |cx| {
|
||||
Tooltip::with_meta(
|
||||
format!(
|
||||
"Using {}",
|
||||
LanguageModelCompletionProvider::read_global(cx)
|
||||
.active_model()
|
||||
.map(|model| model.name().0)
|
||||
.unwrap_or_else(|| "No model selected".into()),
|
||||
),
|
||||
None,
|
||||
"Change Model",
|
||||
cx,
|
||||
)
|
||||
}),
|
||||
)
|
||||
.anchor(gpui::AnchorCorner::BottomRight),
|
||||
)
|
||||
.child(ModelSelector::new(
|
||||
self.fs.clone(),
|
||||
IconButton::new("context", IconName::Settings)
|
||||
.shape(IconButtonShape::Square)
|
||||
.icon_size(IconSize::Small)
|
||||
.icon_color(Color::Muted)
|
||||
.tooltip(move |cx| {
|
||||
Tooltip::with_meta(
|
||||
format!(
|
||||
"Using {}",
|
||||
LanguageModelCompletionProvider::read_global(cx)
|
||||
.active_model()
|
||||
.map(|model| model.name().0)
|
||||
.unwrap_or_else(|| "No model selected".into()),
|
||||
),
|
||||
None,
|
||||
"Change Model",
|
||||
cx,
|
||||
)
|
||||
}),
|
||||
))
|
||||
.children(
|
||||
if let CodegenStatus::Error(error) = &self.codegen.read(cx).status {
|
||||
let error_message = SharedString::from(error.to_string());
|
||||
@@ -2625,6 +2574,7 @@ mod tests {
|
||||
language_settings, tree_sitter_rust, Buffer, Language, LanguageConfig, LanguageMatcher,
|
||||
Point,
|
||||
};
|
||||
use language_model::LanguageModelRegistry;
|
||||
use rand::prelude::*;
|
||||
use serde::Serialize;
|
||||
use settings::SettingsStore;
|
||||
|
||||
@@ -1,127 +1,128 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::{
|
||||
assistant_settings::AssistantSettings, LanguageModelCompletionProvider, ToggleModelSelector,
|
||||
};
|
||||
use crate::{assistant_settings::AssistantSettings, LanguageModelCompletionProvider};
|
||||
use fs::Fs;
|
||||
use language_model::LanguageModelRegistry;
|
||||
use settings::update_settings_file;
|
||||
use ui::{prelude::*, ButtonLike, ContextMenu, PopoverMenu, PopoverMenuHandle, Tooltip};
|
||||
use ui::{prelude::*, ContextMenu, PopoverMenu, PopoverMenuHandle, PopoverTrigger};
|
||||
|
||||
#[derive(IntoElement)]
|
||||
pub struct ModelSelector {
|
||||
handle: PopoverMenuHandle<ContextMenu>,
|
||||
pub struct ModelSelector<T: PopoverTrigger> {
|
||||
handle: Option<PopoverMenuHandle<ContextMenu>>,
|
||||
fs: Arc<dyn Fs>,
|
||||
trigger: T,
|
||||
}
|
||||
|
||||
impl ModelSelector {
|
||||
pub fn new(handle: PopoverMenuHandle<ContextMenu>, fs: Arc<dyn Fs>) -> Self {
|
||||
ModelSelector { handle, fs }
|
||||
impl<T: PopoverTrigger> ModelSelector<T> {
|
||||
pub fn new(fs: Arc<dyn Fs>, trigger: T) -> Self {
|
||||
ModelSelector {
|
||||
handle: None,
|
||||
fs,
|
||||
trigger,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_handle(mut self, handle: PopoverMenuHandle<ContextMenu>) -> Self {
|
||||
self.handle = Some(handle);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderOnce for ModelSelector {
|
||||
fn render(self, cx: &mut WindowContext) -> impl IntoElement {
|
||||
PopoverMenu::new("model-switcher")
|
||||
.with_handle(self.handle)
|
||||
.menu(move |cx| {
|
||||
ContextMenu::build(cx, |mut menu, cx| {
|
||||
for (provider, available_models) in LanguageModelRegistry::global(cx)
|
||||
.read(cx)
|
||||
.available_models_grouped_by_provider(cx)
|
||||
{
|
||||
menu = menu.header(provider.0.clone());
|
||||
impl<T: PopoverTrigger> RenderOnce for ModelSelector<T> {
|
||||
fn render(self, _: &mut WindowContext) -> impl IntoElement {
|
||||
let mut menu = PopoverMenu::new("model-switcher");
|
||||
if let Some(handle) = self.handle {
|
||||
menu = menu.with_handle(handle);
|
||||
}
|
||||
|
||||
if available_models.is_empty() {
|
||||
menu = menu.custom_entry(
|
||||
{
|
||||
move |_| {
|
||||
h_flex()
|
||||
.w_full()
|
||||
.gap_1()
|
||||
.child(Icon::new(IconName::Settings))
|
||||
.child(Label::new("Configure"))
|
||||
.into_any()
|
||||
}
|
||||
},
|
||||
{
|
||||
let provider = provider.clone();
|
||||
move |cx| {
|
||||
LanguageModelCompletionProvider::global(cx).update(
|
||||
cx,
|
||||
|completion_provider, cx| {
|
||||
completion_provider
|
||||
.set_active_provider(provider.clone(), cx)
|
||||
},
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
for available_model in available_models {
|
||||
menu = menu.custom_entry(
|
||||
{
|
||||
let model_name = available_model.name().0.clone();
|
||||
move |_| {
|
||||
h_flex()
|
||||
.w_full()
|
||||
.child(Label::new(model_name.clone()))
|
||||
.into_any()
|
||||
}
|
||||
},
|
||||
{
|
||||
let fs = self.fs.clone();
|
||||
let model = available_model.clone();
|
||||
move |cx| {
|
||||
let model = model.clone();
|
||||
update_settings_file::<AssistantSettings>(
|
||||
fs.clone(),
|
||||
cx,
|
||||
move |settings, _| settings.set_model(model),
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
menu.menu(move |cx| {
|
||||
ContextMenu::build(cx, |mut menu, cx| {
|
||||
for (index, provider) in LanguageModelRegistry::global(cx)
|
||||
.read(cx)
|
||||
.providers()
|
||||
.enumerate()
|
||||
{
|
||||
if index > 0 {
|
||||
menu = menu.separator();
|
||||
}
|
||||
menu
|
||||
})
|
||||
.into()
|
||||
})
|
||||
.trigger(
|
||||
ButtonLike::new("active-model")
|
||||
.style(ButtonStyle::Subtle)
|
||||
.child(
|
||||
h_flex()
|
||||
.w_full()
|
||||
.gap_0p5()
|
||||
.child(
|
||||
div()
|
||||
.overflow_x_hidden()
|
||||
.flex_grow()
|
||||
.whitespace_nowrap()
|
||||
.child(
|
||||
Label::new(
|
||||
LanguageModelCompletionProvider::read_global(cx)
|
||||
.active_model()
|
||||
.map(|model| model.name().0)
|
||||
.unwrap_or_else(|| "No model selected".into()),
|
||||
menu = menu.header(provider.name().0);
|
||||
|
||||
let available_models = provider.provided_models(cx);
|
||||
if available_models.is_empty() {
|
||||
menu = menu.custom_entry(
|
||||
{
|
||||
move |_| {
|
||||
h_flex()
|
||||
.w_full()
|
||||
.gap_1()
|
||||
.child(Icon::new(IconName::Settings))
|
||||
.child(Label::new("Configure"))
|
||||
.into_any()
|
||||
}
|
||||
},
|
||||
{
|
||||
let provider = provider.id();
|
||||
move |cx| {
|
||||
LanguageModelCompletionProvider::global(cx).update(
|
||||
cx,
|
||||
|completion_provider, cx| {
|
||||
completion_provider
|
||||
.set_active_provider(provider.clone(), cx)
|
||||
},
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
let selected_model = LanguageModelCompletionProvider::read_global(cx)
|
||||
.active_model()
|
||||
.map(|m| m.id());
|
||||
let selected_provider = LanguageModelCompletionProvider::read_global(cx)
|
||||
.active_provider()
|
||||
.map(|m| m.id());
|
||||
|
||||
for available_model in available_models {
|
||||
menu = menu.custom_entry(
|
||||
{
|
||||
let id = available_model.id();
|
||||
let provider_id = available_model.provider_id();
|
||||
let model_name = available_model.name().0.clone();
|
||||
let selected_model = selected_model.clone();
|
||||
let selected_provider = selected_provider.clone();
|
||||
move |_| {
|
||||
h_flex()
|
||||
.w_full()
|
||||
.justify_between()
|
||||
.child(Label::new(model_name.clone()))
|
||||
.when(
|
||||
selected_model.as_ref() == Some(&id)
|
||||
&& selected_provider.as_ref() == Some(&provider_id),
|
||||
|this| this.child(Icon::new(IconName::Check)),
|
||||
)
|
||||
.size(LabelSize::Small)
|
||||
.color(Color::Muted),
|
||||
),
|
||||
)
|
||||
.child(
|
||||
Icon::new(IconName::ChevronDown)
|
||||
.color(Color::Muted)
|
||||
.size(IconSize::XSmall),
|
||||
),
|
||||
)
|
||||
.tooltip(move |cx| {
|
||||
Tooltip::for_action("Change Model", &ToggleModelSelector, cx)
|
||||
}),
|
||||
)
|
||||
.attach(gpui::AnchorCorner::BottomLeft)
|
||||
.into_any()
|
||||
}
|
||||
},
|
||||
{
|
||||
let fs = self.fs.clone();
|
||||
let model = available_model.clone();
|
||||
move |cx| {
|
||||
let model = model.clone();
|
||||
update_settings_file::<AssistantSettings>(
|
||||
fs.clone(),
|
||||
cx,
|
||||
move |settings, _| settings.set_model(model),
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
menu
|
||||
})
|
||||
.into()
|
||||
})
|
||||
.trigger(self.trigger)
|
||||
.attach(gpui::AnchorCorner::BottomLeft)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
use crate::{
|
||||
assistant_settings::AssistantSettings, humanize_token_count,
|
||||
prompts::generate_terminal_assistant_prompt, AssistantPanel, AssistantPanelEvent,
|
||||
LanguageModelCompletionProvider,
|
||||
humanize_token_count, prompts::generate_terminal_assistant_prompt, AssistantPanel,
|
||||
AssistantPanelEvent, LanguageModelCompletionProvider, ModelSelector,
|
||||
};
|
||||
use anyhow::{Context as _, Result};
|
||||
use client::telemetry::Telemetry;
|
||||
@@ -17,10 +16,8 @@ use gpui::{
|
||||
Subscription, Task, TextStyle, UpdateGlobal, View, WeakView,
|
||||
};
|
||||
use language::Buffer;
|
||||
use language_model::{
|
||||
LanguageModelRegistry, LanguageModelRequest, LanguageModelRequestMessage, Role,
|
||||
};
|
||||
use settings::{update_settings_file, Settings};
|
||||
use language_model::{LanguageModelRequest, LanguageModelRequestMessage, Role};
|
||||
use settings::Settings;
|
||||
use std::{
|
||||
cmp,
|
||||
sync::Arc,
|
||||
@@ -29,7 +26,7 @@ use std::{
|
||||
use terminal::Terminal;
|
||||
use terminal_view::TerminalView;
|
||||
use theme::ThemeSettings;
|
||||
use ui::{prelude::*, ContextMenu, IconButtonShape, PopoverMenu, Tooltip};
|
||||
use ui::{prelude::*, IconButtonShape, Tooltip};
|
||||
use util::ResultExt;
|
||||
use workspace::{notifications::NotificationId, Toast, Workspace};
|
||||
|
||||
@@ -450,8 +447,6 @@ impl EventEmitter<PromptEditorEvent> for PromptEditor {}
|
||||
|
||||
impl Render for PromptEditor {
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
|
||||
let fs = self.fs.clone();
|
||||
|
||||
let buttons = match &self.codegen.read(cx).status {
|
||||
CodegenStatus::Idle => {
|
||||
vec![
|
||||
@@ -554,74 +549,27 @@ impl Render for PromptEditor {
|
||||
.w_12()
|
||||
.justify_center()
|
||||
.gap_2()
|
||||
.child(
|
||||
PopoverMenu::new("model-switcher")
|
||||
.menu(move |cx| {
|
||||
ContextMenu::build(cx, |mut menu, cx| {
|
||||
for available_model in
|
||||
LanguageModelRegistry::read_global(cx).available_models(cx)
|
||||
{
|
||||
menu = menu.custom_entry(
|
||||
{
|
||||
let model_name = available_model.name().0.clone();
|
||||
let provider =
|
||||
available_model.provider_id().0.clone();
|
||||
move |_| {
|
||||
h_flex()
|
||||
.w_full()
|
||||
.justify_between()
|
||||
.child(Label::new(model_name.clone()))
|
||||
.child(
|
||||
div().ml_4().child(
|
||||
Label::new(provider.clone())
|
||||
.color(Color::Muted),
|
||||
),
|
||||
)
|
||||
.into_any()
|
||||
}
|
||||
},
|
||||
{
|
||||
let fs = fs.clone();
|
||||
let model = available_model.clone();
|
||||
move |cx| {
|
||||
let model = model.clone();
|
||||
update_settings_file::<AssistantSettings>(
|
||||
fs.clone(),
|
||||
cx,
|
||||
move |settings, _| {
|
||||
settings.set_model(model)
|
||||
},
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
menu
|
||||
})
|
||||
.into()
|
||||
})
|
||||
.trigger(
|
||||
IconButton::new("context", IconName::Settings)
|
||||
.shape(IconButtonShape::Square)
|
||||
.icon_size(IconSize::Small)
|
||||
.icon_color(Color::Muted)
|
||||
.tooltip(move |cx| {
|
||||
Tooltip::with_meta(
|
||||
format!(
|
||||
"Using {}",
|
||||
LanguageModelCompletionProvider::read_global(cx)
|
||||
.active_model()
|
||||
.map(|model| model.name().0)
|
||||
.unwrap_or_else(|| "No model selected".into())
|
||||
),
|
||||
None,
|
||||
"Change Model",
|
||||
cx,
|
||||
)
|
||||
}),
|
||||
)
|
||||
.anchor(gpui::AnchorCorner::BottomRight),
|
||||
)
|
||||
.child(ModelSelector::new(
|
||||
self.fs.clone(),
|
||||
IconButton::new("context", IconName::Settings)
|
||||
.shape(IconButtonShape::Square)
|
||||
.icon_size(IconSize::Small)
|
||||
.icon_color(Color::Muted)
|
||||
.tooltip(move |cx| {
|
||||
Tooltip::with_meta(
|
||||
format!(
|
||||
"Using {}",
|
||||
LanguageModelCompletionProvider::read_global(cx)
|
||||
.active_model()
|
||||
.map(|model| model.name().0)
|
||||
.unwrap_or_else(|| "No model selected".into()),
|
||||
),
|
||||
None,
|
||||
"Change Model",
|
||||
cx,
|
||||
)
|
||||
}),
|
||||
))
|
||||
.children(
|
||||
if let CodegenStatus::Error(error) = &self.codegen.read(cx).status {
|
||||
let error_message = SharedString::from(error.to_string());
|
||||
|
||||
@@ -89,10 +89,10 @@ impl LanguageModelCompletionProvider {
|
||||
|
||||
pub fn set_active_provider(
|
||||
&mut self,
|
||||
provider_name: LanguageModelProviderId,
|
||||
provider_id: LanguageModelProviderId,
|
||||
cx: &mut ModelContext<Self>,
|
||||
) {
|
||||
self.active_provider = LanguageModelRegistry::read_global(cx).provider(&provider_name);
|
||||
self.active_provider = LanguageModelRegistry::read_global(cx).provider(&provider_id);
|
||||
self.active_model = None;
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
@@ -59,16 +59,16 @@ pub trait LanguageModelProviderState: 'static {
|
||||
fn subscribe<T: 'static>(&self, cx: &mut gpui::ModelContext<T>) -> Option<gpui::Subscription>;
|
||||
}
|
||||
|
||||
#[derive(Clone, Eq, PartialEq, Hash, Debug)]
|
||||
#[derive(Clone, Eq, PartialEq, Hash, Debug, Ord, PartialOrd)]
|
||||
pub struct LanguageModelId(pub SharedString);
|
||||
|
||||
#[derive(Clone, Eq, PartialEq, Hash, Debug)]
|
||||
#[derive(Clone, Eq, PartialEq, Hash, Debug, Ord, PartialOrd)]
|
||||
pub struct LanguageModelName(pub SharedString);
|
||||
|
||||
#[derive(Clone, Eq, PartialEq, Hash, Debug)]
|
||||
#[derive(Clone, Eq, PartialEq, Hash, Debug, Ord, PartialOrd)]
|
||||
pub struct LanguageModelProviderId(pub SharedString);
|
||||
|
||||
#[derive(Clone, Eq, PartialEq, Hash, Debug)]
|
||||
#[derive(Clone, Eq, PartialEq, Hash, Debug, Ord, PartialOrd)]
|
||||
pub struct LanguageModelProviderName(pub SharedString);
|
||||
|
||||
impl From<String> for LanguageModelId {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use anthropic::{stream_completion, Request, RequestMessage};
|
||||
use anyhow::{anyhow, Result};
|
||||
use collections::BTreeMap;
|
||||
use editor::{Editor, EditorElement, EditorStyle};
|
||||
use futures::{future::BoxFuture, stream::BoxStream, FutureExt, StreamExt};
|
||||
use gpui::{
|
||||
@@ -8,7 +9,7 @@ use gpui::{
|
||||
};
|
||||
use http_client::HttpClient;
|
||||
use settings::{Settings, SettingsStore};
|
||||
use std::{collections::BTreeMap, sync::Arc, time::Duration};
|
||||
use std::{sync::Arc, time::Duration};
|
||||
use strum::IntoEnumIterator;
|
||||
use theme::ThemeSettings;
|
||||
use ui::prelude::*;
|
||||
|
||||
@@ -6,10 +6,11 @@ use crate::{
|
||||
};
|
||||
use anyhow::Result;
|
||||
use client::Client;
|
||||
use collections::BTreeMap;
|
||||
use futures::{future::BoxFuture, stream::BoxStream, FutureExt, StreamExt, TryFutureExt};
|
||||
use gpui::{AnyView, AppContext, AsyncAppContext, Subscription, Task};
|
||||
use settings::{Settings, SettingsStore};
|
||||
use std::{collections::BTreeMap, sync::Arc};
|
||||
use std::sync::Arc;
|
||||
use strum::IntoEnumIterator;
|
||||
use ui::prelude::*;
|
||||
|
||||
|
||||
@@ -75,12 +75,12 @@ impl OllamaLanguageModelProvider {
|
||||
http_client,
|
||||
available_models: Default::default(),
|
||||
_subscription: cx.observe_global::<SettingsStore>(|this: &mut State, cx| {
|
||||
this.fetch_models(cx).detach_and_log_err(cx);
|
||||
this.fetch_models(cx).detach();
|
||||
cx.notify();
|
||||
}),
|
||||
}),
|
||||
};
|
||||
this.fetch_models(cx).detach_and_log_err(cx);
|
||||
this.fetch_models(cx).detach();
|
||||
this
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use client::Client;
|
||||
use collections::HashMap;
|
||||
use collections::BTreeMap;
|
||||
use gpui::{AppContext, Global, Model, ModelContext};
|
||||
use std::sync::Arc;
|
||||
use ui::Context;
|
||||
@@ -65,7 +65,7 @@ impl Global for GlobalLanguageModelRegistry {}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct LanguageModelRegistry {
|
||||
providers: HashMap<LanguageModelProviderId, Arc<dyn LanguageModelProvider>>,
|
||||
providers: BTreeMap<LanguageModelProviderId, Arc<dyn LanguageModelProvider>>,
|
||||
}
|
||||
|
||||
impl LanguageModelRegistry {
|
||||
@@ -114,10 +114,8 @@ impl LanguageModelRegistry {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn providers(
|
||||
&self,
|
||||
) -> impl Iterator<Item = (&LanguageModelProviderId, &Arc<dyn LanguageModelProvider>)> {
|
||||
self.providers.iter()
|
||||
pub fn providers(&self) -> impl Iterator<Item = &Arc<dyn LanguageModelProvider>> {
|
||||
self.providers.values()
|
||||
}
|
||||
|
||||
pub fn available_models(&self, cx: &AppContext) -> Vec<Arc<dyn LanguageModel>> {
|
||||
@@ -127,16 +125,6 @@ impl LanguageModelRegistry {
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn available_models_grouped_by_provider(
|
||||
&self,
|
||||
cx: &AppContext,
|
||||
) -> HashMap<LanguageModelProviderId, Vec<Arc<dyn LanguageModel>>> {
|
||||
self.providers
|
||||
.iter()
|
||||
.map(|(name, provider)| (name.clone(), provider.provided_models(cx)))
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn provider(
|
||||
&self,
|
||||
name: &LanguageModelProviderId,
|
||||
@@ -160,7 +148,7 @@ mod tests {
|
||||
|
||||
let providers = registry.read(cx).providers().collect::<Vec<_>>();
|
||||
assert_eq!(providers.len(), 1);
|
||||
assert_eq!(providers[0].0, &crate::provider::fake::provider_id());
|
||||
assert_eq!(providers[0].id(), crate::provider::fake::provider_id());
|
||||
|
||||
registry.update(cx, |registry, cx| {
|
||||
registry.unregister_provider(&crate::provider::fake::provider_id(), cx);
|
||||
|
||||
@@ -185,7 +185,7 @@ impl RenderOnce for ListItem {
|
||||
.w_full()
|
||||
.relative()
|
||||
.gap_1()
|
||||
.px_2()
|
||||
.px_1p5()
|
||||
.map(|this| match self.spacing {
|
||||
ListItemSpacing::Dense => this,
|
||||
ListItemSpacing::Sparse => this.py_1(),
|
||||
|
||||
@@ -8,7 +8,7 @@ impl RenderOnce for ListSeparator {
|
||||
div()
|
||||
.h_px()
|
||||
.w_full()
|
||||
.my_1()
|
||||
.my_1p5()
|
||||
.bg(cx.theme().colors().border_variant)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ impl Selectable for ListSubHeader {
|
||||
|
||||
impl RenderOnce for ListSubHeader {
|
||||
fn render(self, cx: &mut WindowContext) -> impl IntoElement {
|
||||
h_flex().flex_1().w_full().relative().py_1().child(
|
||||
h_flex().flex_1().w_full().relative().pb_1().px_0p5().child(
|
||||
div()
|
||||
.h_6()
|
||||
.when(self.inset, |this| this.px_2())
|
||||
|
||||
Reference in New Issue
Block a user