// Gradio Server-mode API client: POST /gradio_api/call/ {"data": [...]} // returns {event_id}; GET /gradio_api/call// streams SSE // events ("generating" per chunk, "complete" at the end). async function beginCall(name, data) { const res = await fetch(`./gradio_api/call/${name}`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ data }), }); if (!res.ok) throw new Error(`API ${name}: HTTP ${res.status}`); const { event_id } = await res.json(); return event_id; } // Parse an SSE byte stream and invoke onEvent(type, payload) per event. async function readSSE(name, eventId, onEvent) { const res = await fetch(`./gradio_api/call/${name}/${eventId}`); if (!res.ok) throw new Error(`API ${name}: HTTP ${res.status} on stream`); const reader = res.body.getReader(); const decoder = new TextDecoder(); let buffer = ''; for (;;) { const { done, value } = await reader.read(); if (done) break; buffer += decoder.decode(value, { stream: true }); let sep; while ((sep = buffer.indexOf('\n\n')) !== -1) { const block = buffer.slice(0, sep); buffer = buffer.slice(sep + 2); let type = 'message'; let dataLine = ''; for (const line of block.split('\n')) { if (line.startsWith('event:')) type = line.slice(6).trim(); else if (line.startsWith('data:')) dataLine += line.slice(5).trim(); } if (!dataLine) continue; let payload = null; try { payload = JSON.parse(dataLine); } catch { /* keep null */ } onEvent(type, payload); } } } function unwrap(payload) { // Endpoints return a single dict; gradio wraps outputs in a list. return Array.isArray(payload) ? payload[0] : payload; } // One-shot call: resolves with the final (complete) output dict. export async function call(name, data = []) { const eventId = await beginCall(name, data); let result = null; let errored = null; await readSSE(name, eventId, (type, payload) => { if (type === 'error') errored = payload || 'server error'; else result = unwrap(payload); }); if (errored) throw new Error(typeof errored === 'string' ? errored : JSON.stringify(errored)); return result; } // Streaming call: onChunk(dict) per "generating" event; resolves with the // final dict. Our streaming endpoints tag payloads {type: progress|final}. export async function stream(name, data, onChunk) { const eventId = await beginCall(name, data); let result = null; let errored = null; await readSSE(name, eventId, (type, payload) => { if (type === 'error') { errored = payload || 'server error'; return; } const value = unwrap(payload); if (value == null) return; if (type === 'generating') onChunk(value); else result = value; }); if (errored) throw new Error(typeof errored === 'string' ? errored : JSON.stringify(errored)); return result; } export async function getJSON(url) { const res = await fetch(url); if (!res.ok) throw new Error(`GET ${url}: HTTP ${res.status}`); return res.json(); } export async function uploadFile(url, file) { const form = new FormData(); form.append('file', file); const res = await fetch(url, { method: 'POST', body: form }); if (!res.ok) throw new Error(`upload: HTTP ${res.status}`); return res.json(); }