assistant.rs

  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}