1pub mod assistant_panel;
2mod assistant_settings;
3mod codegen;
4mod streaming_diff;
5
6use ai::completion::Role;
7use anyhow::Result;
8pub use assistant_panel::AssistantPanel;
9use assistant_settings::OpenAIModel;
10use chrono::{DateTime, Local};
11use collections::HashMap;
12use fs::Fs;
13use futures::StreamExt;
14use gpui::AppContext;
15use regex::Regex;
16use serde::{Deserialize, Serialize};
17use std::{cmp::Reverse, ffi::OsStr, path::PathBuf, sync::Arc};
18use util::paths::CONVERSATIONS_DIR;
19
20#[derive(
21 Copy, Clone, Debug, Default, Eq, PartialEq, PartialOrd, Ord, Hash, Serialize, Deserialize,
22)]
23struct MessageId(usize);
24
25#[derive(Clone, Debug, Serialize, Deserialize)]
26struct MessageMetadata {
27 role: Role,
28 sent_at: DateTime<Local>,
29 status: MessageStatus,
30}
31
32#[derive(Clone, Debug, Serialize, Deserialize)]
33enum MessageStatus {
34 Pending,
35 Done,
36 Error(Arc<str>),
37}
38
39#[derive(Serialize, Deserialize)]
40struct SavedMessage {
41 id: MessageId,
42 start: usize,
43}
44
45#[derive(Serialize, Deserialize)]
46struct SavedConversation {
47 id: Option<String>,
48 zed: String,
49 version: String,
50 text: String,
51 messages: Vec<SavedMessage>,
52 message_metadata: HashMap<MessageId, MessageMetadata>,
53 summary: String,
54 model: OpenAIModel,
55}
56
57impl SavedConversation {
58 const VERSION: &'static str = "0.1.0";
59}
60
61struct SavedConversationMetadata {
62 title: String,
63 path: PathBuf,
64 mtime: chrono::DateTime<chrono::Local>,
65}
66
67impl SavedConversationMetadata {
68 pub async fn list(fs: Arc<dyn Fs>) -> Result<Vec<Self>> {
69 fs.create_dir(&CONVERSATIONS_DIR).await?;
70
71 let mut paths = fs.read_dir(&CONVERSATIONS_DIR).await?;
72 let mut conversations = Vec::<SavedConversationMetadata>::new();
73 while let Some(path) = paths.next().await {
74 let path = path?;
75 if path.extension() != Some(OsStr::new("json")) {
76 continue;
77 }
78
79 let pattern = r" - \d+.zed.json$";
80 let re = Regex::new(pattern).unwrap();
81
82 let metadata = fs.metadata(&path).await?;
83 if let Some((file_name, metadata)) = path
84 .file_name()
85 .and_then(|name| name.to_str())
86 .zip(metadata)
87 {
88 let title = re.replace(file_name, "");
89 conversations.push(Self {
90 title: title.into_owned(),
91 path,
92 mtime: metadata.mtime.into(),
93 });
94 }
95 }
96 conversations.sort_unstable_by_key(|conversation| Reverse(conversation.mtime));
97
98 Ok(conversations)
99 }
100}
101
102pub fn init(cx: &mut AppContext) {
103 assistant_panel::init(cx);
104}
105
106#[cfg(test)]
107#[ctor::ctor]
108fn init_logger() {
109 if std::env::var("RUST_LOG").is_ok() {
110 env_logger::init();
111 }
112}