@gradio/client 2.3.1 → 2.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +15 -0
- package/README.md +4 -2
- package/dist/__vite-browser-external-Cgmn0awE.js +7 -0
- package/dist/browser.js +1672 -1913
- package/dist/client.d.ts +5 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/constants.d.ts +4 -0
- package/dist/constants.d.ts.map +1 -1
- package/dist/helpers/api_info.d.ts.map +1 -1
- package/dist/helpers/data.d.ts.map +1 -1
- package/dist/helpers/init_helpers.d.ts +11 -2
- package/dist/helpers/init_helpers.d.ts.map +1 -1
- package/dist/index.cjs +2274 -0
- package/dist/index.js +2154 -2452
- package/dist/index.min.js +1735 -0
- package/dist/types.d.ts +15 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/duplicate.d.ts.map +1 -1
- package/dist/utils/predict.d.ts.map +1 -1
- package/dist/utils/stream.d.ts.map +1 -1
- package/dist/utils/submit.d.ts.map +1 -1
- package/package.json +11 -4
- package/scripts/verify_dist.mjs +46 -0
- package/src/client.ts +44 -5
- package/src/constants.ts +10 -0
- package/src/helpers/api_info.ts +46 -19
- package/src/helpers/data.ts +8 -3
- package/src/helpers/init_helpers.ts +54 -7
- package/src/test/api_info.test.ts +77 -12
- package/src/test/data.test.ts +26 -3
- package/src/test/init.test.ts +21 -3
- package/src/test/init_helpers.test.ts +67 -1
- package/src/test/refresh.test.ts +31 -0
- package/src/test/submit.test.ts +1 -3
- package/src/types.ts +15 -0
- package/src/utils/duplicate.ts +2 -0
- package/src/utils/predict.ts +14 -1
- package/src/utils/stream.ts +17 -4
- package/src/utils/submit.ts +43 -10
- package/vite.config.ts +20 -4
- package/dist/__vite-browser-external-DYxpcVy9.js +0 -4
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,2274 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
//#region src/constants.ts
|
|
3
|
+
var HOST_URL = `host`;
|
|
4
|
+
var SSE_URL = `queue/data`;
|
|
5
|
+
var SSE_DATA_URL = `queue/join`;
|
|
6
|
+
var UPLOAD_URL = `upload`;
|
|
7
|
+
var LOGIN_URL = `login`;
|
|
8
|
+
var CONFIG_URL = `config`;
|
|
9
|
+
var API_INFO_URL = `info`;
|
|
10
|
+
var RUNTIME_URL = `runtime`;
|
|
11
|
+
var SLEEPTIME_URL = `sleeptime`;
|
|
12
|
+
var HEARTBEAT_URL = `heartbeat`;
|
|
13
|
+
var COMPONENT_SERVER_URL = `component_server`;
|
|
14
|
+
var RESET_URL = `reset`;
|
|
15
|
+
var CANCEL_URL = `cancel`;
|
|
16
|
+
var APP_ID_URL = `app_id`;
|
|
17
|
+
var QUEUE_FULL_MSG = "This application is currently busy. Please try again. ";
|
|
18
|
+
var BROKEN_CONNECTION_MSG = "Connection errored out. ";
|
|
19
|
+
var CONFIG_ERROR_MSG = "Could not resolve app config. ";
|
|
20
|
+
var SPACE_STATUS_ERROR_MSG = "Could not get space status. ";
|
|
21
|
+
var API_INFO_ERROR_MSG = "Could not get API info. ";
|
|
22
|
+
var SPACE_METADATA_ERROR_MSG = "Space metadata could not be loaded. ";
|
|
23
|
+
var INVALID_URL_MSG = "Invalid URL. A full URL path is required.";
|
|
24
|
+
var UNAUTHORIZED_MSG = "Not authorized to access this space. ";
|
|
25
|
+
var INVALID_CREDENTIALS_MSG = "Invalid credentials. Could not login. ";
|
|
26
|
+
var MISSING_CREDENTIALS_MSG = "Login credentials are required to access this space.";
|
|
27
|
+
var PRIVATE_SPACE_MSG = "Could not access this app (received a 401 response). If it is a private Hugging Face Space, pass a valid Hugging Face token to the `token` option of `Client.connect`. You can generate a token at https://huggingface.co/settings/tokens.";
|
|
28
|
+
var SPACE_NOT_FOUND_MSG = (space, status) => `Space "${space}" could not be accessed (received a ${status} response from the Hugging Face API). Check that the Space name is spelled correctly and that the Space exists. If the Space is private, pass a valid Hugging Face token to the \`token\` option of \`Client.connect\`. You can generate a token at https://huggingface.co/settings/tokens.`;
|
|
29
|
+
var NO_API_INFO_MSG = "No API information is available for this app. This can happen when the app's `/info` endpoint cannot be reached, or when the app is running a legacy version of Gradio that is not supported by this client. ";
|
|
30
|
+
var WS_PROTOCOL_MSG = "This app appears to be running a legacy version of Gradio (3.x or earlier) that communicates over WebSockets, which is not supported by this version of @gradio/client. Please upgrade the app to a newer version of Gradio, or connect to it with @gradio/client version 0.x.";
|
|
31
|
+
var NODEJS_FS_ERROR_MSG = "File system access is only available in Node.js environments";
|
|
32
|
+
var ROOT_URL_ERROR_MSG = "Root URL not found in client config";
|
|
33
|
+
var FILE_PROCESSING_ERROR_MSG = "Error uploading file";
|
|
34
|
+
//#endregion
|
|
35
|
+
//#region src/helpers/init_helpers.ts
|
|
36
|
+
async function get_jwt(space, token, cookies) {
|
|
37
|
+
try {
|
|
38
|
+
return (await (await fetch(`https://huggingface.co/api/spaces/${space}/jwt`, { headers: {
|
|
39
|
+
Authorization: `Bearer ${token}`,
|
|
40
|
+
...cookies ? { Cookie: cookies } : {}
|
|
41
|
+
} })).json()).token || false;
|
|
42
|
+
} catch (e) {
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* The `hf_token` option was renamed to `token`, but a lot of existing code
|
|
48
|
+
* (and the Python client) still uses `hf_token`. Accept it as an alias so
|
|
49
|
+
* that authenticated requests are not silently sent without credentials,
|
|
50
|
+
* which previously surfaced as "Could not resolve app config" errors when
|
|
51
|
+
* connecting to private Spaces.
|
|
52
|
+
*/
|
|
53
|
+
function normalise_token_option(options) {
|
|
54
|
+
if (options.hf_token && !options.token) {
|
|
55
|
+
options.token = options.hf_token;
|
|
56
|
+
console.warn("The `hf_token` option has been renamed to `token`. Support for `hf_token` will be removed in a future version of @gradio/client.");
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
function map_names_to_ids(fns) {
|
|
60
|
+
let apis = {};
|
|
61
|
+
fns.forEach(({ api_name, id }) => {
|
|
62
|
+
if (api_name) apis[api_name] = id;
|
|
63
|
+
});
|
|
64
|
+
return apis;
|
|
65
|
+
}
|
|
66
|
+
function resolve_config_root(root, current_location) {
|
|
67
|
+
const root_url = new URL(root, current_location);
|
|
68
|
+
const current_url = new URL(current_location);
|
|
69
|
+
if (root_url.hostname !== current_url.hostname) return root;
|
|
70
|
+
root_url.protocol = current_url.protocol;
|
|
71
|
+
root_url.host = current_url.host;
|
|
72
|
+
return root_url.toString().replace(/\/$/, "");
|
|
73
|
+
}
|
|
74
|
+
async function resolve_config(endpoint, strip_current_page = true) {
|
|
75
|
+
const headers = this.options.token ? { Authorization: `Bearer ${this.options.token}` } : {};
|
|
76
|
+
if (typeof window !== "undefined" && window.gradio_config && location.origin !== "http://localhost:9876") {
|
|
77
|
+
if (strip_current_page && window.gradio_config.current_page) endpoint = endpoint.substring(0, endpoint.lastIndexOf("/"));
|
|
78
|
+
if (window.gradio_config.dev_mode || typeof window !== "undefined" && window?.BUILD_MODE === "dev") {
|
|
79
|
+
let config_url = join_urls(endpoint, this.deep_link ? CONFIG_URL + "?deep_link=" + this.deep_link : CONFIG_URL);
|
|
80
|
+
const config = await handleConfigResponse(await this.fetch(config_url, {
|
|
81
|
+
headers,
|
|
82
|
+
credentials: this.options.credentials ?? "same-origin"
|
|
83
|
+
}), !!this.options.auth);
|
|
84
|
+
config.root = endpoint || config.root;
|
|
85
|
+
window.gradio_config = {
|
|
86
|
+
...config,
|
|
87
|
+
current_page: window.gradio_config.current_page
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
const config = { ...window.gradio_config };
|
|
91
|
+
config.root = resolve_config_root(config.root, location.href);
|
|
92
|
+
return config;
|
|
93
|
+
} else if (endpoint) {
|
|
94
|
+
let config_url = join_urls(endpoint, this.deep_link ? CONFIG_URL + "?deep_link=" + this.deep_link : CONFIG_URL);
|
|
95
|
+
const config = await handleConfigResponse(await this.fetch(config_url, {
|
|
96
|
+
headers,
|
|
97
|
+
credentials: this.options.credentials ?? "same-origin"
|
|
98
|
+
}), !!this.options.auth);
|
|
99
|
+
if (!config.root) config.root = endpoint;
|
|
100
|
+
return config;
|
|
101
|
+
}
|
|
102
|
+
throw new Error(CONFIG_ERROR_MSG);
|
|
103
|
+
}
|
|
104
|
+
async function handleConfigResponse(response, authorized) {
|
|
105
|
+
if (response?.status === 401 && !authorized) {
|
|
106
|
+
let error_data = null;
|
|
107
|
+
try {
|
|
108
|
+
error_data = await response.json();
|
|
109
|
+
} catch (e) {
|
|
110
|
+
throw new Error(PRIVATE_SPACE_MSG);
|
|
111
|
+
}
|
|
112
|
+
const auth_message = error_data?.detail?.auth_message;
|
|
113
|
+
throw new Error(auth_message || "Login credentials are required to access this space.");
|
|
114
|
+
} else if (response?.status === 401 && authorized) throw new Error(INVALID_CREDENTIALS_MSG);
|
|
115
|
+
if (response?.status === 200) {
|
|
116
|
+
let config = await response.json();
|
|
117
|
+
config.dependencies?.forEach((dep, i) => {
|
|
118
|
+
if (dep.id === void 0) dep.id = i;
|
|
119
|
+
});
|
|
120
|
+
return config;
|
|
121
|
+
} else if (response?.status === 401) throw new Error(UNAUTHORIZED_MSG);
|
|
122
|
+
throw new Error(`${CONFIG_ERROR_MSG}(received status ${response?.status} when fetching the app config)`);
|
|
123
|
+
}
|
|
124
|
+
async function resolve_cookies() {
|
|
125
|
+
const { http_protocol, host } = await process_endpoint(this.app_reference, this.options.token);
|
|
126
|
+
try {
|
|
127
|
+
if (this.options.auth) {
|
|
128
|
+
const cookie_header = await get_cookie_header(http_protocol, host, this.options.auth, this.fetch, this.options.token, this.options.credentials);
|
|
129
|
+
if (cookie_header) this.set_cookies(cookie_header);
|
|
130
|
+
}
|
|
131
|
+
} catch (e) {
|
|
132
|
+
throw Error(e.message);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
async function get_cookie_header(http_protocol, host, auth, _fetch, token, credentials) {
|
|
136
|
+
const formData = new FormData();
|
|
137
|
+
formData.append("username", auth?.[0]);
|
|
138
|
+
formData.append("password", auth?.[1]);
|
|
139
|
+
let headers = {};
|
|
140
|
+
if (token) headers.Authorization = `Bearer ${token}`;
|
|
141
|
+
const res = await _fetch(`${http_protocol}//${host}/${LOGIN_URL}`, {
|
|
142
|
+
headers,
|
|
143
|
+
method: "POST",
|
|
144
|
+
body: formData,
|
|
145
|
+
credentials: credentials ?? "same-origin"
|
|
146
|
+
});
|
|
147
|
+
if (res.status === 200) return res.headers.get("set-cookie");
|
|
148
|
+
else if (res.status === 401) throw new Error(INVALID_CREDENTIALS_MSG);
|
|
149
|
+
else throw new Error(SPACE_METADATA_ERROR_MSG);
|
|
150
|
+
}
|
|
151
|
+
function determine_protocol(endpoint) {
|
|
152
|
+
if (endpoint.startsWith("http")) {
|
|
153
|
+
const { protocol, host, pathname } = new URL(endpoint);
|
|
154
|
+
return {
|
|
155
|
+
ws_protocol: protocol === "https:" ? "wss" : "ws",
|
|
156
|
+
http_protocol: protocol,
|
|
157
|
+
host: host + (pathname !== "/" ? pathname : "")
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
return {
|
|
161
|
+
ws_protocol: "wss",
|
|
162
|
+
http_protocol: "https:",
|
|
163
|
+
host: new URL(endpoint).host
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
var parse_and_set_cookies = (cookie_header) => {
|
|
167
|
+
let cookies = [];
|
|
168
|
+
cookie_header.split(/,(?=\s*[^\s=;]+=[^\s=;]+)/).forEach((cookie) => {
|
|
169
|
+
const [cookie_name, cookie_value] = cookie.split(";")[0].split("=");
|
|
170
|
+
if (cookie_name && cookie_value) cookies.push(`${cookie_name.trim()}=${cookie_value.trim()}`);
|
|
171
|
+
});
|
|
172
|
+
return cookies;
|
|
173
|
+
};
|
|
174
|
+
//#endregion
|
|
175
|
+
//#region src/helpers/api_info.ts
|
|
176
|
+
var RE_SPACE_NAME = /^[a-zA-Z0-9_\-\.]+\/[a-zA-Z0-9_\-\.]+$/;
|
|
177
|
+
var RE_SPACE_DOMAIN = /.*hf\.space\/{0,1}.*$/;
|
|
178
|
+
async function process_endpoint(app_reference, token) {
|
|
179
|
+
const headers = {};
|
|
180
|
+
if (token) headers.Authorization = `Bearer ${token}`;
|
|
181
|
+
const _app_reference = app_reference.trim().replace(/\/$/, "");
|
|
182
|
+
if (RE_SPACE_NAME.test(_app_reference)) {
|
|
183
|
+
let res;
|
|
184
|
+
try {
|
|
185
|
+
res = await fetch(`https://huggingface.co/api/spaces/${_app_reference}/${HOST_URL}`, { headers });
|
|
186
|
+
} catch (e) {
|
|
187
|
+
throw new Error(SPACE_METADATA_ERROR_MSG);
|
|
188
|
+
}
|
|
189
|
+
if (res.status === 401 || res.status === 404) throw new Error(SPACE_NOT_FOUND_MSG(_app_reference, res.status));
|
|
190
|
+
let _host;
|
|
191
|
+
try {
|
|
192
|
+
_host = (await res.json()).host;
|
|
193
|
+
} catch (e) {
|
|
194
|
+
throw new Error(SPACE_METADATA_ERROR_MSG);
|
|
195
|
+
}
|
|
196
|
+
if (!_host) throw new Error(SPACE_METADATA_ERROR_MSG);
|
|
197
|
+
return {
|
|
198
|
+
space_id: _app_reference,
|
|
199
|
+
...determine_protocol(_host)
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
if (RE_SPACE_DOMAIN.test(_app_reference)) {
|
|
203
|
+
const { ws_protocol, http_protocol, host } = determine_protocol(_app_reference);
|
|
204
|
+
return {
|
|
205
|
+
space_id: host.split("/")[0].replace(".hf.space", ""),
|
|
206
|
+
ws_protocol,
|
|
207
|
+
http_protocol,
|
|
208
|
+
host
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
return {
|
|
212
|
+
space_id: false,
|
|
213
|
+
...determine_protocol(_app_reference)
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
var join_urls = (...urls) => {
|
|
217
|
+
try {
|
|
218
|
+
return urls.reduce((base_url, part) => {
|
|
219
|
+
base_url = base_url.replace(/\/+$/, "");
|
|
220
|
+
part = part.replace(/^\/+/, "");
|
|
221
|
+
return new URL(part, base_url + "/").toString();
|
|
222
|
+
});
|
|
223
|
+
} catch (e) {
|
|
224
|
+
throw new Error(INVALID_URL_MSG);
|
|
225
|
+
}
|
|
226
|
+
};
|
|
227
|
+
function transform_api_info(api_info, config, api_map) {
|
|
228
|
+
const transformed_info = {
|
|
229
|
+
named_endpoints: {},
|
|
230
|
+
unnamed_endpoints: {}
|
|
231
|
+
};
|
|
232
|
+
Object.keys(api_info).forEach((category) => {
|
|
233
|
+
if (category === "named_endpoints" || category === "unnamed_endpoints") {
|
|
234
|
+
transformed_info[category] = {};
|
|
235
|
+
Object.entries(api_info[category]).forEach(([endpoint, endpoint_info]) => {
|
|
236
|
+
const parameters = endpoint_info?.parameters ?? [];
|
|
237
|
+
const returns = endpoint_info?.returns ?? [];
|
|
238
|
+
const dependencyIndex = config.dependencies.find((dep) => dep.api_name === endpoint || dep.api_name === endpoint.replace("/", ""))?.id || api_map[endpoint.replace("/", "")] || -1;
|
|
239
|
+
const dependency = dependencyIndex !== -1 ? config.dependencies.find((dep) => dep.id == dependencyIndex) : void 0;
|
|
240
|
+
const dependencyTypes = dependencyIndex !== -1 ? dependency?.types : {
|
|
241
|
+
generator: false,
|
|
242
|
+
cancel: false
|
|
243
|
+
};
|
|
244
|
+
if (dependency && Array.isArray(dependency.inputs) && dependency.inputs.length !== parameters.length) {
|
|
245
|
+
const components = dependency.inputs.map((input) => config.components.find((c) => c.id === input)?.type);
|
|
246
|
+
try {
|
|
247
|
+
components.forEach((comp, idx) => {
|
|
248
|
+
if (comp === "state") parameters.splice(idx, 0, {
|
|
249
|
+
component: "state",
|
|
250
|
+
example: null,
|
|
251
|
+
parameter_default: null,
|
|
252
|
+
parameter_has_default: true,
|
|
253
|
+
parameter_name: null,
|
|
254
|
+
hidden: true
|
|
255
|
+
});
|
|
256
|
+
});
|
|
257
|
+
} catch (e) {
|
|
258
|
+
console.error(e);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
const transform_type = (data, component, serializer, signature_type) => ({
|
|
262
|
+
...data,
|
|
263
|
+
description: get_description(data?.type, serializer),
|
|
264
|
+
type: get_type(data?.type, component, serializer, signature_type) || ""
|
|
265
|
+
});
|
|
266
|
+
transformed_info[category][endpoint] = {
|
|
267
|
+
parameters: parameters.map((p) => transform_type(p, p?.component, p?.serializer, "parameter")),
|
|
268
|
+
returns: returns.map((r) => transform_type(r, r?.component, r?.serializer, "return")),
|
|
269
|
+
type: dependencyTypes,
|
|
270
|
+
...endpoint_info?.oauth_token ? { oauth_token: endpoint_info.oauth_token } : {}
|
|
271
|
+
};
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
});
|
|
275
|
+
return transformed_info;
|
|
276
|
+
}
|
|
277
|
+
function get_type(type, component, serializer, signature_type) {
|
|
278
|
+
if (component === "Api") return type.type;
|
|
279
|
+
switch (type?.type) {
|
|
280
|
+
case "string": return "string";
|
|
281
|
+
case "boolean": return "boolean";
|
|
282
|
+
case "number": return "number";
|
|
283
|
+
}
|
|
284
|
+
if (serializer === "JSONSerializable" || serializer === "StringSerializable") return "any";
|
|
285
|
+
else if (serializer === "ListStringSerializable") return "string[]";
|
|
286
|
+
else if (component === "Image") return signature_type === "parameter" ? "Blob | File | Buffer" : "string";
|
|
287
|
+
else if (serializer === "FileSerializable") {
|
|
288
|
+
if (type?.type === "array") return signature_type === "parameter" ? "(Blob | File | Buffer)[]" : `{ name: string; data: string; size?: number; is_file?: boolean; orig_name?: string}[]`;
|
|
289
|
+
return signature_type === "parameter" ? "Blob | File | Buffer" : `{ name: string; data: string; size?: number; is_file?: boolean; orig_name?: string}`;
|
|
290
|
+
} 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))][]`;
|
|
291
|
+
}
|
|
292
|
+
function get_description(type, serializer) {
|
|
293
|
+
if (serializer === "GallerySerializable") return "array of [file, label] tuples";
|
|
294
|
+
else if (serializer === "ListStringSerializable") return "array of strings";
|
|
295
|
+
else if (serializer === "FileSerializable") return "array of files or single file";
|
|
296
|
+
return type?.description;
|
|
297
|
+
}
|
|
298
|
+
function handle_message(data, last_status) {
|
|
299
|
+
const queue = true;
|
|
300
|
+
switch (data.msg) {
|
|
301
|
+
case "send_data": return { type: "data" };
|
|
302
|
+
case "send_hash": return { type: "hash" };
|
|
303
|
+
case "queue_full": return {
|
|
304
|
+
type: "update",
|
|
305
|
+
status: {
|
|
306
|
+
queue,
|
|
307
|
+
message: QUEUE_FULL_MSG,
|
|
308
|
+
stage: "error",
|
|
309
|
+
code: data.code,
|
|
310
|
+
success: data.success
|
|
311
|
+
}
|
|
312
|
+
};
|
|
313
|
+
case "heartbeat": return { type: "heartbeat" };
|
|
314
|
+
case "unexpected_error": return {
|
|
315
|
+
type: "unexpected_error",
|
|
316
|
+
status: {
|
|
317
|
+
queue,
|
|
318
|
+
message: data.message,
|
|
319
|
+
session_not_found: data.session_not_found,
|
|
320
|
+
stage: "error",
|
|
321
|
+
success: false
|
|
322
|
+
}
|
|
323
|
+
};
|
|
324
|
+
case "broken_connection": return {
|
|
325
|
+
type: "broken_connection",
|
|
326
|
+
status: {
|
|
327
|
+
queue,
|
|
328
|
+
message: data.message,
|
|
329
|
+
stage: "error",
|
|
330
|
+
success: false
|
|
331
|
+
}
|
|
332
|
+
};
|
|
333
|
+
case "estimation": return {
|
|
334
|
+
type: "update",
|
|
335
|
+
status: {
|
|
336
|
+
queue,
|
|
337
|
+
stage: last_status || "pending",
|
|
338
|
+
code: data.code,
|
|
339
|
+
size: data.queue_size,
|
|
340
|
+
position: data.rank,
|
|
341
|
+
eta: data.rank_eta,
|
|
342
|
+
success: data.success
|
|
343
|
+
}
|
|
344
|
+
};
|
|
345
|
+
case "progress": return {
|
|
346
|
+
type: "update",
|
|
347
|
+
status: {
|
|
348
|
+
queue,
|
|
349
|
+
stage: "pending",
|
|
350
|
+
code: data.code,
|
|
351
|
+
progress_data: data.progress_data,
|
|
352
|
+
success: data.success
|
|
353
|
+
}
|
|
354
|
+
};
|
|
355
|
+
case "log": return {
|
|
356
|
+
type: "log",
|
|
357
|
+
data
|
|
358
|
+
};
|
|
359
|
+
case "process_generating": return {
|
|
360
|
+
type: "generating",
|
|
361
|
+
status: {
|
|
362
|
+
queue,
|
|
363
|
+
message: !data.success ? data.output.error : null,
|
|
364
|
+
stage: data.success ? "generating" : "error",
|
|
365
|
+
code: data.code,
|
|
366
|
+
progress_data: data.progress_data,
|
|
367
|
+
eta: data.average_duration,
|
|
368
|
+
changed_state_ids: data.success ? data.output.changed_state_ids : void 0
|
|
369
|
+
},
|
|
370
|
+
data: data.success ? data.output : null
|
|
371
|
+
};
|
|
372
|
+
case "process_streaming": return {
|
|
373
|
+
type: "streaming",
|
|
374
|
+
status: {
|
|
375
|
+
queue,
|
|
376
|
+
message: data.output.error,
|
|
377
|
+
stage: "streaming",
|
|
378
|
+
time_limit: data.time_limit,
|
|
379
|
+
code: data.code,
|
|
380
|
+
progress_data: data.progress_data,
|
|
381
|
+
changed_state_ids: data.output.changed_state_ids,
|
|
382
|
+
eta: data.eta
|
|
383
|
+
},
|
|
384
|
+
data: data.output
|
|
385
|
+
};
|
|
386
|
+
case "process_completed":
|
|
387
|
+
if ("error" in data.output) return {
|
|
388
|
+
type: "update",
|
|
389
|
+
status: {
|
|
390
|
+
queue,
|
|
391
|
+
title: data.output.title ?? "Error",
|
|
392
|
+
message: data.output.error ?? "An error occurred",
|
|
393
|
+
visible: data.output.visible,
|
|
394
|
+
duration: data.output.duration,
|
|
395
|
+
stage: "error",
|
|
396
|
+
code: data.code,
|
|
397
|
+
success: data.success
|
|
398
|
+
}
|
|
399
|
+
};
|
|
400
|
+
return {
|
|
401
|
+
type: "complete",
|
|
402
|
+
status: {
|
|
403
|
+
queue,
|
|
404
|
+
message: !data.success ? data.output.error : void 0,
|
|
405
|
+
stage: data.success ? "complete" : "error",
|
|
406
|
+
code: data.code,
|
|
407
|
+
progress_data: data.progress_data,
|
|
408
|
+
changed_state_ids: data.success ? data.output.changed_state_ids : void 0,
|
|
409
|
+
used_cache: data.used_cache,
|
|
410
|
+
cache_duration: data.cache_duration,
|
|
411
|
+
avg_time: data.avg_time
|
|
412
|
+
},
|
|
413
|
+
data: data.success ? data.output : null
|
|
414
|
+
};
|
|
415
|
+
case "process_starts": return {
|
|
416
|
+
type: "update",
|
|
417
|
+
status: {
|
|
418
|
+
queue,
|
|
419
|
+
stage: "pending",
|
|
420
|
+
code: data.code,
|
|
421
|
+
size: data.rank,
|
|
422
|
+
position: 0,
|
|
423
|
+
success: data.success,
|
|
424
|
+
eta: data.eta
|
|
425
|
+
},
|
|
426
|
+
original_msg: "process_starts"
|
|
427
|
+
};
|
|
428
|
+
}
|
|
429
|
+
return {
|
|
430
|
+
type: "none",
|
|
431
|
+
status: {
|
|
432
|
+
stage: "error",
|
|
433
|
+
queue
|
|
434
|
+
}
|
|
435
|
+
};
|
|
436
|
+
}
|
|
437
|
+
/**
|
|
438
|
+
* Maps the provided `data` to the parameters defined by the `/info` endpoint response.
|
|
439
|
+
* This allows us to support both positional and keyword arguments passed to the client
|
|
440
|
+
* and ensures that all parameters are either directly provided or have default values assigned.
|
|
441
|
+
*
|
|
442
|
+
* @param {unknown[] | Record<string, unknown>} data - The input data for the function,
|
|
443
|
+
* which can be either an array of values for positional arguments or an object
|
|
444
|
+
* with key-value pairs for keyword arguments.
|
|
445
|
+
* @param {JsApiData[]} parameters - Array of parameter descriptions retrieved from the
|
|
446
|
+
* `/info` endpoint.
|
|
447
|
+
*
|
|
448
|
+
* @returns {unknown[]} - Returns an array of resolved data where each element corresponds
|
|
449
|
+
* to the expected parameter from the API. The `parameter_default` value is used where
|
|
450
|
+
* a value is not provided for a parameter, and optional parameters without defaults are
|
|
451
|
+
* set to `undefined`.
|
|
452
|
+
*
|
|
453
|
+
* @throws {Error} - Throws an error:
|
|
454
|
+
* - If more arguments are provided than are defined in the parameters.
|
|
455
|
+
* * - If no parameter value is provided for a required parameter and no default value is defined.
|
|
456
|
+
* - If an argument is provided that does not match any defined parameter.
|
|
457
|
+
*/
|
|
458
|
+
var map_data_to_params = (data = [], endpoint_info) => {
|
|
459
|
+
const parameters = endpoint_info ? endpoint_info.parameters : [];
|
|
460
|
+
if (Array.isArray(data)) {
|
|
461
|
+
if (endpoint_info && parameters.length > 0 && data.length > parameters.length) console.warn("Too many arguments provided for the endpoint.");
|
|
462
|
+
return data;
|
|
463
|
+
}
|
|
464
|
+
const resolved_data = [];
|
|
465
|
+
const provided_keys = Object.keys(data);
|
|
466
|
+
parameters.forEach((param, index) => {
|
|
467
|
+
if (data.hasOwnProperty(param.parameter_name)) resolved_data[index] = data[param.parameter_name];
|
|
468
|
+
else if (param.parameter_has_default) resolved_data[index] = param.parameter_default;
|
|
469
|
+
else throw new Error(`No value provided for required parameter: ${param.parameter_name}`);
|
|
470
|
+
});
|
|
471
|
+
provided_keys.forEach((key) => {
|
|
472
|
+
if (!parameters.some((param) => param.parameter_name === key)) throw new Error(`Parameter \`${key}\` is not a valid keyword argument. Please refer to the API for usage.`);
|
|
473
|
+
});
|
|
474
|
+
resolved_data.forEach((value, idx) => {
|
|
475
|
+
if (value === void 0 && !parameters[idx].parameter_has_default) throw new Error(`No value provided for required parameter: ${parameters[idx].parameter_name}`);
|
|
476
|
+
});
|
|
477
|
+
return resolved_data;
|
|
478
|
+
};
|
|
479
|
+
//#endregion
|
|
480
|
+
//#region src/utils/view_api.ts
|
|
481
|
+
async function view_api() {
|
|
482
|
+
if (this.api_info) return this.api_info;
|
|
483
|
+
const { token } = this.options;
|
|
484
|
+
const { config } = this;
|
|
485
|
+
const headers = {};
|
|
486
|
+
if (token) headers.Authorization = `Bearer ${token}`;
|
|
487
|
+
if (!config) return;
|
|
488
|
+
try {
|
|
489
|
+
let response;
|
|
490
|
+
let api_info;
|
|
491
|
+
if (typeof window !== "undefined" && window.gradio_api_info) api_info = window.gradio_api_info;
|
|
492
|
+
else {
|
|
493
|
+
const url = join_urls(config.root, this.api_prefix, API_INFO_URL);
|
|
494
|
+
response = await this.fetch(url, {
|
|
495
|
+
headers,
|
|
496
|
+
credentials: this.options.credentials ?? "same-origin"
|
|
497
|
+
});
|
|
498
|
+
if (!response.ok) throw new Error(BROKEN_CONNECTION_MSG);
|
|
499
|
+
api_info = await response.json();
|
|
500
|
+
}
|
|
501
|
+
if ("api" in api_info) api_info = api_info.api;
|
|
502
|
+
if (api_info.named_endpoints["/predict"] && !api_info.unnamed_endpoints["0"]) api_info.unnamed_endpoints[0] = api_info.named_endpoints["/predict"];
|
|
503
|
+
return transform_api_info(api_info, config, this.api_map);
|
|
504
|
+
} catch (e) {
|
|
505
|
+
throw new Error("Could not get API info. " + e.message);
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
//#endregion
|
|
509
|
+
//#region src/utils/upload_files.ts
|
|
510
|
+
async function upload_files(root_url, files, upload_id) {
|
|
511
|
+
const headers = {};
|
|
512
|
+
if (this?.options?.token) headers.Authorization = `Bearer ${this.options.token}`;
|
|
513
|
+
const chunkSize = 1e3;
|
|
514
|
+
const uploadResponses = [];
|
|
515
|
+
let response;
|
|
516
|
+
for (let i = 0; i < files.length; i += chunkSize) {
|
|
517
|
+
const chunk = files.slice(i, i + chunkSize);
|
|
518
|
+
const formData = new FormData();
|
|
519
|
+
chunk.forEach((file) => {
|
|
520
|
+
formData.append("files", file);
|
|
521
|
+
});
|
|
522
|
+
try {
|
|
523
|
+
const upload_url = upload_id ? `${root_url}${this.api_prefix}/${UPLOAD_URL}?upload_id=${upload_id}` : `${root_url}${this.api_prefix}/${UPLOAD_URL}`;
|
|
524
|
+
response = await this.fetch(upload_url, {
|
|
525
|
+
method: "POST",
|
|
526
|
+
body: formData,
|
|
527
|
+
headers,
|
|
528
|
+
credentials: this.options.credentials ?? "same-origin"
|
|
529
|
+
});
|
|
530
|
+
} catch (e) {
|
|
531
|
+
throw new Error(BROKEN_CONNECTION_MSG + e.message);
|
|
532
|
+
}
|
|
533
|
+
if (!response.ok) {
|
|
534
|
+
const error_text = await response.text();
|
|
535
|
+
return { error: `HTTP ${response.status}: ${error_text}` };
|
|
536
|
+
}
|
|
537
|
+
const output = await response.json();
|
|
538
|
+
if (output) uploadResponses.push(...output);
|
|
539
|
+
}
|
|
540
|
+
return { files: uploadResponses };
|
|
541
|
+
}
|
|
542
|
+
var SPECS = {
|
|
543
|
+
si: {
|
|
544
|
+
radix: 1e3,
|
|
545
|
+
unit: [
|
|
546
|
+
"b",
|
|
547
|
+
"kb",
|
|
548
|
+
"Mb",
|
|
549
|
+
"Gb",
|
|
550
|
+
"Tb",
|
|
551
|
+
"Pb",
|
|
552
|
+
"Eb",
|
|
553
|
+
"Zb",
|
|
554
|
+
"Yb"
|
|
555
|
+
]
|
|
556
|
+
},
|
|
557
|
+
iec: {
|
|
558
|
+
radix: 1024,
|
|
559
|
+
unit: [
|
|
560
|
+
"b",
|
|
561
|
+
"Kib",
|
|
562
|
+
"Mib",
|
|
563
|
+
"Gib",
|
|
564
|
+
"Tib",
|
|
565
|
+
"Pib",
|
|
566
|
+
"Eib",
|
|
567
|
+
"Zib",
|
|
568
|
+
"Yib"
|
|
569
|
+
]
|
|
570
|
+
},
|
|
571
|
+
jedec: {
|
|
572
|
+
radix: 1024,
|
|
573
|
+
unit: [
|
|
574
|
+
"b",
|
|
575
|
+
"Kb",
|
|
576
|
+
"Mb",
|
|
577
|
+
"Gb",
|
|
578
|
+
"Tb",
|
|
579
|
+
"Pb",
|
|
580
|
+
"Eb",
|
|
581
|
+
"Zb",
|
|
582
|
+
"Yb"
|
|
583
|
+
]
|
|
584
|
+
}
|
|
585
|
+
};
|
|
586
|
+
/**
|
|
587
|
+
* file size from https://github.com/hustcc/filesize.js
|
|
588
|
+
* @param bytes - The number of bytes to convert to human-readable format
|
|
589
|
+
* @param fixed - Number of decimal places to display (default: 1)
|
|
590
|
+
* @param spec - Size specification to use: "si", "iec", or "jedec" (default: "jedec")
|
|
591
|
+
* @returns Human-readable file size string
|
|
592
|
+
*/
|
|
593
|
+
function filesize(bytes, fixed = 1, spec = "jedec") {
|
|
594
|
+
bytes = Math.abs(bytes);
|
|
595
|
+
const { radix, unit } = SPECS[spec] || SPECS.jedec;
|
|
596
|
+
let loop = 0;
|
|
597
|
+
while (bytes >= radix) {
|
|
598
|
+
bytes /= radix;
|
|
599
|
+
++loop;
|
|
600
|
+
}
|
|
601
|
+
return `${bytes.toFixed(fixed)} ${unit[loop]}`;
|
|
602
|
+
}
|
|
603
|
+
//#endregion
|
|
604
|
+
//#region src/upload.ts
|
|
605
|
+
async function upload(file_data, root_url, upload_id, max_file_size) {
|
|
606
|
+
let files = (Array.isArray(file_data) ? file_data : [file_data]).map((file_data) => file_data.blob);
|
|
607
|
+
const oversized_files = files.filter((f) => f.size > (max_file_size ?? Infinity));
|
|
608
|
+
if (oversized_files.length) throw new Error(`File(s) exceed the maximum allowed size of ${filesize(max_file_size || Infinity)}: ${oversized_files.map((f) => `"${f.name}"`).join(", ")}`);
|
|
609
|
+
return await Promise.all(await this.upload_files(root_url, files, upload_id).then(async (response) => {
|
|
610
|
+
if (response.error) throw new Error(response.error);
|
|
611
|
+
else {
|
|
612
|
+
if (response.files) return response.files.map((f, i) => {
|
|
613
|
+
return new FileData({
|
|
614
|
+
...file_data[i],
|
|
615
|
+
path: f,
|
|
616
|
+
url: `${root_url}${this.api_prefix}/file=${f}`
|
|
617
|
+
});
|
|
618
|
+
});
|
|
619
|
+
return [];
|
|
620
|
+
}
|
|
621
|
+
}));
|
|
622
|
+
}
|
|
623
|
+
async function prepare_files(files, is_stream) {
|
|
624
|
+
return files.map((f) => new FileData({
|
|
625
|
+
path: f.name,
|
|
626
|
+
orig_name: f.name,
|
|
627
|
+
blob: f,
|
|
628
|
+
size: f.size,
|
|
629
|
+
mime_type: f.type,
|
|
630
|
+
is_stream
|
|
631
|
+
}));
|
|
632
|
+
}
|
|
633
|
+
var FileData = class {
|
|
634
|
+
path;
|
|
635
|
+
url;
|
|
636
|
+
orig_name;
|
|
637
|
+
size;
|
|
638
|
+
blob;
|
|
639
|
+
is_stream;
|
|
640
|
+
mime_type;
|
|
641
|
+
alt_text;
|
|
642
|
+
b64;
|
|
643
|
+
meta = { _type: "gradio.FileData" };
|
|
644
|
+
constructor({ path, url, orig_name, size, blob, is_stream, mime_type, alt_text, b64 }) {
|
|
645
|
+
this.path = path;
|
|
646
|
+
this.url = url;
|
|
647
|
+
this.orig_name = orig_name;
|
|
648
|
+
this.size = size;
|
|
649
|
+
this.blob = url ? void 0 : blob;
|
|
650
|
+
this.is_stream = is_stream;
|
|
651
|
+
this.mime_type = mime_type;
|
|
652
|
+
this.alt_text = alt_text;
|
|
653
|
+
this.b64 = b64;
|
|
654
|
+
}
|
|
655
|
+
};
|
|
656
|
+
//#endregion
|
|
657
|
+
//#region src/types.ts
|
|
658
|
+
var Command = class {
|
|
659
|
+
type;
|
|
660
|
+
command;
|
|
661
|
+
meta;
|
|
662
|
+
fileData;
|
|
663
|
+
constructor(command, meta) {
|
|
664
|
+
this.type = "command";
|
|
665
|
+
this.command = command;
|
|
666
|
+
this.meta = meta;
|
|
667
|
+
}
|
|
668
|
+
};
|
|
669
|
+
//#endregion
|
|
670
|
+
//#region src/helpers/data.ts
|
|
671
|
+
var is_node = typeof process !== "undefined" && process.versions && process.versions.node;
|
|
672
|
+
function update_object(object, newValue, stack) {
|
|
673
|
+
while (stack.length > 1) {
|
|
674
|
+
const key = stack.shift();
|
|
675
|
+
if (typeof key === "string" || typeof key === "number") object = object[key];
|
|
676
|
+
else throw new Error("Invalid key type");
|
|
677
|
+
}
|
|
678
|
+
const key = stack.shift();
|
|
679
|
+
if (typeof key === "string" || typeof key === "number") object[key] = newValue;
|
|
680
|
+
else throw new Error("Invalid key type");
|
|
681
|
+
}
|
|
682
|
+
async function walk_and_store_blobs(data, type = void 0, path = [], root = false, endpoint_info = void 0) {
|
|
683
|
+
if (Array.isArray(data)) {
|
|
684
|
+
let blob_refs = [];
|
|
685
|
+
await Promise.all(data.map(async (_, index) => {
|
|
686
|
+
let new_path = path.slice();
|
|
687
|
+
new_path.push(String(index));
|
|
688
|
+
const array_refs = await walk_and_store_blobs(data[index], root ? endpoint_info?.parameters[index]?.component || void 0 : type, new_path, false, endpoint_info);
|
|
689
|
+
blob_refs = blob_refs.concat(array_refs);
|
|
690
|
+
}));
|
|
691
|
+
return blob_refs;
|
|
692
|
+
} else if (globalThis.Buffer && data instanceof globalThis.Buffer || data instanceof Blob) return [{
|
|
693
|
+
path,
|
|
694
|
+
blob: data instanceof Blob ? data : new Blob([data]),
|
|
695
|
+
type
|
|
696
|
+
}];
|
|
697
|
+
else if (typeof data === "object" && data !== null) {
|
|
698
|
+
let blob_refs = [];
|
|
699
|
+
for (const key of Object.keys(data)) {
|
|
700
|
+
const new_path = [...path, key];
|
|
701
|
+
const value = data[key];
|
|
702
|
+
blob_refs = blob_refs.concat(await walk_and_store_blobs(value, void 0, new_path, false, endpoint_info));
|
|
703
|
+
}
|
|
704
|
+
return blob_refs;
|
|
705
|
+
}
|
|
706
|
+
return [];
|
|
707
|
+
}
|
|
708
|
+
function skip_queue(id, config) {
|
|
709
|
+
let fn_queue = config?.dependencies?.find((dep) => dep.id == id)?.queue;
|
|
710
|
+
if (fn_queue != null) return !fn_queue;
|
|
711
|
+
return !config.enable_queue;
|
|
712
|
+
}
|
|
713
|
+
function post_message(message, origin) {
|
|
714
|
+
return new Promise((res, _rej) => {
|
|
715
|
+
const channel = new MessageChannel();
|
|
716
|
+
channel.port1.onmessage = (({ data }) => {
|
|
717
|
+
channel.port1.close();
|
|
718
|
+
res(data);
|
|
719
|
+
});
|
|
720
|
+
window.parent.postMessage(message, origin, [channel.port2]);
|
|
721
|
+
});
|
|
722
|
+
}
|
|
723
|
+
function handle_file(file_or_url) {
|
|
724
|
+
if (typeof file_or_url === "string") {
|
|
725
|
+
if (file_or_url.startsWith("http://") || file_or_url.startsWith("https://")) return {
|
|
726
|
+
path: file_or_url,
|
|
727
|
+
url: file_or_url,
|
|
728
|
+
orig_name: file_or_url.split("/").pop() ?? "unknown",
|
|
729
|
+
meta: { _type: "gradio.FileData" }
|
|
730
|
+
};
|
|
731
|
+
if (is_node) return new Command("upload_file", {
|
|
732
|
+
path: file_or_url,
|
|
733
|
+
name: file_or_url,
|
|
734
|
+
orig_path: file_or_url
|
|
735
|
+
});
|
|
736
|
+
} else if (typeof File !== "undefined" && file_or_url instanceof File) return file_or_url;
|
|
737
|
+
else if (globalThis.Buffer && file_or_url instanceof globalThis.Buffer) return new Blob([file_or_url]);
|
|
738
|
+
else if (file_or_url instanceof Blob) return file_or_url;
|
|
739
|
+
throw new Error("Invalid input: must be a URL, File, Blob, or Buffer object.");
|
|
740
|
+
}
|
|
741
|
+
/**
|
|
742
|
+
* Handles the payload by filtering out state inputs and returning an array of resolved payload values.
|
|
743
|
+
* We send null values for state inputs to the server, but we don't want to include them in the resolved payload.
|
|
744
|
+
*
|
|
745
|
+
* @param resolved_payload - The resolved payload values received from the client or the server
|
|
746
|
+
* @param dependency - The dependency object.
|
|
747
|
+
* @param components - The array of component metadata.
|
|
748
|
+
* @param with_null_state - Optional. Specifies whether to include null values for state inputs. Default is false.
|
|
749
|
+
* @returns An array of resolved payload values, filtered based on the dependency and component metadata.
|
|
750
|
+
*/
|
|
751
|
+
function handle_payload(resolved_payload, dependency, components, type, with_null_state = false) {
|
|
752
|
+
if (type === "input" && !with_null_state) throw new Error("Invalid code path. Cannot skip state inputs for input.");
|
|
753
|
+
if (type === "output" && with_null_state) return resolved_payload;
|
|
754
|
+
let updated_payload = [];
|
|
755
|
+
let payload_index = 0;
|
|
756
|
+
const deps = type === "input" ? dependency.inputs : dependency.outputs;
|
|
757
|
+
for (let i = 0; i < deps.length; i++) {
|
|
758
|
+
const input_id = deps[i];
|
|
759
|
+
if (components.find((c) => c.id === input_id)?.type === "state") {
|
|
760
|
+
if (with_null_state) if (resolved_payload.length === deps.length) {
|
|
761
|
+
const value = resolved_payload[payload_index];
|
|
762
|
+
updated_payload.push(value);
|
|
763
|
+
payload_index++;
|
|
764
|
+
} else updated_payload.push(null);
|
|
765
|
+
else {
|
|
766
|
+
payload_index++;
|
|
767
|
+
continue;
|
|
768
|
+
}
|
|
769
|
+
continue;
|
|
770
|
+
} else {
|
|
771
|
+
const value = resolved_payload[payload_index];
|
|
772
|
+
updated_payload.push(value);
|
|
773
|
+
payload_index++;
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
return updated_payload;
|
|
777
|
+
}
|
|
778
|
+
//#endregion
|
|
779
|
+
//#region src/utils/handle_blob.ts
|
|
780
|
+
async function handle_blob(endpoint, data, api_info) {
|
|
781
|
+
const self = this;
|
|
782
|
+
await process_local_file_commands(self, data);
|
|
783
|
+
const blobRefs = await walk_and_store_blobs(data, void 0, [], true, api_info);
|
|
784
|
+
(await Promise.all(blobRefs.map(async ({ path, blob, type }) => {
|
|
785
|
+
if (!blob) return {
|
|
786
|
+
path,
|
|
787
|
+
type
|
|
788
|
+
};
|
|
789
|
+
const response = await self.upload_files(endpoint, [blob]);
|
|
790
|
+
return {
|
|
791
|
+
path,
|
|
792
|
+
file_url: response.files && response.files[0],
|
|
793
|
+
type,
|
|
794
|
+
name: typeof File !== "undefined" && blob instanceof File ? blob?.name : void 0
|
|
795
|
+
};
|
|
796
|
+
}))).forEach(({ path, file_url, type, name }) => {
|
|
797
|
+
if (type === "Gallery") update_object(data, file_url, path);
|
|
798
|
+
else if (file_url) update_object(data, new FileData({
|
|
799
|
+
path: file_url,
|
|
800
|
+
orig_name: name
|
|
801
|
+
}), path);
|
|
802
|
+
});
|
|
803
|
+
return data;
|
|
804
|
+
}
|
|
805
|
+
async function process_local_file_commands(client, data) {
|
|
806
|
+
if (!(client.config?.root || client.config?.root_url)) throw new Error(ROOT_URL_ERROR_MSG);
|
|
807
|
+
await recursively_process_commands(client, data);
|
|
808
|
+
}
|
|
809
|
+
async function recursively_process_commands(client, data, path = []) {
|
|
810
|
+
for (const key in data) if (data[key] instanceof Command) await process_single_command(client, data, key);
|
|
811
|
+
else if (typeof data[key] === "object" && data[key] !== null) await recursively_process_commands(client, data[key], [...path, key]);
|
|
812
|
+
}
|
|
813
|
+
async function process_single_command(client, data, key) {
|
|
814
|
+
let cmd_item = data[key];
|
|
815
|
+
const root = client.config?.root || client.config?.root_url;
|
|
816
|
+
if (!root) throw new Error(ROOT_URL_ERROR_MSG);
|
|
817
|
+
try {
|
|
818
|
+
let fileBuffer;
|
|
819
|
+
let fullPath;
|
|
820
|
+
if (typeof process !== "undefined" && process.versions && process.versions.node) {
|
|
821
|
+
const fs = await import("fs/promises");
|
|
822
|
+
fullPath = (await import("path")).resolve(process.cwd(), cmd_item.meta.path);
|
|
823
|
+
fileBuffer = await fs.readFile(fullPath);
|
|
824
|
+
} else throw new Error(NODEJS_FS_ERROR_MSG);
|
|
825
|
+
const file = new Blob([fileBuffer], { type: "application/octet-stream" });
|
|
826
|
+
const response = await client.upload_files(root, [file]);
|
|
827
|
+
const file_url = response.files && response.files[0];
|
|
828
|
+
if (file_url) data[key] = new FileData({
|
|
829
|
+
path: file_url,
|
|
830
|
+
orig_name: cmd_item.meta.name || ""
|
|
831
|
+
});
|
|
832
|
+
} catch (error) {
|
|
833
|
+
console.error(FILE_PROCESSING_ERROR_MSG, error);
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
//#endregion
|
|
837
|
+
//#region src/utils/post_data.ts
|
|
838
|
+
async function post_data(url, body, additional_headers) {
|
|
839
|
+
const headers = { "Content-Type": "application/json" };
|
|
840
|
+
if (this.options.token) headers.Authorization = `Bearer ${this.options.token}`;
|
|
841
|
+
try {
|
|
842
|
+
var response = await this.fetch(url, {
|
|
843
|
+
method: "POST",
|
|
844
|
+
body: JSON.stringify(body),
|
|
845
|
+
headers: {
|
|
846
|
+
...headers,
|
|
847
|
+
...additional_headers
|
|
848
|
+
},
|
|
849
|
+
credentials: this.options.credentials ?? "same-origin"
|
|
850
|
+
});
|
|
851
|
+
} catch (e) {
|
|
852
|
+
return [{ error: BROKEN_CONNECTION_MSG }, 500];
|
|
853
|
+
}
|
|
854
|
+
let output;
|
|
855
|
+
let status;
|
|
856
|
+
try {
|
|
857
|
+
output = await response.json();
|
|
858
|
+
status = response.status;
|
|
859
|
+
} catch (e) {
|
|
860
|
+
output = { error: `Could not parse server response: ${e}` };
|
|
861
|
+
status = 500;
|
|
862
|
+
}
|
|
863
|
+
return [output, status];
|
|
864
|
+
}
|
|
865
|
+
//#endregion
|
|
866
|
+
//#region src/utils/predict.ts
|
|
867
|
+
async function predict(endpoint, data = {}) {
|
|
868
|
+
let data_returned = false;
|
|
869
|
+
let status_complete = false;
|
|
870
|
+
if (!this.config) throw new Error("Could not resolve app config");
|
|
871
|
+
if (typeof endpoint === "number") this.config.dependencies.find((dep) => dep.id == endpoint);
|
|
872
|
+
else {
|
|
873
|
+
const trimmed_endpoint = endpoint.replace(/^\//, "");
|
|
874
|
+
this.config.dependencies.find((dep) => dep.id == this.api_map[trimmed_endpoint]);
|
|
875
|
+
}
|
|
876
|
+
const app = this.submit(endpoint, data, null, null, true);
|
|
877
|
+
let result;
|
|
878
|
+
for await (const message of app) {
|
|
879
|
+
if (message.type === "data") {
|
|
880
|
+
data_returned = true;
|
|
881
|
+
result = message;
|
|
882
|
+
if (status_complete) return result;
|
|
883
|
+
}
|
|
884
|
+
if (message.type === "status") {
|
|
885
|
+
if (message.stage === "error") {
|
|
886
|
+
const { message: error_message, ...status } = message;
|
|
887
|
+
const error = new Error((typeof error_message === "string" ? error_message : error_message && JSON.stringify(error_message)) || "An unknown error occurred while making a prediction.");
|
|
888
|
+
Object.assign(error, status);
|
|
889
|
+
throw error;
|
|
890
|
+
}
|
|
891
|
+
if (message.stage === "complete") {
|
|
892
|
+
status_complete = true;
|
|
893
|
+
if (data_returned) return result;
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
return result;
|
|
898
|
+
}
|
|
899
|
+
//#endregion
|
|
900
|
+
//#region src/helpers/spaces.ts
|
|
901
|
+
async function check_space_status(id, type, status_callback) {
|
|
902
|
+
let endpoint = type === "subdomain" ? `https://huggingface.co/api/spaces/by-subdomain/${id}` : `https://huggingface.co/api/spaces/${id}`;
|
|
903
|
+
let response;
|
|
904
|
+
let _status;
|
|
905
|
+
try {
|
|
906
|
+
response = await fetch(endpoint);
|
|
907
|
+
_status = response.status;
|
|
908
|
+
if (_status !== 200) throw new Error();
|
|
909
|
+
response = await response.json();
|
|
910
|
+
} catch (e) {
|
|
911
|
+
status_callback({
|
|
912
|
+
status: "error",
|
|
913
|
+
load_status: "error",
|
|
914
|
+
message: SPACE_STATUS_ERROR_MSG,
|
|
915
|
+
detail: "NOT_FOUND"
|
|
916
|
+
});
|
|
917
|
+
return;
|
|
918
|
+
}
|
|
919
|
+
if (!response || _status !== 200) return;
|
|
920
|
+
const { runtime: { stage }, id: space_name } = response;
|
|
921
|
+
switch (stage) {
|
|
922
|
+
case "STOPPED":
|
|
923
|
+
case "SLEEPING":
|
|
924
|
+
status_callback({
|
|
925
|
+
status: "sleeping",
|
|
926
|
+
load_status: "pending",
|
|
927
|
+
message: "Space is asleep. Waking it up...",
|
|
928
|
+
detail: stage
|
|
929
|
+
});
|
|
930
|
+
setTimeout(() => {
|
|
931
|
+
check_space_status(id, type, status_callback);
|
|
932
|
+
}, 1e3);
|
|
933
|
+
break;
|
|
934
|
+
case "PAUSED":
|
|
935
|
+
status_callback({
|
|
936
|
+
status: "paused",
|
|
937
|
+
load_status: "error",
|
|
938
|
+
message: "This space has been paused by the author. If you would like to try this demo, consider duplicating the space.",
|
|
939
|
+
detail: stage,
|
|
940
|
+
discussions_enabled: await discussions_enabled(space_name)
|
|
941
|
+
});
|
|
942
|
+
break;
|
|
943
|
+
case "RUNNING":
|
|
944
|
+
case "RUNNING_BUILDING":
|
|
945
|
+
status_callback({
|
|
946
|
+
status: "running",
|
|
947
|
+
load_status: "complete",
|
|
948
|
+
message: "Space is running.",
|
|
949
|
+
detail: stage
|
|
950
|
+
});
|
|
951
|
+
break;
|
|
952
|
+
case "BUILDING":
|
|
953
|
+
status_callback({
|
|
954
|
+
status: "building",
|
|
955
|
+
load_status: "pending",
|
|
956
|
+
message: "Space is building...",
|
|
957
|
+
detail: stage
|
|
958
|
+
});
|
|
959
|
+
setTimeout(() => {
|
|
960
|
+
check_space_status(id, type, status_callback);
|
|
961
|
+
}, 1e3);
|
|
962
|
+
break;
|
|
963
|
+
case "APP_STARTING":
|
|
964
|
+
status_callback({
|
|
965
|
+
status: "starting",
|
|
966
|
+
load_status: "pending",
|
|
967
|
+
message: "Space is starting...",
|
|
968
|
+
detail: stage
|
|
969
|
+
});
|
|
970
|
+
setTimeout(() => {
|
|
971
|
+
check_space_status(id, type, status_callback);
|
|
972
|
+
}, 1e3);
|
|
973
|
+
break;
|
|
974
|
+
default:
|
|
975
|
+
status_callback({
|
|
976
|
+
status: "space_error",
|
|
977
|
+
load_status: "error",
|
|
978
|
+
message: "This space is experiencing an issue.",
|
|
979
|
+
detail: stage,
|
|
980
|
+
discussions_enabled: await discussions_enabled(space_name)
|
|
981
|
+
});
|
|
982
|
+
break;
|
|
983
|
+
}
|
|
984
|
+
}
|
|
985
|
+
var check_and_wake_space = async (space_id, status_callback) => {
|
|
986
|
+
let retries = 0;
|
|
987
|
+
const max_retries = 12;
|
|
988
|
+
const check_interval = 5e3;
|
|
989
|
+
return new Promise((resolve) => {
|
|
990
|
+
check_space_status(space_id, RE_SPACE_NAME.test(space_id) ? "space_name" : "subdomain", (status) => {
|
|
991
|
+
status_callback(status);
|
|
992
|
+
if (status.status === "running") resolve();
|
|
993
|
+
else if (status.status === "error" || status.status === "paused" || status.status === "space_error") resolve();
|
|
994
|
+
else if (status.status === "sleeping" || status.status === "building") if (retries < max_retries) {
|
|
995
|
+
retries++;
|
|
996
|
+
setTimeout(() => {
|
|
997
|
+
check_and_wake_space(space_id, status_callback).then(resolve);
|
|
998
|
+
}, check_interval);
|
|
999
|
+
} else resolve();
|
|
1000
|
+
});
|
|
1001
|
+
});
|
|
1002
|
+
};
|
|
1003
|
+
var RE_DISABLED_DISCUSSION = /^(?=[^]*\b[dD]iscussions{0,1}\b)(?=[^]*\b[dD]isabled\b)[^]*$/;
|
|
1004
|
+
async function discussions_enabled(space_id) {
|
|
1005
|
+
try {
|
|
1006
|
+
const r = await fetch(`https://huggingface.co/api/spaces/${space_id}/discussions`, { method: "HEAD" });
|
|
1007
|
+
const error = r.headers.get("x-error-message");
|
|
1008
|
+
if (!r.ok || error && RE_DISABLED_DISCUSSION.test(error)) return false;
|
|
1009
|
+
return true;
|
|
1010
|
+
} catch (e) {
|
|
1011
|
+
return false;
|
|
1012
|
+
}
|
|
1013
|
+
}
|
|
1014
|
+
async function get_space_hardware(space_id, token) {
|
|
1015
|
+
const headers = {};
|
|
1016
|
+
if (token) headers.Authorization = `Bearer ${token}`;
|
|
1017
|
+
try {
|
|
1018
|
+
const res = await fetch(`https://huggingface.co/api/spaces/${space_id}/${RUNTIME_URL}`, { headers });
|
|
1019
|
+
if (res.status !== 200) throw new Error("Space hardware could not be obtained.");
|
|
1020
|
+
const { hardware } = await res.json();
|
|
1021
|
+
return hardware.current;
|
|
1022
|
+
} catch (e) {
|
|
1023
|
+
throw new Error(e.message);
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
1026
|
+
async function set_space_timeout(space_id, timeout, token) {
|
|
1027
|
+
const headers = {};
|
|
1028
|
+
if (token) headers.Authorization = `Bearer ${token}`;
|
|
1029
|
+
const body = { seconds: timeout };
|
|
1030
|
+
try {
|
|
1031
|
+
const res = await fetch(`https://huggingface.co/api/spaces/${space_id}/${SLEEPTIME_URL}`, {
|
|
1032
|
+
method: "POST",
|
|
1033
|
+
headers: {
|
|
1034
|
+
"Content-Type": "application/json",
|
|
1035
|
+
...headers
|
|
1036
|
+
},
|
|
1037
|
+
body: JSON.stringify(body)
|
|
1038
|
+
});
|
|
1039
|
+
if (res.status !== 200) throw new Error("Could not set sleep timeout on duplicated Space. Please visit *ADD HF LINK TO SETTINGS* to set a timeout manually to reduce billing charges.");
|
|
1040
|
+
return await res.json();
|
|
1041
|
+
} catch (e) {
|
|
1042
|
+
throw new Error(e.message);
|
|
1043
|
+
}
|
|
1044
|
+
}
|
|
1045
|
+
var hardware_types = [
|
|
1046
|
+
"cpu-basic",
|
|
1047
|
+
"cpu-upgrade",
|
|
1048
|
+
"cpu-xl",
|
|
1049
|
+
"t4-small",
|
|
1050
|
+
"t4-medium",
|
|
1051
|
+
"a10g-small",
|
|
1052
|
+
"a10g-large",
|
|
1053
|
+
"a10g-largex2",
|
|
1054
|
+
"a10g-largex4",
|
|
1055
|
+
"a100-large",
|
|
1056
|
+
"zero-a10g",
|
|
1057
|
+
"h100",
|
|
1058
|
+
"h100x8"
|
|
1059
|
+
];
|
|
1060
|
+
//#endregion
|
|
1061
|
+
//#region src/utils/duplicate.ts
|
|
1062
|
+
async function duplicate(app_reference, options) {
|
|
1063
|
+
normalise_token_option(options);
|
|
1064
|
+
const { token, private: _private, hardware, timeout, auth } = options;
|
|
1065
|
+
if (hardware && !hardware_types.includes(hardware)) throw new Error(`Invalid hardware type provided. Valid types are: ${hardware_types.map((v) => `"${v}"`).join(",")}.`);
|
|
1066
|
+
const { http_protocol, host } = await process_endpoint(app_reference, token);
|
|
1067
|
+
let cookies = null;
|
|
1068
|
+
if (auth) {
|
|
1069
|
+
const cookie_header = await get_cookie_header(http_protocol, host, auth, fetch, void 0, options.credentials);
|
|
1070
|
+
if (cookie_header) cookies = parse_and_set_cookies(cookie_header);
|
|
1071
|
+
}
|
|
1072
|
+
const headers = {
|
|
1073
|
+
Authorization: `Bearer ${token}`,
|
|
1074
|
+
"Content-Type": "application/json",
|
|
1075
|
+
...cookies ? { Cookie: cookies.join("; ") } : {}
|
|
1076
|
+
};
|
|
1077
|
+
const user = (await (await fetch(`https://huggingface.co/api/whoami-v2`, { headers })).json()).name;
|
|
1078
|
+
const space_name = app_reference.split("/")[1];
|
|
1079
|
+
const body = { repository: `${user}/${space_name}` };
|
|
1080
|
+
if (_private) body.private = true;
|
|
1081
|
+
let original_hardware;
|
|
1082
|
+
try {
|
|
1083
|
+
if (!hardware) original_hardware = await get_space_hardware(app_reference, token);
|
|
1084
|
+
} catch (e) {
|
|
1085
|
+
throw Error(SPACE_METADATA_ERROR_MSG + e.message);
|
|
1086
|
+
}
|
|
1087
|
+
body.hardware = hardware || original_hardware || "cpu-basic";
|
|
1088
|
+
try {
|
|
1089
|
+
const response = await fetch(`https://huggingface.co/api/spaces/${app_reference}/duplicate`, {
|
|
1090
|
+
method: "POST",
|
|
1091
|
+
headers,
|
|
1092
|
+
body: JSON.stringify(body)
|
|
1093
|
+
});
|
|
1094
|
+
if (response.status === 409) try {
|
|
1095
|
+
return await Client.connect(`${user}/${space_name}`, options);
|
|
1096
|
+
} catch (error) {
|
|
1097
|
+
console.error("Failed to connect Client instance:", error);
|
|
1098
|
+
throw error;
|
|
1099
|
+
}
|
|
1100
|
+
else if (response.status !== 200) throw new Error(response.statusText);
|
|
1101
|
+
const duplicated_space = await response.json();
|
|
1102
|
+
await set_space_timeout(`${user}/${space_name}`, timeout || 300, token);
|
|
1103
|
+
return await Client.connect(get_space_reference(duplicated_space.url), options);
|
|
1104
|
+
} catch (e) {
|
|
1105
|
+
throw new Error(e);
|
|
1106
|
+
}
|
|
1107
|
+
}
|
|
1108
|
+
function get_space_reference(url) {
|
|
1109
|
+
const match = url.match(/https:\/\/huggingface.co\/spaces\/([^/]+\/[^/]+)/);
|
|
1110
|
+
if (match) return match[1];
|
|
1111
|
+
}
|
|
1112
|
+
//#endregion
|
|
1113
|
+
//#region src/helpers/zerogpu.ts
|
|
1114
|
+
var ZEROGPU_HEADERS_MESSAGE = "supports-zerogpu-headers";
|
|
1115
|
+
var zerogpu_handshake_initialized = false;
|
|
1116
|
+
function supports_browser_handshake() {
|
|
1117
|
+
return typeof window !== "undefined" && typeof document !== "undefined" && typeof window.addEventListener === "function";
|
|
1118
|
+
}
|
|
1119
|
+
function get_zerogpu_origin(hostname) {
|
|
1120
|
+
if (hostname.includes(".dev.")) return `https://moon-${hostname.split(".")[1]}.dev.spaces.huggingface.tech`;
|
|
1121
|
+
if (hostname.endsWith(".hf.space")) return "https://huggingface.co";
|
|
1122
|
+
return null;
|
|
1123
|
+
}
|
|
1124
|
+
function initialize_zerogpu_handshake() {
|
|
1125
|
+
if (!supports_browser_handshake() || zerogpu_handshake_initialized) return;
|
|
1126
|
+
window.addEventListener("message", (event) => {
|
|
1127
|
+
if (event.data === ZEROGPU_HEADERS_MESSAGE) window.supports_zerogpu_headers = true;
|
|
1128
|
+
});
|
|
1129
|
+
zerogpu_handshake_initialized = true;
|
|
1130
|
+
const origin = get_zerogpu_origin(window.location.hostname);
|
|
1131
|
+
if (origin && window.parent !== window) window.parent.postMessage(ZEROGPU_HEADERS_MESSAGE, origin);
|
|
1132
|
+
}
|
|
1133
|
+
//#endregion
|
|
1134
|
+
//#region ../../node_modules/.pnpm/fetch-event-stream@0.1.6/node_modules/fetch-event-stream/esm/deps/jsr.io/@std/streams/0.221.0/text_line_stream.js
|
|
1135
|
+
/**
|
|
1136
|
+
* Transform a stream into a stream where each chunk is divided by a newline,
|
|
1137
|
+
* be it `\n` or `\r\n`. `\r` can be enabled via the `allowCR` option.
|
|
1138
|
+
*
|
|
1139
|
+
* @example
|
|
1140
|
+
* ```ts
|
|
1141
|
+
* import { TextLineStream } from "@std/streams/text-line-stream";
|
|
1142
|
+
*
|
|
1143
|
+
* const res = await fetch("https://example.com");
|
|
1144
|
+
* const lines = res.body!
|
|
1145
|
+
* .pipeThrough(new TextDecoderStream())
|
|
1146
|
+
* .pipeThrough(new TextLineStream());
|
|
1147
|
+
* ```
|
|
1148
|
+
*/
|
|
1149
|
+
var TextLineStream = class extends TransformStream {
|
|
1150
|
+
#currentLine = "";
|
|
1151
|
+
/** Constructs a new instance. */
|
|
1152
|
+
constructor(options = { allowCR: false }) {
|
|
1153
|
+
super({
|
|
1154
|
+
transform: (chars, controller) => {
|
|
1155
|
+
chars = this.#currentLine + chars;
|
|
1156
|
+
while (true) {
|
|
1157
|
+
const lfIndex = chars.indexOf("\n");
|
|
1158
|
+
const crIndex = options.allowCR ? chars.indexOf("\r") : -1;
|
|
1159
|
+
if (crIndex !== -1 && crIndex !== chars.length - 1 && (lfIndex === -1 || lfIndex - 1 > crIndex)) {
|
|
1160
|
+
controller.enqueue(chars.slice(0, crIndex));
|
|
1161
|
+
chars = chars.slice(crIndex + 1);
|
|
1162
|
+
continue;
|
|
1163
|
+
}
|
|
1164
|
+
if (lfIndex === -1) break;
|
|
1165
|
+
const endIndex = chars[lfIndex - 1] === "\r" ? lfIndex - 1 : lfIndex;
|
|
1166
|
+
controller.enqueue(chars.slice(0, endIndex));
|
|
1167
|
+
chars = chars.slice(lfIndex + 1);
|
|
1168
|
+
}
|
|
1169
|
+
this.#currentLine = chars;
|
|
1170
|
+
},
|
|
1171
|
+
flush: (controller) => {
|
|
1172
|
+
if (this.#currentLine === "") return;
|
|
1173
|
+
const currentLine = options.allowCR && this.#currentLine.endsWith("\r") ? this.#currentLine.slice(0, -1) : this.#currentLine;
|
|
1174
|
+
controller.enqueue(currentLine);
|
|
1175
|
+
}
|
|
1176
|
+
});
|
|
1177
|
+
}
|
|
1178
|
+
};
|
|
1179
|
+
//#endregion
|
|
1180
|
+
//#region ../../node_modules/.pnpm/fetch-event-stream@0.1.6/node_modules/fetch-event-stream/esm/utils.js
|
|
1181
|
+
function stream$1(input) {
|
|
1182
|
+
let decoder = new TextDecoderStream();
|
|
1183
|
+
let split = new TextLineStream({ allowCR: true });
|
|
1184
|
+
return input.pipeThrough(decoder).pipeThrough(split);
|
|
1185
|
+
}
|
|
1186
|
+
function split(input) {
|
|
1187
|
+
let match = /[:]\s*/.exec(input);
|
|
1188
|
+
let idx = match && match.index;
|
|
1189
|
+
if (idx) return [input.substring(0, idx), input.substring(idx + match[0].length)];
|
|
1190
|
+
}
|
|
1191
|
+
function fallback(headers, key, value) {
|
|
1192
|
+
if (!headers.get(key)) headers.set(key, value);
|
|
1193
|
+
}
|
|
1194
|
+
//#endregion
|
|
1195
|
+
//#region ../../node_modules/.pnpm/fetch-event-stream@0.1.6/node_modules/fetch-event-stream/esm/mod.js
|
|
1196
|
+
/**
|
|
1197
|
+
* Convert a `Response` body containing Server Sent Events (SSE) into an Async Iterator that yields {@linkcode ServerSentEventMessage} objects.
|
|
1198
|
+
*
|
|
1199
|
+
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events}
|
|
1200
|
+
*
|
|
1201
|
+
* @example
|
|
1202
|
+
* ```js
|
|
1203
|
+
* // Optional
|
|
1204
|
+
* let abort = new AbortController;
|
|
1205
|
+
*
|
|
1206
|
+
* // Manually fetch a Response
|
|
1207
|
+
* let res = await fetch('https://...', {
|
|
1208
|
+
* method: 'POST',
|
|
1209
|
+
* signal: abort.signal,
|
|
1210
|
+
* headers: {
|
|
1211
|
+
* 'api-key': 'token <value>',
|
|
1212
|
+
* 'content-type': 'application/json',
|
|
1213
|
+
* },
|
|
1214
|
+
* body: JSON.stringify({
|
|
1215
|
+
* stream: true, // <- hypothetical
|
|
1216
|
+
* // ...
|
|
1217
|
+
* })
|
|
1218
|
+
* });
|
|
1219
|
+
*
|
|
1220
|
+
* if (res.ok) {
|
|
1221
|
+
* let stream = events(res, abort.signal);
|
|
1222
|
+
* for await (let event of stream) {
|
|
1223
|
+
* console.log('<<', event.data);
|
|
1224
|
+
* }
|
|
1225
|
+
* }
|
|
1226
|
+
* ```
|
|
1227
|
+
*/
|
|
1228
|
+
async function* events(res, signal) {
|
|
1229
|
+
if (!res.body) return;
|
|
1230
|
+
let iter = stream$1(res.body);
|
|
1231
|
+
let line, reader = iter.getReader();
|
|
1232
|
+
let event;
|
|
1233
|
+
for (;;) {
|
|
1234
|
+
if (signal && signal.aborted) return reader.cancel();
|
|
1235
|
+
line = await reader.read();
|
|
1236
|
+
if (line.done) return;
|
|
1237
|
+
if (!line.value) {
|
|
1238
|
+
if (event) yield event;
|
|
1239
|
+
event = void 0;
|
|
1240
|
+
continue;
|
|
1241
|
+
}
|
|
1242
|
+
let [field, value] = split(line.value) || [];
|
|
1243
|
+
if (field === "data") {
|
|
1244
|
+
event ||= {};
|
|
1245
|
+
event[field] = event[field] ? event[field] + "\n" + value : value;
|
|
1246
|
+
} else if (field === "event") {
|
|
1247
|
+
event ||= {};
|
|
1248
|
+
event[field] = value;
|
|
1249
|
+
} else if (field === "id") {
|
|
1250
|
+
event ||= {};
|
|
1251
|
+
event[field] = String(+value) === value ? +value : value;
|
|
1252
|
+
} else if (field === "retry") {
|
|
1253
|
+
event ||= {};
|
|
1254
|
+
event[field] = +value || void 0;
|
|
1255
|
+
}
|
|
1256
|
+
}
|
|
1257
|
+
}
|
|
1258
|
+
/**
|
|
1259
|
+
* Convenience function that will `fetch` with the given arguments and, if ok, will return the {@linkcode events} async iterator.
|
|
1260
|
+
*
|
|
1261
|
+
* If the response is not ok (status 200-299), the `Response` is thrown.
|
|
1262
|
+
*
|
|
1263
|
+
* @example
|
|
1264
|
+
* ```js
|
|
1265
|
+
* // NOTE: throws `Response` if not 2xx status
|
|
1266
|
+
* let events = await stream('https://api.openai.com/...', {
|
|
1267
|
+
* method: 'POST',
|
|
1268
|
+
* headers: {
|
|
1269
|
+
* 'Authorization': 'Bearer <token>',
|
|
1270
|
+
* 'Content-Type': 'application/json',
|
|
1271
|
+
* },
|
|
1272
|
+
* body: JSON.stringify({
|
|
1273
|
+
* stream: true,
|
|
1274
|
+
* // ...
|
|
1275
|
+
* })
|
|
1276
|
+
* });
|
|
1277
|
+
*
|
|
1278
|
+
* for await (let event of events) {
|
|
1279
|
+
* console.log('<<', JSON.parse(event.data));
|
|
1280
|
+
* }
|
|
1281
|
+
* ```
|
|
1282
|
+
*/
|
|
1283
|
+
async function stream(input, init) {
|
|
1284
|
+
let req = new Request(input, init);
|
|
1285
|
+
fallback(req.headers, "Accept", "text/event-stream");
|
|
1286
|
+
fallback(req.headers, "Content-Type", "application/json");
|
|
1287
|
+
let r = await fetch(req);
|
|
1288
|
+
if (!r.ok) throw r;
|
|
1289
|
+
return events(r, req.signal);
|
|
1290
|
+
}
|
|
1291
|
+
//#endregion
|
|
1292
|
+
//#region src/utils/stream.ts
|
|
1293
|
+
async function open_stream() {
|
|
1294
|
+
let { event_callbacks, unclosed_events, pending_stream_messages, stream_status, config, jwt } = this;
|
|
1295
|
+
const that = this;
|
|
1296
|
+
if (!config) throw new Error("Could not resolve app config");
|
|
1297
|
+
stream_status.open = true;
|
|
1298
|
+
let stream = null;
|
|
1299
|
+
let params = new URLSearchParams({ session_hash: this.session_hash }).toString();
|
|
1300
|
+
let url = new URL(`${config.root}${this.api_prefix}/${SSE_URL}?${params}`);
|
|
1301
|
+
if (jwt) url.searchParams.set("__sign", jwt);
|
|
1302
|
+
stream = this.stream(url);
|
|
1303
|
+
if (!stream) {
|
|
1304
|
+
console.warn("Cannot connect to SSE endpoint: " + url.toString());
|
|
1305
|
+
return;
|
|
1306
|
+
}
|
|
1307
|
+
stream.onmessage = async function(event) {
|
|
1308
|
+
let _data = JSON.parse(event.data);
|
|
1309
|
+
if (_data.msg === "close_stream") {
|
|
1310
|
+
close_stream(stream_status, that.abort_controller);
|
|
1311
|
+
return;
|
|
1312
|
+
}
|
|
1313
|
+
const event_id = _data.event_id;
|
|
1314
|
+
if (!event_id) await Promise.all(Object.keys(event_callbacks).map((event_id) => event_callbacks[event_id](_data)));
|
|
1315
|
+
else if (event_callbacks[event_id] && config) {
|
|
1316
|
+
if (_data.msg === "process_completed" && [
|
|
1317
|
+
"sse",
|
|
1318
|
+
"sse_v1",
|
|
1319
|
+
"sse_v2",
|
|
1320
|
+
"sse_v2.1",
|
|
1321
|
+
"sse_v3"
|
|
1322
|
+
].includes(config.protocol)) unclosed_events.delete(event_id);
|
|
1323
|
+
let fn = event_callbacks[event_id];
|
|
1324
|
+
if (typeof window !== "undefined" && typeof document !== "undefined" && document.visibilityState !== "hidden") setTimeout(fn, 0, _data);
|
|
1325
|
+
else fn(_data);
|
|
1326
|
+
} else {
|
|
1327
|
+
if (!pending_stream_messages[event_id]) pending_stream_messages[event_id] = [];
|
|
1328
|
+
pending_stream_messages[event_id].push(_data);
|
|
1329
|
+
}
|
|
1330
|
+
};
|
|
1331
|
+
stream.onerror = async function(e) {
|
|
1332
|
+
console.error(e);
|
|
1333
|
+
await Promise.all(Object.keys(event_callbacks).map((event_id) => event_callbacks[event_id]({
|
|
1334
|
+
msg: "broken_connection",
|
|
1335
|
+
message: BROKEN_CONNECTION_MSG
|
|
1336
|
+
})));
|
|
1337
|
+
};
|
|
1338
|
+
}
|
|
1339
|
+
function close_stream(stream_status, abort_controller) {
|
|
1340
|
+
if (stream_status) {
|
|
1341
|
+
stream_status.open = false;
|
|
1342
|
+
abort_controller?.abort();
|
|
1343
|
+
}
|
|
1344
|
+
}
|
|
1345
|
+
function apply_diff_stream(pending_diff_streams, event_id, data) {
|
|
1346
|
+
if (!pending_diff_streams[event_id]) {
|
|
1347
|
+
pending_diff_streams[event_id] = [];
|
|
1348
|
+
data.data.forEach((value, i) => {
|
|
1349
|
+
pending_diff_streams[event_id][i] = value;
|
|
1350
|
+
});
|
|
1351
|
+
} else data.data.forEach((value, i) => {
|
|
1352
|
+
let new_data = apply_diff(i < pending_diff_streams[event_id].length ? pending_diff_streams[event_id][i] : null, value);
|
|
1353
|
+
pending_diff_streams[event_id][i] = new_data;
|
|
1354
|
+
data.data[i] = new_data;
|
|
1355
|
+
});
|
|
1356
|
+
}
|
|
1357
|
+
function apply_diff(obj, diff) {
|
|
1358
|
+
diff.forEach(([action, path, value]) => {
|
|
1359
|
+
obj = apply_edit(obj, path, action, value);
|
|
1360
|
+
});
|
|
1361
|
+
return obj;
|
|
1362
|
+
}
|
|
1363
|
+
function apply_edit(target, path, action, value) {
|
|
1364
|
+
if (path.length === 0) {
|
|
1365
|
+
if (action === "replace") return value;
|
|
1366
|
+
else if (action === "append") return target + value;
|
|
1367
|
+
throw new Error(`Unsupported action: ${action}`);
|
|
1368
|
+
}
|
|
1369
|
+
let current = target;
|
|
1370
|
+
for (let i = 0; i < path.length - 1; i++) current = current[path[i]];
|
|
1371
|
+
const last_path = path[path.length - 1];
|
|
1372
|
+
switch (action) {
|
|
1373
|
+
case "replace":
|
|
1374
|
+
current[last_path] = value;
|
|
1375
|
+
break;
|
|
1376
|
+
case "append":
|
|
1377
|
+
current[last_path] += value;
|
|
1378
|
+
break;
|
|
1379
|
+
case "add":
|
|
1380
|
+
if (Array.isArray(current)) current.splice(Number(last_path), 0, value);
|
|
1381
|
+
else current[last_path] = value;
|
|
1382
|
+
break;
|
|
1383
|
+
case "delete":
|
|
1384
|
+
if (Array.isArray(current)) current.splice(Number(last_path), 1);
|
|
1385
|
+
else delete current[last_path];
|
|
1386
|
+
break;
|
|
1387
|
+
default: throw new Error(`Unknown action: ${action}`);
|
|
1388
|
+
}
|
|
1389
|
+
return target;
|
|
1390
|
+
}
|
|
1391
|
+
function readable_stream(input, init = {}) {
|
|
1392
|
+
const instance = {
|
|
1393
|
+
close: () => {
|
|
1394
|
+
console.warn("Method not implemented.");
|
|
1395
|
+
},
|
|
1396
|
+
onerror: null,
|
|
1397
|
+
onmessage: null,
|
|
1398
|
+
onopen: null,
|
|
1399
|
+
readyState: 0,
|
|
1400
|
+
url: input.toString(),
|
|
1401
|
+
withCredentials: false,
|
|
1402
|
+
CONNECTING: 0,
|
|
1403
|
+
OPEN: 1,
|
|
1404
|
+
CLOSED: 2,
|
|
1405
|
+
addEventListener: () => {
|
|
1406
|
+
throw new Error("Method not implemented.");
|
|
1407
|
+
},
|
|
1408
|
+
dispatchEvent: () => {
|
|
1409
|
+
throw new Error("Method not implemented.");
|
|
1410
|
+
},
|
|
1411
|
+
removeEventListener: () => {
|
|
1412
|
+
throw new Error("Method not implemented.");
|
|
1413
|
+
}
|
|
1414
|
+
};
|
|
1415
|
+
stream(input, init).then(async (res) => {
|
|
1416
|
+
instance.readyState = instance.OPEN;
|
|
1417
|
+
try {
|
|
1418
|
+
for await (const chunk of res) instance.onmessage && instance.onmessage(chunk);
|
|
1419
|
+
instance.readyState = instance.CLOSED;
|
|
1420
|
+
} catch (e) {
|
|
1421
|
+
instance.onerror && instance.onerror(e);
|
|
1422
|
+
instance.readyState = instance.CLOSED;
|
|
1423
|
+
}
|
|
1424
|
+
}).catch((e) => {
|
|
1425
|
+
console.error(e);
|
|
1426
|
+
instance.onerror && instance.onerror(e);
|
|
1427
|
+
instance.readyState = instance.CLOSED;
|
|
1428
|
+
});
|
|
1429
|
+
return instance;
|
|
1430
|
+
}
|
|
1431
|
+
//#endregion
|
|
1432
|
+
//#region src/utils/submit.ts
|
|
1433
|
+
function submit(endpoint, data = {}, event_data, trigger_id, all_events, additional_headers) {
|
|
1434
|
+
try {
|
|
1435
|
+
const { token } = this.options;
|
|
1436
|
+
const { fetch, app_reference, config, session_hash, api_info, api_map, stream_status, pending_stream_messages, pending_diff_streams, event_callbacks, unclosed_events, post_data, options, api_prefix } = this;
|
|
1437
|
+
const addt_headers = additional_headers || { "x-gradio-user": "api" };
|
|
1438
|
+
const that = this;
|
|
1439
|
+
if (!api_info) throw new Error(NO_API_INFO_MSG);
|
|
1440
|
+
if (!config) throw new Error("Could not resolve app config");
|
|
1441
|
+
let { fn_index, endpoint_info, dependency } = get_endpoint_info(api_info, endpoint, api_map, config);
|
|
1442
|
+
let resolved_data = map_data_to_params(data, endpoint_info);
|
|
1443
|
+
let stream;
|
|
1444
|
+
let protocol = config.protocol ?? "ws";
|
|
1445
|
+
if (protocol === "ws") throw new Error(WS_PROTOCOL_MSG);
|
|
1446
|
+
let event_id_final = "";
|
|
1447
|
+
const _endpoint = typeof endpoint === "number" ? "/predict" : endpoint;
|
|
1448
|
+
let payload;
|
|
1449
|
+
let event_id = null;
|
|
1450
|
+
let complete = false;
|
|
1451
|
+
let last_status = {};
|
|
1452
|
+
let url_params = typeof window !== "undefined" && typeof document !== "undefined" ? new URLSearchParams(window.location.search).toString() : "";
|
|
1453
|
+
const events_to_publish = options?.events?.reduce((acc, event) => {
|
|
1454
|
+
acc[event] = true;
|
|
1455
|
+
return acc;
|
|
1456
|
+
}, {}) || {};
|
|
1457
|
+
function fire_event(event) {
|
|
1458
|
+
if (all_events || events_to_publish[event.type]) push_event(event);
|
|
1459
|
+
}
|
|
1460
|
+
async function cancel() {
|
|
1461
|
+
let reset_request = {};
|
|
1462
|
+
let cancel_request = {};
|
|
1463
|
+
reset_request = { event_id };
|
|
1464
|
+
cancel_request = {
|
|
1465
|
+
event_id,
|
|
1466
|
+
session_hash,
|
|
1467
|
+
fn_index
|
|
1468
|
+
};
|
|
1469
|
+
try {
|
|
1470
|
+
if (!config) throw new Error("Could not resolve app config");
|
|
1471
|
+
if ("event_id" in cancel_request) await fetch(`${config.root}${api_prefix}/${CANCEL_URL}`, {
|
|
1472
|
+
headers: { "Content-Type": "application/json" },
|
|
1473
|
+
method: "POST",
|
|
1474
|
+
body: JSON.stringify(cancel_request)
|
|
1475
|
+
});
|
|
1476
|
+
await fetch(`${config.root}${api_prefix}/${RESET_URL}`, {
|
|
1477
|
+
headers: { "Content-Type": "application/json" },
|
|
1478
|
+
method: "POST",
|
|
1479
|
+
body: JSON.stringify(reset_request)
|
|
1480
|
+
});
|
|
1481
|
+
} catch (e) {
|
|
1482
|
+
console.warn("The `/reset` endpoint could not be called. Subsequent endpoint results may be unreliable.");
|
|
1483
|
+
}
|
|
1484
|
+
}
|
|
1485
|
+
const resolve_heartbeat = async (config) => {
|
|
1486
|
+
await this._resolve_heartbeat(config);
|
|
1487
|
+
};
|
|
1488
|
+
async function handle_render_config(render_config) {
|
|
1489
|
+
if (!config) return;
|
|
1490
|
+
let render_id = render_config.render_id;
|
|
1491
|
+
config.components = [...config.components.filter((c) => c.props.rendered_in !== render_id), ...render_config.components];
|
|
1492
|
+
config.dependencies = [...config.dependencies.filter((d) => d.rendered_in !== render_id), ...render_config.dependencies];
|
|
1493
|
+
const any_state = config.components.some((c) => c.type === "state");
|
|
1494
|
+
const any_unload = config.dependencies.some((d) => d.targets.some((t) => t[1] === "unload"));
|
|
1495
|
+
config.connect_heartbeat = any_state || any_unload;
|
|
1496
|
+
await resolve_heartbeat(config);
|
|
1497
|
+
fire_event({
|
|
1498
|
+
type: "render",
|
|
1499
|
+
data: render_config,
|
|
1500
|
+
endpoint: _endpoint,
|
|
1501
|
+
fn_index
|
|
1502
|
+
});
|
|
1503
|
+
}
|
|
1504
|
+
const job = this.handle_blob(config.root, resolved_data, endpoint_info).then(async (_payload) => {
|
|
1505
|
+
payload = {
|
|
1506
|
+
data: handle_payload(_payload, dependency, config.components, "input", true) || [],
|
|
1507
|
+
event_data,
|
|
1508
|
+
fn_index,
|
|
1509
|
+
trigger_id,
|
|
1510
|
+
...options.oauth_token && endpoint_info?.oauth_token ? { oauth_token: options.oauth_token } : {}
|
|
1511
|
+
};
|
|
1512
|
+
if (skip_queue(fn_index, config)) {
|
|
1513
|
+
fire_event({
|
|
1514
|
+
type: "status",
|
|
1515
|
+
endpoint: _endpoint,
|
|
1516
|
+
stage: "pending",
|
|
1517
|
+
queue: false,
|
|
1518
|
+
fn_index,
|
|
1519
|
+
time: /* @__PURE__ */ new Date()
|
|
1520
|
+
});
|
|
1521
|
+
post_data(`${config.root}${api_prefix}/run${_endpoint.startsWith("/") ? _endpoint : `/${_endpoint}`}${url_params ? "?" + url_params : ""}`, {
|
|
1522
|
+
...payload,
|
|
1523
|
+
session_hash
|
|
1524
|
+
}, addt_headers).then(async ([output, status_code]) => {
|
|
1525
|
+
const data = output.data;
|
|
1526
|
+
if (status_code == 200) {
|
|
1527
|
+
fire_event({
|
|
1528
|
+
type: "data",
|
|
1529
|
+
endpoint: _endpoint,
|
|
1530
|
+
fn_index,
|
|
1531
|
+
data: handle_payload(data, dependency, config.components, "output", options.with_null_state),
|
|
1532
|
+
time: /* @__PURE__ */ new Date(),
|
|
1533
|
+
event_data,
|
|
1534
|
+
trigger_id
|
|
1535
|
+
});
|
|
1536
|
+
if (output.render_config) await handle_render_config(output.render_config);
|
|
1537
|
+
fire_event({
|
|
1538
|
+
type: "status",
|
|
1539
|
+
endpoint: _endpoint,
|
|
1540
|
+
fn_index,
|
|
1541
|
+
stage: "complete",
|
|
1542
|
+
eta: output.average_duration,
|
|
1543
|
+
queue: false,
|
|
1544
|
+
time: /* @__PURE__ */ new Date()
|
|
1545
|
+
});
|
|
1546
|
+
} else {
|
|
1547
|
+
const is_connection_error = output?.error === BROKEN_CONNECTION_MSG;
|
|
1548
|
+
fire_event({
|
|
1549
|
+
type: "status",
|
|
1550
|
+
stage: "error",
|
|
1551
|
+
endpoint: _endpoint,
|
|
1552
|
+
fn_index,
|
|
1553
|
+
message: output.error,
|
|
1554
|
+
broken: is_connection_error,
|
|
1555
|
+
queue: false,
|
|
1556
|
+
time: /* @__PURE__ */ new Date()
|
|
1557
|
+
});
|
|
1558
|
+
}
|
|
1559
|
+
}).catch((e) => {
|
|
1560
|
+
fire_event({
|
|
1561
|
+
type: "status",
|
|
1562
|
+
stage: "error",
|
|
1563
|
+
message: e.message,
|
|
1564
|
+
endpoint: _endpoint,
|
|
1565
|
+
fn_index,
|
|
1566
|
+
queue: false,
|
|
1567
|
+
time: /* @__PURE__ */ new Date()
|
|
1568
|
+
});
|
|
1569
|
+
});
|
|
1570
|
+
} else if (protocol == "sse") {
|
|
1571
|
+
fire_event({
|
|
1572
|
+
type: "status",
|
|
1573
|
+
stage: "pending",
|
|
1574
|
+
queue: true,
|
|
1575
|
+
endpoint: _endpoint,
|
|
1576
|
+
fn_index,
|
|
1577
|
+
time: /* @__PURE__ */ new Date()
|
|
1578
|
+
});
|
|
1579
|
+
var params = new URLSearchParams({
|
|
1580
|
+
fn_index: fn_index.toString(),
|
|
1581
|
+
session_hash
|
|
1582
|
+
}).toString();
|
|
1583
|
+
let url = new URL(`${config.root}${api_prefix}/${SSE_URL}?${url_params ? url_params + "&" : ""}${params}`);
|
|
1584
|
+
if (this.jwt) url.searchParams.set("__sign", this.jwt);
|
|
1585
|
+
stream = this.stream(url);
|
|
1586
|
+
if (!stream) return Promise.reject(/* @__PURE__ */ new Error("Cannot connect to SSE endpoint: " + url.toString()));
|
|
1587
|
+
stream.onmessage = async function(event) {
|
|
1588
|
+
const { type, status, data } = handle_message(JSON.parse(event.data), last_status[fn_index]);
|
|
1589
|
+
if (type === "update" && status && !complete) {
|
|
1590
|
+
fire_event({
|
|
1591
|
+
type: "status",
|
|
1592
|
+
endpoint: _endpoint,
|
|
1593
|
+
fn_index,
|
|
1594
|
+
time: /* @__PURE__ */ new Date(),
|
|
1595
|
+
...status
|
|
1596
|
+
});
|
|
1597
|
+
if (status.stage === "error") {
|
|
1598
|
+
stream?.close();
|
|
1599
|
+
close();
|
|
1600
|
+
}
|
|
1601
|
+
} else if (type === "data") {
|
|
1602
|
+
let [_, status] = await post_data(`${config.root}${api_prefix}/queue/data`, {
|
|
1603
|
+
...payload,
|
|
1604
|
+
session_hash,
|
|
1605
|
+
event_id
|
|
1606
|
+
});
|
|
1607
|
+
if (status !== 200) {
|
|
1608
|
+
fire_event({
|
|
1609
|
+
type: "status",
|
|
1610
|
+
stage: "error",
|
|
1611
|
+
message: BROKEN_CONNECTION_MSG,
|
|
1612
|
+
queue: true,
|
|
1613
|
+
endpoint: _endpoint,
|
|
1614
|
+
fn_index,
|
|
1615
|
+
time: /* @__PURE__ */ new Date()
|
|
1616
|
+
});
|
|
1617
|
+
stream?.close();
|
|
1618
|
+
close();
|
|
1619
|
+
}
|
|
1620
|
+
} else if (type === "complete") complete = status;
|
|
1621
|
+
else if (type === "log") fire_event({
|
|
1622
|
+
type: "log",
|
|
1623
|
+
title: data.title,
|
|
1624
|
+
log: data.log,
|
|
1625
|
+
level: data.level,
|
|
1626
|
+
endpoint: _endpoint,
|
|
1627
|
+
duration: data.duration,
|
|
1628
|
+
visible: data.visible,
|
|
1629
|
+
fn_index
|
|
1630
|
+
});
|
|
1631
|
+
else if (type === "generating" || type === "streaming") fire_event({
|
|
1632
|
+
type: "status",
|
|
1633
|
+
time: /* @__PURE__ */ new Date(),
|
|
1634
|
+
...status,
|
|
1635
|
+
stage: status?.stage,
|
|
1636
|
+
queue: true,
|
|
1637
|
+
endpoint: _endpoint,
|
|
1638
|
+
fn_index
|
|
1639
|
+
});
|
|
1640
|
+
if (data) {
|
|
1641
|
+
fire_event({
|
|
1642
|
+
type: "data",
|
|
1643
|
+
time: /* @__PURE__ */ new Date(),
|
|
1644
|
+
data: handle_payload(data.data, dependency, config.components, "output", options.with_null_state),
|
|
1645
|
+
endpoint: _endpoint,
|
|
1646
|
+
fn_index,
|
|
1647
|
+
event_data,
|
|
1648
|
+
trigger_id
|
|
1649
|
+
});
|
|
1650
|
+
if (complete) {
|
|
1651
|
+
fire_event({
|
|
1652
|
+
type: "status",
|
|
1653
|
+
time: /* @__PURE__ */ new Date(),
|
|
1654
|
+
...complete,
|
|
1655
|
+
stage: status?.stage,
|
|
1656
|
+
queue: true,
|
|
1657
|
+
endpoint: _endpoint,
|
|
1658
|
+
fn_index
|
|
1659
|
+
});
|
|
1660
|
+
stream?.close();
|
|
1661
|
+
close();
|
|
1662
|
+
}
|
|
1663
|
+
}
|
|
1664
|
+
};
|
|
1665
|
+
} else if (protocol == "sse_v1" || protocol == "sse_v2" || protocol == "sse_v2.1" || protocol == "sse_v3") {
|
|
1666
|
+
fire_event({
|
|
1667
|
+
type: "status",
|
|
1668
|
+
stage: "pending",
|
|
1669
|
+
queue: true,
|
|
1670
|
+
endpoint: _endpoint,
|
|
1671
|
+
fn_index,
|
|
1672
|
+
time: /* @__PURE__ */ new Date()
|
|
1673
|
+
});
|
|
1674
|
+
let hostname = "";
|
|
1675
|
+
if (typeof window !== "undefined" && typeof document !== "undefined") hostname = window?.location?.hostname;
|
|
1676
|
+
const origin = get_zerogpu_origin(hostname);
|
|
1677
|
+
return (typeof window !== "undefined" && typeof document !== "undefined" && window.parent != window && !!origin && window.supports_zerogpu_headers ? post_message("zerogpu-headers", origin) : Promise.resolve(null)).then((headers) => {
|
|
1678
|
+
const combined_headers = {
|
|
1679
|
+
...addt_headers,
|
|
1680
|
+
...headers || {}
|
|
1681
|
+
};
|
|
1682
|
+
return post_data(`${config.root}${api_prefix}/${SSE_DATA_URL}?${url_params}`, {
|
|
1683
|
+
...payload,
|
|
1684
|
+
session_hash
|
|
1685
|
+
}, combined_headers);
|
|
1686
|
+
}).then(async ([response, status]) => {
|
|
1687
|
+
if (response.event_id) event_id_final = response.event_id;
|
|
1688
|
+
if (status === 503) {
|
|
1689
|
+
fire_event({
|
|
1690
|
+
type: "status",
|
|
1691
|
+
stage: "error",
|
|
1692
|
+
message: QUEUE_FULL_MSG,
|
|
1693
|
+
queue: true,
|
|
1694
|
+
endpoint: _endpoint,
|
|
1695
|
+
fn_index,
|
|
1696
|
+
time: /* @__PURE__ */ new Date(),
|
|
1697
|
+
visible: true
|
|
1698
|
+
});
|
|
1699
|
+
close();
|
|
1700
|
+
} else if (status === 422) {
|
|
1701
|
+
fire_event({
|
|
1702
|
+
type: "status",
|
|
1703
|
+
stage: "error",
|
|
1704
|
+
message: response.detail,
|
|
1705
|
+
queue: true,
|
|
1706
|
+
endpoint: _endpoint,
|
|
1707
|
+
fn_index,
|
|
1708
|
+
code: "validation_error",
|
|
1709
|
+
time: /* @__PURE__ */ new Date(),
|
|
1710
|
+
visible: true
|
|
1711
|
+
});
|
|
1712
|
+
close();
|
|
1713
|
+
} else if (status !== 200) {
|
|
1714
|
+
const is_connection_error = response?.error === BROKEN_CONNECTION_MSG;
|
|
1715
|
+
fire_event({
|
|
1716
|
+
type: "status",
|
|
1717
|
+
stage: "error",
|
|
1718
|
+
broken: is_connection_error,
|
|
1719
|
+
message: is_connection_error ? BROKEN_CONNECTION_MSG : response.detail || response.error,
|
|
1720
|
+
queue: true,
|
|
1721
|
+
endpoint: _endpoint,
|
|
1722
|
+
fn_index,
|
|
1723
|
+
time: /* @__PURE__ */ new Date(),
|
|
1724
|
+
visible: true
|
|
1725
|
+
});
|
|
1726
|
+
close();
|
|
1727
|
+
} else {
|
|
1728
|
+
event_id = response.event_id;
|
|
1729
|
+
event_id_final = event_id;
|
|
1730
|
+
let callback = async function(_data) {
|
|
1731
|
+
try {
|
|
1732
|
+
const { type, status, data, original_msg } = handle_message(_data, last_status[fn_index]);
|
|
1733
|
+
if (type == "heartbeat") return;
|
|
1734
|
+
if (type === "update" && status && !complete) fire_event({
|
|
1735
|
+
type: "status",
|
|
1736
|
+
endpoint: _endpoint,
|
|
1737
|
+
fn_index,
|
|
1738
|
+
time: /* @__PURE__ */ new Date(),
|
|
1739
|
+
original_msg,
|
|
1740
|
+
...status
|
|
1741
|
+
});
|
|
1742
|
+
else if (type === "complete") complete = status;
|
|
1743
|
+
else if (type == "unexpected_error" || type == "broken_connection") {
|
|
1744
|
+
console.error("Unexpected error", status?.message);
|
|
1745
|
+
const broken = type === "broken_connection";
|
|
1746
|
+
fire_event({
|
|
1747
|
+
type: "status",
|
|
1748
|
+
stage: "error",
|
|
1749
|
+
message: status?.message || "An Unexpected Error Occurred!",
|
|
1750
|
+
queue: true,
|
|
1751
|
+
endpoint: _endpoint,
|
|
1752
|
+
broken,
|
|
1753
|
+
session_not_found: status?.session_not_found,
|
|
1754
|
+
fn_index,
|
|
1755
|
+
time: /* @__PURE__ */ new Date()
|
|
1756
|
+
});
|
|
1757
|
+
} else if (type === "log") {
|
|
1758
|
+
fire_event({
|
|
1759
|
+
type: "log",
|
|
1760
|
+
title: data.title,
|
|
1761
|
+
log: data.log,
|
|
1762
|
+
level: data.level,
|
|
1763
|
+
endpoint: _endpoint,
|
|
1764
|
+
duration: data.duration,
|
|
1765
|
+
visible: data.visible,
|
|
1766
|
+
fn_index
|
|
1767
|
+
});
|
|
1768
|
+
return;
|
|
1769
|
+
} else if (type === "generating" || type === "streaming") {
|
|
1770
|
+
fire_event({
|
|
1771
|
+
type: "status",
|
|
1772
|
+
time: /* @__PURE__ */ new Date(),
|
|
1773
|
+
...status,
|
|
1774
|
+
stage: status?.stage,
|
|
1775
|
+
queue: true,
|
|
1776
|
+
endpoint: _endpoint,
|
|
1777
|
+
fn_index
|
|
1778
|
+
});
|
|
1779
|
+
if (data && dependency.connection !== "stream" && [
|
|
1780
|
+
"sse_v2",
|
|
1781
|
+
"sse_v2.1",
|
|
1782
|
+
"sse_v3"
|
|
1783
|
+
].includes(protocol)) apply_diff_stream(pending_diff_streams, event_id, data);
|
|
1784
|
+
}
|
|
1785
|
+
if (data) {
|
|
1786
|
+
fire_event({
|
|
1787
|
+
type: "data",
|
|
1788
|
+
time: /* @__PURE__ */ new Date(),
|
|
1789
|
+
data: handle_payload(data.data, dependency, config.components, "output", options.with_null_state),
|
|
1790
|
+
endpoint: _endpoint,
|
|
1791
|
+
fn_index
|
|
1792
|
+
});
|
|
1793
|
+
if (data.render_config) await handle_render_config(data.render_config);
|
|
1794
|
+
if (complete) {
|
|
1795
|
+
fire_event({
|
|
1796
|
+
type: "status",
|
|
1797
|
+
time: /* @__PURE__ */ new Date(),
|
|
1798
|
+
...complete,
|
|
1799
|
+
stage: status?.stage,
|
|
1800
|
+
queue: true,
|
|
1801
|
+
endpoint: _endpoint,
|
|
1802
|
+
fn_index
|
|
1803
|
+
});
|
|
1804
|
+
close();
|
|
1805
|
+
}
|
|
1806
|
+
}
|
|
1807
|
+
if (status?.stage === "complete" || status?.stage === "error") {
|
|
1808
|
+
if (event_callbacks[event_id]) delete event_callbacks[event_id];
|
|
1809
|
+
if (event_id in pending_diff_streams) delete pending_diff_streams[event_id];
|
|
1810
|
+
close();
|
|
1811
|
+
}
|
|
1812
|
+
} catch (e) {
|
|
1813
|
+
console.error("Unexpected client exception", e);
|
|
1814
|
+
fire_event({
|
|
1815
|
+
type: "status",
|
|
1816
|
+
stage: "error",
|
|
1817
|
+
message: "An Unexpected Error Occurred!",
|
|
1818
|
+
queue: true,
|
|
1819
|
+
endpoint: _endpoint,
|
|
1820
|
+
fn_index,
|
|
1821
|
+
time: /* @__PURE__ */ new Date()
|
|
1822
|
+
});
|
|
1823
|
+
if ([
|
|
1824
|
+
"sse_v2",
|
|
1825
|
+
"sse_v2.1",
|
|
1826
|
+
"sse_v3"
|
|
1827
|
+
].includes(protocol)) {
|
|
1828
|
+
close_stream(stream_status, that.abort_controller);
|
|
1829
|
+
stream_status.open = false;
|
|
1830
|
+
close();
|
|
1831
|
+
}
|
|
1832
|
+
}
|
|
1833
|
+
};
|
|
1834
|
+
if (event_id in pending_stream_messages) {
|
|
1835
|
+
pending_stream_messages[event_id].forEach((msg) => callback(msg));
|
|
1836
|
+
delete pending_stream_messages[event_id];
|
|
1837
|
+
}
|
|
1838
|
+
event_callbacks[event_id] = callback;
|
|
1839
|
+
unclosed_events.add(event_id);
|
|
1840
|
+
if (!stream_status.open) await this.open_stream();
|
|
1841
|
+
}
|
|
1842
|
+
});
|
|
1843
|
+
}
|
|
1844
|
+
});
|
|
1845
|
+
job.catch((e) => {
|
|
1846
|
+
fire_event({
|
|
1847
|
+
type: "status",
|
|
1848
|
+
stage: "error",
|
|
1849
|
+
message: e instanceof Error ? e.message : String(e),
|
|
1850
|
+
queue: !skip_queue(fn_index, config),
|
|
1851
|
+
endpoint: _endpoint,
|
|
1852
|
+
fn_index,
|
|
1853
|
+
time: /* @__PURE__ */ new Date()
|
|
1854
|
+
});
|
|
1855
|
+
close();
|
|
1856
|
+
});
|
|
1857
|
+
let done = false;
|
|
1858
|
+
const values = [];
|
|
1859
|
+
const resolvers = [];
|
|
1860
|
+
function close() {
|
|
1861
|
+
done = true;
|
|
1862
|
+
while (resolvers.length > 0) resolvers.shift()({
|
|
1863
|
+
value: void 0,
|
|
1864
|
+
done: true
|
|
1865
|
+
});
|
|
1866
|
+
}
|
|
1867
|
+
function push(data) {
|
|
1868
|
+
if (resolvers.length > 0) resolvers.shift()(data);
|
|
1869
|
+
else values.push(data);
|
|
1870
|
+
}
|
|
1871
|
+
function push_error(error) {
|
|
1872
|
+
push(thenable_reject(error));
|
|
1873
|
+
close();
|
|
1874
|
+
}
|
|
1875
|
+
function push_event(event) {
|
|
1876
|
+
push({
|
|
1877
|
+
value: event,
|
|
1878
|
+
done: false
|
|
1879
|
+
});
|
|
1880
|
+
}
|
|
1881
|
+
function next() {
|
|
1882
|
+
if (values.length > 0) return Promise.resolve(values.shift());
|
|
1883
|
+
if (done) return Promise.resolve({
|
|
1884
|
+
value: void 0,
|
|
1885
|
+
done: true
|
|
1886
|
+
});
|
|
1887
|
+
return new Promise((resolve) => resolvers.push(resolve));
|
|
1888
|
+
}
|
|
1889
|
+
const iterator = {
|
|
1890
|
+
[Symbol.asyncIterator]: () => iterator,
|
|
1891
|
+
next,
|
|
1892
|
+
throw: async (value) => {
|
|
1893
|
+
push_error(value);
|
|
1894
|
+
return next();
|
|
1895
|
+
},
|
|
1896
|
+
return: async () => {
|
|
1897
|
+
close();
|
|
1898
|
+
return {
|
|
1899
|
+
value: void 0,
|
|
1900
|
+
done: true
|
|
1901
|
+
};
|
|
1902
|
+
},
|
|
1903
|
+
cancel,
|
|
1904
|
+
send_chunk: (payload) => {
|
|
1905
|
+
this.post_data(`${config.root}${api_prefix}/stream/${event_id_final}`, {
|
|
1906
|
+
...payload,
|
|
1907
|
+
session_hash: this.session_hash
|
|
1908
|
+
});
|
|
1909
|
+
},
|
|
1910
|
+
close_stream: () => {
|
|
1911
|
+
this.post_data(`${config.root}${api_prefix}/stream/${event_id_final}/close`, {});
|
|
1912
|
+
close();
|
|
1913
|
+
},
|
|
1914
|
+
event_id: () => event_id_final,
|
|
1915
|
+
wait_for_id: async () => {
|
|
1916
|
+
await job;
|
|
1917
|
+
return event_id;
|
|
1918
|
+
}
|
|
1919
|
+
};
|
|
1920
|
+
return iterator;
|
|
1921
|
+
} catch (error) {
|
|
1922
|
+
console.error("Submit function encountered an error:", error);
|
|
1923
|
+
throw error;
|
|
1924
|
+
}
|
|
1925
|
+
}
|
|
1926
|
+
function thenable_reject(error) {
|
|
1927
|
+
return { then: (resolve, reject) => reject(error) };
|
|
1928
|
+
}
|
|
1929
|
+
function get_endpoint_info(api_info, endpoint, api_map, config) {
|
|
1930
|
+
let fn_index;
|
|
1931
|
+
let endpoint_info;
|
|
1932
|
+
let dependency;
|
|
1933
|
+
if (typeof endpoint === "number") {
|
|
1934
|
+
fn_index = endpoint;
|
|
1935
|
+
endpoint_info = api_info.unnamed_endpoints[fn_index];
|
|
1936
|
+
dependency = config.dependencies.find((dep) => dep.id == endpoint);
|
|
1937
|
+
} else {
|
|
1938
|
+
const trimmed_endpoint = endpoint.replace(/^\//, "");
|
|
1939
|
+
fn_index = api_map[trimmed_endpoint];
|
|
1940
|
+
endpoint_info = api_info.named_endpoints[endpoint.trim()] ?? api_info.named_endpoints[`/${trimmed_endpoint}`];
|
|
1941
|
+
dependency = config.dependencies.find((dep) => dep.id == api_map[trimmed_endpoint]);
|
|
1942
|
+
}
|
|
1943
|
+
if (typeof fn_index !== "number" || !dependency) {
|
|
1944
|
+
const valid_endpoints = config.dependencies.filter((dep) => dep.api_name).map((dep) => `"/${dep.api_name}"`).join(", ");
|
|
1945
|
+
throw new Error(`No endpoint matching ${JSON.stringify(endpoint)} was found. ` + (valid_endpoints ? `Valid named endpoints are: ${valid_endpoints}. ` : "This app exposes no named endpoints. ") + "An fn_index (number) of an existing dependency can also be used.");
|
|
1946
|
+
}
|
|
1947
|
+
return {
|
|
1948
|
+
fn_index,
|
|
1949
|
+
endpoint_info,
|
|
1950
|
+
dependency
|
|
1951
|
+
};
|
|
1952
|
+
}
|
|
1953
|
+
//#endregion
|
|
1954
|
+
//#region src/client.ts
|
|
1955
|
+
var Client = class {
|
|
1956
|
+
app_reference;
|
|
1957
|
+
options;
|
|
1958
|
+
deep_link = null;
|
|
1959
|
+
config;
|
|
1960
|
+
api_prefix = "";
|
|
1961
|
+
api_info;
|
|
1962
|
+
api_map = {};
|
|
1963
|
+
session_hash = Math.random().toString(36).substring(2);
|
|
1964
|
+
jwt = false;
|
|
1965
|
+
last_status = {};
|
|
1966
|
+
cookies = null;
|
|
1967
|
+
stream_status = { open: false };
|
|
1968
|
+
closed = false;
|
|
1969
|
+
pending_stream_messages = {};
|
|
1970
|
+
pending_diff_streams = {};
|
|
1971
|
+
event_callbacks = {};
|
|
1972
|
+
unclosed_events = /* @__PURE__ */ new Set();
|
|
1973
|
+
heartbeat_event = null;
|
|
1974
|
+
abort_controller = null;
|
|
1975
|
+
stream_instance = null;
|
|
1976
|
+
current_payload;
|
|
1977
|
+
get_url_config(url = null) {
|
|
1978
|
+
if (!this.config) throw new Error(CONFIG_ERROR_MSG);
|
|
1979
|
+
if (url === null) url = window.location.href;
|
|
1980
|
+
const stripSlashes = (str) => str.replace(/^\/+|\/+$/g, "");
|
|
1981
|
+
let root_path = stripSlashes(new URL(this.config.root).pathname);
|
|
1982
|
+
let url_path = stripSlashes(new URL(url).pathname);
|
|
1983
|
+
let page;
|
|
1984
|
+
if (!url_path.startsWith(root_path)) page = "";
|
|
1985
|
+
else page = stripSlashes(url_path.substring(root_path.length));
|
|
1986
|
+
return this.get_page_config(page);
|
|
1987
|
+
}
|
|
1988
|
+
get_page_config(page) {
|
|
1989
|
+
if (!this.config) throw new Error(CONFIG_ERROR_MSG);
|
|
1990
|
+
let config = this.config;
|
|
1991
|
+
if (!(page in config.page)) page = "";
|
|
1992
|
+
return {
|
|
1993
|
+
...config,
|
|
1994
|
+
current_page: page,
|
|
1995
|
+
layout: config.page[page].layout,
|
|
1996
|
+
components: config.components.filter((c) => config.page[page].components.includes(c.id)),
|
|
1997
|
+
dependencies: this.config.dependencies.filter((d) => config.page[page].dependencies.includes(d.id))
|
|
1998
|
+
};
|
|
1999
|
+
}
|
|
2000
|
+
fetch(input, init) {
|
|
2001
|
+
const headers = new Headers(init?.headers || {});
|
|
2002
|
+
if (this && this.cookies) headers.append("Cookie", this.cookies);
|
|
2003
|
+
if (this && this.options.headers) new Headers(this.options.headers).forEach((value, name) => {
|
|
2004
|
+
headers.append(name, value);
|
|
2005
|
+
});
|
|
2006
|
+
return fetch(input, {
|
|
2007
|
+
...init,
|
|
2008
|
+
headers
|
|
2009
|
+
});
|
|
2010
|
+
}
|
|
2011
|
+
stream(url) {
|
|
2012
|
+
const headers = new Headers();
|
|
2013
|
+
if (this && this.cookies) headers.append("Cookie", this.cookies);
|
|
2014
|
+
if (this && this.options.headers) new Headers(this.options.headers).forEach((value, name) => {
|
|
2015
|
+
headers.append(name, value);
|
|
2016
|
+
});
|
|
2017
|
+
if (this && this.options.token) headers.append("Authorization", `Bearer ${this.options.token}`);
|
|
2018
|
+
this.abort_controller = new AbortController();
|
|
2019
|
+
this.stream_instance = readable_stream(url.toString(), {
|
|
2020
|
+
credentials: this.options.credentials ?? "same-origin",
|
|
2021
|
+
headers,
|
|
2022
|
+
signal: this.abort_controller.signal
|
|
2023
|
+
});
|
|
2024
|
+
return this.stream_instance;
|
|
2025
|
+
}
|
|
2026
|
+
view_api;
|
|
2027
|
+
upload_files;
|
|
2028
|
+
upload;
|
|
2029
|
+
handle_blob;
|
|
2030
|
+
post_data;
|
|
2031
|
+
submit;
|
|
2032
|
+
predict;
|
|
2033
|
+
open_stream;
|
|
2034
|
+
resolve_config;
|
|
2035
|
+
resolve_cookies;
|
|
2036
|
+
constructor(app_reference, options = { events: ["data"] }) {
|
|
2037
|
+
this.app_reference = app_reference;
|
|
2038
|
+
this.deep_link = options.query_params?.deep_link || null;
|
|
2039
|
+
if (!options.events) options.events = ["data"];
|
|
2040
|
+
normalise_token_option(options);
|
|
2041
|
+
this.options = options;
|
|
2042
|
+
this.current_payload = {};
|
|
2043
|
+
if (options.cookies) this.cookies = options.cookies;
|
|
2044
|
+
this.view_api = view_api.bind(this);
|
|
2045
|
+
this.upload_files = upload_files.bind(this);
|
|
2046
|
+
this.handle_blob = handle_blob.bind(this);
|
|
2047
|
+
this.post_data = post_data.bind(this);
|
|
2048
|
+
this.submit = submit.bind(this);
|
|
2049
|
+
this.predict = predict.bind(this);
|
|
2050
|
+
this.open_stream = open_stream.bind(this);
|
|
2051
|
+
this.resolve_config = resolve_config.bind(this);
|
|
2052
|
+
this.resolve_cookies = resolve_cookies.bind(this);
|
|
2053
|
+
this.upload = upload.bind(this);
|
|
2054
|
+
this.fetch = this.fetch.bind(this);
|
|
2055
|
+
this.handle_space_success = this.handle_space_success.bind(this);
|
|
2056
|
+
this.stream = this.stream.bind(this);
|
|
2057
|
+
}
|
|
2058
|
+
async init() {
|
|
2059
|
+
initialize_zerogpu_handshake();
|
|
2060
|
+
if (this.options.auth) await this.resolve_cookies();
|
|
2061
|
+
await this._resolve_config().then((res) => res?.config && this._resolve_heartbeat(res.config));
|
|
2062
|
+
try {
|
|
2063
|
+
this.api_info = await this.view_api();
|
|
2064
|
+
} catch (e) {
|
|
2065
|
+
console.error(e.message);
|
|
2066
|
+
}
|
|
2067
|
+
this.api_map = map_names_to_ids(this.config?.dependencies || []);
|
|
2068
|
+
}
|
|
2069
|
+
async _resolve_heartbeat(_config) {
|
|
2070
|
+
if (_config) {
|
|
2071
|
+
this.config = _config;
|
|
2072
|
+
this.api_prefix = _config.api_prefix || "";
|
|
2073
|
+
if (this.config && this.config.connect_heartbeat) {
|
|
2074
|
+
if (this.config.space_id && this.options.token) this.jwt = await get_jwt(this.config.space_id, this.options.token, this.cookies);
|
|
2075
|
+
}
|
|
2076
|
+
}
|
|
2077
|
+
if (_config.space_id && this.options.token) this.jwt = await get_jwt(_config.space_id, this.options.token);
|
|
2078
|
+
if (this.config && this.config.connect_heartbeat) {
|
|
2079
|
+
const heartbeat_url = new URL(`${this.config.root}${this.api_prefix}/${HEARTBEAT_URL}/${this.session_hash}`);
|
|
2080
|
+
if (this.jwt) heartbeat_url.searchParams.set("__sign", this.jwt);
|
|
2081
|
+
if (!this.heartbeat_event) this.heartbeat_event = this.stream(heartbeat_url);
|
|
2082
|
+
}
|
|
2083
|
+
}
|
|
2084
|
+
static async connect(app_reference, options = { events: ["data"] }) {
|
|
2085
|
+
const client = new this(app_reference, options);
|
|
2086
|
+
if (options.session_hash) client.session_hash = options.session_hash;
|
|
2087
|
+
await client.init();
|
|
2088
|
+
return client;
|
|
2089
|
+
}
|
|
2090
|
+
async reconnect() {
|
|
2091
|
+
const app_id_url = new URL(`${this.config.root}${this.api_prefix}/${APP_ID_URL}`);
|
|
2092
|
+
let app_id;
|
|
2093
|
+
try {
|
|
2094
|
+
const response = await this.fetch(app_id_url);
|
|
2095
|
+
if (!response.ok) throw new Error();
|
|
2096
|
+
app_id = (await response.json()).app_id;
|
|
2097
|
+
} catch (e) {
|
|
2098
|
+
return "broken";
|
|
2099
|
+
}
|
|
2100
|
+
if (app_id !== this.config.app_id) return "changed";
|
|
2101
|
+
return "connected";
|
|
2102
|
+
}
|
|
2103
|
+
close() {
|
|
2104
|
+
this.closed = true;
|
|
2105
|
+
close_stream(this.stream_status, this.abort_controller);
|
|
2106
|
+
}
|
|
2107
|
+
/**
|
|
2108
|
+
* Re-fetch the app config without closing the SSE stream.
|
|
2109
|
+
* Used by hot-reload so in-flight generators keep delivering updates.
|
|
2110
|
+
*/
|
|
2111
|
+
async refresh() {
|
|
2112
|
+
if (!this.config) throw new Error(CONFIG_ERROR_MSG);
|
|
2113
|
+
const config = await this.resolve_config(this.config.root, false);
|
|
2114
|
+
if (!config) throw new Error(CONFIG_ERROR_MSG);
|
|
2115
|
+
this.config = config;
|
|
2116
|
+
this.api_prefix = config.api_prefix || "";
|
|
2117
|
+
this.api_map = map_names_to_ids(config.dependencies || []);
|
|
2118
|
+
try {
|
|
2119
|
+
this.api_info = await this.view_api();
|
|
2120
|
+
} catch (e) {
|
|
2121
|
+
console.error(API_INFO_ERROR_MSG + e.message);
|
|
2122
|
+
}
|
|
2123
|
+
return this.get_url_config();
|
|
2124
|
+
}
|
|
2125
|
+
set_current_payload(payload) {
|
|
2126
|
+
this.current_payload = payload;
|
|
2127
|
+
}
|
|
2128
|
+
static async duplicate(app_reference, options = { events: ["data"] }) {
|
|
2129
|
+
return duplicate(app_reference, options);
|
|
2130
|
+
}
|
|
2131
|
+
async _resolve_config() {
|
|
2132
|
+
const { http_protocol, host, space_id } = await process_endpoint(this.app_reference, this.options.token);
|
|
2133
|
+
const { status_callback } = this.options;
|
|
2134
|
+
if (space_id && status_callback) await check_and_wake_space(space_id, status_callback);
|
|
2135
|
+
let config;
|
|
2136
|
+
try {
|
|
2137
|
+
let configUrl = `${http_protocol}//${host}`;
|
|
2138
|
+
config = await this.resolve_config(configUrl);
|
|
2139
|
+
if (!config) throw new Error(CONFIG_ERROR_MSG);
|
|
2140
|
+
return this.config_success(config);
|
|
2141
|
+
} catch (e) {
|
|
2142
|
+
if (space_id && status_callback) check_space_status(space_id, RE_SPACE_NAME.test(space_id) ? "space_name" : "subdomain", this.handle_space_success);
|
|
2143
|
+
else {
|
|
2144
|
+
if (status_callback) status_callback({
|
|
2145
|
+
status: "error",
|
|
2146
|
+
message: "Could not load this space.",
|
|
2147
|
+
load_status: "error",
|
|
2148
|
+
detail: "NOT_FOUND"
|
|
2149
|
+
});
|
|
2150
|
+
throw e instanceof Error ? e : new Error(String(e));
|
|
2151
|
+
}
|
|
2152
|
+
}
|
|
2153
|
+
}
|
|
2154
|
+
async config_success(_config) {
|
|
2155
|
+
this.config = _config;
|
|
2156
|
+
this.api_prefix = _config.api_prefix || "";
|
|
2157
|
+
if (this.config.auth_required) return this.prepare_return_obj();
|
|
2158
|
+
try {
|
|
2159
|
+
this.api_info = await this.view_api();
|
|
2160
|
+
} catch (e) {
|
|
2161
|
+
console.error(API_INFO_ERROR_MSG + e.message);
|
|
2162
|
+
}
|
|
2163
|
+
return this.prepare_return_obj();
|
|
2164
|
+
}
|
|
2165
|
+
async handle_space_success(status) {
|
|
2166
|
+
if (!this) throw new Error(CONFIG_ERROR_MSG);
|
|
2167
|
+
const { status_callback } = this.options;
|
|
2168
|
+
if (status_callback) status_callback(status);
|
|
2169
|
+
if (status.status === "running") try {
|
|
2170
|
+
this.config = await this._resolve_config();
|
|
2171
|
+
this.api_prefix = this?.config?.api_prefix || "";
|
|
2172
|
+
if (!this.config) throw new Error(CONFIG_ERROR_MSG);
|
|
2173
|
+
return await this.config_success(this.config);
|
|
2174
|
+
} catch (e) {
|
|
2175
|
+
if (status_callback) status_callback({
|
|
2176
|
+
status: "error",
|
|
2177
|
+
message: "Could not load this space.",
|
|
2178
|
+
load_status: "error",
|
|
2179
|
+
detail: "NOT_FOUND"
|
|
2180
|
+
});
|
|
2181
|
+
throw e;
|
|
2182
|
+
}
|
|
2183
|
+
}
|
|
2184
|
+
async component_server(component_id, fn_name, data) {
|
|
2185
|
+
if (!this.config) throw new Error(CONFIG_ERROR_MSG);
|
|
2186
|
+
const headers = {};
|
|
2187
|
+
const { token } = this.options;
|
|
2188
|
+
const { session_hash } = this;
|
|
2189
|
+
if (token) headers.Authorization = `Bearer ${this.options.token}`;
|
|
2190
|
+
let root_url;
|
|
2191
|
+
let component = this.config.components.find((comp) => comp.id === component_id);
|
|
2192
|
+
if (component?.props?.root_url) root_url = component.props.root_url;
|
|
2193
|
+
else root_url = this.config.root;
|
|
2194
|
+
let body;
|
|
2195
|
+
if (typeof data === "object" && data !== null && "binary" in data) {
|
|
2196
|
+
const _data = data;
|
|
2197
|
+
body = new FormData();
|
|
2198
|
+
for (const key in _data.data) {
|
|
2199
|
+
if (key === "binary") continue;
|
|
2200
|
+
body.append(key, _data.data[key]);
|
|
2201
|
+
}
|
|
2202
|
+
body.set("component_id", component_id.toString());
|
|
2203
|
+
body.set("fn_name", fn_name);
|
|
2204
|
+
body.set("session_hash", session_hash);
|
|
2205
|
+
} else {
|
|
2206
|
+
body = JSON.stringify({
|
|
2207
|
+
data,
|
|
2208
|
+
component_id,
|
|
2209
|
+
fn_name,
|
|
2210
|
+
session_hash
|
|
2211
|
+
});
|
|
2212
|
+
headers["Content-Type"] = "application/json";
|
|
2213
|
+
}
|
|
2214
|
+
if (token) headers.Authorization = `Bearer ${token}`;
|
|
2215
|
+
try {
|
|
2216
|
+
const response = await this.fetch(`${root_url}${this.api_prefix}/${COMPONENT_SERVER_URL}/`, {
|
|
2217
|
+
method: "POST",
|
|
2218
|
+
body,
|
|
2219
|
+
headers,
|
|
2220
|
+
credentials: this.options.credentials ?? "same-origin"
|
|
2221
|
+
});
|
|
2222
|
+
if (!response.ok) throw new Error("Could not connect to component server: " + response.statusText);
|
|
2223
|
+
return await response.json();
|
|
2224
|
+
} catch (e) {
|
|
2225
|
+
console.warn(e);
|
|
2226
|
+
}
|
|
2227
|
+
}
|
|
2228
|
+
set_cookies(raw_cookies) {
|
|
2229
|
+
this.cookies = parse_and_set_cookies(raw_cookies).join("; ");
|
|
2230
|
+
}
|
|
2231
|
+
prepare_return_obj() {
|
|
2232
|
+
return {
|
|
2233
|
+
config: this.config,
|
|
2234
|
+
predict: this.predict,
|
|
2235
|
+
submit: this.submit,
|
|
2236
|
+
view_api: this.view_api,
|
|
2237
|
+
component_server: this.component_server
|
|
2238
|
+
};
|
|
2239
|
+
}
|
|
2240
|
+
};
|
|
2241
|
+
/**
|
|
2242
|
+
* @deprecated This method will be removed in v1.0. Use `Client.connect()` instead.
|
|
2243
|
+
* Creates a client instance for interacting with Gradio apps.
|
|
2244
|
+
*
|
|
2245
|
+
* @param {string} app_reference - The reference or URL to a Gradio space or app.
|
|
2246
|
+
* @param {ClientOptions} options - Configuration options for the client.
|
|
2247
|
+
* @returns {Promise<Client>} A promise that resolves to a `Client` instance.
|
|
2248
|
+
*/
|
|
2249
|
+
async function client(app_reference, options = { events: ["data"] }) {
|
|
2250
|
+
return await Client.connect(app_reference, options);
|
|
2251
|
+
}
|
|
2252
|
+
/**
|
|
2253
|
+
* @deprecated This method will be removed in v1.0. Use `Client.duplicate()` instead.
|
|
2254
|
+
* Creates a duplicate of a space and returns a client instance for the duplicated space.
|
|
2255
|
+
*
|
|
2256
|
+
* @param {string} app_reference - The reference or URL to a Gradio space or app to duplicate.
|
|
2257
|
+
* @param {DuplicateOptions} options - Configuration options for the client.
|
|
2258
|
+
* @returns {Promise<Client>} A promise that resolves to a `Client` instance.
|
|
2259
|
+
*/
|
|
2260
|
+
async function duplicate_space(app_reference, options) {
|
|
2261
|
+
return await Client.duplicate(app_reference, options);
|
|
2262
|
+
}
|
|
2263
|
+
//#endregion
|
|
2264
|
+
exports.Client = Client;
|
|
2265
|
+
exports.FileData = FileData;
|
|
2266
|
+
exports.MISSING_CREDENTIALS_MSG = MISSING_CREDENTIALS_MSG;
|
|
2267
|
+
exports.client = client;
|
|
2268
|
+
exports.duplicate = duplicate_space;
|
|
2269
|
+
exports.handle_file = handle_file;
|
|
2270
|
+
exports.predict = predict;
|
|
2271
|
+
exports.prepare_files = prepare_files;
|
|
2272
|
+
exports.submit = submit;
|
|
2273
|
+
exports.upload = upload;
|
|
2274
|
+
exports.upload_files = upload_files;
|