Fix language model selector (#26138)
This PR fixes the language model selector.
I tried to piece together the state prior to #25697 (the state it was in
at 11838cf89e
) while retaining unrelated
changes that happened since then.
Release Notes:
- Fixed an issue where language models would not be authenticated until
after the model selector was opened (Preview only).
This commit is contained in:
parent
ad9c508a72
commit
66784c0b3f
7 changed files with 289 additions and 200 deletions
|
@ -35,7 +35,7 @@ use language_model::{
|
||||||
report_assistant_event, LanguageModel, LanguageModelRegistry, LanguageModelRequest,
|
report_assistant_event, LanguageModel, LanguageModelRegistry, LanguageModelRequest,
|
||||||
LanguageModelRequestMessage, LanguageModelTextStream, Role,
|
LanguageModelRequestMessage, LanguageModelTextStream, Role,
|
||||||
};
|
};
|
||||||
use language_model_selector::inline_language_model_selector;
|
use language_model_selector::{LanguageModelSelector, LanguageModelSelectorPopoverMenu};
|
||||||
use multi_buffer::MultiBufferRow;
|
use multi_buffer::MultiBufferRow;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use project::{CodeAction, ProjectTransaction};
|
use project::{CodeAction, ProjectTransaction};
|
||||||
|
@ -1425,6 +1425,7 @@ enum PromptEditorEvent {
|
||||||
struct PromptEditor {
|
struct PromptEditor {
|
||||||
id: InlineAssistId,
|
id: InlineAssistId,
|
||||||
editor: Entity<Editor>,
|
editor: Entity<Editor>,
|
||||||
|
language_model_selector: Entity<LanguageModelSelector>,
|
||||||
edited_since_done: bool,
|
edited_since_done: bool,
|
||||||
gutter_dimensions: Arc<Mutex<GutterDimensions>>,
|
gutter_dimensions: Arc<Mutex<GutterDimensions>>,
|
||||||
prompt_history: VecDeque<String>,
|
prompt_history: VecDeque<String>,
|
||||||
|
@ -1438,7 +1439,6 @@ struct PromptEditor {
|
||||||
_token_count_subscriptions: Vec<Subscription>,
|
_token_count_subscriptions: Vec<Subscription>,
|
||||||
workspace: Option<WeakEntity<Workspace>>,
|
workspace: Option<WeakEntity<Workspace>>,
|
||||||
show_rate_limit_notice: bool,
|
show_rate_limit_notice: bool,
|
||||||
fs: Arc<dyn Fs>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
|
@ -1589,16 +1589,29 @@ impl Render for PromptEditor {
|
||||||
.w(gutter_dimensions.full_width() + (gutter_dimensions.margin / 2.0))
|
.w(gutter_dimensions.full_width() + (gutter_dimensions.margin / 2.0))
|
||||||
.justify_center()
|
.justify_center()
|
||||||
.gap_2()
|
.gap_2()
|
||||||
.child(inline_language_model_selector({
|
.child(LanguageModelSelectorPopoverMenu::new(
|
||||||
let fs = self.fs.clone();
|
self.language_model_selector.clone(),
|
||||||
move |model, cx| {
|
IconButton::new("context", IconName::SettingsAlt)
|
||||||
update_settings_file::<AssistantSettings>(
|
.shape(IconButtonShape::Square)
|
||||||
fs.clone(),
|
.icon_size(IconSize::Small)
|
||||||
|
.icon_color(Color::Muted),
|
||||||
|
move |window, cx| {
|
||||||
|
Tooltip::with_meta(
|
||||||
|
format!(
|
||||||
|
"Using {}",
|
||||||
|
LanguageModelRegistry::read_global(cx)
|
||||||
|
.active_model()
|
||||||
|
.map(|model| model.name().0)
|
||||||
|
.unwrap_or_else(|| "No model selected".into()),
|
||||||
|
),
|
||||||
|
None,
|
||||||
|
"Change Model",
|
||||||
|
window,
|
||||||
cx,
|
cx,
|
||||||
move |settings, _| settings.set_model(model.clone()),
|
)
|
||||||
);
|
},
|
||||||
}
|
gpui::Corner::TopRight,
|
||||||
}))
|
))
|
||||||
.map(|el| {
|
.map(|el| {
|
||||||
let CodegenStatus::Error(error) = self.codegen.read(cx).status(cx) else {
|
let CodegenStatus::Error(error) = self.codegen.read(cx).status(cx) else {
|
||||||
return el;
|
return el;
|
||||||
|
@ -1711,8 +1724,21 @@ impl PromptEditor {
|
||||||
|
|
||||||
let mut this = Self {
|
let mut this = Self {
|
||||||
id,
|
id,
|
||||||
fs,
|
|
||||||
editor: prompt_editor,
|
editor: prompt_editor,
|
||||||
|
language_model_selector: cx.new(|cx| {
|
||||||
|
let fs = fs.clone();
|
||||||
|
LanguageModelSelector::new(
|
||||||
|
move |model, cx| {
|
||||||
|
update_settings_file::<AssistantSettings>(
|
||||||
|
fs.clone(),
|
||||||
|
cx,
|
||||||
|
move |settings, _| settings.set_model(model.clone()),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
window,
|
||||||
|
cx,
|
||||||
|
)
|
||||||
|
}),
|
||||||
edited_since_done: false,
|
edited_since_done: false,
|
||||||
gutter_dimensions,
|
gutter_dimensions,
|
||||||
prompt_history,
|
prompt_history,
|
||||||
|
|
|
@ -19,7 +19,7 @@ use language_model::{
|
||||||
report_assistant_event, LanguageModelRegistry, LanguageModelRequest,
|
report_assistant_event, LanguageModelRegistry, LanguageModelRequest,
|
||||||
LanguageModelRequestMessage, Role,
|
LanguageModelRequestMessage, Role,
|
||||||
};
|
};
|
||||||
use language_model_selector::inline_language_model_selector;
|
use language_model_selector::{LanguageModelSelector, LanguageModelSelectorPopoverMenu};
|
||||||
use prompt_store::PromptBuilder;
|
use prompt_store::PromptBuilder;
|
||||||
use settings::{update_settings_file, Settings};
|
use settings::{update_settings_file, Settings};
|
||||||
use std::{
|
use std::{
|
||||||
|
@ -487,9 +487,9 @@ enum PromptEditorEvent {
|
||||||
|
|
||||||
struct PromptEditor {
|
struct PromptEditor {
|
||||||
id: TerminalInlineAssistId,
|
id: TerminalInlineAssistId,
|
||||||
fs: Arc<dyn Fs>,
|
|
||||||
height_in_lines: u8,
|
height_in_lines: u8,
|
||||||
editor: Entity<Editor>,
|
editor: Entity<Editor>,
|
||||||
|
language_model_selector: Entity<LanguageModelSelector>,
|
||||||
edited_since_done: bool,
|
edited_since_done: bool,
|
||||||
prompt_history: VecDeque<String>,
|
prompt_history: VecDeque<String>,
|
||||||
prompt_history_ix: Option<usize>,
|
prompt_history_ix: Option<usize>,
|
||||||
|
@ -641,16 +641,29 @@ impl Render for PromptEditor {
|
||||||
.w_12()
|
.w_12()
|
||||||
.justify_center()
|
.justify_center()
|
||||||
.gap_2()
|
.gap_2()
|
||||||
.child(inline_language_model_selector({
|
.child(LanguageModelSelectorPopoverMenu::new(
|
||||||
let fs = self.fs.clone();
|
self.language_model_selector.clone(),
|
||||||
move |model, cx| {
|
IconButton::new("change-model", IconName::SettingsAlt)
|
||||||
update_settings_file::<AssistantSettings>(
|
.shape(IconButtonShape::Square)
|
||||||
fs.clone(),
|
.icon_size(IconSize::Small)
|
||||||
|
.icon_color(Color::Muted),
|
||||||
|
move |window, cx| {
|
||||||
|
Tooltip::with_meta(
|
||||||
|
format!(
|
||||||
|
"Using {}",
|
||||||
|
LanguageModelRegistry::read_global(cx)
|
||||||
|
.active_model()
|
||||||
|
.map(|model| model.name().0)
|
||||||
|
.unwrap_or_else(|| "No model selected".into()),
|
||||||
|
),
|
||||||
|
None,
|
||||||
|
"Change Model",
|
||||||
|
window,
|
||||||
cx,
|
cx,
|
||||||
move |settings, _| settings.set_model(model.clone()),
|
)
|
||||||
);
|
},
|
||||||
}
|
gpui::Corner::TopRight,
|
||||||
}))
|
))
|
||||||
.children(
|
.children(
|
||||||
if let CodegenStatus::Error(error) = &self.codegen.read(cx).status {
|
if let CodegenStatus::Error(error) = &self.codegen.read(cx).status {
|
||||||
let error_message = SharedString::from(error.to_string());
|
let error_message = SharedString::from(error.to_string());
|
||||||
|
@ -728,9 +741,22 @@ impl PromptEditor {
|
||||||
|
|
||||||
let mut this = Self {
|
let mut this = Self {
|
||||||
id,
|
id,
|
||||||
fs,
|
|
||||||
height_in_lines: 1,
|
height_in_lines: 1,
|
||||||
editor: prompt_editor,
|
editor: prompt_editor,
|
||||||
|
language_model_selector: cx.new(|cx| {
|
||||||
|
let fs = fs.clone();
|
||||||
|
LanguageModelSelector::new(
|
||||||
|
move |model, cx| {
|
||||||
|
update_settings_file::<AssistantSettings>(
|
||||||
|
fs.clone(),
|
||||||
|
cx,
|
||||||
|
move |settings, _| settings.set_model(model.clone()),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
window,
|
||||||
|
cx,
|
||||||
|
)
|
||||||
|
}),
|
||||||
edited_since_done: false,
|
edited_since_done: false,
|
||||||
prompt_history,
|
prompt_history,
|
||||||
prompt_history_ix: None,
|
prompt_history_ix: None,
|
||||||
|
|
|
@ -1,28 +1,45 @@
|
||||||
use assistant_settings::AssistantSettings;
|
use assistant_settings::AssistantSettings;
|
||||||
use fs::Fs;
|
use fs::Fs;
|
||||||
use gpui::FocusHandle;
|
use gpui::{Entity, FocusHandle, SharedString};
|
||||||
use language_model_selector::{assistant_language_model_selector, LanguageModelSelector};
|
use language_model::LanguageModelRegistry;
|
||||||
|
use language_model_selector::{
|
||||||
|
LanguageModelSelector, LanguageModelSelectorPopoverMenu, ToggleModelSelector,
|
||||||
|
};
|
||||||
use settings::update_settings_file;
|
use settings::update_settings_file;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use ui::{prelude::*, PopoverMenuHandle};
|
use ui::{prelude::*, ButtonLike, PopoverMenuHandle, Tooltip};
|
||||||
|
|
||||||
pub struct AssistantModelSelector {
|
pub struct AssistantModelSelector {
|
||||||
|
selector: Entity<LanguageModelSelector>,
|
||||||
menu_handle: PopoverMenuHandle<LanguageModelSelector>,
|
menu_handle: PopoverMenuHandle<LanguageModelSelector>,
|
||||||
focus_handle: FocusHandle,
|
focus_handle: FocusHandle,
|
||||||
fs: Arc<dyn Fs>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AssistantModelSelector {
|
impl AssistantModelSelector {
|
||||||
pub(crate) fn new(
|
pub(crate) fn new(
|
||||||
fs: Arc<dyn Fs>,
|
fs: Arc<dyn Fs>,
|
||||||
|
menu_handle: PopoverMenuHandle<LanguageModelSelector>,
|
||||||
focus_handle: FocusHandle,
|
focus_handle: FocusHandle,
|
||||||
_window: &mut Window,
|
window: &mut Window,
|
||||||
_cx: &mut App,
|
cx: &mut App,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
fs,
|
selector: cx.new(|cx| {
|
||||||
|
let fs = fs.clone();
|
||||||
|
LanguageModelSelector::new(
|
||||||
|
move |model, cx| {
|
||||||
|
update_settings_file::<AssistantSettings>(
|
||||||
|
fs.clone(),
|
||||||
|
cx,
|
||||||
|
move |settings, _cx| settings.set_model(model.clone()),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
window,
|
||||||
|
cx,
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
menu_handle,
|
||||||
focus_handle,
|
focus_handle,
|
||||||
menu_handle: PopoverMenuHandle::default(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,21 +49,43 @@ impl AssistantModelSelector {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Render for AssistantModelSelector {
|
impl Render for AssistantModelSelector {
|
||||||
fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||||
assistant_language_model_selector(
|
let active_model = LanguageModelRegistry::read_global(cx).active_model();
|
||||||
self.focus_handle.clone(),
|
let focus_handle = self.focus_handle.clone();
|
||||||
Some(self.menu_handle.clone()),
|
let model_name = match active_model {
|
||||||
cx,
|
Some(model) => model.name().0,
|
||||||
{
|
_ => SharedString::from("No model selected"),
|
||||||
let fs = self.fs.clone();
|
};
|
||||||
move |model, cx| {
|
|
||||||
update_settings_file::<AssistantSettings>(
|
LanguageModelSelectorPopoverMenu::new(
|
||||||
fs.clone(),
|
self.selector.clone(),
|
||||||
cx,
|
ButtonLike::new("active-model")
|
||||||
move |settings, _| settings.set_model(model.clone()),
|
.style(ButtonStyle::Subtle)
|
||||||
);
|
.child(
|
||||||
}
|
h_flex()
|
||||||
|
.gap_0p5()
|
||||||
|
.child(
|
||||||
|
Label::new(model_name)
|
||||||
|
.size(LabelSize::Small)
|
||||||
|
.color(Color::Muted),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
Icon::new(IconName::ChevronDown)
|
||||||
|
.color(Color::Muted)
|
||||||
|
.size(IconSize::XSmall),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
move |window, cx| {
|
||||||
|
Tooltip::for_action_in(
|
||||||
|
"Change Model",
|
||||||
|
&ToggleModelSelector,
|
||||||
|
&focus_handle,
|
||||||
|
window,
|
||||||
|
cx,
|
||||||
|
)
|
||||||
},
|
},
|
||||||
|
gpui::Corner::BottomRight,
|
||||||
)
|
)
|
||||||
|
.with_handle(self.menu_handle.clone())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -857,6 +857,7 @@ impl PromptEditor<BufferCodegen> {
|
||||||
editor
|
editor
|
||||||
});
|
});
|
||||||
let context_picker_menu_handle = PopoverMenuHandle::default();
|
let context_picker_menu_handle = PopoverMenuHandle::default();
|
||||||
|
let model_selector_menu_handle = PopoverMenuHandle::default();
|
||||||
|
|
||||||
let context_strip = cx.new(|cx| {
|
let context_strip = cx.new(|cx| {
|
||||||
ContextStrip::new(
|
ContextStrip::new(
|
||||||
|
@ -880,7 +881,13 @@ impl PromptEditor<BufferCodegen> {
|
||||||
context_strip,
|
context_strip,
|
||||||
context_picker_menu_handle,
|
context_picker_menu_handle,
|
||||||
model_selector: cx.new(|cx| {
|
model_selector: cx.new(|cx| {
|
||||||
AssistantModelSelector::new(fs, prompt_editor.focus_handle(cx), window, cx)
|
AssistantModelSelector::new(
|
||||||
|
fs,
|
||||||
|
model_selector_menu_handle,
|
||||||
|
prompt_editor.focus_handle(cx),
|
||||||
|
window,
|
||||||
|
cx,
|
||||||
|
)
|
||||||
}),
|
}),
|
||||||
edited_since_done: false,
|
edited_since_done: false,
|
||||||
prompt_history,
|
prompt_history,
|
||||||
|
@ -1005,6 +1012,7 @@ impl PromptEditor<TerminalCodegen> {
|
||||||
editor
|
editor
|
||||||
});
|
});
|
||||||
let context_picker_menu_handle = PopoverMenuHandle::default();
|
let context_picker_menu_handle = PopoverMenuHandle::default();
|
||||||
|
let model_selector_menu_handle = PopoverMenuHandle::default();
|
||||||
|
|
||||||
let context_strip = cx.new(|cx| {
|
let context_strip = cx.new(|cx| {
|
||||||
ContextStrip::new(
|
ContextStrip::new(
|
||||||
|
@ -1028,7 +1036,13 @@ impl PromptEditor<TerminalCodegen> {
|
||||||
context_strip,
|
context_strip,
|
||||||
context_picker_menu_handle,
|
context_picker_menu_handle,
|
||||||
model_selector: cx.new(|cx| {
|
model_selector: cx.new(|cx| {
|
||||||
AssistantModelSelector::new(fs, prompt_editor.focus_handle(cx), window, cx)
|
AssistantModelSelector::new(
|
||||||
|
fs,
|
||||||
|
model_selector_menu_handle.clone(),
|
||||||
|
prompt_editor.focus_handle(cx),
|
||||||
|
window,
|
||||||
|
cx,
|
||||||
|
)
|
||||||
}),
|
}),
|
||||||
edited_since_done: false,
|
edited_since_done: false,
|
||||||
prompt_history,
|
prompt_history,
|
||||||
|
|
|
@ -54,6 +54,7 @@ impl MessageEditor {
|
||||||
let context_store = cx.new(|_cx| ContextStore::new(workspace.clone()));
|
let context_store = cx.new(|_cx| ContextStore::new(workspace.clone()));
|
||||||
let context_picker_menu_handle = PopoverMenuHandle::default();
|
let context_picker_menu_handle = PopoverMenuHandle::default();
|
||||||
let inline_context_picker_menu_handle = PopoverMenuHandle::default();
|
let inline_context_picker_menu_handle = PopoverMenuHandle::default();
|
||||||
|
let model_selector_menu_handle = PopoverMenuHandle::default();
|
||||||
|
|
||||||
let editor = cx.new(|cx| {
|
let editor = cx.new(|cx| {
|
||||||
let mut editor = Editor::auto_height(10, window, cx);
|
let mut editor = Editor::auto_height(10, window, cx);
|
||||||
|
@ -106,8 +107,15 @@ impl MessageEditor {
|
||||||
context_picker_menu_handle,
|
context_picker_menu_handle,
|
||||||
inline_context_picker,
|
inline_context_picker,
|
||||||
inline_context_picker_menu_handle,
|
inline_context_picker_menu_handle,
|
||||||
model_selector: cx
|
model_selector: cx.new(|cx| {
|
||||||
.new(|cx| AssistantModelSelector::new(fs, editor.focus_handle(cx), window, cx)),
|
AssistantModelSelector::new(
|
||||||
|
fs,
|
||||||
|
model_selector_menu_handle,
|
||||||
|
editor.focus_handle(cx),
|
||||||
|
window,
|
||||||
|
cx,
|
||||||
|
)
|
||||||
|
}),
|
||||||
use_tools: false,
|
use_tools: false,
|
||||||
_subscriptions: subscriptions,
|
_subscriptions: subscriptions,
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,7 +38,7 @@ use language_model::{
|
||||||
Role,
|
Role,
|
||||||
};
|
};
|
||||||
use language_model_selector::{
|
use language_model_selector::{
|
||||||
assistant_language_model_selector, LanguageModelSelector, ToggleModelSelector,
|
LanguageModelSelector, LanguageModelSelectorPopoverMenu, ToggleModelSelector,
|
||||||
};
|
};
|
||||||
use multi_buffer::MultiBufferRow;
|
use multi_buffer::MultiBufferRow;
|
||||||
use picker::Picker;
|
use picker::Picker;
|
||||||
|
@ -197,7 +197,8 @@ pub struct ContextEditor {
|
||||||
// the file is opened. In order to keep the worktree alive for the duration of the
|
// the file is opened. In order to keep the worktree alive for the duration of the
|
||||||
// context editor, we keep a reference here.
|
// context editor, we keep a reference here.
|
||||||
dragged_file_worktrees: Vec<Entity<Worktree>>,
|
dragged_file_worktrees: Vec<Entity<Worktree>>,
|
||||||
language_model_selector: PopoverMenuHandle<LanguageModelSelector>,
|
language_model_selector: Entity<LanguageModelSelector>,
|
||||||
|
language_model_selector_menu_handle: PopoverMenuHandle<LanguageModelSelector>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const DEFAULT_TAB_TITLE: &str = "New Chat";
|
pub const DEFAULT_TAB_TITLE: &str = "New Chat";
|
||||||
|
@ -263,7 +264,7 @@ impl ContextEditor {
|
||||||
image_blocks: Default::default(),
|
image_blocks: Default::default(),
|
||||||
scroll_position: None,
|
scroll_position: None,
|
||||||
remote_id: None,
|
remote_id: None,
|
||||||
fs,
|
fs: fs.clone(),
|
||||||
workspace,
|
workspace,
|
||||||
project,
|
project,
|
||||||
pending_slash_command_creases: HashMap::default(),
|
pending_slash_command_creases: HashMap::default(),
|
||||||
|
@ -275,7 +276,20 @@ impl ContextEditor {
|
||||||
show_accept_terms: false,
|
show_accept_terms: false,
|
||||||
slash_menu_handle: Default::default(),
|
slash_menu_handle: Default::default(),
|
||||||
dragged_file_worktrees: Vec::new(),
|
dragged_file_worktrees: Vec::new(),
|
||||||
language_model_selector: PopoverMenuHandle::default(),
|
language_model_selector: cx.new(|cx| {
|
||||||
|
LanguageModelSelector::new(
|
||||||
|
move |model, cx| {
|
||||||
|
update_settings_file::<AssistantSettings>(
|
||||||
|
fs.clone(),
|
||||||
|
cx,
|
||||||
|
move |settings, _| settings.set_model(model.clone()),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
window,
|
||||||
|
cx,
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
language_model_selector_menu_handle: PopoverMenuHandle::default(),
|
||||||
};
|
};
|
||||||
this.update_message_headers(cx);
|
this.update_message_headers(cx);
|
||||||
this.update_image_blocks(cx);
|
this.update_image_blocks(cx);
|
||||||
|
@ -2375,6 +2389,46 @@ impl ContextEditor {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn render_language_model_selector(&self, cx: &mut Context<Self>) -> impl IntoElement {
|
||||||
|
let active_model = LanguageModelRegistry::read_global(cx).active_model();
|
||||||
|
let focus_handle = self.editor().focus_handle(cx).clone();
|
||||||
|
let model_name = match active_model {
|
||||||
|
Some(model) => model.name().0,
|
||||||
|
None => SharedString::from("No model selected"),
|
||||||
|
};
|
||||||
|
|
||||||
|
LanguageModelSelectorPopoverMenu::new(
|
||||||
|
self.language_model_selector.clone(),
|
||||||
|
ButtonLike::new("active-model")
|
||||||
|
.style(ButtonStyle::Subtle)
|
||||||
|
.child(
|
||||||
|
h_flex()
|
||||||
|
.gap_0p5()
|
||||||
|
.child(
|
||||||
|
Label::new(model_name)
|
||||||
|
.size(LabelSize::Small)
|
||||||
|
.color(Color::Muted),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
Icon::new(IconName::ChevronDown)
|
||||||
|
.color(Color::Muted)
|
||||||
|
.size(IconSize::XSmall),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
move |window, cx| {
|
||||||
|
Tooltip::for_action_in(
|
||||||
|
"Change Model",
|
||||||
|
&ToggleModelSelector,
|
||||||
|
&focus_handle,
|
||||||
|
window,
|
||||||
|
cx,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
gpui::Corner::BottomLeft,
|
||||||
|
)
|
||||||
|
.with_handle(self.language_model_selector_menu_handle.clone())
|
||||||
|
}
|
||||||
|
|
||||||
fn render_last_error(&self, cx: &mut Context<Self>) -> Option<AnyElement> {
|
fn render_last_error(&self, cx: &mut Context<Self>) -> Option<AnyElement> {
|
||||||
let last_error = self.last_error.as_ref()?;
|
let last_error = self.last_error.as_ref()?;
|
||||||
|
|
||||||
|
@ -2819,7 +2873,7 @@ impl Render for ContextEditor {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
let language_model_selector = self.language_model_selector.clone();
|
let language_model_selector = self.language_model_selector_menu_handle.clone();
|
||||||
v_flex()
|
v_flex()
|
||||||
.key_context("ContextEditor")
|
.key_context("ContextEditor")
|
||||||
.capture_action(cx.listener(ContextEditor::cancel))
|
.capture_action(cx.listener(ContextEditor::cancel))
|
||||||
|
@ -2872,23 +2926,11 @@ impl Render for ContextEditor {
|
||||||
.gap_1()
|
.gap_1()
|
||||||
.child(self.render_inject_context_menu(cx))
|
.child(self.render_inject_context_menu(cx))
|
||||||
.child(ui::Divider::vertical())
|
.child(ui::Divider::vertical())
|
||||||
.child(div().pl_0p5().child(assistant_language_model_selector(
|
.child(
|
||||||
self.editor().focus_handle(cx),
|
div()
|
||||||
Some(self.language_model_selector.clone()),
|
.pl_0p5()
|
||||||
cx,
|
.child(self.render_language_model_selector(cx)),
|
||||||
{
|
),
|
||||||
let fs = self.fs.clone();
|
|
||||||
move |model, cx| {
|
|
||||||
update_settings_file::<AssistantSettings>(
|
|
||||||
fs.clone(),
|
|
||||||
cx,
|
|
||||||
move |settings, _| {
|
|
||||||
settings.set_model(model.clone())
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
))),
|
|
||||||
)
|
)
|
||||||
.child(
|
.child(
|
||||||
h_flex()
|
h_flex()
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
use std::{rc::Rc, sync::Arc};
|
use std::sync::Arc;
|
||||||
|
|
||||||
use feature_flags::ZedPro;
|
use feature_flags::ZedPro;
|
||||||
use gpui::{
|
use gpui::{
|
||||||
action_with_deprecated_aliases, Action, AnyElement, App, Corner, DismissEvent, Entity,
|
action_with_deprecated_aliases, Action, AnyElement, AnyView, App, Corner, DismissEvent, Entity,
|
||||||
EventEmitter, FocusHandle, Focusable, Subscription, Task, WeakEntity,
|
EventEmitter, FocusHandle, Focusable, Subscription, Task, WeakEntity,
|
||||||
};
|
};
|
||||||
use language_model::{
|
use language_model::{
|
||||||
|
@ -10,10 +10,7 @@ use language_model::{
|
||||||
};
|
};
|
||||||
use picker::{Picker, PickerDelegate};
|
use picker::{Picker, PickerDelegate};
|
||||||
use proto::Plan;
|
use proto::Plan;
|
||||||
use ui::{
|
use ui::{prelude::*, ListItem, ListItemSpacing, PopoverMenu, PopoverMenuHandle, PopoverTrigger};
|
||||||
prelude::*, ButtonLike, IconButtonShape, ListItem, ListItemSpacing, PopoverMenu,
|
|
||||||
PopoverMenuHandle, Tooltip,
|
|
||||||
};
|
|
||||||
use workspace::ShowConfiguration;
|
use workspace::ShowConfiguration;
|
||||||
|
|
||||||
action_with_deprecated_aliases!(
|
action_with_deprecated_aliases!(
|
||||||
|
@ -31,7 +28,6 @@ pub struct LanguageModelSelector {
|
||||||
/// The task used to update the picker's matches when there is a change to
|
/// The task used to update the picker's matches when there is a change to
|
||||||
/// the language model registry.
|
/// the language model registry.
|
||||||
update_matches_task: Option<Task<()>>,
|
update_matches_task: Option<Task<()>>,
|
||||||
popover_menu_handle: PopoverMenuHandle<LanguageModelSelector>,
|
|
||||||
_authenticate_all_providers_task: Task<()>,
|
_authenticate_all_providers_task: Task<()>,
|
||||||
_subscriptions: Vec<Subscription>,
|
_subscriptions: Vec<Subscription>,
|
||||||
}
|
}
|
||||||
|
@ -63,7 +59,6 @@ impl LanguageModelSelector {
|
||||||
LanguageModelSelector {
|
LanguageModelSelector {
|
||||||
picker,
|
picker,
|
||||||
update_matches_task: None,
|
update_matches_task: None,
|
||||||
popover_menu_handle: PopoverMenuHandle::default(),
|
|
||||||
_authenticate_all_providers_task: Self::authenticate_all_providers(cx),
|
_authenticate_all_providers_task: Self::authenticate_all_providers(cx),
|
||||||
_subscriptions: vec![cx.subscribe_in(
|
_subscriptions: vec![cx.subscribe_in(
|
||||||
&LanguageModelRegistry::global(cx),
|
&LanguageModelRegistry::global(cx),
|
||||||
|
@ -73,15 +68,6 @@ impl LanguageModelSelector {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn toggle_model_selector(
|
|
||||||
&mut self,
|
|
||||||
_: &ToggleModelSelector,
|
|
||||||
window: &mut Window,
|
|
||||||
cx: &mut Context<Self>,
|
|
||||||
) {
|
|
||||||
self.popover_menu_handle.toggle(window, cx);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn handle_language_model_registry_event(
|
fn handle_language_model_registry_event(
|
||||||
&mut self,
|
&mut self,
|
||||||
_registry: &Entity<LanguageModelRegistry>,
|
_registry: &Entity<LanguageModelRegistry>,
|
||||||
|
@ -201,6 +187,65 @@ impl Render for LanguageModelSelector {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(IntoElement)]
|
||||||
|
pub struct LanguageModelSelectorPopoverMenu<T, TT>
|
||||||
|
where
|
||||||
|
T: PopoverTrigger + ButtonCommon,
|
||||||
|
TT: Fn(&mut Window, &mut App) -> AnyView + 'static,
|
||||||
|
{
|
||||||
|
language_model_selector: Entity<LanguageModelSelector>,
|
||||||
|
trigger: T,
|
||||||
|
tooltip: TT,
|
||||||
|
handle: Option<PopoverMenuHandle<LanguageModelSelector>>,
|
||||||
|
anchor: Corner,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, TT> LanguageModelSelectorPopoverMenu<T, TT>
|
||||||
|
where
|
||||||
|
T: PopoverTrigger + ButtonCommon,
|
||||||
|
TT: Fn(&mut Window, &mut App) -> AnyView + 'static,
|
||||||
|
{
|
||||||
|
pub fn new(
|
||||||
|
language_model_selector: Entity<LanguageModelSelector>,
|
||||||
|
trigger: T,
|
||||||
|
tooltip: TT,
|
||||||
|
anchor: Corner,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
language_model_selector,
|
||||||
|
trigger,
|
||||||
|
tooltip,
|
||||||
|
handle: None,
|
||||||
|
anchor,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_handle(mut self, handle: PopoverMenuHandle<LanguageModelSelector>) -> Self {
|
||||||
|
self.handle = Some(handle);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, TT> RenderOnce for LanguageModelSelectorPopoverMenu<T, TT>
|
||||||
|
where
|
||||||
|
T: PopoverTrigger + ButtonCommon,
|
||||||
|
TT: Fn(&mut Window, &mut App) -> AnyView + 'static,
|
||||||
|
{
|
||||||
|
fn render(self, _window: &mut Window, _cx: &mut App) -> impl IntoElement {
|
||||||
|
let language_model_selector = self.language_model_selector.clone();
|
||||||
|
|
||||||
|
PopoverMenu::new("model-switcher")
|
||||||
|
.menu(move |_window, _cx| Some(language_model_selector.clone()))
|
||||||
|
.trigger_with_tooltip(self.trigger, self.tooltip)
|
||||||
|
.anchor(self.anchor)
|
||||||
|
.when_some(self.handle.clone(), |menu, handle| menu.with_handle(handle))
|
||||||
|
.offset(gpui::Point {
|
||||||
|
x: px(0.0),
|
||||||
|
y: px(-2.0),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct ModelInfo {
|
struct ModelInfo {
|
||||||
model: Arc<dyn LanguageModel>,
|
model: Arc<dyn LanguageModel>,
|
||||||
|
@ -482,114 +527,3 @@ impl PickerDelegate for LanguageModelPickerDelegate {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn inline_language_model_selector(
|
|
||||||
on_model_changed: impl Fn(Arc<dyn LanguageModel>, &App) + 'static,
|
|
||||||
) -> PopoverMenu<LanguageModelSelector> {
|
|
||||||
let on_model_changed = Rc::new(on_model_changed);
|
|
||||||
PopoverMenu::new("popover-button")
|
|
||||||
.menu(move |window, cx| {
|
|
||||||
Some(cx.new(|cx| {
|
|
||||||
LanguageModelSelector::new(
|
|
||||||
{
|
|
||||||
let on_model_changed = on_model_changed.clone();
|
|
||||||
move |model, cx| {
|
|
||||||
on_model_changed(model, cx);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
window,
|
|
||||||
cx,
|
|
||||||
)
|
|
||||||
}))
|
|
||||||
})
|
|
||||||
.trigger_with_tooltip(
|
|
||||||
IconButton::new("context", IconName::SettingsAlt)
|
|
||||||
.shape(IconButtonShape::Square)
|
|
||||||
.icon_size(IconSize::Small)
|
|
||||||
.icon_color(Color::Muted),
|
|
||||||
move |window, cx| {
|
|
||||||
Tooltip::with_meta(
|
|
||||||
format!(
|
|
||||||
"Using {}",
|
|
||||||
LanguageModelRegistry::read_global(cx)
|
|
||||||
.active_model()
|
|
||||||
.map(|model| model.name().0)
|
|
||||||
.unwrap_or_else(|| "No model selected".into()),
|
|
||||||
),
|
|
||||||
None,
|
|
||||||
"Change Model",
|
|
||||||
window,
|
|
||||||
cx,
|
|
||||||
)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.anchor(gpui::Corner::TopRight)
|
|
||||||
.offset(gpui::Point {
|
|
||||||
x: px(0.0),
|
|
||||||
y: px(-2.0),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn assistant_language_model_selector(
|
|
||||||
keybinding_target: FocusHandle,
|
|
||||||
menu_handle: Option<PopoverMenuHandle<LanguageModelSelector>>,
|
|
||||||
cx: &App,
|
|
||||||
on_model_changed: impl Fn(Arc<dyn LanguageModel>, &App) + 'static,
|
|
||||||
) -> PopoverMenu<LanguageModelSelector> {
|
|
||||||
let active_model = LanguageModelRegistry::read_global(cx).active_model();
|
|
||||||
let model_name = match active_model {
|
|
||||||
Some(model) => model.name().0,
|
|
||||||
_ => SharedString::from("No model selected"),
|
|
||||||
};
|
|
||||||
|
|
||||||
let on_model_changed = Rc::new(on_model_changed);
|
|
||||||
|
|
||||||
PopoverMenu::new("popover-button")
|
|
||||||
.menu(move |window, cx| {
|
|
||||||
Some(cx.new(|cx| {
|
|
||||||
LanguageModelSelector::new(
|
|
||||||
{
|
|
||||||
let on_model_changed = on_model_changed.clone();
|
|
||||||
move |model, cx| {
|
|
||||||
on_model_changed(model, cx);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
window,
|
|
||||||
cx,
|
|
||||||
)
|
|
||||||
}))
|
|
||||||
})
|
|
||||||
.trigger_with_tooltip(
|
|
||||||
ButtonLike::new("active-model")
|
|
||||||
.style(ButtonStyle::Subtle)
|
|
||||||
.child(
|
|
||||||
h_flex()
|
|
||||||
.gap_0p5()
|
|
||||||
.child(
|
|
||||||
Label::new(model_name)
|
|
||||||
.size(LabelSize::Small)
|
|
||||||
.color(Color::Muted),
|
|
||||||
)
|
|
||||||
.child(
|
|
||||||
Icon::new(IconName::ChevronDown)
|
|
||||||
.color(Color::Muted)
|
|
||||||
.size(IconSize::XSmall),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
move |window, cx| {
|
|
||||||
Tooltip::for_action_in(
|
|
||||||
"Change Model",
|
|
||||||
&ToggleModelSelector,
|
|
||||||
&keybinding_target,
|
|
||||||
window,
|
|
||||||
cx,
|
|
||||||
)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.anchor(Corner::BottomRight)
|
|
||||||
.when_some(menu_handle, |el, handle| el.with_handle(handle))
|
|
||||||
.offset(gpui::Point {
|
|
||||||
x: px(0.0),
|
|
||||||
y: px(-2.0),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue