assistant2: List saved conversations from disk (#11627)

This PR updates the saved conversation picker to use a list of
conversations retrieved from disk instead of the static placeholder
values.

Release Notes:

- N/A
This commit is contained in:
Marshall Bowers 2024-05-09 16:17:07 -04:00 committed by GitHub
parent 8b5a0cff10
commit c73ef1a5f3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 84 additions and 29 deletions

2
Cargo.lock generated
View file

@ -377,6 +377,7 @@ dependencies = [
"anyhow", "anyhow",
"assets", "assets",
"assistant_tooling", "assistant_tooling",
"chrono",
"client", "client",
"collections", "collections",
"editor", "editor",
@ -395,6 +396,7 @@ dependencies = [
"picker", "picker",
"project", "project",
"rand 0.8.5", "rand 0.8.5",
"regex",
"release_channel", "release_channel",
"rich_text", "rich_text",
"schemars", "schemars",

View file

@ -19,6 +19,7 @@ stories = ["dep:story"]
anyhow.workspace = true anyhow.workspace = true
assistant_tooling.workspace = true assistant_tooling.workspace = true
client.workspace = true client.workspace = true
chrono.workspace = true
collections.workspace = true collections.workspace = true
editor.workspace = true editor.workspace = true
feature_flags.workspace = true feature_flags.workspace = true
@ -32,6 +33,7 @@ nanoid.workspace = true
open_ai.workspace = true open_ai.workspace = true
picker.workspace = true picker.workspace = true
project.workspace = true project.workspace = true
regex.workspace = true
rich_text.workspace = true rich_text.workspace = true
schemars.workspace = true schemars.workspace = true
semantic_index.workspace = true semantic_index.workspace = true

View file

@ -1,6 +1,16 @@
use std::cmp::Reverse;
use std::ffi::OsStr;
use std::path::PathBuf;
use std::sync::Arc;
use anyhow::Result;
use assistant_tooling::{SavedToolFunctionCall, SavedUserAttachment}; use assistant_tooling::{SavedToolFunctionCall, SavedUserAttachment};
use fs::Fs;
use futures::StreamExt;
use gpui::SharedString; use gpui::SharedString;
use regex::Regex;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use util::paths::CONVERSATIONS_DIR;
use crate::MessageId; use crate::MessageId;
@ -33,25 +43,48 @@ pub struct SavedAssistantMessagePart {
pub tool_calls: Vec<SavedToolFunctionCall>, pub tool_calls: Vec<SavedToolFunctionCall>,
} }
/// Returns a list of placeholder conversations for mocking the UI. pub struct SavedConversationMetadata {
/// pub title: String,
/// Once we have real saved conversations to pull from we can use those instead. pub path: PathBuf,
pub fn placeholder_conversations() -> Vec<SavedConversation> { pub mtime: chrono::DateTime<chrono::Local>,
vec![ }
SavedConversation {
version: "0.3.0".to_string(), impl SavedConversationMetadata {
title: "How to get a list of exported functions in an Erlang module".to_string(), pub async fn list(fs: Arc<dyn Fs>) -> Result<Vec<Self>> {
messages: vec![], fs.create_dir(&CONVERSATIONS_DIR).await?;
},
SavedConversation { let mut paths = fs.read_dir(&CONVERSATIONS_DIR).await?;
version: "0.3.0".to_string(), let mut conversations = Vec::new();
title: "7 wonders of the ancient world".to_string(), while let Some(path) = paths.next().await {
messages: vec![], let path = path?;
}, if path.extension() != Some(OsStr::new("json")) {
SavedConversation { continue;
version: "0.3.0".to_string(), }
title: "Size difference between u8 and a reference to u8 in Rust".to_string(),
messages: vec![], let pattern = r" - \d+.zed.\d.\d.\d.json$";
}, let re = Regex::new(pattern).unwrap();
]
let metadata = fs.metadata(&path).await?;
if let Some((file_name, metadata)) = path
.file_name()
.and_then(|name| name.to_str())
.zip(metadata)
{
// This is used to filter out conversations saved by the old assistant.
if !re.is_match(file_name) {
continue;
}
let title = re.replace(file_name, "");
conversations.push(Self {
title: title.into_owned(),
path,
mtime: metadata.mtime.into(),
});
}
}
conversations.sort_unstable_by_key(|conversation| Reverse(conversation.mtime));
Ok(conversations)
}
} }

View file

@ -7,7 +7,7 @@ use ui::{prelude::*, HighlightedLabel, ListItem, ListItemSpacing};
use util::ResultExt; use util::ResultExt;
use workspace::{ModalView, Workspace}; use workspace::{ModalView, Workspace};
use crate::saved_conversation::{self, SavedConversation}; use crate::saved_conversation::SavedConversationMetadata;
use crate::ToggleSavedConversations; use crate::ToggleSavedConversations;
pub struct SavedConversationPicker { pub struct SavedConversationPicker {
@ -27,10 +27,26 @@ impl FocusableView for SavedConversationPicker {
impl SavedConversationPicker { impl SavedConversationPicker {
pub fn register(workspace: &mut Workspace, _cx: &mut ViewContext<Workspace>) { pub fn register(workspace: &mut Workspace, _cx: &mut ViewContext<Workspace>) {
workspace.register_action(|workspace, _: &ToggleSavedConversations, cx| { workspace.register_action(|workspace, _: &ToggleSavedConversations, cx| {
workspace.toggle_modal(cx, move |cx| { let fs = workspace.project().read(cx).fs().clone();
let delegate = SavedConversationPickerDelegate::new(cx.view().downgrade());
Self::new(delegate, cx) cx.spawn(|workspace, mut cx| async move {
}); let saved_conversations = SavedConversationMetadata::list(fs).await?;
cx.update(|cx| {
workspace.update(cx, |workspace, cx| {
workspace.toggle_modal(cx, move |cx| {
let delegate = SavedConversationPickerDelegate::new(
cx.view().downgrade(),
saved_conversations,
);
Self::new(delegate, cx)
});
})
})??;
anyhow::Ok(())
})
.detach_and_log_err(cx);
}); });
} }
@ -48,14 +64,16 @@ impl Render for SavedConversationPicker {
pub struct SavedConversationPickerDelegate { pub struct SavedConversationPickerDelegate {
view: WeakView<SavedConversationPicker>, view: WeakView<SavedConversationPicker>,
saved_conversations: Vec<SavedConversation>, saved_conversations: Vec<SavedConversationMetadata>,
selected_index: usize, selected_index: usize,
matches: Vec<StringMatch>, matches: Vec<StringMatch>,
} }
impl SavedConversationPickerDelegate { impl SavedConversationPickerDelegate {
pub fn new(weak_view: WeakView<SavedConversationPicker>) -> Self { pub fn new(
let saved_conversations = saved_conversation::placeholder_conversations(); weak_view: WeakView<SavedConversationPicker>,
saved_conversations: Vec<SavedConversationMetadata>,
) -> Self {
let matches = saved_conversations let matches = saved_conversations
.iter() .iter()
.map(|conversation| StringMatch { .map(|conversation| StringMatch {