From b8cc0ab9ed6900e559a74393b632d0894febb4f0 Mon Sep 17 00:00:00 2001 From: codestory Date: Thu, 30 Jan 2025 10:13:57 +0000 Subject: [PATCH 1/3] feat: sync local changes --- sidecar/src/user_context/types.rs | 42 +++++++++++++------------------ 1 file changed, 17 insertions(+), 25 deletions(-) diff --git a/sidecar/src/user_context/types.rs b/sidecar/src/user_context/types.rs index 550ec1f86..17c52f505 100644 --- a/sidecar/src/user_context/types.rs +++ b/sidecar/src/user_context/types.rs @@ -487,24 +487,24 @@ impl UserContext { } pub fn add_image(mut self, image: ImageInformation) -> Self { - self.images.push(image); + // Compress the image once when adding it + if let Ok(compressed_image) = ImageInformation::new( + image.r#type().to_owned(), + image.media_type().to_owned(), + image.data().to_owned(), + ) + .compress_base64_image() + { + self.images.push(compressed_image); + } else { + // If compression fails, store original + self.images.push(image); + } self } pub fn images(&self) -> Vec { - let mut processed_images = Vec::new(); - for image in self.images.iter() { - if let Ok(compressed_image) = ImageInformation::new( - image.r#type().to_owned(), - image.media_type().to_owned(), - image.data().to_owned(), - ) - .compress_base64_image() - { - processed_images.push(compressed_image); - } - } - processed_images + self.images.clone() // Simply return the already stored (and compressed) images } pub fn copy_at_instance(mut self) -> Self { @@ -790,16 +790,8 @@ impl UserContext { }) .collect::>(); - // Process and compress any new images before merging - let mut processed_new_images = Vec::new(); - for image in new_user_context.images { - let compressed_image = ImageInformation::new( - image.r#type().to_owned(), - image.media_type().to_owned(), - image.data().to_owned(), - ); - processed_new_images.push(compressed_image); - } + // No need to compress images here as they were compressed when added + let processed_new_images = new_user_context.images; new_user_context.images = processed_new_images; let images_to_extend = self @@ -946,4 +938,4 @@ pub async fn read_folder_selection( output.push_str("\n\n"); Ok(output) -} +} \ No newline at end of file From 83614c75af87401cf6d0ab935f98a9f26f9e899f Mon Sep 17 00:00:00 2001 From: codestory Date: Thu, 30 Jan 2025 11:20:52 +0000 Subject: [PATCH 2/3] feat: sync local changes --- sidecar/src/user_context/types.rs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/sidecar/src/user_context/types.rs b/sidecar/src/user_context/types.rs index 17c52f505..d31bcabf0 100644 --- a/sidecar/src/user_context/types.rs +++ b/sidecar/src/user_context/types.rs @@ -488,13 +488,7 @@ impl UserContext { pub fn add_image(mut self, image: ImageInformation) -> Self { // Compress the image once when adding it - if let Ok(compressed_image) = ImageInformation::new( - image.r#type().to_owned(), - image.media_type().to_owned(), - image.data().to_owned(), - ) - .compress_base64_image() - { + if let Ok(compressed_image) = image.compress_base64_image() { self.images.push(compressed_image); } else { // If compression fails, store original From 0468e39f032e3fe378796450eb07f9385d305791 Mon Sep 17 00:00:00 2001 From: codestory Date: Thu, 30 Jan 2025 11:40:16 +0000 Subject: [PATCH 3/3] feat: sync local changes --- sidecar/src/user_context/types.rs | 84 +++++++++++++++++++++++-------- 1 file changed, 62 insertions(+), 22 deletions(-) diff --git a/sidecar/src/user_context/types.rs b/sidecar/src/user_context/types.rs index d31bcabf0..5566c4ab5 100644 --- a/sidecar/src/user_context/types.rs +++ b/sidecar/src/user_context/types.rs @@ -78,7 +78,7 @@ pub struct ImageInformation { } impl ImageInformation { - fn compress_base64_image(self) -> Result { + fn compress_base64_image(&self) -> Result { #[cfg(feature = "image_compression")] { // Decode base64 @@ -116,13 +116,13 @@ impl ImageInformation { // Re-encode as base64 let result = BASE64.encode(compressed); - Ok(Self::new(self.r#type, self.media_type, result)) + Ok(Self::new(self.r#type.clone(), self.media_type.clone(), result)) } #[cfg(not(feature = "image_compression"))] { // When compression is disabled, return self unchanged - Ok(self) + Ok(self.clone()) } } @@ -439,24 +439,11 @@ pub struct UserContext { #[serde(default)] original_variables: Vec, #[serde(default)] + #[serde(deserialize_with = "compress_images_on_deserialize")] images: Vec, - // TODO(skcd): The file content map over here contains the full context through out the - // lifecycle of ownership - // so we can freely update it when we want - // if we want to perform any operations comparing this to another user context - // we can do so over here - // - we want to track the changed file content values over here relative to - // another filecontent value - // use https://github.com/aorwall/moatless-tree-search/blob/59340c9aaa08af28919f563a9a5e0b229da7b3cb/moatless/file_context.py#L194-L204 to - // generate the changed span ids which are present - // TLDR: we want to get the patch and then get the line numbers which have changed - // everytime we apply any change on a file over here pub file_content_map: Vec, pub terminal_selection: Option, - // These paths will be absolute and need to be used to get the - // context of the folders here, we will output it properly folder_paths: Vec, - // These are all hacks for now, we will move them to proper strucutre later on is_plan_generation: bool, is_plan_execution_until: Option, #[serde(default)] @@ -465,6 +452,18 @@ pub struct UserContext { is_plan_drop_from: Option, } +fn compress_images_on_deserialize<'de, D>(deserializer: D) -> Result, D::Error> +where + D: serde::Deserializer<'de>, +{ + let images = Vec::::deserialize(deserializer)?; + let compressed_images = images + .into_iter() + .map(|image| image.compress_base64_image().unwrap_or(image)) + .collect(); + Ok(compressed_images) +} + impl UserContext { pub fn new( variables: Vec, @@ -488,11 +487,9 @@ impl UserContext { pub fn add_image(mut self, image: ImageInformation) -> Self { // Compress the image once when adding it - if let Ok(compressed_image) = image.compress_base64_image() { - self.images.push(compressed_image); - } else { - // If compression fails, store original - self.images.push(image); + match image.compress_base64_image() { + Ok(compressed_image) => self.images.push(compressed_image), + Err(_) => self.images.push(image), } self } @@ -932,4 +929,47 @@ pub async fn read_folder_selection( output.push_str("\n\n"); Ok(output) +} + +#[cfg(test)] +mod tests { + use super::*; + use serde_json::json; + + #[test] + fn test_image_compression_on_deserialize() { + // Skip test if image compression feature is not enabled + #[cfg(feature = "image_compression")] + { + // Create a test image (base64 encoded 1x1 PNG) + let test_image = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg=="; + let json_data = json!({ + "variables": [], + "original_variables": [], + "images": [ + { + "type": "png", + "media_type": "image/png", + "data": test_image + } + ], + "file_content_map": [], + "terminal_selection": null, + "folder_paths": [], + "is_plan_generation": false, + "is_plan_execution_until": null, + "is_plan_append": false, + "is_plan_drop_from": null + }); + + // Deserialize JSON into UserContext + let user_context: UserContext = serde_json::from_value(json_data).unwrap(); + + // Verify image was compressed + assert!(!user_context.images.is_empty()); + let compressed_image = &user_context.images[0]; + assert!(compressed_image.data().len() <= test_image.len(), + "Image should be compressed or unchanged, but was larger"); + } + } } \ No newline at end of file