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