feat: implement SparkPlug backend API integration for Gemini and Meshy
Build and Deploy / build-and-push (push) Successful in 1m20s
Build and Deploy / build-and-push (push) Successful in 1m20s
This commit is contained in:
@@ -192,5 +192,57 @@ export const api = {
|
||||
if (!res.ok) throw new Error(data.error);
|
||||
return data.url;
|
||||
}
|
||||
},
|
||||
|
||||
// ==========================================
|
||||
// SPARK PLUG APIS
|
||||
// ==========================================
|
||||
|
||||
extractPDF: async (file: File) => {
|
||||
const formData = new FormData();
|
||||
formData.append('file', file);
|
||||
|
||||
const res = await fetch(`${API_URL}/sparkplug/extract`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Authorization': `Bearer ${localStorage.getItem('auth_token')}`
|
||||
},
|
||||
body: formData
|
||||
});
|
||||
const data = await res.json();
|
||||
if (!res.ok) throw new Error(data.error);
|
||||
return data.prompt;
|
||||
},
|
||||
|
||||
generateImages: async (prompt: string) => {
|
||||
const res = await fetch(`${API_URL}/sparkplug/generate-images`, {
|
||||
method: 'POST',
|
||||
headers: getHeaders(),
|
||||
body: JSON.stringify({ prompt })
|
||||
});
|
||||
const data = await res.json();
|
||||
if (!res.ok) throw new Error(data.error);
|
||||
return data.images; // Array of 4 image URLs
|
||||
},
|
||||
|
||||
generate3D: async (imageUrls: string[]) => {
|
||||
const res = await fetch(`${API_URL}/sparkplug/generate-3d`, {
|
||||
method: 'POST',
|
||||
headers: getHeaders(),
|
||||
body: JSON.stringify({ imageUrls })
|
||||
});
|
||||
const data = await res.json();
|
||||
if (!res.ok) throw new Error(data.error);
|
||||
return data.taskId;
|
||||
},
|
||||
|
||||
get3DStatus: async (taskId: string) => {
|
||||
const res = await fetch(`${API_URL}/sparkplug/status-3d/${taskId}`, {
|
||||
method: 'GET',
|
||||
headers: getHeaders()
|
||||
});
|
||||
const data = await res.json();
|
||||
if (!res.ok) throw new Error(data.error);
|
||||
return data; // { status: "SUCCEEDED"|"PENDING", model_urls: { glb: "..." }, ... }
|
||||
}
|
||||
};
|
||||
|
||||
+50
-33
@@ -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
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user