Grabber-In-Tanstack-LlmDesk / src /routes /api /describe-image.ts
launch-calcium's picture
Upload folder using huggingface_hub
2704918 verified
Raw
History Blame Contribute Delete
3.71 kB
import { createFileRoute } from "@tanstack/react-router";
import { z } from "zod";
import { writeFileSync, unlinkSync } from "fs";
import { execSync } from "child_process";
import path from "path";
const QuerySchema = z.object({
imageUrl: z.string().url(),
prompt: z.string().optional(),
});
// Cache descriptions to avoid running expensive CPU inference repeatedly
const descriptionCache: Record<string, string> = {};
export const Route = createFileRoute("/api/describe-image")({
server: {
handlers: {
POST: async ({ request }) => {
try {
const body = QuerySchema.parse(await request.json());
const imageUrl = body.imageUrl;
const prompt = body.prompt || "Describe this image in detail.";
// Check cache
if (descriptionCache[imageUrl]) {
return new Response(JSON.stringify({ success: true, description: descriptionCache[imageUrl] }), {
status: 200,
headers: { "Content-Type": "application/json" },
});
}
// Fetch original image
const response = await fetch(imageUrl, {
headers: { "User-Agent": "LovableGrabber/1.0" },
});
if (!response.ok) {
throw new Error(`Failed to fetch image: HTTP ${response.status}`);
}
const arrayBuffer = await response.arrayBuffer();
const buffer = Buffer.from(arrayBuffer);
// Get file extension from URL
const urlObj = new URL(imageUrl);
let ext = path.extname(urlObj.pathname).toLowerCase() || ".jpg";
if (ext === ".ico" || !ext) ext = ".jpg"; // fallback to standard jpg for llama.cpp
const ts = Date.now();
const tempImagePath = `/tmp/describe_${ts}${ext}`;
writeFileSync(tempImagePath, buffer);
// Spawn python describer script
const scriptPath = path.join(process.cwd(), "src", "lib", "grabber", "describe.py");
let description = "";
try {
console.log(`Spawning description job for ${imageUrl}...`);
const stdout = execSync(
`python3 "${scriptPath}" "${tempImagePath}" "${prompt.replace(/"/g, '\\"')}"`,
{ encoding: "utf-8" }
);
// Parse result from python logs
const startMarker = "---RESULT_START---";
const endMarker = "---RESULT_END---";
const startIdx = stdout.indexOf(startMarker);
const endIdx = stdout.indexOf(endMarker);
if (startIdx !== -1 && endIdx !== -1) {
description = stdout.substring(startIdx + startMarker.length, endIdx).trim();
} else {
console.error("Python output lacked start/end markers. Full output:", stdout);
throw new Error("Failed to extract description from output");
}
} finally {
// Clean up temp image
try {
unlinkSync(tempImagePath);
} catch {
// ignore
}
}
if (description) {
descriptionCache[imageUrl] = description;
}
return new Response(JSON.stringify({ success: true, description }), {
status: 200,
headers: { "Content-Type": "application/json" },
});
} catch (err) {
return new Response(
JSON.stringify({ error: err instanceof Error ? err.message : "Failed to describe image" }),
{
status: 400,
headers: { "Content-Type": "application/json" },
}
);
}
},
},
},
});