philschmid's picture
init
faee5d2 unverified
import { NextRequest, NextResponse } from "next/server";
import { GoogleGenerativeAI } from "@google/generative-ai";
import { HistoryItem, HistoryPart } from "@/lib/types";
// Initialize the Google Gen AI client with your API key
const GEMINI_API_KEY = process.env.GEMINI_API_KEY || "";
const genAI = new GoogleGenerativeAI(GEMINI_API_KEY);
// Define the model ID for Gemini 2.0 Flash experimental
const MODEL_ID = "gemini-2.0-flash-exp";
// Define interface for the formatted history item
interface FormattedHistoryItem {
role: "user" | "model";
parts: Array<{
text?: string;
inlineData?: { data: string; mimeType: string };
}>;
}
export async function POST(req: NextRequest) {
try {
// Parse JSON request instead of FormData
const requestData = await req.json();
const { prompt, image: inputImage, history } = requestData;
if (!prompt) {
return NextResponse.json(
{ error: "Prompt is required" },
{ status: 400 }
);
}
// Get the model with the correct configuration
const model = genAI.getGenerativeModel({
model: MODEL_ID,
generationConfig: {
temperature: 1,
topP: 0.95,
topK: 40,
// @ts-expect-error - Gemini API JS is missing this type
responseModalities: ["Text", "Image"],
},
});
let result;
try {
// Convert history to the format expected by Gemini API
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), // Remove empty parts
};
})
.filter((item: FormattedHistoryItem) => item.parts.length > 0) // Remove items with no parts
: [];
// Create a chat session with the formatted history
const chat = model.startChat({
history: formattedHistory,
});
// Prepare the current message parts
const messageParts = [];
// Add the text prompt
messageParts.push({ text: prompt });
// Add the image if provided
if (inputImage) {
// For image editing
console.log("Processing image edit request");
// Check if the image is a valid data URL
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
);
// Add the image to message parts
messageParts.push({
inlineData: {
data: base64Image,
mimeType: mimeType,
},
});
}
// Send the message to the chat
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";
// Process the response
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) {
// Get the image data
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) {
// Store the text
textResponse = part.text;
console.log(
"Text response received:",
textResponse.substring(0, 50) + "..."
);
}
}
}
// Return just the base64 image and description as JSON
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 }
);
}
}