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::{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}