23dfactory / frontend /api /job-status.js
LogicalTrue
fix: agregar cabecera obligatoria Accept: text/event-stream para la lectura de eventos Gradio 4
7b8522b
Raw
History Blame Contribute Delete
7.54 kB
export default async function handler(req, res) {
res.setHeader('Cache-Control', 'no-store, no-cache, must-revalidate, proxy-revalidate');
res.setHeader('Pragma', 'no-cache');
res.setHeader('Expires', '0');
if (req.method !== 'GET') {
return res.status(405).json({ error: 'Método no permitido' });
}
try {
let jobId = req.query?.job_id;
if (!jobId && req.url) {
try {
const parsedUrl = new URL(req.url, 'https://localhost');
jobId = parsedUrl.searchParams.get('job_id');
} catch (e) {}
}
if (!jobId) {
return res.status(400).json({ error: 'El parámetro job_id es requerido.' });
}
const hfToken = process.env.HF_TOKEN;
if (jobId.startsWith('hf:')) {
const parts = jobId.split(':');
const spaceHost = parts[1] || 'logicaltrue-trellis-2';
const sessionHash = parts[2];
const eventId = parts[3];
const decimationTarget = parseInt(parts[4]) || 300000;
const textureSize = parseInt(parts[5]) || 1024;
const spaceUrl = `https://${spaceHost}.hf.space`;
const sseHeaders = {
'Accept': 'text/event-stream',
'Cache-Control': 'no-cache',
...(hfToken ? { 'Authorization': `Bearer ${hfToken}` } : {})
};
const jsonHeaders = {
'Content-Type': 'application/json',
...(hfToken ? { 'Authorization': `Bearer ${hfToken}` } : {})
};
const step1Urls = [
`${spaceUrl}/gradio_api/call/image_to_3d/${eventId}`,
`${spaceUrl}/call/image_to_3d/${eventId}`,
`${spaceUrl}/gradio_api/queue/data?session_hash=${sessionHash}`,
`${spaceUrl}/queue/data?session_hash=${sessionHash}`
];
let isStep1Complete = false;
for (const statusUrl of step1Urls) {
try {
const qRes = await fetch(statusUrl, {
headers: sseHeaders,
signal: AbortSignal.timeout(9000)
}).catch(() => null);
if (qRes && qRes.ok) {
const txt = await qRes.text().catch(() => '');
console.log(`[Paso 1 Stream SSE ${eventId}]`, txt.slice(0, 300));
if (txt.includes('event: complete') || txt.includes('process_completed') || txt.includes('event: generating')) {
if (txt.includes('"success":false')) {
continue;
}
isStep1Complete = true;
break;
}
if (txt.includes('process_starts') || txt.includes('estimation') || txt.includes('event: heartbeat')) {
return res.status(200).json({
status: 'processing',
progress: 65,
message: 'Inferencia 3D en progreso en la GPU A100 (Sparse & SLaT)...'
});
}
}
} catch (e) {}
}
if (!isStep1Complete) {
return res.status(200).json({
status: 'processing',
progress: 60,
message: 'Calculando volumen 3D en la GPU A100...'
});
}
console.log(`[Paso 1 completado] Iniciando Paso 2 (/extract_glb) decimation=${decimationTarget}...`);
let extractEventId = null;
try {
const extractRes = await fetch(`${spaceUrl}/gradio_api/call/extract_glb`, {
method: 'POST',
headers: jsonHeaders,
body: JSON.stringify({
data: [decimationTarget, textureSize],
session_hash: sessionHash
}),
signal: AbortSignal.timeout(10000)
}).catch(() => null);
if (extractRes && extractRes.ok) {
const extractJson = await extractRes.json().catch(() => ({}));
extractEventId = extractJson.event_id;
console.log(`[Paso 2 /extract_glb Iniciado] event_id: ${extractEventId}`);
}
} catch (e) {}
const extractCheckUrls = [
extractEventId ? `${spaceUrl}/gradio_api/call/extract_glb/${extractEventId}` : null,
extractEventId ? `${spaceUrl}/call/extract_glb/${extractEventId}` : null,
`${spaceUrl}/gradio_api/queue/data?session_hash=${sessionHash}`,
`${spaceUrl}/queue/data?session_hash=${sessionHash}`
].filter(Boolean);
for (const extUrl of extractCheckUrls) {
try {
const extRes = await fetch(extUrl, {
headers: sseHeaders,
signal: AbortSignal.timeout(9000)
}).catch(() => null);
if (extRes && extRes.ok) {
const extTxt = await extRes.text().catch(() => '');
const glbMatch = extTxt.match(/"([^"]+\.glb)"/i);
if (glbMatch && glbMatch[1]) {
let rawGlb = glbMatch[1];
let fullGlbUrl = rawGlb;
if (!rawGlb.startsWith('http')) {
if (rawGlb.startsWith('/tmp/') || rawGlb.startsWith('tmp/')) {
fullGlbUrl = `${spaceUrl}/file=${rawGlb.startsWith('/') ? '' : '/'}${rawGlb}`;
} else if (rawGlb.startsWith('/file=') || rawGlb.startsWith('file=')) {
fullGlbUrl = `${spaceUrl}${rawGlb.startsWith('/') ? '' : '/'}${rawGlb}`;
} else {
fullGlbUrl = `${spaceUrl}/file=${rawGlb}`;
}
}
console.log(`[GLB EXTRACCION EXITOSA 100%] ${fullGlbUrl}`);
return res.status(200).json({
status: 'completed',
progress: 100,
result: {
gltfUrl: fullGlbUrl,
glbUrl: fullGlbUrl,
fbxUrl: fullGlbUrl,
detectedCategory: 'objeto'
}
});
}
}
} catch (e) {}
}
return res.status(200).json({
status: 'processing',
progress: 85,
message: 'Extrayendo texturas PBR y generando archivo GLB...'
});
}
return res.status(200).json({
status: 'processing',
progress: 50,
message: 'Procesando modelo 3D en la nube...'
});
} catch (err) {
console.error('[Vercel Job-Status Error]', err);
return res.status(500).json({ error: err.message || 'Error verificando el estado de la tarea.' });
}
}