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