Browse Source

feat(05-02): convert large images to URLs with cleanup

- Import uploadImageForUrl, shouldUseImageUrl, deleteImages from @/lib/images
- Process images for Replicate/fal providers before generation
- Small images (<256KB) use data URIs directly
- Large images converted to temporary URLs via /api/images/[id]
- Cleanup uploaded images in finally block (success or failure)
- Gemini path unchanged (handles base64 directly)
handoff-20260429-1057
shrimbly 6 months ago
parent
commit
2195a6ed9c
  1. 59
      src/app/api/generate/route.ts

59
src/app/api/generate/route.ts

@ -2,6 +2,7 @@ import { NextRequest, NextResponse } from "next/server";
import { GoogleGenAI } from "@google/genai"; import { GoogleGenAI } from "@google/genai";
import { GenerateRequest, GenerateResponse, ModelType, SelectedModel, ProviderType } from "@/types"; import { GenerateRequest, GenerateResponse, ModelType, SelectedModel, ProviderType } from "@/types";
import { GenerationInput, GenerationOutput, ProviderModel } from "@/lib/providers/types"; import { GenerationInput, GenerationOutput, ProviderModel } from "@/lib/providers/types";
import { uploadImageForUrl, shouldUseImageUrl, deleteImages } from "@/lib/images";
export const maxDuration = 300; // 5 minute timeout for API calls export const maxDuration = 300; // 5 minute timeout for API calls
export const dynamic = 'force-dynamic'; // Ensure this route is always dynamic export const dynamic = 'force-dynamic'; // Ensure this route is always dynamic
@ -567,6 +568,26 @@ export async function POST(request: NextRequest) {
); );
} }
// Process images: convert large images to URLs for provider to fetch
const baseUrl = new URL(request.url).origin;
const uploadedImageIds: string[] = [];
const processedImages: string[] = [];
if (images && images.length > 0) {
for (const image of images) {
if (shouldUseImageUrl(image)) {
const { url, id } = uploadImageForUrl(image, baseUrl);
uploadedImageIds.push(id);
processedImages.push(url);
console.log(`[API:${requestId}] Converted large image to URL: ${url}`);
} else {
processedImages.push(image);
console.log(`[API:${requestId}] Using base64 directly (small image)`);
}
}
}
try {
// Build generation input // Build generation input
const genInput: GenerationInput = { const genInput: GenerationInput = {
model: { model: {
@ -577,7 +598,7 @@ export async function POST(request: NextRequest) {
description: null, description: null,
}, },
prompt, prompt,
images, images: processedImages,
parameters, parameters,
}; };
@ -609,12 +630,39 @@ export async function POST(request: NextRequest) {
success: true, success: true,
image: outputImage, image: outputImage,
}); });
} finally {
// Clean up uploaded images
if (uploadedImageIds.length > 0) {
deleteImages(uploadedImageIds);
console.log(`[API:${requestId}] Cleaned up ${uploadedImageIds.length} uploaded images`);
}
}
} }
if (provider === "fal") { if (provider === "fal") {
// Get fal.ai API key from request headers (optional - fal.ai works without key but rate limited) // Get fal.ai API key from request headers (optional - fal.ai works without key but rate limited)
const falApiKey = request.headers.get("X-Fal-API-Key"); const falApiKey = request.headers.get("X-Fal-API-Key");
// Process images: convert large images to URLs for provider to fetch
const baseUrl = new URL(request.url).origin;
const uploadedImageIds: string[] = [];
const processedImages: string[] = [];
if (images && images.length > 0) {
for (const image of images) {
if (shouldUseImageUrl(image)) {
const { url, id } = uploadImageForUrl(image, baseUrl);
uploadedImageIds.push(id);
processedImages.push(url);
console.log(`[API:${requestId}] Converted large image to URL: ${url}`);
} else {
processedImages.push(image);
console.log(`[API:${requestId}] Using base64 directly (small image)`);
}
}
}
try {
// Build generation input // Build generation input
const genInput: GenerationInput = { const genInput: GenerationInput = {
model: { model: {
@ -625,7 +673,7 @@ export async function POST(request: NextRequest) {
description: null, description: null,
}, },
prompt, prompt,
images, images: processedImages,
parameters, parameters,
}; };
@ -657,6 +705,13 @@ export async function POST(request: NextRequest) {
success: true, success: true,
image: outputImage, image: outputImage,
}); });
} finally {
// Clean up uploaded images
if (uploadedImageIds.length > 0) {
deleteImages(uploadedImageIds);
console.log(`[API:${requestId}] Cleaned up ${uploadedImageIds.length} uploaded images`);
}
}
} }
// Default: Use Gemini // Default: Use Gemini

Loading…
Cancel
Save