diff --git a/BackendSchema.cs b/BackendSchema.cs
index 98f7e39..7648fb4 100644
--- a/BackendSchema.cs
+++ b/BackendSchema.cs
@@ -57,13 +57,13 @@ public static object GetSchemaType(string type, MessageContent content, string m
/// Compresses image data to optimize for LLM vision models
/// The media content containing image data
- /// The target format ("PNG" or "WEBP")
- /// Compressed base64 image data without the data URL prefix
- public static string CompressImageForVision(MediaContent media, string targetFormat = "WEBP")
+ /// The target format ("PNG", "JPG", or "WEBP")
+ /// Compressed base64 image data (without a data URL prefix) together with its resulting MIME type
+ public static (string Data, string MimeType) CompressImageForVision(MediaContent media, string targetFormat = "WEBP")
{
if (media.Type != "base64")
{
- return media.Data;
+ return (media.Data, media.MediaType);
}
try
{
@@ -71,10 +71,14 @@ public static string CompressImageForVision(MediaContent media, string targetFor
// Skip compression for videos etc..
if (image.Type.MetaType != MediaMetaType.Image)
{
- return media.Data;
+ return (media.Data, media.MediaType);
}
ISImage img = image.ToIS;
- int maxDimension = 256; // TODO: This needs to be tested and adjusted
+ // Fix (Claude, 2026-07-27): 256px was too aggressive for modern higher-resolution vision
+ // encoders (confirmed via direct testing: the exact same image reliably misidentified at
+ // 256px/quality-40 was reliably correct at full resolution against the same model). 1024px
+ // preserves much more real detail while still keeping payload size reasonable.
+ int maxDimension = 1024;
if (img.Width > maxDimension || img.Height > maxDimension)
{
float scaleFactor = maxDimension / (float)Math.Max(img.Width, img.Height);
@@ -82,17 +86,28 @@ public static string CompressImageForVision(MediaContent media, string targetFor
int newHeight = (int)(img.Height * scaleFactor);
img.Mutate(i => i.Resize(newWidth, newHeight));
}
- // Set compression quality based on format TODO: This needs to be tested and adjusted
- int quality = targetFormat == "PNG" ? 60 : 40;
+ // Fix (Claude, 2026-07-27): quality 40/60 was heavily lossy on top of the aggressive
+ // downscale above; 90 preserves detail much better at a modest size cost.
+ int quality = 90;
ImageFile tempImage = new Image(ImageFile.ISImgToPngBytes(img), image.Type);
ImageFile compressedImage = tempImage.ConvertTo(targetFormat, quality: quality);
- // Return just the base64 data (without the data:image/webp;base64, prefix)
- return compressedImage.AsBase64;
+ // Fix (CodeRabbit review, 2026-07-27): report the actual resulting MIME type instead of
+ // letting callers assume one from targetFormat - the fallback paths above return the
+ // original untouched bytes on non-image media or a conversion failure, so callers need
+ // to know that happened in order to label the data URL correctly.
+ string resultMimeType = targetFormat switch
+ {
+ "PNG" => "image/png",
+ "JPG" => "image/jpeg",
+ "WEBP" => "image/webp",
+ _ => media.MediaType
+ };
+ return (compressedImage.AsBase64, resultMimeType);
}
catch (Exception ex)
{
Logs.Error($"Failed to compress image: {ex.Message}");
- return media.Data;
+ return (media.Data, media.MediaType);
}
}
@@ -115,7 +130,7 @@ private static object OllamaRequestBody(MessageContent content, string model, Me
{
role = "user",
content = content.Text,
- images = content.Media.Select(m => CompressImageForVision(m, "JPG")).ToArray()
+ images = content.Media.Select(m => CompressImageForVision(m, "JPG").Data).ToArray()
});
return new
@@ -152,12 +167,18 @@ private static object OpenAICompatibleRequestBody(MessageContent content, string
List