Panno-AI-API / src /app /api /generate /route.ts
GitHub Actions Bot
Sync: Thu Feb 12 07:00:42 UTC 2026
dce7eca
import { NextRequest, NextResponse } from "next/server";
import { exec } from "child_process";
import { promisify } from "util";
import fs from "fs";
import path from "path";
import os from "os";
import { GoogleGenerativeAI } from "@google/generative-ai";
const execAsync = promisify(exec);
export async function POST(req: NextRequest) {
const tempFiles: string[] = [];
try {
const formData = await req.formData();
const prompt = formData.get("prompt") as string || "a photographic 360 panorama";
const images = formData.getAll("images") as File[];
// 1. Save uploaded images to temp directory
const tempDir = path.join(os.tmpdir(), `panno-${Date.now()}`);
if (!fs.existsSync(tempDir)) fs.mkdirSync(tempDir);
for (let i = 0; i < images.length; i++) {
const buffer = Buffer.from(await images[i].arrayBuffer());
const fileName = path.join(tempDir, `img_${i}.png`);
fs.writeFileSync(fileName, buffer);
tempFiles.push(fileName);
}
// 2. Load Config & Keys
const stabilityKey = process.env.Home_STABILITY_API_KEY || process.env.STABILITY_API_KEY;
const geminiKey = process.env.GEMINI_API_KEY || process.env.GOOGLE_API_KEY;
if (!stabilityKey) {
throw new Error("Missing STABILITY_API_KEY in Environment Variables.");
}
let result: { image: string | null, method: string } = { image: null, method: "failed" };
// --- CORE LOGIC: Direct Python Local Execution ---
// Since we are in a unified Docker container on HF, we always run locally.
try {
const pythonScript = path.join(process.cwd(), "scripts", "processor.py");
const imageArgs = tempFiles.map(img => `"${img}"`).join(" ");
console.log("Unified Container: Executing specialized Python engine...");
// Detect OS and use appropriate python command
const pythonCmd = process.platform === "win32" ? "python" : "python3";
const { stdout } = await execAsync(`${pythonCmd} "${pythonScript}" "${stabilityKey}" "${prompt.replace(/"/g, '\\"')}" ${imageArgs}`, {
maxBuffer: 1024 * 1024 * 20,
timeout: 120000 // 2 minute limit for heavy processing
});
const parsed = JSON.parse(stdout);
if (parsed.success) {
result = { image: parsed.image, method: "unified_hf_engine" };
} else {
throw new Error(parsed.error || "Stitching engine failed");
}
} catch (err: any) {
console.warn("Local Engine Error, attempting Vision Fallback:", err.message);
// --- FALLBACK: Pure AI Cloud (If Python fails) ---
let visionPrompt = prompt;
if (geminiKey && tempFiles.length > 0) {
try {
const genAI = new GoogleGenerativeAI(geminiKey);
const model = genAI.getGenerativeModel({ model: "gemini-2.0-flash" });
const visionResult = await model.generateContent([
"Describe room type and style in 10 words.",
{ inlineData: { data: fs.readFileSync(tempFiles[0]).toString("base64"), mimeType: "image/png" } }
]);
visionPrompt = `${prompt}. Style: ${visionResult.response.text()}`;
} catch (e) { }
}
const aiFormData = new FormData();
aiFormData.append("prompt", `${visionPrompt}, 360 panorama, rectilinear, high quality, seamless`);
aiFormData.append("output_format", "webp");
aiFormData.append("aspect_ratio", "21:9");
const response = await fetch("https://api.stability.ai/v2beta/stable-image/generate/ultra", {
method: "POST",
headers: { "Authorization": `Bearer ${stabilityKey}`, "Accept": "application/json" },
body: aiFormData
});
const data = await response.json();
if (response.ok && data.image) {
result = { image: `data:image/webp;base64,${data.image}`, method: "unified_pure_ai_fallback" };
} else {
throw new Error(data.message || "All methods failed");
}
}
return NextResponse.json({
url: result.image,
success: true,
method: result.method
});
} catch (error: any) {
console.error("Unified Pipeline Error:", error.message);
return NextResponse.json({
success: false,
message: error.message || "Process failed"
}, { status: 500 });
} finally {
// Cleanup
try {
tempFiles.forEach(f => { if (fs.existsSync(f)) fs.unlinkSync(f); });
const dir = path.dirname(tempFiles[0]);
if (dir && fs.existsSync(dir)) fs.rmdirSync(dir);
} catch (e) { }
}
}