assistant2: Render messages in the thread using a list
(#21491)
This PR updates the rendering of the messages in the current thread to use a `gpui::list`. Release Notes: - N/A
This commit is contained in:
parent
db34f29300
commit
aca23da971
3 changed files with 57 additions and 40 deletions
|
@ -4,9 +4,9 @@ use anyhow::Result;
|
||||||
use assistant_tool::ToolWorkingSet;
|
use assistant_tool::ToolWorkingSet;
|
||||||
use client::zed_urls;
|
use client::zed_urls;
|
||||||
use gpui::{
|
use gpui::{
|
||||||
prelude::*, px, Action, AnyElement, AppContext, AsyncWindowContext, EventEmitter, FocusHandle,
|
list, prelude::*, px, Action, AnyElement, AppContext, AsyncWindowContext, Empty, EventEmitter,
|
||||||
FocusableView, FontWeight, Model, Pixels, Subscription, Task, View, ViewContext, WeakView,
|
FocusHandle, FocusableView, FontWeight, ListAlignment, ListState, Model, Pixels, Subscription,
|
||||||
WindowContext,
|
Task, View, ViewContext, WeakView, WindowContext,
|
||||||
};
|
};
|
||||||
use language_model::{LanguageModelRegistry, Role};
|
use language_model::{LanguageModelRegistry, Role};
|
||||||
use language_model_selector::LanguageModelSelector;
|
use language_model_selector::LanguageModelSelector;
|
||||||
|
@ -15,7 +15,7 @@ use workspace::dock::{DockPosition, Panel, PanelEvent};
|
||||||
use workspace::Workspace;
|
use workspace::Workspace;
|
||||||
|
|
||||||
use crate::message_editor::MessageEditor;
|
use crate::message_editor::MessageEditor;
|
||||||
use crate::thread::{Message, Thread, ThreadError, ThreadEvent};
|
use crate::thread::{MessageId, Thread, ThreadError, ThreadEvent};
|
||||||
use crate::thread_store::ThreadStore;
|
use crate::thread_store::ThreadStore;
|
||||||
use crate::{NewThread, ToggleFocus, ToggleModelSelector};
|
use crate::{NewThread, ToggleFocus, ToggleModelSelector};
|
||||||
|
|
||||||
|
@ -35,6 +35,8 @@ pub struct AssistantPanel {
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
thread_store: Model<ThreadStore>,
|
thread_store: Model<ThreadStore>,
|
||||||
thread: Model<Thread>,
|
thread: Model<Thread>,
|
||||||
|
thread_messages: Vec<MessageId>,
|
||||||
|
thread_list_state: ListState,
|
||||||
message_editor: View<MessageEditor>,
|
message_editor: View<MessageEditor>,
|
||||||
tools: Arc<ToolWorkingSet>,
|
tools: Arc<ToolWorkingSet>,
|
||||||
last_error: Option<ThreadError>,
|
last_error: Option<ThreadError>,
|
||||||
|
@ -77,6 +79,14 @@ impl AssistantPanel {
|
||||||
workspace: workspace.weak_handle(),
|
workspace: workspace.weak_handle(),
|
||||||
thread_store,
|
thread_store,
|
||||||
thread: thread.clone(),
|
thread: thread.clone(),
|
||||||
|
thread_messages: Vec::new(),
|
||||||
|
thread_list_state: ListState::new(0, ListAlignment::Bottom, px(1024.), {
|
||||||
|
let this = cx.view().downgrade();
|
||||||
|
move |ix, cx: &mut WindowContext| {
|
||||||
|
this.update(cx, |this, cx| this.render_message(ix, cx))
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
}),
|
||||||
message_editor: cx.new_view(|cx| MessageEditor::new(thread, cx)),
|
message_editor: cx.new_view(|cx| MessageEditor::new(thread, cx)),
|
||||||
tools,
|
tools,
|
||||||
last_error: None,
|
last_error: None,
|
||||||
|
@ -110,6 +120,12 @@ impl AssistantPanel {
|
||||||
self.last_error = Some(error.clone());
|
self.last_error = Some(error.clone());
|
||||||
}
|
}
|
||||||
ThreadEvent::StreamedCompletion => {}
|
ThreadEvent::StreamedCompletion => {}
|
||||||
|
ThreadEvent::MessageAdded(message_id) => {
|
||||||
|
let old_len = self.thread_messages.len();
|
||||||
|
self.thread_messages.push(*message_id);
|
||||||
|
self.thread_list_state.splice(old_len..old_len, 1);
|
||||||
|
cx.notify();
|
||||||
|
}
|
||||||
ThreadEvent::UsePendingTools => {
|
ThreadEvent::UsePendingTools => {
|
||||||
let pending_tool_uses = self
|
let pending_tool_uses = self
|
||||||
.thread
|
.thread
|
||||||
|
@ -301,31 +317,42 @@ impl AssistantPanel {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_message(&self, message: Message, cx: &mut ViewContext<Self>) -> impl IntoElement {
|
fn render_message(&self, ix: usize, cx: &mut ViewContext<Self>) -> AnyElement {
|
||||||
|
let message_id = self.thread_messages[ix];
|
||||||
|
let Some(message) = self.thread.read(cx).message(message_id) else {
|
||||||
|
return Empty.into_any();
|
||||||
|
};
|
||||||
|
|
||||||
let (role_icon, role_name) = match message.role {
|
let (role_icon, role_name) = match message.role {
|
||||||
Role::User => (IconName::Person, "You"),
|
Role::User => (IconName::Person, "You"),
|
||||||
Role::Assistant => (IconName::ZedAssistant, "Assistant"),
|
Role::Assistant => (IconName::ZedAssistant, "Assistant"),
|
||||||
Role::System => (IconName::Settings, "System"),
|
Role::System => (IconName::Settings, "System"),
|
||||||
};
|
};
|
||||||
|
|
||||||
v_flex()
|
div()
|
||||||
.border_1()
|
.id(("message-container", ix))
|
||||||
.border_color(cx.theme().colors().border_variant)
|
.p_2()
|
||||||
.rounded_md()
|
|
||||||
.child(
|
.child(
|
||||||
h_flex()
|
v_flex()
|
||||||
.justify_between()
|
.border_1()
|
||||||
.p_1p5()
|
|
||||||
.border_b_1()
|
|
||||||
.border_color(cx.theme().colors().border_variant)
|
.border_color(cx.theme().colors().border_variant)
|
||||||
|
.rounded_md()
|
||||||
.child(
|
.child(
|
||||||
h_flex()
|
h_flex()
|
||||||
.gap_2()
|
.justify_between()
|
||||||
.child(Icon::new(role_icon).size(IconSize::Small))
|
.p_1p5()
|
||||||
.child(Label::new(role_name).size(LabelSize::Small)),
|
.border_b_1()
|
||||||
),
|
.border_color(cx.theme().colors().border_variant)
|
||||||
|
.child(
|
||||||
|
h_flex()
|
||||||
|
.gap_2()
|
||||||
|
.child(Icon::new(role_icon).size(IconSize::Small))
|
||||||
|
.child(Label::new(role_name).size(LabelSize::Small)),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.child(v_flex().p_1p5().child(Label::new(message.text.clone()))),
|
||||||
)
|
)
|
||||||
.child(v_flex().p_1p5().child(Label::new(message.text.clone())))
|
.into_any()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_last_error(&self, cx: &mut ViewContext<Self>) -> Option<AnyElement> {
|
fn render_last_error(&self, cx: &mut ViewContext<Self>) -> Option<AnyElement> {
|
||||||
|
@ -477,8 +504,6 @@ impl AssistantPanel {
|
||||||
|
|
||||||
impl Render for AssistantPanel {
|
impl Render for AssistantPanel {
|
||||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
|
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
|
||||||
let messages = self.thread.read(cx).messages().cloned().collect::<Vec<_>>();
|
|
||||||
|
|
||||||
v_flex()
|
v_flex()
|
||||||
.key_context("AssistantPanel2")
|
.key_context("AssistantPanel2")
|
||||||
.justify_between()
|
.justify_between()
|
||||||
|
@ -487,20 +512,7 @@ impl Render for AssistantPanel {
|
||||||
this.new_thread(cx);
|
this.new_thread(cx);
|
||||||
}))
|
}))
|
||||||
.child(self.render_toolbar(cx))
|
.child(self.render_toolbar(cx))
|
||||||
.child(
|
.child(list(self.thread_list_state.clone()).flex_1())
|
||||||
v_flex()
|
|
||||||
.id("message-list")
|
|
||||||
.gap_2()
|
|
||||||
.size_full()
|
|
||||||
.p_2()
|
|
||||||
.overflow_y_scroll()
|
|
||||||
.bg(cx.theme().colors().panel_background)
|
|
||||||
.children(
|
|
||||||
messages
|
|
||||||
.into_iter()
|
|
||||||
.map(|message| self.render_message(message, cx)),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.child(
|
.child(
|
||||||
h_flex()
|
h_flex()
|
||||||
.border_t_1()
|
.border_t_1()
|
||||||
|
|
|
@ -56,7 +56,7 @@ impl MessageEditor {
|
||||||
});
|
});
|
||||||
|
|
||||||
self.thread.update(cx, |thread, cx| {
|
self.thread.update(cx, |thread, cx| {
|
||||||
thread.insert_user_message(user_message);
|
thread.insert_user_message(user_message, cx);
|
||||||
let mut request = thread.to_completion_request(request_kind, cx);
|
let mut request = thread.to_completion_request(request_kind, cx);
|
||||||
|
|
||||||
if self.use_tools {
|
if self.use_tools {
|
||||||
|
|
|
@ -63,8 +63,8 @@ impl Thread {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn messages(&self) -> impl Iterator<Item = &Message> {
|
pub fn message(&self, id: MessageId) -> Option<&Message> {
|
||||||
self.messages.iter()
|
self.messages.iter().find(|message| message.id == id)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn tools(&self) -> &Arc<ToolWorkingSet> {
|
pub fn tools(&self) -> &Arc<ToolWorkingSet> {
|
||||||
|
@ -75,12 +75,14 @@ impl Thread {
|
||||||
self.pending_tool_uses_by_id.values().collect()
|
self.pending_tool_uses_by_id.values().collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn insert_user_message(&mut self, text: impl Into<String>) {
|
pub fn insert_user_message(&mut self, text: impl Into<String>, cx: &mut ModelContext<Self>) {
|
||||||
|
let id = self.next_message_id.post_inc();
|
||||||
self.messages.push(Message {
|
self.messages.push(Message {
|
||||||
id: self.next_message_id.post_inc(),
|
id,
|
||||||
role: Role::User,
|
role: Role::User,
|
||||||
text: text.into(),
|
text: text.into(),
|
||||||
});
|
});
|
||||||
|
cx.emit(ThreadEvent::MessageAdded(id));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_completion_request(
|
pub fn to_completion_request(
|
||||||
|
@ -150,11 +152,13 @@ impl Thread {
|
||||||
thread.update(&mut cx, |thread, cx| {
|
thread.update(&mut cx, |thread, cx| {
|
||||||
match event {
|
match event {
|
||||||
LanguageModelCompletionEvent::StartMessage { .. } => {
|
LanguageModelCompletionEvent::StartMessage { .. } => {
|
||||||
|
let id = thread.next_message_id.post_inc();
|
||||||
thread.messages.push(Message {
|
thread.messages.push(Message {
|
||||||
id: thread.next_message_id.post_inc(),
|
id,
|
||||||
role: Role::Assistant,
|
role: Role::Assistant,
|
||||||
text: String::new(),
|
text: String::new(),
|
||||||
});
|
});
|
||||||
|
cx.emit(ThreadEvent::MessageAdded(id));
|
||||||
}
|
}
|
||||||
LanguageModelCompletionEvent::Stop(reason) => {
|
LanguageModelCompletionEvent::Stop(reason) => {
|
||||||
stop_reason = reason;
|
stop_reason = reason;
|
||||||
|
@ -316,6 +320,7 @@ pub enum ThreadError {
|
||||||
pub enum ThreadEvent {
|
pub enum ThreadEvent {
|
||||||
ShowError(ThreadError),
|
ShowError(ThreadError),
|
||||||
StreamedCompletion,
|
StreamedCompletion,
|
||||||
|
MessageAdded(MessageId),
|
||||||
UsePendingTools,
|
UsePendingTools,
|
||||||
ToolFinished {
|
ToolFinished {
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue