Spaces:
Runtime error
Runtime error
var fn = new Intl.Collator(0, { numeric: 1 }).compare; | |
function semiver(a, b, bool) { | |
a = a.split("."); | |
b = b.split("."); | |
return fn(a[0], b[0]) || fn(a[1], b[1]) || (b[2] = b.slice(2).join("."), bool = /[.-]/.test(a[2] = a.slice(2).join(".")), bool == /[.-]/.test(b[2]) ? fn(a[2], b[2]) : bool ? -1 : 1); | |
} | |
function resolve_root(base_url, root_path, prioritize_base) { | |
if (root_path.startsWith("http://") || root_path.startsWith("https://")) { | |
return prioritize_base ? base_url : root_path; | |
} | |
return base_url + root_path; | |
} | |
function determine_protocol(endpoint) { | |
if (endpoint.startsWith("http")) { | |
const { protocol, host } = new URL(endpoint); | |
if (host.endsWith("hf.space")) { | |
return { | |
ws_protocol: "wss", | |
host, | |
http_protocol: protocol | |
}; | |
} | |
return { | |
ws_protocol: protocol === "https:" ? "wss" : "ws", | |
http_protocol: protocol, | |
host | |
}; | |
} else if (endpoint.startsWith("file:")) { | |
return { | |
ws_protocol: "ws", | |
http_protocol: "http:", | |
host: "lite.local" | |
// Special fake hostname only used for this case. This matches the hostname allowed in `is_self_host()` in `js/wasm/network/host.ts`. | |
}; | |
} | |
return { | |
ws_protocol: "wss", | |
http_protocol: "https:", | |
host: endpoint | |
}; | |
} | |
const RE_SPACE_NAME = /^[^\/]*\/[^\/]*$/; | |
const RE_SPACE_DOMAIN = /.*hf\.space\/{0,1}$/; | |
async function process_endpoint(app_reference, token) { | |
const headers = {}; | |
if (token) { | |
headers.Authorization = `Bearer ${token}`; | |
} | |
const _app_reference = app_reference.trim(); | |
if (RE_SPACE_NAME.test(_app_reference)) { | |
try { | |
const res = await fetch( | |
`https://huggingface.co/api/spaces/${_app_reference}/host`, | |
{ headers } | |
); | |
if (res.status !== 200) | |
throw new Error("Space metadata could not be loaded."); | |
const _host = (await res.json()).host; | |
return { | |
space_id: app_reference, | |
...determine_protocol(_host) | |
}; | |
} catch (e) { | |
throw new Error("Space metadata could not be loaded." + e.message); | |
} | |
} | |
if (RE_SPACE_DOMAIN.test(_app_reference)) { | |
const { ws_protocol, http_protocol, host } = determine_protocol(_app_reference); | |
return { | |
space_id: host.replace(".hf.space", ""), | |
ws_protocol, | |
http_protocol, | |
host | |
}; | |
} | |
return { | |
space_id: false, | |
...determine_protocol(_app_reference) | |
}; | |
} | |
function map_names_to_ids(fns) { | |
let apis = {}; | |
fns.forEach(({ api_name }, i) => { | |
if (api_name) | |
apis[api_name] = i; | |
}); | |
return apis; | |
} | |
const RE_DISABLED_DISCUSSION = /^(?=[^]*\b[dD]iscussions{0,1}\b)(?=[^]*\b[dD]isabled\b)[^]*$/; | |
async function discussions_enabled(space_id) { | |
try { | |
const r = await fetch( | |
`https://huggingface.co/api/spaces/${space_id}/discussions`, | |
{ | |
method: "HEAD" | |
} | |
); | |
const error = r.headers.get("x-error-message"); | |
if (error && RE_DISABLED_DISCUSSION.test(error)) | |
return false; | |
return true; | |
} catch (e) { | |
return false; | |
} | |
} | |
async function get_space_hardware(space_id, token) { | |
const headers = {}; | |
if (token) { | |
headers.Authorization = `Bearer ${token}`; | |
} | |
try { | |
const res = await fetch( | |
`https://huggingface.co/api/spaces/${space_id}/runtime`, | |
{ headers } | |
); | |
if (res.status !== 200) | |
throw new Error("Space hardware could not be obtained."); | |
const { hardware } = await res.json(); | |
return hardware; | |
} catch (e) { | |
throw new Error(e.message); | |
} | |
} | |
async function set_space_hardware(space_id, new_hardware, token) { | |
const headers = {}; | |
if (token) { | |
headers.Authorization = `Bearer ${token}`; | |
} | |
try { | |
const res = await fetch( | |
`https://huggingface.co/api/spaces/${space_id}/hardware`, | |
{ headers, body: JSON.stringify(new_hardware) } | |
); | |
if (res.status !== 200) | |
throw new Error( | |
"Space hardware could not be set. Please ensure the space hardware provided is valid and that a Hugging Face token is passed in." | |
); | |
const { hardware } = await res.json(); | |
return hardware; | |
} catch (e) { | |
throw new Error(e.message); | |
} | |
} | |
async function set_space_timeout(space_id, timeout, token) { | |
const headers = {}; | |
if (token) { | |
headers.Authorization = `Bearer ${token}`; | |
} | |
try { | |
const res = await fetch( | |
`https://huggingface.co/api/spaces/${space_id}/hardware`, | |
{ headers, body: JSON.stringify({ seconds: timeout }) } | |
); | |
if (res.status !== 200) | |
throw new Error( | |
"Space hardware could not be set. Please ensure the space hardware provided is valid and that a Hugging Face token is passed in." | |
); | |
const { hardware } = await res.json(); | |
return hardware; | |
} catch (e) { | |
throw new Error(e.message); | |
} | |
} | |
const hardware_types = [ | |
"cpu-basic", | |
"cpu-upgrade", | |
"t4-small", | |
"t4-medium", | |
"a10g-small", | |
"a10g-large", | |
"a100-large" | |
]; | |
function normalise_file(file, server_url, proxy_url) { | |
if (file == null) { | |
return null; | |
} | |
if (Array.isArray(file)) { | |
const normalized_file = []; | |
for (const x of file) { | |
if (x == null) { | |
normalized_file.push(null); | |
} else { | |
normalized_file.push(normalise_file(x, server_url, proxy_url)); | |
} | |
} | |
return normalized_file; | |
} | |
if (file.is_stream) { | |
if (proxy_url == null) { | |
return new FileData({ | |
...file, | |
url: server_url + "/stream/" + file.path | |
}); | |
} | |
return new FileData({ | |
...file, | |
url: "/proxy=" + proxy_url + "stream/" + file.path | |
}); | |
} | |
return new FileData({ | |
...file, | |
url: get_fetchable_url_or_file(file.path, server_url, proxy_url) | |
}); | |
} | |
function is_url(str) { | |
try { | |
const url = new URL(str); | |
return url.protocol === "http:" || url.protocol === "https:"; | |
} catch { | |
return false; | |
} | |
} | |
function get_fetchable_url_or_file(path, server_url, proxy_url) { | |
if (path == null) { | |
return proxy_url ? `/proxy=${proxy_url}file=` : `${server_url}/file=`; | |
} | |
if (is_url(path)) { | |
return path; | |
} | |
return proxy_url ? `/proxy=${proxy_url}file=${path}` : `${server_url}/file=${path}`; | |
} | |
async function upload(file_data, root, upload_id, upload_fn = upload_files) { | |
let files = (Array.isArray(file_data) ? file_data : [file_data]).map( | |
(file_data2) => file_data2.blob | |
); | |
return await Promise.all( | |
await upload_fn(root, files, void 0, upload_id).then( | |
async (response) => { | |
if (response.error) { | |
throw new Error(response.error); | |
} else { | |
if (response.files) { | |
return response.files.map((f, i) => { | |
const file = new FileData({ ...file_data[i], path: f }); | |
return normalise_file(file, root, null); | |
}); | |
} | |
return []; | |
} | |
} | |
) | |
); | |
} | |
async function prepare_files(files, is_stream) { | |
return files.map( | |
(f, i) => new FileData({ | |
path: f.name, | |
orig_name: f.name, | |
blob: f, | |
size: f.size, | |
mime_type: f.type, | |
is_stream | |
}) | |
); | |
} | |
class FileData { | |
constructor({ | |
path, | |
url, | |
orig_name, | |
size, | |
blob, | |
is_stream, | |
mime_type, | |
alt_text | |
}) { | |
this.path = path; | |
this.url = url; | |
this.orig_name = orig_name; | |
this.size = size; | |
this.blob = url ? void 0 : blob; | |
this.is_stream = is_stream; | |
this.mime_type = mime_type; | |
this.alt_text = alt_text; | |
} | |
} | |
const QUEUE_FULL_MSG = "This application is too busy. Keep trying!"; | |
const BROKEN_CONNECTION_MSG = "Connection errored out."; | |
let NodeBlob; | |
async function duplicate(app_reference, options) { | |
const { hf_token, private: _private, hardware, timeout } = options; | |
if (hardware && !hardware_types.includes(hardware)) { | |
throw new Error( | |
`Invalid hardware type provided. Valid types are: ${hardware_types.map((v) => `"${v}"`).join(",")}.` | |
); | |
} | |
const headers = { | |
Authorization: `Bearer ${hf_token}` | |
}; | |
const user = (await (await fetch(`https://huggingface.co/api/whoami-v2`, { | |
headers | |
})).json()).name; | |
const space_name = app_reference.split("/")[1]; | |
const body = { | |
repository: `${user}/${space_name}` | |
}; | |
if (_private) { | |
body.private = true; | |
} | |
try { | |
const response = await fetch( | |
`https://huggingface.co/api/spaces/${app_reference}/duplicate`, | |
{ | |
method: "POST", | |
headers: { "Content-Type": "application/json", ...headers }, | |
body: JSON.stringify(body) | |
} | |
); | |
if (response.status === 409) { | |
return client(`${user}/${space_name}`, options); | |
} | |
const duplicated_space = await response.json(); | |
let original_hardware; | |
if (!hardware) { | |
original_hardware = await get_space_hardware(app_reference, hf_token); | |
} | |
const requested_hardware = hardware || original_hardware || "cpu-basic"; | |
await set_space_hardware( | |
`${user}/${space_name}`, | |
requested_hardware, | |
hf_token | |
); | |
await set_space_timeout(`${user}/${space_name}`, timeout || 300, hf_token); | |
return client(duplicated_space.url, options); | |
} catch (e) { | |
throw new Error(e); | |
} | |
} | |
function api_factory(fetch_implementation, EventSource_factory) { | |
return { post_data: post_data2, upload_files: upload_files2, client: client2, handle_blob: handle_blob2 }; | |
async function post_data2(url, body, token) { | |
const headers = { "Content-Type": "application/json" }; | |
if (token) { | |
headers.Authorization = `Bearer ${token}`; | |
} | |
try { | |
var response = await fetch_implementation(url, { | |
method: "POST", | |
body: JSON.stringify(body), | |
headers | |
}); | |
} catch (e) { | |
return [{ error: BROKEN_CONNECTION_MSG }, 500]; | |
} | |
let output; | |
let status; | |
try { | |
output = await response.json(); | |
status = response.status; | |
} catch (e) { | |
output = { error: `Could not parse server response: ${e}` }; | |
status = 500; | |
} | |
return [output, status]; | |
} | |
async function upload_files2(root, files, token, upload_id) { | |
const headers = {}; | |
if (token) { | |
headers.Authorization = `Bearer ${token}`; | |
} | |
const chunkSize = 1e3; | |
const uploadResponses = []; | |
for (let i = 0; i < files.length; i += chunkSize) { | |
const chunk = files.slice(i, i + chunkSize); | |
const formData = new FormData(); | |
chunk.forEach((file) => { | |
formData.append("files", file); | |
}); | |
try { | |
const upload_url = upload_id ? `${root}/upload?upload_id=${upload_id}` : `${root}/upload`; | |
var response = await fetch_implementation(upload_url, { | |
method: "POST", | |
body: formData, | |
headers | |
}); | |
} catch (e) { | |
return { error: BROKEN_CONNECTION_MSG }; | |
} | |
const output = await response.json(); | |
uploadResponses.push(...output); | |
} | |
return { files: uploadResponses }; | |
} | |
async function client2(app_reference, options = { normalise_files: true }) { | |
return new Promise(async (res) => { | |
const { status_callback, hf_token, normalise_files } = options; | |
const return_obj = { | |
predict, | |
submit, | |
view_api, | |
component_server | |
}; | |
const transform_files = normalise_files ?? true; | |
if ((typeof window === "undefined" || !("WebSocket" in window)) && !global.Websocket) { | |
const ws = await import("./wrapper-6f348d45.js"); | |
NodeBlob = (await import("node:buffer")).Blob; | |
global.WebSocket = ws.WebSocket; | |
} | |
const { ws_protocol, http_protocol, host, space_id } = await process_endpoint(app_reference, hf_token); | |
const session_hash = Math.random().toString(36).substring(2); | |
const last_status = {}; | |
let stream_open = false; | |
let pending_stream_messages = {}; | |
let event_stream = null; | |
const event_callbacks = {}; | |
const unclosed_events = /* @__PURE__ */ new Set(); | |
let config; | |
let api_map = {}; | |
let jwt = false; | |
if (hf_token && space_id) { | |
jwt = await get_jwt(space_id, hf_token); | |
} | |
async function config_success(_config) { | |
config = _config; | |
api_map = map_names_to_ids((_config == null ? void 0 : _config.dependencies) || []); | |
if (config.auth_required) { | |
return { | |
config, | |
...return_obj | |
}; | |
} | |
try { | |
api = await view_api(config); | |
} catch (e) { | |
console.error(`Could not get api details: ${e.message}`); | |
} | |
return { | |
config, | |
...return_obj | |
}; | |
} | |
let api; | |
async function handle_space_sucess(status) { | |
if (status_callback) | |
status_callback(status); | |
if (status.status === "running") | |
try { | |
config = await resolve_config( | |
fetch_implementation, | |
`${http_protocol}//${host}`, | |
hf_token | |
); | |
const _config = await config_success(config); | |
res(_config); | |
} catch (e) { | |
console.error(e); | |
if (status_callback) { | |
status_callback({ | |
status: "error", | |
message: "Could not load this space.", | |
load_status: "error", | |
detail: "NOT_FOUND" | |
}); | |
} | |
} | |
} | |
try { | |
config = await resolve_config( | |
fetch_implementation, | |
`${http_protocol}//${host}`, | |
hf_token | |
); | |
const _config = await config_success(config); | |
res(_config); | |
} catch (e) { | |
console.error(e); | |
if (space_id) { | |
check_space_status( | |
space_id, | |
RE_SPACE_NAME.test(space_id) ? "space_name" : "subdomain", | |
handle_space_sucess | |
); | |
} else { | |
if (status_callback) | |
status_callback({ | |
status: "error", | |
message: "Could not load this space.", | |
load_status: "error", | |
detail: "NOT_FOUND" | |
}); | |
} | |
} | |
function predict(endpoint, data, event_data) { | |
let data_returned = false; | |
let status_complete = false; | |
let dependency; | |
if (typeof endpoint === "number") { | |
dependency = config.dependencies[endpoint]; | |
} else { | |
const trimmed_endpoint = endpoint.replace(/^\//, ""); | |
dependency = config.dependencies[api_map[trimmed_endpoint]]; | |
} | |
if (dependency.types.continuous) { | |
throw new Error( | |
"Cannot call predict on this function as it may run forever. Use submit instead" | |
); | |
} | |
return new Promise((res2, rej) => { | |
const app = submit(endpoint, data, event_data); | |
let result; | |
app.on("data", (d) => { | |
if (status_complete) { | |
app.destroy(); | |
res2(d); | |
} | |
data_returned = true; | |
result = d; | |
}).on("status", (status) => { | |
if (status.stage === "error") | |
rej(status); | |
if (status.stage === "complete") { | |
status_complete = true; | |
if (data_returned) { | |
app.destroy(); | |
res2(result); | |
} | |
} | |
}); | |
}); | |
} | |
function submit(endpoint, data, event_data, trigger_id = null) { | |
let fn_index; | |
let api_info; | |
if (typeof endpoint === "number") { | |
fn_index = endpoint; | |
api_info = api.unnamed_endpoints[fn_index]; | |
} else { | |
const trimmed_endpoint = endpoint.replace(/^\//, ""); | |
fn_index = api_map[trimmed_endpoint]; | |
api_info = api.named_endpoints[endpoint.trim()]; | |
} | |
if (typeof fn_index !== "number") { | |
throw new Error( | |
"There is no endpoint matching that name of fn_index matching that number." | |
); | |
} | |
let websocket; | |
let eventSource; | |
let protocol = config.protocol ?? "ws"; | |
const _endpoint = typeof endpoint === "number" ? "/predict" : endpoint; | |
let payload; | |
let event_id = null; | |
let complete = false; | |
const listener_map = {}; | |
let url_params = ""; | |
if (typeof window !== "undefined") { | |
url_params = new URLSearchParams(window.location.search).toString(); | |
} | |
handle_blob2(`${config.root}`, data, api_info, hf_token).then( | |
(_payload) => { | |
payload = { | |
data: _payload || [], | |
event_data, | |
fn_index, | |
trigger_id | |
}; | |
if (skip_queue(fn_index, config)) { | |
fire_event({ | |
type: "status", | |
endpoint: _endpoint, | |
stage: "pending", | |
queue: false, | |
fn_index, | |
time: /* @__PURE__ */ new Date() | |
}); | |
post_data2( | |
`${config.root}/run${_endpoint.startsWith("/") ? _endpoint : `/${_endpoint}`}${url_params ? "?" + url_params : ""}`, | |
{ | |
...payload, | |
session_hash | |
}, | |
hf_token | |
).then(([output, status_code]) => { | |
const data2 = transform_files ? transform_output( | |
output.data, | |
api_info, | |
config.root, | |
config.root_url | |
) : output.data; | |
if (status_code == 200) { | |
fire_event({ | |
type: "data", | |
endpoint: _endpoint, | |
fn_index, | |
data: data2, | |
time: /* @__PURE__ */ new Date() | |
}); | |
fire_event({ | |
type: "status", | |
endpoint: _endpoint, | |
fn_index, | |
stage: "complete", | |
eta: output.average_duration, | |
queue: false, | |
time: /* @__PURE__ */ new Date() | |
}); | |
} else { | |
fire_event({ | |
type: "status", | |
stage: "error", | |
endpoint: _endpoint, | |
fn_index, | |
message: output.error, | |
queue: false, | |
time: /* @__PURE__ */ new Date() | |
}); | |
} | |
}).catch((e) => { | |
fire_event({ | |
type: "status", | |
stage: "error", | |
message: e.message, | |
endpoint: _endpoint, | |
fn_index, | |
queue: false, | |
time: /* @__PURE__ */ new Date() | |
}); | |
}); | |
} else if (protocol == "ws") { | |
fire_event({ | |
type: "status", | |
stage: "pending", | |
queue: true, | |
endpoint: _endpoint, | |
fn_index, | |
time: /* @__PURE__ */ new Date() | |
}); | |
let url = new URL(`${ws_protocol}://${resolve_root( | |
host, | |
config.path, | |
true | |
)} | |
/queue/join${url_params ? "?" + url_params : ""}`); | |
if (jwt) { | |
url.searchParams.set("__sign", jwt); | |
} | |
websocket = new WebSocket(url); | |
websocket.onclose = (evt) => { | |
if (!evt.wasClean) { | |
fire_event({ | |
type: "status", | |
stage: "error", | |
broken: true, | |
message: BROKEN_CONNECTION_MSG, | |
queue: true, | |
endpoint: _endpoint, | |
fn_index, | |
time: /* @__PURE__ */ new Date() | |
}); | |
} | |
}; | |
websocket.onmessage = function(event) { | |
const _data = JSON.parse(event.data); | |
const { type, status, data: data2 } = handle_message( | |
_data, | |
last_status[fn_index] | |
); | |
if (type === "update" && status && !complete) { | |
fire_event({ | |
type: "status", | |
endpoint: _endpoint, | |
fn_index, | |
time: /* @__PURE__ */ new Date(), | |
...status | |
}); | |
if (status.stage === "error") { | |
websocket.close(); | |
} | |
} else if (type === "hash") { | |
websocket.send(JSON.stringify({ fn_index, session_hash })); | |
return; | |
} else if (type === "data") { | |
websocket.send(JSON.stringify({ ...payload, session_hash })); | |
} else if (type === "complete") { | |
complete = status; | |
} else if (type === "log") { | |
fire_event({ | |
type: "log", | |
log: data2.log, | |
level: data2.level, | |
endpoint: _endpoint, | |
fn_index | |
}); | |
} else if (type === "generating") { | |
fire_event({ | |
type: "status", | |
time: /* @__PURE__ */ new Date(), | |
...status, | |
stage: status == null ? void 0 : status.stage, | |
queue: true, | |
endpoint: _endpoint, | |
fn_index | |
}); | |
} | |
if (data2) { | |
fire_event({ | |
type: "data", | |
time: /* @__PURE__ */ new Date(), | |
data: transform_files ? transform_output( | |
data2.data, | |
api_info, | |
config.root, | |
config.root_url | |
) : data2.data, | |
endpoint: _endpoint, | |
fn_index | |
}); | |
if (complete) { | |
fire_event({ | |
type: "status", | |
time: /* @__PURE__ */ new Date(), | |
...complete, | |
stage: status == null ? void 0 : status.stage, | |
queue: true, | |
endpoint: _endpoint, | |
fn_index | |
}); | |
websocket.close(); | |
} | |
} | |
}; | |
if (semiver(config.version || "2.0.0", "3.6") < 0) { | |
addEventListener( | |
"open", | |
() => websocket.send(JSON.stringify({ hash: session_hash })) | |
); | |
} | |
} else if (protocol == "sse") { | |
fire_event({ | |
type: "status", | |
stage: "pending", | |
queue: true, | |
endpoint: _endpoint, | |
fn_index, | |
time: /* @__PURE__ */ new Date() | |
}); | |
var params = new URLSearchParams({ | |
fn_index: fn_index.toString(), | |
session_hash | |
}).toString(); | |
let url = new URL( | |
`${config.root}/queue/join?${url_params ? url_params + "&" : ""}${params}` | |
); | |
eventSource = EventSource_factory(url.toString()); | |
eventSource.onmessage = async function(event) { | |
const _data = JSON.parse(event.data); | |
const { type, status, data: data2 } = handle_message( | |
_data, | |
last_status[fn_index] | |
); | |
if (type === "update" && status && !complete) { | |
fire_event({ | |
type: "status", | |
endpoint: _endpoint, | |
fn_index, | |
time: /* @__PURE__ */ new Date(), | |
...status | |
}); | |
if (status.stage === "error") { | |
eventSource.close(); | |
} | |
} else if (type === "data") { | |
event_id = _data.event_id; | |
let [_, status2] = await post_data2( | |
`${config.root}/queue/data`, | |
{ | |
...payload, | |
session_hash, | |
event_id | |
}, | |
hf_token | |
); | |
if (status2 !== 200) { | |
fire_event({ | |
type: "status", | |
stage: "error", | |
message: BROKEN_CONNECTION_MSG, | |
queue: true, | |
endpoint: _endpoint, | |
fn_index, | |
time: /* @__PURE__ */ new Date() | |
}); | |
eventSource.close(); | |
} | |
} else if (type === "complete") { | |
complete = status; | |
} else if (type === "log") { | |
fire_event({ | |
type: "log", | |
log: data2.log, | |
level: data2.level, | |
endpoint: _endpoint, | |
fn_index | |
}); | |
} else if (type === "generating") { | |
fire_event({ | |
type: "status", | |
time: /* @__PURE__ */ new Date(), | |
...status, | |
stage: status == null ? void 0 : status.stage, | |
queue: true, | |
endpoint: _endpoint, | |
fn_index | |
}); | |
} | |
if (data2) { | |
fire_event({ | |
type: "data", | |
time: /* @__PURE__ */ new Date(), | |
data: transform_files ? transform_output( | |
data2.data, | |
api_info, | |
config.root, | |
config.root_url | |
) : data2.data, | |
endpoint: _endpoint, | |
fn_index | |
}); | |
if (complete) { | |
fire_event({ | |
type: "status", | |
time: /* @__PURE__ */ new Date(), | |
...complete, | |
stage: status == null ? void 0 : status.stage, | |
queue: true, | |
endpoint: _endpoint, | |
fn_index | |
}); | |
eventSource.close(); | |
} | |
} | |
}; | |
} else if (protocol == "sse_v1") { | |
fire_event({ | |
type: "status", | |
stage: "pending", | |
queue: true, | |
endpoint: _endpoint, | |
fn_index, | |
time: /* @__PURE__ */ new Date() | |
}); | |
post_data2( | |
`${config.root}/queue/join?${url_params}`, | |
{ | |
...payload, | |
session_hash | |
}, | |
hf_token | |
).then(([response, status]) => { | |
if (status === 503) { | |
fire_event({ | |
type: "status", | |
stage: "error", | |
message: QUEUE_FULL_MSG, | |
queue: true, | |
endpoint: _endpoint, | |
fn_index, | |
time: /* @__PURE__ */ new Date() | |
}); | |
} else if (status !== 200) { | |
fire_event({ | |
type: "status", | |
stage: "error", | |
message: BROKEN_CONNECTION_MSG, | |
queue: true, | |
endpoint: _endpoint, | |
fn_index, | |
time: /* @__PURE__ */ new Date() | |
}); | |
} else { | |
event_id = response.event_id; | |
let callback = async function(_data) { | |
try { | |
const { type, status: status2, data: data2 } = handle_message( | |
_data, | |
last_status[fn_index] | |
); | |
if (type == "heartbeat") { | |
return; | |
} | |
if (type === "update" && status2 && !complete) { | |
fire_event({ | |
type: "status", | |
endpoint: _endpoint, | |
fn_index, | |
time: /* @__PURE__ */ new Date(), | |
...status2 | |
}); | |
} else if (type === "complete") { | |
complete = status2; | |
} else if (type == "unexpected_error") { | |
console.error("Unexpected error", status2 == null ? void 0 : status2.message); | |
fire_event({ | |
type: "status", | |
stage: "error", | |
message: (status2 == null ? void 0 : status2.message) || "An Unexpected Error Occurred!", | |
queue: true, | |
endpoint: _endpoint, | |
fn_index, | |
time: /* @__PURE__ */ new Date() | |
}); | |
} else if (type === "log") { | |
fire_event({ | |
type: "log", | |
log: data2.log, | |
level: data2.level, | |
endpoint: _endpoint, | |
fn_index | |
}); | |
return; | |
} else if (type === "generating") { | |
fire_event({ | |
type: "status", | |
time: /* @__PURE__ */ new Date(), | |
...status2, | |
stage: status2 == null ? void 0 : status2.stage, | |
queue: true, | |
endpoint: _endpoint, | |
fn_index | |
}); | |
} | |
if (data2) { | |
fire_event({ | |
type: "data", | |
time: /* @__PURE__ */ new Date(), | |
data: transform_files ? transform_output( | |
data2.data, | |
api_info, | |
config.root, | |
config.root_url | |
) : data2.data, | |
endpoint: _endpoint, | |
fn_index | |
}); | |
if (complete) { | |
fire_event({ | |
type: "status", | |
time: /* @__PURE__ */ new Date(), | |
...complete, | |
stage: status2 == null ? void 0 : status2.stage, | |
queue: true, | |
endpoint: _endpoint, | |
fn_index | |
}); | |
} | |
} | |
if ((status2 == null ? void 0 : status2.stage) === "complete" || (status2 == null ? void 0 : status2.stage) === "error") { | |
if (event_callbacks[event_id]) { | |
delete event_callbacks[event_id]; | |
} | |
} | |
} catch (e) { | |
console.error("Unexpected client exception", e); | |
fire_event({ | |
type: "status", | |
stage: "error", | |
message: "An Unexpected Error Occurred!", | |
queue: true, | |
endpoint: _endpoint, | |
fn_index, | |
time: /* @__PURE__ */ new Date() | |
}); | |
close_stream(); | |
} | |
}; | |
if (event_id in pending_stream_messages) { | |
pending_stream_messages[event_id].forEach( | |
(msg) => callback(msg) | |
); | |
delete pending_stream_messages[event_id]; | |
} | |
event_callbacks[event_id] = callback; | |
unclosed_events.add(event_id); | |
if (!stream_open) { | |
open_stream(); | |
} | |
} | |
}); | |
} | |
} | |
); | |
function fire_event(event) { | |
const narrowed_listener_map = listener_map; | |
const listeners = narrowed_listener_map[event.type] || []; | |
listeners == null ? void 0 : listeners.forEach((l) => l(event)); | |
} | |
function on(eventType, listener) { | |
const narrowed_listener_map = listener_map; | |
const listeners = narrowed_listener_map[eventType] || []; | |
narrowed_listener_map[eventType] = listeners; | |
listeners == null ? void 0 : listeners.push(listener); | |
return { on, off, cancel, destroy }; | |
} | |
function off(eventType, listener) { | |
const narrowed_listener_map = listener_map; | |
let listeners = narrowed_listener_map[eventType] || []; | |
listeners = listeners == null ? void 0 : listeners.filter((l) => l !== listener); | |
narrowed_listener_map[eventType] = listeners; | |
return { on, off, cancel, destroy }; | |
} | |
async function cancel() { | |
const _status = { | |
stage: "complete", | |
queue: false, | |
time: /* @__PURE__ */ new Date() | |
}; | |
complete = _status; | |
fire_event({ | |
..._status, | |
type: "status", | |
endpoint: _endpoint, | |
fn_index | |
}); | |
let cancel_request = {}; | |
if (protocol === "ws") { | |
if (websocket && websocket.readyState === 0) { | |
websocket.addEventListener("open", () => { | |
websocket.close(); | |
}); | |
} else { | |
websocket.close(); | |
} | |
cancel_request = { fn_index, session_hash }; | |
} else { | |
eventSource.close(); | |
cancel_request = { event_id }; | |
} | |
try { | |
await fetch_implementation(`${config.root}/reset`, { | |
headers: { "Content-Type": "application/json" }, | |
method: "POST", | |
body: JSON.stringify(cancel_request) | |
}); | |
} catch (e) { | |
console.warn( | |
"The `/reset` endpoint could not be called. Subsequent endpoint results may be unreliable." | |
); | |
} | |
} | |
function destroy() { | |
for (const event_type in listener_map) { | |
listener_map[event_type].forEach((fn2) => { | |
off(event_type, fn2); | |
}); | |
} | |
} | |
return { | |
on, | |
off, | |
cancel, | |
destroy | |
}; | |
} | |
function open_stream() { | |
stream_open = true; | |
let params = new URLSearchParams({ | |
session_hash | |
}).toString(); | |
let url = new URL(`${config.root}/queue/data?${params}`); | |
event_stream = EventSource_factory(url.toString()); | |
event_stream.onmessage = async function(event) { | |
let _data = JSON.parse(event.data); | |
const event_id = _data.event_id; | |
if (!event_id) { | |
await Promise.all( | |
Object.keys(event_callbacks).map( | |
(event_id2) => event_callbacks[event_id2](_data) | |
) | |
); | |
} else if (event_callbacks[event_id]) { | |
if (_data.msg === "process_completed") { | |
unclosed_events.delete(event_id); | |
if (unclosed_events.size === 0) { | |
close_stream(); | |
} | |
} | |
let fn2 = event_callbacks[event_id]; | |
if (typeof window !== "undefined") { | |
window.setTimeout(fn2, 0, _data); | |
} else { | |
setTimeout(fn2, 0, _data); | |
} | |
} else { | |
if (!pending_stream_messages[event_id]) { | |
pending_stream_messages[event_id] = []; | |
} | |
pending_stream_messages[event_id].push(_data); | |
} | |
}; | |
event_stream.onerror = async function(event) { | |
await Promise.all( | |
Object.keys(event_callbacks).map( | |
(event_id) => event_callbacks[event_id]({ | |
msg: "unexpected_error", | |
message: BROKEN_CONNECTION_MSG | |
}) | |
) | |
); | |
close_stream(); | |
}; | |
} | |
function close_stream() { | |
stream_open = false; | |
event_stream == null ? void 0 : event_stream.close(); | |
} | |
async function component_server(component_id, fn_name, data) { | |
var _a; | |
const headers = { "Content-Type": "application/json" }; | |
if (hf_token) { | |
headers.Authorization = `Bearer ${hf_token}`; | |
} | |
let root_url; | |
let component = config.components.find( | |
(comp) => comp.id === component_id | |
); | |
if ((_a = component == null ? void 0 : component.props) == null ? void 0 : _a.root_url) { | |
root_url = component.props.root_url; | |
} else { | |
root_url = config.root; | |
} | |
const response = await fetch_implementation( | |
`${root_url}/component_server/`, | |
{ | |
method: "POST", | |
body: JSON.stringify({ | |
data, | |
component_id, | |
fn_name, | |
session_hash | |
}), | |
headers | |
} | |
); | |
if (!response.ok) { | |
throw new Error( | |
"Could not connect to component server: " + response.statusText | |
); | |
} | |
const output = await response.json(); | |
return output; | |
} | |
async function view_api(config2) { | |
if (api) | |
return api; | |
const headers = { "Content-Type": "application/json" }; | |
if (hf_token) { | |
headers.Authorization = `Bearer ${hf_token}`; | |
} | |
let response; | |
if (semiver(config2.version || "2.0.0", "3.30") < 0) { | |
response = await fetch_implementation( | |
"https://gradio-space-api-fetcher-v2.hf.space/api", | |
{ | |
method: "POST", | |
body: JSON.stringify({ | |
serialize: false, | |
config: JSON.stringify(config2) | |
}), | |
headers | |
} | |
); | |
} else { | |
response = await fetch_implementation(`${config2.root}/info`, { | |
headers | |
}); | |
} | |
if (!response.ok) { | |
throw new Error(BROKEN_CONNECTION_MSG); | |
} | |
let api_info = await response.json(); | |
if ("api" in api_info) { | |
api_info = api_info.api; | |
} | |
if (api_info.named_endpoints["/predict"] && !api_info.unnamed_endpoints["0"]) { | |
api_info.unnamed_endpoints[0] = api_info.named_endpoints["/predict"]; | |
} | |
const x = transform_api_info(api_info, config2, api_map); | |
return x; | |
} | |
}); | |
} | |
async function handle_blob2(endpoint, data, api_info, token) { | |
const blob_refs = await walk_and_store_blobs( | |
data, | |
void 0, | |
[], | |
true, | |
api_info | |
); | |
return Promise.all( | |
blob_refs.map(async ({ path, blob, type }) => { | |
if (blob) { | |
const file_url = (await upload_files2(endpoint, [blob], token)).files[0]; | |
return { path, file_url, type, name: blob == null ? void 0 : blob.name }; | |
} | |
return { path, type }; | |
}) | |
).then((r) => { | |
r.forEach(({ path, file_url, type, name }) => { | |
if (type === "Gallery") { | |
update_object(data, file_url, path); | |
} else if (file_url) { | |
const file = new FileData({ path: file_url, orig_name: name }); | |
update_object(data, file, path); | |
} | |
}); | |
return data; | |
}); | |
} | |
} | |
const { post_data, upload_files, client, handle_blob } = api_factory( | |
fetch, | |
(...args) => new EventSource(...args) | |
); | |
function transform_output(data, api_info, root_url, remote_url) { | |
return data.map((d, i) => { | |
var _a, _b, _c, _d; | |
if (((_b = (_a = api_info == null ? void 0 : api_info.returns) == null ? void 0 : _a[i]) == null ? void 0 : _b.component) === "File") { | |
return normalise_file(d, root_url, remote_url); | |
} else if (((_d = (_c = api_info == null ? void 0 : api_info.returns) == null ? void 0 : _c[i]) == null ? void 0 : _d.component) === "Gallery") { | |
return d.map((img) => { | |
return Array.isArray(img) ? [normalise_file(img[0], root_url, remote_url), img[1]] : [normalise_file(img, root_url, remote_url), null]; | |
}); | |
} else if (typeof d === "object" && d.path) { | |
return normalise_file(d, root_url, remote_url); | |
} | |
return d; | |
}); | |
} | |
function get_type(type, component, serializer, signature_type) { | |
switch (type.type) { | |
case "string": | |
return "string"; | |
case "boolean": | |
return "boolean"; | |
case "number": | |
return "number"; | |
} | |
if (serializer === "JSONSerializable" || serializer === "StringSerializable") { | |
return "any"; | |
} else if (serializer === "ListStringSerializable") { | |
return "string[]"; | |
} else if (component === "Image") { | |
return signature_type === "parameter" ? "Blob | File | Buffer" : "string"; | |
} else if (serializer === "FileSerializable") { | |
if ((type == null ? void 0 : type.type) === "array") { | |
return signature_type === "parameter" ? "(Blob | File | Buffer)[]" : `{ name: string; data: string; size?: number; is_file?: boolean; orig_name?: string}[]`; | |
} | |
return signature_type === "parameter" ? "Blob | File | Buffer" : `{ name: string; data: string; size?: number; is_file?: boolean; orig_name?: string}`; | |
} else if (serializer === "GallerySerializable") { | |
return signature_type === "parameter" ? "[(Blob | File | Buffer), (string | null)][]" : `[{ name: string; data: string; size?: number; is_file?: boolean; orig_name?: string}, (string | null))][]`; | |
} | |
} | |
function get_description(type, serializer) { | |
if (serializer === "GallerySerializable") { | |
return "array of [file, label] tuples"; | |
} else if (serializer === "ListStringSerializable") { | |
return "array of strings"; | |
} else if (serializer === "FileSerializable") { | |
return "array of files or single file"; | |
} | |
return type.description; | |
} | |
function transform_api_info(api_info, config, api_map) { | |
const new_data = { | |
named_endpoints: {}, | |
unnamed_endpoints: {} | |
}; | |
for (const key in api_info) { | |
const cat = api_info[key]; | |
for (const endpoint in cat) { | |
const dep_index = config.dependencies[endpoint] ? endpoint : api_map[endpoint.replace("/", "")]; | |
const info = cat[endpoint]; | |
new_data[key][endpoint] = {}; | |
new_data[key][endpoint].parameters = {}; | |
new_data[key][endpoint].returns = {}; | |
new_data[key][endpoint].type = config.dependencies[dep_index].types; | |
new_data[key][endpoint].parameters = info.parameters.map( | |
({ label, component, type, serializer }) => ({ | |
label, | |
component, | |
type: get_type(type, component, serializer, "parameter"), | |
description: get_description(type, serializer) | |
}) | |
); | |
new_data[key][endpoint].returns = info.returns.map( | |
({ label, component, type, serializer }) => ({ | |
label, | |
component, | |
type: get_type(type, component, serializer, "return"), | |
description: get_description(type, serializer) | |
}) | |
); | |
} | |
} | |
return new_data; | |
} | |
async function get_jwt(space, token) { | |
try { | |
const r = await fetch(`https://huggingface.co/api/spaces/${space}/jwt`, { | |
headers: { | |
Authorization: `Bearer ${token}` | |
} | |
}); | |
const jwt = (await r.json()).token; | |
return jwt || false; | |
} catch (e) { | |
console.error(e); | |
return false; | |
} | |
} | |
function update_object(object, newValue, stack) { | |
while (stack.length > 1) { | |
object = object[stack.shift()]; | |
} | |
object[stack.shift()] = newValue; | |
} | |
async function walk_and_store_blobs(param, type = void 0, path = [], root = false, api_info = void 0) { | |
if (Array.isArray(param)) { | |
let blob_refs = []; | |
await Promise.all( | |
param.map(async (v, i) => { | |
var _a; | |
let new_path = path.slice(); | |
new_path.push(i); | |
const array_refs = await walk_and_store_blobs( | |
param[i], | |
root ? ((_a = api_info == null ? void 0 : api_info.parameters[i]) == null ? void 0 : _a.component) || void 0 : type, | |
new_path, | |
false, | |
api_info | |
); | |
blob_refs = blob_refs.concat(array_refs); | |
}) | |
); | |
return blob_refs; | |
} else if (globalThis.Buffer && param instanceof globalThis.Buffer) { | |
const is_image = type === "Image"; | |
return [ | |
{ | |
path, | |
blob: is_image ? false : new NodeBlob([param]), | |
type | |
} | |
]; | |
} else if (typeof param === "object") { | |
let blob_refs = []; | |
for (let key in param) { | |
if (param.hasOwnProperty(key)) { | |
let new_path = path.slice(); | |
new_path.push(key); | |
blob_refs = blob_refs.concat( | |
await walk_and_store_blobs( | |
param[key], | |
void 0, | |
new_path, | |
false, | |
api_info | |
) | |
); | |
} | |
} | |
return blob_refs; | |
} | |
return []; | |
} | |
function skip_queue(id, config) { | |
var _a, _b, _c, _d; | |
return !(((_b = (_a = config == null ? void 0 : config.dependencies) == null ? void 0 : _a[id]) == null ? void 0 : _b.queue) === null ? config.enable_queue : (_d = (_c = config == null ? void 0 : config.dependencies) == null ? void 0 : _c[id]) == null ? void 0 : _d.queue) || false; | |
} | |
async function resolve_config(fetch_implementation, endpoint, token) { | |
const headers = {}; | |
if (token) { | |
headers.Authorization = `Bearer ${token}`; | |
} | |
if (typeof window !== "undefined" && window.gradio_config && location.origin !== "http://localhost:9876" && !window.gradio_config.dev_mode) { | |
const path = window.gradio_config.root; | |
const config = window.gradio_config; | |
config.root = resolve_root(endpoint, config.root, false); | |
return { ...config, path }; | |
} else if (endpoint) { | |
let response = await fetch_implementation(`${endpoint}/config`, { | |
headers | |
}); | |
if (response.status === 200) { | |
const config = await response.json(); | |
config.path = config.path ?? ""; | |
config.root = endpoint; | |
return config; | |
} | |
throw new Error("Could not get config."); | |
} | |
throw new Error("No config or app endpoint found"); | |
} | |
async function check_space_status(id, type, status_callback) { | |
let endpoint = type === "subdomain" ? `https://huggingface.co/api/spaces/by-subdomain/${id}` : `https://huggingface.co/api/spaces/${id}`; | |
let response; | |
let _status; | |
try { | |
response = await fetch(endpoint); | |
_status = response.status; | |
if (_status !== 200) { | |
throw new Error(); | |
} | |
response = await response.json(); | |
} catch (e) { | |
status_callback({ | |
status: "error", | |
load_status: "error", | |
message: "Could not get space status", | |
detail: "NOT_FOUND" | |
}); | |
return; | |
} | |
if (!response || _status !== 200) | |
return; | |
const { | |
runtime: { stage }, | |
id: space_name | |
} = response; | |
switch (stage) { | |
case "STOPPED": | |
case "SLEEPING": | |
status_callback({ | |
status: "sleeping", | |
load_status: "pending", | |
message: "Space is asleep. Waking it up...", | |
detail: stage | |
}); | |
setTimeout(() => { | |
check_space_status(id, type, status_callback); | |
}, 1e3); | |
break; | |
case "PAUSED": | |
status_callback({ | |
status: "paused", | |
load_status: "error", | |
message: "This space has been paused by the author. If you would like to try this demo, consider duplicating the space.", | |
detail: stage, | |
discussions_enabled: await discussions_enabled(space_name) | |
}); | |
break; | |
case "RUNNING": | |
case "RUNNING_BUILDING": | |
status_callback({ | |
status: "running", | |
load_status: "complete", | |
message: "", | |
detail: stage | |
}); | |
break; | |
case "BUILDING": | |
status_callback({ | |
status: "building", | |
load_status: "pending", | |
message: "Space is building...", | |
detail: stage | |
}); | |
setTimeout(() => { | |
check_space_status(id, type, status_callback); | |
}, 1e3); | |
break; | |
default: | |
status_callback({ | |
status: "space_error", | |
load_status: "error", | |
message: "This space is experiencing an issue.", | |
detail: stage, | |
discussions_enabled: await discussions_enabled(space_name) | |
}); | |
break; | |
} | |
} | |
function handle_message(data, last_status) { | |
const queue = true; | |
switch (data.msg) { | |
case "send_data": | |
return { type: "data" }; | |
case "send_hash": | |
return { type: "hash" }; | |
case "queue_full": | |
return { | |
type: "update", | |
status: { | |
queue, | |
message: QUEUE_FULL_MSG, | |
stage: "error", | |
code: data.code, | |
success: data.success | |
} | |
}; | |
case "heartbeat": | |
return { | |
type: "heartbeat" | |
}; | |
case "unexpected_error": | |
return { | |
type: "unexpected_error", | |
status: { | |
queue, | |
message: data.message, | |
stage: "error", | |
success: false | |
} | |
}; | |
case "estimation": | |
return { | |
type: "update", | |
status: { | |
queue, | |
stage: last_status || "pending", | |
code: data.code, | |
size: data.queue_size, | |
position: data.rank, | |
eta: data.rank_eta, | |
success: data.success | |
} | |
}; | |
case "progress": | |
return { | |
type: "update", | |
status: { | |
queue, | |
stage: "pending", | |
code: data.code, | |
progress_data: data.progress_data, | |
success: data.success | |
} | |
}; | |
case "log": | |
return { type: "log", data }; | |
case "process_generating": | |
return { | |
type: "generating", | |
status: { | |
queue, | |
message: !data.success ? data.output.error : null, | |
stage: data.success ? "generating" : "error", | |
code: data.code, | |
progress_data: data.progress_data, | |
eta: data.average_duration | |
}, | |
data: data.success ? data.output : null | |
}; | |
case "process_completed": | |
if ("error" in data.output) { | |
return { | |
type: "update", | |
status: { | |
queue, | |
message: data.output.error, | |
stage: "error", | |
code: data.code, | |
success: data.success | |
} | |
}; | |
} | |
return { | |
type: "complete", | |
status: { | |
queue, | |
message: !data.success ? data.output.error : void 0, | |
stage: data.success ? "complete" : "error", | |
code: data.code, | |
progress_data: data.progress_data | |
}, | |
data: data.success ? data.output : null | |
}; | |
case "process_starts": | |
return { | |
type: "update", | |
status: { | |
queue, | |
stage: "pending", | |
code: data.code, | |
size: data.rank, | |
position: 0, | |
success: data.success, | |
eta: data.eta | |
} | |
}; | |
} | |
return { type: "none", status: { stage: "error", queue } }; | |
} | |
export { | |
FileData, | |
api_factory, | |
client, | |
duplicate, | |
get_fetchable_url_or_file, | |
normalise_file, | |
post_data, | |
prepare_files, | |
upload, | |
upload_files | |
}; | |