mirror of
https://github.com/zed-industries/zed.git
synced 2025-02-05 10:20:51 +00:00
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:
parent
8b5a0cff10
commit
c73ef1a5f3
4 changed files with 84 additions and 29 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -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",
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
Loading…
Reference in a new issue