diff --git a/components/elements/response.tsx b/components/elements/response.tsx index b908c4b7e..82deb547e 100644 --- a/components/elements/response.tsx +++ b/components/elements/response.tsx @@ -905,11 +905,17 @@ export const Response = ({ ), }; - const renderImagePreview = (src: string, alt: string | undefined, key: string) => ( + const renderImagePreview = ( + src: string, + alt: string | undefined, + key: string, + compactGrid = false + ) => ( ); + const renderImageGrid = ( + images: Array<{ alt?: string; src: string }>, + keyPrefix: string + ) => { + if (images.length === 0) return null; + if (images.length === 1) { + const image = images[0]; + return renderImagePreview( + image.src, + image.alt, + `${image.src}-${processingKey}-${keyPrefix}-single` + ); + } + + const gridClassName = + images.length === 2 + ? "grid-cols-2 sm:max-w-[640px]" + : images.length === 3 + ? "grid-cols-2 sm:grid-cols-3 sm:max-w-[780px]" + : "grid-cols-2 sm:grid-cols-4 sm:max-w-[920px]"; + + return ( +
+ {images.map((image, index) => ( +
+ {renderImagePreview( + image.src, + image.alt, + `${image.src}-${processingKey}-${keyPrefix}-grid-${index}`, + true + )} +
+ ))} +
+ ); + }; + const renderTextSegment = (text: string, keyPrefix: string): ReactNode => { if (!text.trim()) return null; @@ -937,20 +986,31 @@ export const Response = ({ const hasImageSegments = segments.some((segment) => segment.type === "image"); if (hasImageSegments) { - return segments.map((segment, index) => { + const nodes: ReactNode[] = []; + let pendingImages: Array<{ alt?: string; src: string }> = []; + const flushImages = (index: number) => { + if (pendingImages.length === 0) return; + nodes.push( + renderImageGrid(pendingImages, `${keyPrefix}-images-${index}`) + ); + pendingImages = []; + }; + + segments.forEach((segment, index) => { if (segment.type === "image") { - return renderImagePreview( - segment.src, - segment.alt, - `${segment.src}-${processingKey}-${keyPrefix}-${index}` - ); + pendingImages.push({ + alt: segment.alt, + src: segment.src, + }); + return; } if (!segment.value.trim()) { - return null; + return; } - return ( + flushImages(index); + nodes.push( ); }); + flushImages(segments.length); + return nodes; } } diff --git a/components/image-preview.tsx b/components/image-preview.tsx index 6908df6fd..1750c9332 100644 --- a/components/image-preview.tsx +++ b/components/image-preview.tsx @@ -42,7 +42,8 @@ export function ImagePreview({ isProcessingExtend, isProcessingAngleEdit, isProcessingWatermarkRemove, - imageIndex + imageIndex, + compactGrid = false }: { src: string; alt?: string; @@ -64,6 +65,7 @@ export function ImagePreview({ isProcessingAngleEdit?: boolean; isProcessingWatermarkRemove?: boolean; imageIndex?: number; + compactGrid?: boolean; }) { const [isOpen, setIsOpen] = useState(false); const [isAnnotationMode, setIsAnnotationMode] = useState(false); @@ -293,16 +295,26 @@ export function ImagePreview({ return ( <> {/* 缩略图(显示压缩后的缩略图)*/} -
+
Generated Image 1; const result = await generateCanvasImage({ prompt, negativePrompt: params.imageParams?.negativePrompt, model: params.finalModelId, ownerKey: params.userId, + debugLabel: "direct-agent-image-generation", + debugMeta: { + agentIgnoreModelMaxImages: shouldIgnoreModelMaxImages, + requestedAgentImageCount: imageCount ?? null, + }, aspectRatio: params.imageParams?.aspectRatio || params.defaultImageQualityParams?.aspectRatio, imageSize, imageCount, + ignoreModelMaxImages: shouldIgnoreModelMaxImages, referenceImages: params.referenceImageUrls, }); diff --git a/lib/canvas/generate-image.ts b/lib/canvas/generate-image.ts index e29df494e..6a82b853e 100644 --- a/lib/canvas/generate-image.ts +++ b/lib/canvas/generate-image.ts @@ -116,6 +116,7 @@ export interface CanvasGenerateImageRequest { ownerKey?: string; debugLabel?: string; debugMeta?: Record; + ignoreModelMaxImages?: boolean; localEditComposite?: CanvasLocalEditCompositeConfig; scaleAdjustPatchComposite?: CanvasScaleAdjustPatchCompositeConfig; scaleAdjustVerification?: CanvasScaleAdjustVerificationConfig; @@ -3167,12 +3168,14 @@ function normalizeCanvasImageParamsByModel(params: { imageCount: number; imageSize: string; imageGenConfig: CanvasImageGenConfig | null | undefined; + ignoreModelMaxImages?: boolean; }) { - const maxImages = + const configuredMaxImages = typeof params.imageGenConfig?.maxImages === "number" && Number.isFinite(params.imageGenConfig.maxImages) ? Math.max(1, Math.floor(params.imageGenConfig.maxImages)) : 12; + const maxImages = params.ignoreModelMaxImages ? 12 : configuredMaxImages; const imageCount = Math.max(1, Math.min(maxImages, Math.floor(params.imageCount))); const requestedSize = normalizeCanvasImageSizeValue(params.imageSize); @@ -3635,6 +3638,7 @@ export async function generateCanvasImage( imageCount: requestedImageCount, imageSize: requestedImageSize, imageGenConfig: selectedModel.model.imageGenConfig as CanvasImageGenConfig | null | undefined, + ignoreModelMaxImages: request.ignoreModelMaxImages === true, }); const imageSize = normalizedImageParams.imageSize; const imageCount =