|
|
import { NextRequest, NextResponse } from "next/server"; |
|
|
import { GoogleGenerativeAI } from "@google/generative-ai"; |
|
|
import { HistoryItem, HistoryPart } from "@/lib/types"; |
|
|
|
|
|
|
|
|
const GEMINI_API_KEY = process.env.GEMINI_API_KEY || ""; |
|
|
const genAI = new GoogleGenerativeAI(GEMINI_API_KEY); |
|
|
|
|
|
|
|
|
const MODEL_ID = "gemini-2.0-flash-exp"; |
|
|
|
|
|
|
|
|
interface FormattedHistoryItem { |
|
|
role: "user" | "model"; |
|
|
parts: Array<{ |
|
|
text?: string; |
|
|
inlineData?: { data: string; mimeType: string }; |
|
|
}>; |
|
|
} |
|
|
|
|
|
export async function POST(req: NextRequest) { |
|
|
try { |
|
|
|
|
|
const requestData = await req.json(); |
|
|
const { prompt, image: inputImage, history } = requestData; |
|
|
|
|
|
if (!prompt) { |
|
|
return NextResponse.json( |
|
|
{ error: "Prompt is required" }, |
|
|
{ status: 400 } |
|
|
); |
|
|
} |
|
|
|
|
|
|
|
|
const model = genAI.getGenerativeModel({ |
|
|
model: MODEL_ID, |
|
|
generationConfig: { |
|
|
temperature: 1, |
|
|
topP: 0.95, |
|
|
topK: 40, |
|
|
|
|
|
responseModalities: ["Text", "Image"], |
|
|
}, |
|
|
}); |
|
|
|
|
|
let result; |
|
|
|
|
|
try { |
|
|
|
|
|
const formattedHistory = |
|
|
history && history.length > 0 |
|
|
? history |
|
|
.map((item: HistoryItem) => { |
|
|
return { |
|
|
role: item.role, |
|
|
parts: item.parts |
|
|
.map((part: HistoryPart) => { |
|
|
if (part.text) { |
|
|
return { text: part.text }; |
|
|
} |
|
|
if (part.image && item.role === "user") { |
|
|
const imgParts = part.image.split(","); |
|
|
if (imgParts.length > 1) { |
|
|
return { |
|
|
inlineData: { |
|
|
data: imgParts[1], |
|
|
mimeType: part.image.includes("image/png") |
|
|
? "image/png" |
|
|
: "image/jpeg", |
|
|
}, |
|
|
}; |
|
|
} |
|
|
} |
|
|
return { text: "" }; |
|
|
}) |
|
|
.filter((part) => Object.keys(part).length > 0), |
|
|
}; |
|
|
}) |
|
|
.filter((item: FormattedHistoryItem) => item.parts.length > 0) |
|
|
: []; |
|
|
|
|
|
|
|
|
const chat = model.startChat({ |
|
|
history: formattedHistory, |
|
|
}); |
|
|
|
|
|
|
|
|
const messageParts = []; |
|
|
|
|
|
|
|
|
messageParts.push({ text: prompt }); |
|
|
|
|
|
|
|
|
if (inputImage) { |
|
|
|
|
|
console.log("Processing image edit request"); |
|
|
|
|
|
|
|
|
if (!inputImage.startsWith("data:")) { |
|
|
throw new Error("Invalid image data URL format"); |
|
|
} |
|
|
|
|
|
const imageParts = inputImage.split(","); |
|
|
if (imageParts.length < 2) { |
|
|
throw new Error("Invalid image data URL format"); |
|
|
} |
|
|
|
|
|
const base64Image = imageParts[1]; |
|
|
const mimeType = inputImage.includes("image/png") |
|
|
? "image/png" |
|
|
: "image/jpeg"; |
|
|
console.log( |
|
|
"Base64 image length:", |
|
|
base64Image.length, |
|
|
"MIME type:", |
|
|
mimeType |
|
|
); |
|
|
|
|
|
|
|
|
messageParts.push({ |
|
|
inlineData: { |
|
|
data: base64Image, |
|
|
mimeType: mimeType, |
|
|
}, |
|
|
}); |
|
|
} |
|
|
|
|
|
|
|
|
console.log("Sending message with", messageParts.length, "parts"); |
|
|
result = await chat.sendMessage(messageParts); |
|
|
} catch (error) { |
|
|
console.error("Error in chat.sendMessage:", error); |
|
|
throw error; |
|
|
} |
|
|
|
|
|
const response = result.response; |
|
|
|
|
|
let textResponse = null; |
|
|
let imageData = null; |
|
|
let mimeType = "image/png"; |
|
|
|
|
|
|
|
|
if (response.candidates && response.candidates.length > 0) { |
|
|
const parts = response.candidates[0].content.parts; |
|
|
console.log("Number of parts in response:", parts.length); |
|
|
|
|
|
for (const part of parts) { |
|
|
if ("inlineData" in part && part.inlineData) { |
|
|
|
|
|
imageData = part.inlineData.data; |
|
|
mimeType = part.inlineData.mimeType || "image/png"; |
|
|
console.log( |
|
|
"Image data received, length:", |
|
|
imageData.length, |
|
|
"MIME type:", |
|
|
mimeType |
|
|
); |
|
|
} else if ("text" in part && part.text) { |
|
|
|
|
|
textResponse = part.text; |
|
|
console.log( |
|
|
"Text response received:", |
|
|
textResponse.substring(0, 50) + "..." |
|
|
); |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
return NextResponse.json({ |
|
|
image: imageData ? `data:${mimeType};base64,${imageData}` : null, |
|
|
description: textResponse, |
|
|
}); |
|
|
} catch (error) { |
|
|
console.error("Error generating image:", error); |
|
|
return NextResponse.json( |
|
|
{ |
|
|
error: "Failed to generate image", |
|
|
details: error instanceof Error ? error.message : String(error), |
|
|
}, |
|
|
{ status: 500 } |
|
|
); |
|
|
} |
|
|
} |
|
|
|