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