assistant2: Summarize threads in context and continue long ones (#27851)
We'll now prompt the user to start a new thread when the active one gets too long. <img width=500 src="https://github.com/user-attachments/assets/91445bc0-3e81-422f-aa4a-b8f0741f9d9a"></img> When they click "Start New Thread", will create a new one with the previous one added as context. <img width=500 src="https://github.com/user-attachments/assets/c3b4223f-5bdd-4ba4-956f-5a5880d5e2c3"></img> Instead of including the full thread text, we'll now add summarized versions of threads to the context, allowing you to continue the conversation even if it was near the token limit. - Thread summaries are cached and persisted. - A cached summary is invalidated if the thread is continued. - We start generating the thread summary as soon as it's selected from the picker. Most times, the summary will be ready by the time the user sends the message. - If the summary isn't ready by the time a message is sent, the user message will be displayed in the thread immediately, and a "Summarizing context..." indicator will appear. After the summaries are ready, we'll start generating the response and show the usual "Generating..." indicator. Release Notes: - N/A --------- Co-authored-by: Danilo Leal <daniloleal09@gmail.com> Co-authored-by: Marshall Bowers <git@maxdeviant.com>
This commit is contained in:
parent
16f625bd07
commit
d26c477d86
12 changed files with 453 additions and 64 deletions
|
@ -56,9 +56,9 @@ pub fn init(cx: &mut App) {
|
|||
cx.observe_new(
|
||||
|workspace: &mut Workspace, _window, _cx: &mut Context<Workspace>| {
|
||||
workspace
|
||||
.register_action(|workspace, _: &NewThread, window, cx| {
|
||||
.register_action(|workspace, action: &NewThread, window, cx| {
|
||||
if let Some(panel) = workspace.panel::<AssistantPanel>(cx) {
|
||||
panel.update(cx, |panel, cx| panel.new_thread(window, cx));
|
||||
panel.update(cx, |panel, cx| panel.new_thread(action, window, cx));
|
||||
workspace.focus_panel::<AssistantPanel>(window, cx);
|
||||
}
|
||||
})
|
||||
|
@ -181,8 +181,12 @@ impl AssistantPanel {
|
|||
let workspace = workspace.weak_handle();
|
||||
let weak_self = cx.entity().downgrade();
|
||||
|
||||
let message_editor_context_store =
|
||||
cx.new(|_cx| crate::context_store::ContextStore::new(workspace.clone()));
|
||||
let message_editor_context_store = cx.new(|_cx| {
|
||||
crate::context_store::ContextStore::new(
|
||||
workspace.clone(),
|
||||
Some(thread_store.downgrade()),
|
||||
)
|
||||
});
|
||||
|
||||
let message_editor = cx.new(|cx| {
|
||||
MessageEditor::new(
|
||||
|
@ -268,15 +272,39 @@ impl AssistantPanel {
|
|||
.update(cx, |thread, cx| thread.cancel_last_completion(cx));
|
||||
}
|
||||
|
||||
fn new_thread(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
||||
fn new_thread(&mut self, action: &NewThread, window: &mut Window, cx: &mut Context<Self>) {
|
||||
let thread = self
|
||||
.thread_store
|
||||
.update(cx, |this, cx| this.create_thread(cx));
|
||||
|
||||
self.active_view = ActiveView::Thread;
|
||||
|
||||
let message_editor_context_store =
|
||||
cx.new(|_cx| crate::context_store::ContextStore::new(self.workspace.clone()));
|
||||
let message_editor_context_store = cx.new(|_cx| {
|
||||
crate::context_store::ContextStore::new(
|
||||
self.workspace.clone(),
|
||||
Some(self.thread_store.downgrade()),
|
||||
)
|
||||
});
|
||||
|
||||
if let Some(other_thread_id) = action.from_thread_id.clone() {
|
||||
let other_thread_task = self
|
||||
.thread_store
|
||||
.update(cx, |this, cx| this.open_thread(&other_thread_id, cx));
|
||||
|
||||
cx.spawn({
|
||||
let context_store = message_editor_context_store.clone();
|
||||
|
||||
async move |_panel, cx| {
|
||||
let other_thread = other_thread_task.await?;
|
||||
|
||||
context_store.update(cx, |this, cx| {
|
||||
this.add_thread(other_thread, false, cx);
|
||||
})?;
|
||||
anyhow::Ok(())
|
||||
}
|
||||
})
|
||||
.detach_and_log_err(cx);
|
||||
}
|
||||
|
||||
self.thread = cx.new(|cx| {
|
||||
ActiveThread::new(
|
||||
|
@ -414,8 +442,12 @@ impl AssistantPanel {
|
|||
let thread = open_thread_task.await?;
|
||||
this.update_in(cx, |this, window, cx| {
|
||||
this.active_view = ActiveView::Thread;
|
||||
let message_editor_context_store =
|
||||
cx.new(|_cx| crate::context_store::ContextStore::new(this.workspace.clone()));
|
||||
let message_editor_context_store = cx.new(|_cx| {
|
||||
crate::context_store::ContextStore::new(
|
||||
this.workspace.clone(),
|
||||
Some(this.thread_store.downgrade()),
|
||||
)
|
||||
});
|
||||
this.thread = cx.new(|cx| {
|
||||
ActiveThread::new(
|
||||
thread.clone(),
|
||||
|
@ -556,7 +588,7 @@ impl AssistantPanel {
|
|||
}
|
||||
}
|
||||
|
||||
self.new_thread(window, cx);
|
||||
self.new_thread(&NewThread::default(), window, cx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -688,11 +720,14 @@ impl Panel for AssistantPanel {
|
|||
impl AssistantPanel {
|
||||
fn render_toolbar(&self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
let thread = self.thread.read(cx);
|
||||
let is_empty = thread.is_empty();
|
||||
|
||||
let thread_id = thread.thread().read(cx).id().clone();
|
||||
let focus_handle = self.focus_handle(cx);
|
||||
|
||||
let title = match self.active_view {
|
||||
ActiveView::Thread => {
|
||||
if thread.is_empty() {
|
||||
if is_empty {
|
||||
thread.summary_or_default(cx)
|
||||
} else {
|
||||
thread
|
||||
|
@ -754,14 +789,17 @@ impl AssistantPanel {
|
|||
.tooltip(move |window, cx| {
|
||||
Tooltip::for_action_in(
|
||||
"New Thread",
|
||||
&NewThread,
|
||||
&NewThread::default(),
|
||||
&focus_handle,
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
})
|
||||
.on_click(move |_event, window, cx| {
|
||||
window.dispatch_action(NewThread.boxed_clone(), cx);
|
||||
window.dispatch_action(
|
||||
NewThread::default().boxed_clone(),
|
||||
cx,
|
||||
);
|
||||
}),
|
||||
)
|
||||
.child(
|
||||
|
@ -780,9 +818,23 @@ impl AssistantPanel {
|
|||
cx,
|
||||
|menu, _window, _cx| {
|
||||
menu.action(
|
||||
"New Thread",
|
||||
Box::new(NewThread {
|
||||
from_thread_id: None,
|
||||
}),
|
||||
)
|
||||
.action(
|
||||
"New Prompt Editor",
|
||||
NewPromptEditor.boxed_clone(),
|
||||
)
|
||||
.when(!is_empty, |menu| {
|
||||
menu.action(
|
||||
"Continue in New Thread",
|
||||
Box::new(NewThread {
|
||||
from_thread_id: Some(thread_id.clone()),
|
||||
}),
|
||||
)
|
||||
})
|
||||
.separator()
|
||||
.action("History", OpenHistory.boxed_clone())
|
||||
.action("Settings", OpenConfiguration.boxed_clone())
|
||||
|
@ -871,13 +923,13 @@ impl AssistantPanel {
|
|||
.icon_color(Color::Muted)
|
||||
.full_width()
|
||||
.key_binding(KeyBinding::for_action_in(
|
||||
&NewThread,
|
||||
&NewThread::default(),
|
||||
&focus_handle,
|
||||
window,
|
||||
cx,
|
||||
))
|
||||
.on_click(|_event, window, cx| {
|
||||
window.dispatch_action(NewThread.boxed_clone(), cx)
|
||||
window.dispatch_action(NewThread::default().boxed_clone(), cx)
|
||||
}),
|
||||
)
|
||||
.child(
|
||||
|
@ -1267,8 +1319,8 @@ impl Render for AssistantPanel {
|
|||
.justify_between()
|
||||
.size_full()
|
||||
.on_action(cx.listener(Self::cancel))
|
||||
.on_action(cx.listener(|this, _: &NewThread, window, cx| {
|
||||
this.new_thread(window, cx);
|
||||
.on_action(cx.listener(|this, action: &NewThread, window, cx| {
|
||||
this.new_thread(action, window, cx);
|
||||
}))
|
||||
.on_action(cx.listener(|this, _: &OpenHistory, window, cx| {
|
||||
this.open_history(window, cx);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue