assistant2: Add configuration (#23481)
This PR wires up the ability to configure Assistant2. <img width="1309" alt="Screenshot 2025-01-22 at 1 52 56 PM" src="https://github.com/user-attachments/assets/3de47797-7959-47af-bd93-51f105e87c28" /> Release Notes: - N/A
This commit is contained in:
parent
f7703973d2
commit
514d9b4161
3 changed files with 229 additions and 6 deletions
|
@ -1,4 +1,5 @@
|
||||||
mod active_thread;
|
mod active_thread;
|
||||||
|
mod assistant_configuration;
|
||||||
mod assistant_model_selector;
|
mod assistant_model_selector;
|
||||||
mod assistant_panel;
|
mod assistant_panel;
|
||||||
mod buffer_codegen;
|
mod buffer_codegen;
|
||||||
|
@ -41,6 +42,7 @@ actions!(
|
||||||
RemoveAllContext,
|
RemoveAllContext,
|
||||||
OpenHistory,
|
OpenHistory,
|
||||||
OpenPromptEditorHistory,
|
OpenPromptEditorHistory,
|
||||||
|
OpenConfiguration,
|
||||||
RemoveSelectedThread,
|
RemoveSelectedThread,
|
||||||
Chat,
|
Chat,
|
||||||
ChatMode,
|
ChatMode,
|
||||||
|
|
159
crates/assistant2/src/assistant_configuration.rs
Normal file
159
crates/assistant2/src/assistant_configuration.rs
Normal file
|
@ -0,0 +1,159 @@
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use collections::HashMap;
|
||||||
|
use gpui::{AnyView, AppContext, EventEmitter, FocusHandle, FocusableView, Subscription};
|
||||||
|
use language_model::{LanguageModelProvider, LanguageModelProviderId, LanguageModelRegistry};
|
||||||
|
use ui::{prelude::*, ElevationIndex};
|
||||||
|
|
||||||
|
pub struct AssistantConfiguration {
|
||||||
|
focus_handle: FocusHandle,
|
||||||
|
configuration_views_by_provider: HashMap<LanguageModelProviderId, AnyView>,
|
||||||
|
_registry_subscription: Subscription,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AssistantConfiguration {
|
||||||
|
pub fn new(cx: &mut ViewContext<Self>) -> Self {
|
||||||
|
let focus_handle = cx.focus_handle();
|
||||||
|
|
||||||
|
let registry_subscription = cx.subscribe(
|
||||||
|
&LanguageModelRegistry::global(cx),
|
||||||
|
|this, _, event: &language_model::Event, cx| match event {
|
||||||
|
language_model::Event::AddedProvider(provider_id) => {
|
||||||
|
let provider = LanguageModelRegistry::read_global(cx).provider(provider_id);
|
||||||
|
if let Some(provider) = provider {
|
||||||
|
this.add_provider_configuration_view(&provider, cx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
language_model::Event::RemovedProvider(provider_id) => {
|
||||||
|
this.remove_provider_configuration_view(provider_id);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut this = Self {
|
||||||
|
focus_handle,
|
||||||
|
configuration_views_by_provider: HashMap::default(),
|
||||||
|
_registry_subscription: registry_subscription,
|
||||||
|
};
|
||||||
|
this.build_provider_configuration_views(cx);
|
||||||
|
this
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_provider_configuration_views(&mut self, cx: &mut ViewContext<Self>) {
|
||||||
|
let providers = LanguageModelRegistry::read_global(cx).providers();
|
||||||
|
for provider in providers {
|
||||||
|
self.add_provider_configuration_view(&provider, cx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn remove_provider_configuration_view(&mut self, provider_id: &LanguageModelProviderId) {
|
||||||
|
self.configuration_views_by_provider.remove(provider_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_provider_configuration_view(
|
||||||
|
&mut self,
|
||||||
|
provider: &Arc<dyn LanguageModelProvider>,
|
||||||
|
cx: &mut ViewContext<Self>,
|
||||||
|
) {
|
||||||
|
let configuration_view = provider.configuration_view(cx);
|
||||||
|
self.configuration_views_by_provider
|
||||||
|
.insert(provider.id(), configuration_view);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FocusableView for AssistantConfiguration {
|
||||||
|
fn focus_handle(&self, _: &AppContext) -> FocusHandle {
|
||||||
|
self.focus_handle.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum AssistantConfigurationEvent {
|
||||||
|
NewThread(Arc<dyn LanguageModelProvider>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EventEmitter<AssistantConfigurationEvent> for AssistantConfiguration {}
|
||||||
|
|
||||||
|
impl AssistantConfiguration {
|
||||||
|
fn render_provider_configuration(
|
||||||
|
&mut self,
|
||||||
|
provider: &Arc<dyn LanguageModelProvider>,
|
||||||
|
cx: &mut ViewContext<Self>,
|
||||||
|
) -> impl IntoElement {
|
||||||
|
let provider_id = provider.id().0.clone();
|
||||||
|
let provider_name = provider.name().0.clone();
|
||||||
|
let configuration_view = self
|
||||||
|
.configuration_views_by_provider
|
||||||
|
.get(&provider.id())
|
||||||
|
.cloned();
|
||||||
|
|
||||||
|
v_flex()
|
||||||
|
.gap_2()
|
||||||
|
.child(
|
||||||
|
h_flex()
|
||||||
|
.justify_between()
|
||||||
|
.child(Headline::new(provider_name.clone()).size(HeadlineSize::Small))
|
||||||
|
.when(provider.is_authenticated(cx), |parent| {
|
||||||
|
parent.child(
|
||||||
|
h_flex().justify_end().child(
|
||||||
|
Button::new(
|
||||||
|
SharedString::from(format!("new-thread-{provider_id}")),
|
||||||
|
"Open New Thread",
|
||||||
|
)
|
||||||
|
.icon_position(IconPosition::Start)
|
||||||
|
.icon(IconName::Plus)
|
||||||
|
.style(ButtonStyle::Filled)
|
||||||
|
.layer(ElevationIndex::ModalSurface)
|
||||||
|
.on_click(cx.listener({
|
||||||
|
let provider = provider.clone();
|
||||||
|
move |_this, _event, cx| {
|
||||||
|
cx.emit(AssistantConfigurationEvent::NewThread(
|
||||||
|
provider.clone(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
})),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.p(DynamicSpacing::Base08.rems(cx))
|
||||||
|
.bg(cx.theme().colors().surface_background)
|
||||||
|
.border_1()
|
||||||
|
.border_color(cx.theme().colors().border_variant)
|
||||||
|
.rounded_md()
|
||||||
|
.map(|parent| match configuration_view {
|
||||||
|
Some(configuration_view) => parent.child(configuration_view),
|
||||||
|
None => parent.child(div().child(Label::new(format!(
|
||||||
|
"No configuration view for {provider_name}",
|
||||||
|
)))),
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Render for AssistantConfiguration {
|
||||||
|
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
|
||||||
|
let providers = LanguageModelRegistry::read_global(cx).providers();
|
||||||
|
|
||||||
|
v_flex()
|
||||||
|
.id("assistant-configuration")
|
||||||
|
.track_focus(&self.focus_handle(cx))
|
||||||
|
.bg(cx.theme().colors().editor_background)
|
||||||
|
.size_full()
|
||||||
|
.overflow_y_scroll()
|
||||||
|
.child(
|
||||||
|
v_flex()
|
||||||
|
.p(DynamicSpacing::Base16.rems(cx))
|
||||||
|
.mt_1()
|
||||||
|
.gap_6()
|
||||||
|
.flex_1()
|
||||||
|
.children(
|
||||||
|
providers
|
||||||
|
.into_iter()
|
||||||
|
.map(|provider| self.render_provider_configuration(&provider, cx)),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -12,13 +12,14 @@ use client::zed_urls;
|
||||||
use fs::Fs;
|
use fs::Fs;
|
||||||
use gpui::{
|
use gpui::{
|
||||||
prelude::*, px, svg, Action, AnyElement, AppContext, AsyncWindowContext, Corner, EventEmitter,
|
prelude::*, px, svg, Action, AnyElement, AppContext, AsyncWindowContext, Corner, EventEmitter,
|
||||||
FocusHandle, FocusableView, FontWeight, Model, Pixels, Task, View, ViewContext, WeakView,
|
FocusHandle, FocusableView, FontWeight, Model, Pixels, Subscription, Task, View, ViewContext,
|
||||||
WindowContext,
|
WeakView, WindowContext,
|
||||||
};
|
};
|
||||||
use language::LanguageRegistry;
|
use language::LanguageRegistry;
|
||||||
|
use language_model::LanguageModelRegistry;
|
||||||
use project::Project;
|
use project::Project;
|
||||||
use prompt_library::PromptBuilder;
|
use prompt_library::PromptBuilder;
|
||||||
use settings::Settings;
|
use settings::{update_settings_file, Settings};
|
||||||
use time::UtcOffset;
|
use time::UtcOffset;
|
||||||
use ui::{prelude::*, ContextMenu, KeyBinding, PopoverMenu, PopoverMenuHandle, Tab, Tooltip};
|
use ui::{prelude::*, ContextMenu, KeyBinding, PopoverMenu, PopoverMenuHandle, Tab, Tooltip};
|
||||||
use util::ResultExt as _;
|
use util::ResultExt as _;
|
||||||
|
@ -27,11 +28,12 @@ use workspace::Workspace;
|
||||||
use zed_actions::assistant::ToggleFocus;
|
use zed_actions::assistant::ToggleFocus;
|
||||||
|
|
||||||
use crate::active_thread::ActiveThread;
|
use crate::active_thread::ActiveThread;
|
||||||
|
use crate::assistant_configuration::{AssistantConfiguration, AssistantConfigurationEvent};
|
||||||
use crate::message_editor::MessageEditor;
|
use crate::message_editor::MessageEditor;
|
||||||
use crate::thread::{Thread, ThreadError, ThreadId};
|
use crate::thread::{Thread, ThreadError, ThreadId};
|
||||||
use crate::thread_history::{PastThread, ThreadHistory};
|
use crate::thread_history::{PastThread, ThreadHistory};
|
||||||
use crate::thread_store::ThreadStore;
|
use crate::thread_store::ThreadStore;
|
||||||
use crate::{NewPromptEditor, NewThread, OpenHistory, OpenPromptEditorHistory};
|
use crate::{NewPromptEditor, NewThread, OpenConfiguration, OpenHistory, OpenPromptEditorHistory};
|
||||||
|
|
||||||
pub fn init(cx: &mut AppContext) {
|
pub fn init(cx: &mut AppContext) {
|
||||||
cx.observe_new_views(
|
cx.observe_new_views(
|
||||||
|
@ -60,6 +62,12 @@ pub fn init(cx: &mut AppContext) {
|
||||||
workspace.focus_panel::<AssistantPanel>(cx);
|
workspace.focus_panel::<AssistantPanel>(cx);
|
||||||
panel.update(cx, |panel, cx| panel.open_prompt_editor_history(cx));
|
panel.update(cx, |panel, cx| panel.open_prompt_editor_history(cx));
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
.register_action(|workspace, _: &OpenConfiguration, cx| {
|
||||||
|
if let Some(panel) = workspace.panel::<AssistantPanel>(cx) {
|
||||||
|
workspace.focus_panel::<AssistantPanel>(cx);
|
||||||
|
panel.update(cx, |panel, cx| panel.open_configuration(cx));
|
||||||
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
@ -71,6 +79,7 @@ enum ActiveView {
|
||||||
PromptEditor,
|
PromptEditor,
|
||||||
History,
|
History,
|
||||||
PromptEditorHistory,
|
PromptEditorHistory,
|
||||||
|
Configuration,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct AssistantPanel {
|
pub struct AssistantPanel {
|
||||||
|
@ -84,6 +93,8 @@ pub struct AssistantPanel {
|
||||||
context_store: Model<assistant_context_editor::ContextStore>,
|
context_store: Model<assistant_context_editor::ContextStore>,
|
||||||
context_editor: Option<View<ContextEditor>>,
|
context_editor: Option<View<ContextEditor>>,
|
||||||
context_history: Option<View<ContextHistory>>,
|
context_history: Option<View<ContextHistory>>,
|
||||||
|
configuration: Option<View<AssistantConfiguration>>,
|
||||||
|
configuration_subscription: Option<Subscription>,
|
||||||
tools: Arc<ToolWorkingSet>,
|
tools: Arc<ToolWorkingSet>,
|
||||||
local_timezone: UtcOffset,
|
local_timezone: UtcOffset,
|
||||||
active_view: ActiveView,
|
active_view: ActiveView,
|
||||||
|
@ -173,6 +184,8 @@ impl AssistantPanel {
|
||||||
context_store,
|
context_store,
|
||||||
context_editor: None,
|
context_editor: None,
|
||||||
context_history: None,
|
context_history: None,
|
||||||
|
configuration: None,
|
||||||
|
configuration_subscription: None,
|
||||||
tools,
|
tools,
|
||||||
local_timezone: UtcOffset::from_whole_seconds(
|
local_timezone: UtcOffset::from_whole_seconds(
|
||||||
chrono::Local::now().offset().local_minus_utc(),
|
chrono::Local::now().offset().local_minus_utc(),
|
||||||
|
@ -357,6 +370,46 @@ impl AssistantPanel {
|
||||||
self.message_editor.focus_handle(cx).focus(cx);
|
self.message_editor.focus_handle(cx).focus(cx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn open_configuration(&mut self, cx: &mut ViewContext<Self>) {
|
||||||
|
self.active_view = ActiveView::Configuration;
|
||||||
|
self.configuration = Some(cx.new_view(AssistantConfiguration::new));
|
||||||
|
|
||||||
|
if let Some(configuration) = self.configuration.as_ref() {
|
||||||
|
self.configuration_subscription =
|
||||||
|
Some(cx.subscribe(configuration, Self::handle_assistant_configuration_event));
|
||||||
|
|
||||||
|
configuration.focus_handle(cx).focus(cx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_assistant_configuration_event(
|
||||||
|
&mut self,
|
||||||
|
_view: View<AssistantConfiguration>,
|
||||||
|
event: &AssistantConfigurationEvent,
|
||||||
|
cx: &mut ViewContext<Self>,
|
||||||
|
) {
|
||||||
|
match event {
|
||||||
|
AssistantConfigurationEvent::NewThread(provider) => {
|
||||||
|
if LanguageModelRegistry::read_global(cx)
|
||||||
|
.active_provider()
|
||||||
|
.map_or(true, |active_provider| {
|
||||||
|
active_provider.id() != provider.id()
|
||||||
|
})
|
||||||
|
{
|
||||||
|
if let Some(model) = provider.provided_models(cx).first().cloned() {
|
||||||
|
update_settings_file::<AssistantSettings>(
|
||||||
|
self.fs.clone(),
|
||||||
|
cx,
|
||||||
|
move |settings, _| settings.set_model(model),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.new_thread(cx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn active_thread(&self, cx: &AppContext) -> Model<Thread> {
|
pub(crate) fn active_thread(&self, cx: &AppContext) -> Model<Thread> {
|
||||||
self.thread.read(cx).thread.clone()
|
self.thread.read(cx).thread.clone()
|
||||||
}
|
}
|
||||||
|
@ -386,6 +439,13 @@ impl FocusableView for AssistantPanel {
|
||||||
cx.focus_handle()
|
cx.focus_handle()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ActiveView::Configuration => {
|
||||||
|
if let Some(configuration) = self.configuration.as_ref() {
|
||||||
|
configuration.focus_handle(cx)
|
||||||
|
} else {
|
||||||
|
cx.focus_handle()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -493,6 +553,7 @@ impl AssistantPanel {
|
||||||
.unwrap_or_else(|| SharedString::from("Loading Summary…")),
|
.unwrap_or_else(|| SharedString::from("Loading Summary…")),
|
||||||
ActiveView::History => "History / Thread".into(),
|
ActiveView::History => "History / Thread".into(),
|
||||||
ActiveView::PromptEditorHistory => "History / Prompt Editor".into(),
|
ActiveView::PromptEditorHistory => "History / Prompt Editor".into(),
|
||||||
|
ActiveView::Configuration => "Configuration".into(),
|
||||||
};
|
};
|
||||||
|
|
||||||
h_flex()
|
h_flex()
|
||||||
|
@ -555,8 +616,8 @@ impl AssistantPanel {
|
||||||
.icon_size(IconSize::Small)
|
.icon_size(IconSize::Small)
|
||||||
.style(ButtonStyle::Subtle)
|
.style(ButtonStyle::Subtle)
|
||||||
.tooltip(move |cx| Tooltip::text("Configure Assistant", cx))
|
.tooltip(move |cx| Tooltip::text("Configure Assistant", cx))
|
||||||
.on_click(move |_event, _cx| {
|
.on_click(move |_event, cx| {
|
||||||
println!("Configure Assistant");
|
cx.dispatch_action(OpenConfiguration.boxed_clone());
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
@ -810,6 +871,7 @@ impl Render for AssistantPanel {
|
||||||
ActiveView::History => parent.child(self.history.clone()),
|
ActiveView::History => parent.child(self.history.clone()),
|
||||||
ActiveView::PromptEditor => parent.children(self.context_editor.clone()),
|
ActiveView::PromptEditor => parent.children(self.context_editor.clone()),
|
||||||
ActiveView::PromptEditorHistory => parent.children(self.context_history.clone()),
|
ActiveView::PromptEditorHistory => parent.children(self.context_history.clone()),
|
||||||
|
ActiveView::Configuration => parent.children(self.configuration.clone()),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue