Files
playground/frontend/app/api/generate-image/route.ts
2025-11-26 19:00:04 +00:00

155 lines
4.4 KiB
TypeScript

import { NextRequest, NextResponse } from 'next/server';
import { type ImageGenerationConfig, getSafetySettings } from '@/lib/google-ai';
import { vertexAI } from '@/lib/google-ai-server';
export const runtime = 'nodejs';
export const maxDuration = 60;
export async function POST(req: NextRequest) {
try {
const body: ImageGenerationConfig = await req.json();
const {
model,
prompt,
aspectRatio = '1:1',
numberOfImages = 1,
negativePrompt,
temperature = 1,
safetyFilterLevel = 'block_some',
referenceImages,
} = body;
if (!prompt || !model) {
return NextResponse.json(
{ error: 'Prompt y modelo son requeridos' },
{ status: 400 }
);
}
// Obtener el modelo generativo de Vertex AI
const generativeModel = vertexAI.getGenerativeModel({
model: model,
safetySettings: getSafetySettings(safetyFilterLevel) as any,
generationConfig: {
temperature: temperature,
maxOutputTokens: 8192,
},
});
// Construir el prompt completo
let fullPrompt = prompt;
if (negativePrompt) {
fullPrompt += `\n\nNo incluir: ${negativePrompt}`;
}
// Agregar instrucciones de aspect ratio si no es 1:1
if (aspectRatio !== '1:1') {
fullPrompt += `\n\nGenera la imagen en formato ${aspectRatio}.`;
}
// Construir parts con imágenes de referencia si existen
const parts: any[] = [];
// Agregar imágenes de referencia primero
if (referenceImages && referenceImages.length > 0) {
for (const refImg of referenceImages) {
parts.push({
inlineData: {
data: refImg.data,
mimeType: refImg.mimeType,
},
});
}
}
// Agregar el prompt de texto
parts.push({ text: fullPrompt });
// Generar contenido
const result = await generativeModel.generateContent({
contents: [
{
role: 'user',
parts: parts,
},
],
});
const response = result.response;
// Extraer imágenes de la respuesta
const images: string[] = [];
if (response.candidates && response.candidates.length > 0) {
for (const candidate of response.candidates) {
if (candidate.content?.parts) {
for (const part of candidate.content.parts) {
// Verificar si la parte contiene datos inline (imagen)
if ('inlineData' in part && part.inlineData) {
const imageData = part.inlineData.data;
if (imageData) {
images.push(imageData);
}
}
}
}
}
}
// Si no hay imágenes, intentar generar más información de debug
if (images.length === 0) {
console.error('No se encontraron imágenes en la respuesta');
return NextResponse.json(
{
error: 'El modelo no generó imágenes',
details: 'El modelo respondió pero no incluyó datos de imagen',
response: response,
suggestion: 'Prueba con un prompt más descriptivo o cambia el modelo',
},
{ status: 500 }
);
}
return NextResponse.json({
images: images,
model: model,
prompt: prompt,
aspectRatio: aspectRatio,
count: images.length,
});
} catch (error: any) {
console.error('Error generando imagen:', error);
// Manejar errores específicos de la API
let errorMessage = 'Error generando imagen';
let errorDetails = error.message;
if (error.message?.includes('credentials') || error.message?.includes('authentication')) {
errorMessage = 'Error de autenticación';
errorDetails = 'Verifica las credenciales del Service Account';
} else if (error.message?.includes('quota')) {
errorMessage = 'Límite de cuota excedido';
errorDetails = 'Has alcanzado el límite de uso de la API';
} else if (error.message?.includes('safety')) {
errorMessage = 'Contenido bloqueado por seguridad';
errorDetails = 'El prompt fue bloqueado por políticas de seguridad';
} else if (error.message?.includes('permission')) {
errorMessage = 'Error de permisos';
errorDetails = 'El Service Account no tiene permisos para Vertex AI';
}
return NextResponse.json(
{
error: errorMessage,
details: errorDetails,
stack: process.env.NODE_ENV === 'development' ? error.stack : undefined,
},
{ status: 500 }
);
}
}