From dc0f7436e06a4a6e6d71644147759f164cf96a36 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 1 Aug 2024 15:07:14 +0200 Subject: [PATCH] Add support for backwards compatibility in `PromptStore` (#15602) Release Notes: - N/A --------- Co-authored-by: Nathan --- crates/assistant/src/prompt_library.rs | 119 ++++++++++++++++++++++--- 1 file changed, 106 insertions(+), 13 deletions(-) diff --git a/crates/assistant/src/prompt_library.rs b/crates/assistant/src/prompt_library.rs index 81091b3fd5..1e15576fe4 100644 --- a/crates/assistant/src/prompt_library.rs +++ b/crates/assistant/src/prompt_library.rs @@ -16,7 +16,10 @@ use gpui::{ EventEmitter, Global, HighlightStyle, PromptLevel, ReadGlobal, Subscription, Task, TextStyle, TitlebarOptions, UpdateGlobal, View, WindowBounds, WindowHandle, WindowOptions, }; -use heed::{types::SerdeBincode, Database, RoTxn}; +use heed::{ + types::{SerdeBincode, SerdeJson, Str}, + Database, RoTxn, +}; use language::{language_settings::SoftWrap, Buffer, LanguageRegistry}; use language_model::{ LanguageModelRegistry, LanguageModelRequest, LanguageModelRequestMessage, Role, @@ -1059,23 +1062,30 @@ pub struct PromptMetadata { pub title: Option, pub default: bool, pub saved_at: DateTime, + pub built_in: bool, } #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] -pub struct PromptId(Uuid); +#[serde(tag = "kind")] +pub enum PromptId { + User { uuid: Uuid }, + EditWorkflow, +} impl PromptId { pub fn new() -> PromptId { - PromptId(Uuid::new_v4()) + PromptId::User { + uuid: Uuid::new_v4(), + } } } pub struct PromptStore { executor: BackgroundExecutor, env: heed::Env, - bodies: Database, SerdeBincode>, - metadata: Database, SerdeBincode>, metadata_cache: RwLock, + metadata: Database, SerdeJson>, + bodies: Database, Str>, } #[derive(Default)] @@ -1086,7 +1096,7 @@ struct MetadataCache { impl MetadataCache { fn from_db( - db: Database, SerdeBincode>, + db: Database, SerdeJson>, txn: &RoTxn, ) -> Result { let mut cache = MetadataCache::default(); @@ -1138,35 +1148,116 @@ impl PromptStore { let db_env = unsafe { heed::EnvOpenOptions::new() .map_size(1024 * 1024 * 1024) // 1GB - .max_dbs(2) // bodies and metadata + .max_dbs(4) // Metadata and bodies (possibly v1 of both as well) .open(db_path)? }; let mut txn = db_env.write_txn()?; - let bodies = db_env.create_database(&mut txn, Some("bodies"))?; - let metadata = db_env.create_database(&mut txn, Some("metadata"))?; + let metadata = db_env.create_database(&mut txn, Some("metadata.v2"))?; + let bodies = db_env.create_database(&mut txn, Some("bodies.v2"))?; + txn.commit()?; + + Self::upgrade_dbs(&db_env, metadata, bodies).log_err(); + + let txn = db_env.read_txn()?; let metadata_cache = MetadataCache::from_db(metadata, &txn)?; txn.commit()?; Ok(PromptStore { executor, env: db_env, - bodies, - metadata, metadata_cache: RwLock::new(metadata_cache), + metadata, + bodies, }) } }) } + fn upgrade_dbs( + env: &heed::Env, + metadata_db: heed::Database, SerdeJson>, + bodies_db: heed::Database, Str>, + ) -> Result<()> { + #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Hash)] + pub struct PromptIdV1(Uuid); + + #[derive(Clone, Debug, Serialize, Deserialize)] + pub struct PromptMetadataV1 { + pub id: PromptIdV1, + pub title: Option, + pub default: bool, + pub saved_at: DateTime, + } + + let mut txn = env.write_txn()?; + let Some(bodies_v1_db) = env + .open_database::, SerdeBincode>( + &txn, + Some("bodies"), + )? + else { + return Ok(()); + }; + let mut bodies_v1 = bodies_v1_db + .iter(&txn)? + .collect::>>()?; + + let Some(metadata_v1_db) = env + .open_database::, SerdeBincode>( + &txn, + Some("metadata"), + )? + else { + return Ok(()); + }; + let metadata_v1 = metadata_v1_db + .iter(&txn)? + .collect::>>()?; + + for (prompt_id_v1, metadata_v1) in metadata_v1 { + let prompt_id_v2 = PromptId::User { + uuid: prompt_id_v1.0, + }; + let Some(body_v1) = bodies_v1.remove(&prompt_id_v1) else { + continue; + }; + + if metadata_db + .get(&txn, &prompt_id_v2)? + .map_or(true, |metadata_v2| { + metadata_v1.saved_at > metadata_v2.saved_at + }) + { + metadata_db.put( + &mut txn, + &prompt_id_v2, + &PromptMetadata { + id: prompt_id_v2, + title: metadata_v1.title.clone(), + default: metadata_v1.default, + saved_at: metadata_v1.saved_at, + built_in: false, + }, + )?; + bodies_db.put(&mut txn, &prompt_id_v2, &body_v1)?; + } + } + + txn.commit()?; + + Ok(()) + } + pub fn load(&self, id: PromptId) -> Task> { let env = self.env.clone(); let bodies = self.bodies; self.executor.spawn(async move { let txn = env.read_txn()?; - bodies + Ok(bodies .get(&txn, &id)? - .ok_or_else(|| anyhow!("prompt not found")) + .ok_or_else(|| anyhow!("prompt not found"))? + .into()) }) } @@ -1260,6 +1351,7 @@ impl PromptStore { title, default, saved_at: Utc::now(), + built_in: false, }; self.metadata_cache.write().insert(prompt_metadata.clone()); @@ -1290,6 +1382,7 @@ impl PromptStore { title, default, saved_at: Utc::now(), + built_in: false, }; self.metadata_cache.write().insert(prompt_metadata.clone());