feat: implement SparkPlug backend API integration for Gemini and Meshy
Build and Deploy / build-and-push (push) Successful in 1m20s

This commit is contained in:
AI Bot
2026-09-02 12:38:01 +05:30
parent a89fdd5f3f
commit 93b9fa90a0
5 changed files with 620 additions and 33 deletions
+50 -33
View File
@@ -27,6 +27,7 @@ export default function SparkPlug() {
// Step 4: 3D Model
const [modelProgress, setModelProgress] = useState(0);
const [generatedGlbUrl, setGeneratedGlbUrl] = useState<string | null>(null);
const handleFileUpload = async (e: React.ChangeEvent<HTMLInputElement>) => {
if (e.target.files && e.target.files[0]) {
@@ -49,17 +50,14 @@ export default function SparkPlug() {
setIsLoading(false);
return;
}
// We would normally send the PDF to the backend or use Gemini SDK directly here.
// Mocking the extraction for now based on the Indica Shampoo PDF context:
await new Promise(r => setTimeout(r, 2000));
setExtractedPrompt("Product: Indica Easy Herbal Shield Colour Shampoo\nType: Sachet/Packet\nColor Palette: Green, White\nKey Elements: Woman with black hair, Amla & Aloe illustration, 10 Minutes Herbal Care text.\n\nDescription: A glossy green rectangular sachet with vivid branding and product imagery for an herbal hair colour shampoo.");
// Call our backend endpoint to parse PDF and query Gemini
const prompt = await api.extractPDF(pdfFile);
setExtractedPrompt(prompt);
setCurrentStep(2);
} catch (err) {
} catch (err: any) {
console.error(err);
alert("Failed to process PDF.");
alert(`Failed to process PDF: ${err.message || 'Unknown error'}`);
} finally {
setIsLoading(false);
}
@@ -80,22 +78,18 @@ export default function SparkPlug() {
setIsLoading(false);
return;
}
// Mocking the image generation with Nano Banana
await new Promise(r => setTimeout(r, 3000));
// Call our backend endpoint which queries Gemini Imagen
const images = await api.generateImages(extractedPrompt);
// Placeholder images for the 4 views (front, back, left, right)
setGeneratedImages([
"https://images.unsplash.com/photo-1629198688000-71f23e745b6e?w=400&q=80", // front
"https://images.unsplash.com/photo-1629198725848-18e55e975cc3?w=400&q=80", // back
"https://images.unsplash.com/photo-1629198725838-511bb7405234?w=400&q=80", // left
"https://images.unsplash.com/photo-1629198688000-71f23e745b6e?w=400&q=80", // right
]);
setCurrentStep(3);
} catch (err) {
if (images && images.length === 4) {
setGeneratedImages(images);
setCurrentStep(3);
} else {
throw new Error("Failed to generate exactly 4 images.");
}
} catch (err: any) {
console.error(err);
alert("Failed to generate image.");
alert(`Failed to generate images: ${err.message || 'Unknown error'}`);
} finally {
setIsLoading(false);
}
@@ -116,17 +110,39 @@ export default function SparkPlug() {
setIsLoading(false);
return;
}
// Simulate Meshy generation
for (let i = 1; i <= 10; i++) {
await new Promise(r => setTimeout(r, 500));
setModelProgress(i * 10);
// Call backend to start Meshy task
const taskId = await api.generate3D(generatedImages);
// Poll Meshy status
let isDone = false;
let attempts = 0;
while (!isDone && attempts < 60) { // Max 5 mins (60 * 5s)
attempts++;
await new Promise(resolve => setTimeout(resolve, 5000));
const statusData = await api.get3DStatus(taskId);
const { status, progress, model_urls, task_error } = statusData;
if (progress) {
setModelProgress(progress);
}
if (status === 'SUCCEEDED') {
setGeneratedGlbUrl(model_urls.glb);
isDone = true;
setCurrentStep(4);
} else if (status === 'FAILED') {
throw new Error(`Meshy generation failed: ${task_error?.message || 'Unknown error'}`);
}
}
setCurrentStep(4);
} catch (err) {
if (!isDone) {
throw new Error("Meshy task timed out after 5 minutes.");
}
} catch (err: any) {
console.error(err);
alert("Failed to generate 3D model.");
alert(`Failed to generate 3D model: ${err.message || 'Unknown error'}`);
} finally {
setIsLoading(false);
}
@@ -136,8 +152,9 @@ export default function SparkPlug() {
setLoadingMessage('Saving project to your workspace...');
setIsLoading(true);
try {
// Mock generated GLB URL from Meshy
const mockGlbUrl = "https://raw.githubusercontent.com/KhronosGroup/glTF-Sample-Models/master/2.0/BoxTextured/glTF-Binary/BoxTextured.glb";
if (!generatedGlbUrl) {
throw new Error("No GLB URL available to save.");
}
// Create project and save with model injected into scene_data
const res = await api.saveProject({
@@ -152,7 +169,7 @@ export default function SparkPlug() {
position: [0, 0, 0],
rotation: [0, 0, 0],
scale: [1, 1, 1],
url: mockGlbUrl
url: generatedGlbUrl
}
]
}