assistant.rs

  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}