fix: address PR review findings for image pipeline
- Add defense-in-depth count and size validation in InsightsExecutor (NEW-005) - Make image analysis unsupported warning more prominent with icon and background styling (REVIEW-001/CMT-001) - Add inline notice on sent messages that images were not analyzed (CMT-001) - Add i18n keys for image not-analyzed notice (en + fr) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -13,7 +13,7 @@ import type {
|
||||
InsightsModelConfig,
|
||||
ImageAttachment
|
||||
} from '../../shared/types';
|
||||
import { MODEL_ID_MAP } from '../../shared/constants';
|
||||
import { MODEL_ID_MAP, MAX_IMAGES_PER_TASK, MAX_IMAGE_SIZE } from '../../shared/constants';
|
||||
import { InsightsConfig } from './config';
|
||||
import { detectRateLimit, createSDKRateLimitInfo } from '../rate-limit-detector';
|
||||
|
||||
@@ -120,6 +120,14 @@ export class InsightsExecutor extends EventEmitter {
|
||||
const imagesTempFiles: string[] = [];
|
||||
let imagesManifestFile: string | undefined;
|
||||
|
||||
// Defense-in-depth: cap image count and filter oversized images in the executor
|
||||
if (images && images.length > MAX_IMAGES_PER_TASK) {
|
||||
images = images.slice(0, MAX_IMAGES_PER_TASK);
|
||||
}
|
||||
if (images) {
|
||||
images = images.filter(img => !img.data || Buffer.byteLength(img.data, 'base64') <= MAX_IMAGE_SIZE);
|
||||
}
|
||||
|
||||
if (images && images.length > 0) {
|
||||
try {
|
||||
const manifest: Array<{ path: string; mimeType: string }> = [];
|
||||
|
||||
@@ -526,7 +526,10 @@ export function Insights({ projectId }: InsightsProps) {
|
||||
|
||||
{/* Image analysis warning */}
|
||||
{pendingImages.length > 0 && (
|
||||
<p className="mt-1 text-xs text-amber-500">{t('insights.images.analysisUnsupported')}</p>
|
||||
<div className="mt-1 flex items-center gap-1.5 rounded-md bg-amber-500/10 px-2 py-1 text-xs text-amber-500">
|
||||
<AlertCircle className="h-3 w-3 shrink-0" />
|
||||
<span>{t('insights.images.analysisUnsupported')}</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Image error */}
|
||||
@@ -625,17 +628,20 @@ function MessageBubble({
|
||||
|
||||
{/* Image attachments for user messages */}
|
||||
{isUser && message.images && message.images.length > 0 && (
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{message.images
|
||||
.filter(img => img.thumbnail || img.data)
|
||||
.map((image) => (
|
||||
<img
|
||||
key={image.id}
|
||||
src={image.thumbnail || `data:${image.mimeType};base64,${image.data}`}
|
||||
alt={image.filename}
|
||||
className="max-w-[200px] max-h-[200px] rounded-md border border-border object-contain"
|
||||
/>
|
||||
))}
|
||||
<div className="space-y-1.5">
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{message.images
|
||||
.filter(img => img.thumbnail || img.data)
|
||||
.map((image) => (
|
||||
<img
|
||||
key={image.id}
|
||||
src={image.thumbnail || `data:${image.mimeType};base64,${image.data}`}
|
||||
alt={image.filename}
|
||||
className="max-w-[200px] max-h-[200px] rounded-md border border-border object-contain"
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
<p className="text-xs text-muted-foreground italic">{t('insights.images.notAnalyzed')}</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
@@ -442,7 +442,8 @@
|
||||
"processFailed": "Failed to process image",
|
||||
"dragOver": "Drop image to attach",
|
||||
"analysisUnsupported": "Note: Image analysis is not yet supported. Images are stored for reference but cannot be analyzed by the model.",
|
||||
"screenshotTooLarge": "Screenshot is too large ({{size}}MB). Maximum size is {{max}}MB. Consider capturing a smaller area."
|
||||
"screenshotTooLarge": "Screenshot is too large ({{size}}MB). Maximum size is {{max}}MB. Consider capturing a smaller area.",
|
||||
"notAnalyzed": "Images were stored for reference but not analyzed by the model."
|
||||
}
|
||||
},
|
||||
"ideation": {
|
||||
|
||||
@@ -442,7 +442,8 @@
|
||||
"processFailed": "Échec du traitement de l'image",
|
||||
"dragOver": "Déposer l'image pour joindre",
|
||||
"analysisUnsupported": "Note : L'analyse d'images n'est pas encore prise en charge. Les images sont stockées pour référence mais ne peuvent pas être analysées par le modèle.",
|
||||
"screenshotTooLarge": "La capture d'écran est trop volumineuse ({{size}}Mo). La taille maximale est de {{max}}Mo. Essayez de capturer une zone plus petite."
|
||||
"screenshotTooLarge": "La capture d'écran est trop volumineuse ({{size}}Mo). La taille maximale est de {{max}}Mo. Essayez de capturer une zone plus petite.",
|
||||
"notAnalyzed": "Les images ont été stockées pour référence mais n'ont pas été analysées par le modèle."
|
||||
}
|
||||
},
|
||||
"ideation": {
|
||||
|
||||
Reference in New Issue
Block a user