-
Notifications
You must be signed in to change notification settings - Fork 2.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
assistant2: Add general structure for conversation history (#11516)
This PR adds the general structure for conversation history to the new assistant. Right now we have a placeholder button in the assistant panel that will toggle a picker containing some placeholder saved conversations. Release Notes: - N/A
- Loading branch information
1 parent
47ca343
commit 6a64b73
Showing
5 changed files
with
239 additions
and
1 deletion.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
pub struct SavedConversation { | ||
/// The title of the conversation, generated by the Assistant. | ||
pub title: String, | ||
pub messages: Vec<SavedMessage>, | ||
} | ||
|
||
pub struct SavedMessage { | ||
pub text: String, | ||
} | ||
|
||
/// Returns a list of placeholder conversations for mocking the UI. | ||
/// | ||
/// Once we have real saved conversations to pull from we can use those instead. | ||
pub fn placeholder_conversations() -> Vec<SavedConversation> { | ||
vec![ | ||
SavedConversation { | ||
title: "How to get a list of exported functions in an Erlang module".to_string(), | ||
messages: vec![], | ||
}, | ||
SavedConversation { | ||
title: "7 wonders of the ancient world".to_string(), | ||
messages: vec![], | ||
}, | ||
SavedConversation { | ||
title: "Size difference between u8 and a reference to u8 in Rust".to_string(), | ||
messages: vec![], | ||
}, | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,188 @@ | ||
use std::sync::Arc; | ||
|
||
use fuzzy::{match_strings, StringMatch, StringMatchCandidate}; | ||
use gpui::{AppContext, DismissEvent, EventEmitter, FocusHandle, FocusableView, View, WeakView}; | ||
use picker::{Picker, PickerDelegate}; | ||
use ui::{prelude::*, HighlightedLabel, ListItem, ListItemSpacing}; | ||
use util::ResultExt; | ||
use workspace::{ModalView, Workspace}; | ||
|
||
use crate::saved_conversation::{self, SavedConversation}; | ||
use crate::ToggleSavedConversations; | ||
|
||
pub struct SavedConversationPicker { | ||
picker: View<Picker<SavedConversationPickerDelegate>>, | ||
} | ||
|
||
impl EventEmitter<DismissEvent> for SavedConversationPicker {} | ||
|
||
impl ModalView for SavedConversationPicker {} | ||
|
||
impl FocusableView for SavedConversationPicker { | ||
fn focus_handle(&self, cx: &AppContext) -> FocusHandle { | ||
self.picker.focus_handle(cx) | ||
} | ||
} | ||
|
||
impl SavedConversationPicker { | ||
pub fn register(workspace: &mut Workspace, _cx: &mut ViewContext<Workspace>) { | ||
workspace.register_action(|workspace, _: &ToggleSavedConversations, cx| { | ||
workspace.toggle_modal(cx, move |cx| { | ||
let delegate = SavedConversationPickerDelegate::new(cx.view().downgrade()); | ||
Self::new(delegate, cx) | ||
}); | ||
}); | ||
} | ||
|
||
pub fn new(delegate: SavedConversationPickerDelegate, cx: &mut ViewContext<Self>) -> Self { | ||
let picker = cx.new_view(|cx| Picker::uniform_list(delegate, cx)); | ||
Self { picker } | ||
} | ||
} | ||
|
||
impl Render for SavedConversationPicker { | ||
fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement { | ||
v_flex().w(rems(34.)).child(self.picker.clone()) | ||
} | ||
} | ||
|
||
pub struct SavedConversationPickerDelegate { | ||
view: WeakView<SavedConversationPicker>, | ||
saved_conversations: Vec<SavedConversation>, | ||
selected_index: usize, | ||
matches: Vec<StringMatch>, | ||
} | ||
|
||
impl SavedConversationPickerDelegate { | ||
pub fn new(weak_view: WeakView<SavedConversationPicker>) -> Self { | ||
let saved_conversations = saved_conversation::placeholder_conversations(); | ||
let matches = saved_conversations | ||
.iter() | ||
.map(|conversation| StringMatch { | ||
candidate_id: 0, | ||
score: 0.0, | ||
positions: Default::default(), | ||
string: conversation.title.clone(), | ||
}) | ||
.collect(); | ||
|
||
Self { | ||
view: weak_view, | ||
saved_conversations, | ||
selected_index: 0, | ||
matches, | ||
} | ||
} | ||
} | ||
|
||
impl PickerDelegate for SavedConversationPickerDelegate { | ||
type ListItem = ui::ListItem; | ||
|
||
fn placeholder_text(&self, _cx: &mut WindowContext) -> Arc<str> { | ||
"Select saved conversation...".into() | ||
} | ||
|
||
fn match_count(&self) -> usize { | ||
self.matches.len() | ||
} | ||
|
||
fn selected_index(&self) -> usize { | ||
self.selected_index | ||
} | ||
|
||
fn set_selected_index(&mut self, ix: usize, _cx: &mut ViewContext<Picker<Self>>) { | ||
self.selected_index = ix; | ||
} | ||
|
||
fn update_matches( | ||
&mut self, | ||
query: String, | ||
cx: &mut ViewContext<Picker<Self>>, | ||
) -> gpui::Task<()> { | ||
let background_executor = cx.background_executor().clone(); | ||
let candidates = self | ||
.saved_conversations | ||
.iter() | ||
.enumerate() | ||
.map(|(id, conversation)| { | ||
let text = conversation.title.clone(); | ||
|
||
StringMatchCandidate { | ||
id, | ||
char_bag: text.as_str().into(), | ||
string: text, | ||
} | ||
}) | ||
.collect::<Vec<_>>(); | ||
|
||
cx.spawn(move |this, mut cx| async move { | ||
let matches = if query.is_empty() { | ||
candidates | ||
.into_iter() | ||
.enumerate() | ||
.map(|(index, candidate)| StringMatch { | ||
candidate_id: index, | ||
string: candidate.string, | ||
positions: Vec::new(), | ||
score: 0.0, | ||
}) | ||
.collect() | ||
} else { | ||
match_strings( | ||
&candidates, | ||
&query, | ||
false, | ||
100, | ||
&Default::default(), | ||
background_executor, | ||
) | ||
.await | ||
}; | ||
|
||
this.update(&mut cx, |this, _cx| { | ||
this.delegate.matches = matches; | ||
this.delegate.selected_index = this | ||
.delegate | ||
.selected_index | ||
.min(this.delegate.matches.len().saturating_sub(1)); | ||
}) | ||
.log_err(); | ||
}) | ||
} | ||
|
||
fn confirm(&mut self, _secondary: bool, cx: &mut ViewContext<Picker<Self>>) { | ||
if self.matches.is_empty() { | ||
self.dismissed(cx); | ||
return; | ||
} | ||
|
||
// TODO: Implement selecting a saved conversation. | ||
} | ||
|
||
fn dismissed(&mut self, cx: &mut ui::prelude::ViewContext<Picker<Self>>) { | ||
self.view | ||
.update(cx, |_, cx| cx.emit(DismissEvent)) | ||
.log_err(); | ||
} | ||
|
||
fn render_match( | ||
&self, | ||
ix: usize, | ||
selected: bool, | ||
_cx: &mut ViewContext<Picker<Self>>, | ||
) -> Option<Self::ListItem> { | ||
let conversation_match = &self.matches[ix]; | ||
let _conversation = &self.saved_conversations[conversation_match.candidate_id]; | ||
|
||
Some( | ||
ListItem::new(ix) | ||
.inset(true) | ||
.spacing(ListItemSpacing::Sparse) | ||
.selected(selected) | ||
.child(HighlightedLabel::new( | ||
conversation_match.string.clone(), | ||
conversation_match.positions.clone(), | ||
)), | ||
) | ||
} | ||
} |