@gradio/client 0.1.3 → 0.1.4

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/src/client.ts CHANGED
@@ -58,62 +58,8 @@ type SubmitReturn = {
58
58
  const QUEUE_FULL_MSG = "This application is too busy. Keep trying!";
59
59
  const BROKEN_CONNECTION_MSG = "Connection errored out.";
60
60
 
61
- export async function post_data(
62
- url: string,
63
- body: unknown,
64
- token?: `hf_${string}`
65
- ): Promise<[PostResponse, number]> {
66
- const headers: {
67
- Authorization?: string;
68
- "Content-Type": "application/json";
69
- } = { "Content-Type": "application/json" };
70
- if (token) {
71
- headers.Authorization = `Bearer ${token}`;
72
- }
73
- try {
74
- var response = await fetch(url, {
75
- method: "POST",
76
- body: JSON.stringify(body),
77
- headers
78
- });
79
- } catch (e) {
80
- return [{ error: BROKEN_CONNECTION_MSG }, 500];
81
- }
82
- const output: PostResponse = await response.json();
83
- return [output, response.status];
84
- }
85
-
86
61
  export let NodeBlob;
87
62
 
88
- export async function upload_files(
89
- root: string,
90
- files: Array<File>,
91
- token?: `hf_${string}`
92
- ): Promise<UploadResponse> {
93
- const headers: {
94
- Authorization?: string;
95
- } = {};
96
- if (token) {
97
- headers.Authorization = `Bearer ${token}`;
98
- }
99
-
100
- const formData = new FormData();
101
- files.forEach((file) => {
102
- formData.append("files", file);
103
- });
104
- try {
105
- var response = await fetch(`${root}/upload`, {
106
- method: "POST",
107
- body: formData,
108
- headers
109
- });
110
- } catch (e) {
111
- return { error: BROKEN_CONNECTION_MSG };
112
- }
113
- const output: UploadResponse["files"] = await response.json();
114
- return { files: output };
115
- }
116
-
117
63
  export async function duplicate(
118
64
  app_reference: string,
119
65
  options: {
@@ -197,489 +143,615 @@ export async function duplicate(
197
143
  }
198
144
  }
199
145
 
200
- export async function client(
201
- app_reference: string,
202
- options: {
203
- hf_token?: `hf_${string}`;
204
- status_callback?: SpaceStatusCallback;
205
- normalise_files?: boolean;
206
- } = { normalise_files: true }
207
- ): Promise<client_return> {
208
- return new Promise(async (res) => {
209
- const { status_callback, hf_token, normalise_files } = options;
210
- const return_obj = {
211
- predict,
212
- submit,
213
- view_api
214
- // duplicate
215
- };
146
+ /**
147
+ * We need to inject a customized fetch implementation for the Wasm version.
148
+ */
149
+ export function api_factory(fetch_implementation: typeof fetch) {
150
+ return { post_data, upload_files, client, handle_blob };
151
+
152
+ async function post_data(
153
+ url: string,
154
+ body: unknown,
155
+ token?: `hf_${string}`
156
+ ): Promise<[PostResponse, number]> {
157
+ const headers: {
158
+ Authorization?: string;
159
+ "Content-Type": "application/json";
160
+ } = { "Content-Type": "application/json" };
161
+ if (token) {
162
+ headers.Authorization = `Bearer ${token}`;
163
+ }
164
+ try {
165
+ var response = await fetch_implementation(url, {
166
+ method: "POST",
167
+ body: JSON.stringify(body),
168
+ headers
169
+ });
170
+ } catch (e) {
171
+ return [{ error: BROKEN_CONNECTION_MSG }, 500];
172
+ }
173
+ const output: PostResponse = await response.json();
174
+ return [output, response.status];
175
+ }
216
176
 
217
- const transform_files = normalise_files ?? true;
218
- if (typeof window === "undefined" || !("WebSocket" in window)) {
219
- const ws = await import("ws");
220
- NodeBlob = (await import("node:buffer")).Blob;
221
- //@ts-ignore
222
- global.WebSocket = ws.WebSocket;
177
+ async function upload_files(
178
+ root: string,
179
+ files: Array<File>,
180
+ token?: `hf_${string}`
181
+ ): Promise<UploadResponse> {
182
+ const headers: {
183
+ Authorization?: string;
184
+ } = {};
185
+ if (token) {
186
+ headers.Authorization = `Bearer ${token}`;
223
187
  }
224
188
 
225
- const { ws_protocol, http_protocol, host, space_id } =
226
- await process_endpoint(app_reference, hf_token);
189
+ const formData = new FormData();
190
+ files.forEach((file) => {
191
+ formData.append("files", file);
192
+ });
193
+ try {
194
+ var response = await fetch_implementation(`${root}/upload`, {
195
+ method: "POST",
196
+ body: formData,
197
+ headers
198
+ });
199
+ } catch (e) {
200
+ return { error: BROKEN_CONNECTION_MSG };
201
+ }
202
+ const output: UploadResponse["files"] = await response.json();
203
+ return { files: output };
204
+ }
227
205
 
228
- const session_hash = Math.random().toString(36).substring(2);
229
- const last_status: Record<string, Status["stage"]> = {};
230
- let config: Config;
231
- let api_map: Record<string, number> = {};
206
+ async function client(
207
+ app_reference: string,
208
+ options: {
209
+ hf_token?: `hf_${string}`;
210
+ status_callback?: SpaceStatusCallback;
211
+ normalise_files?: boolean;
212
+ } = { normalise_files: true }
213
+ ): Promise<client_return> {
214
+ return new Promise(async (res) => {
215
+ const { status_callback, hf_token, normalise_files } = options;
216
+ const return_obj = {
217
+ predict,
218
+ submit,
219
+ view_api
220
+ // duplicate
221
+ };
232
222
 
233
- let jwt: false | string = false;
223
+ const transform_files = normalise_files ?? true;
224
+ if (typeof window === "undefined" || !("WebSocket" in window)) {
225
+ const ws = await import("ws");
226
+ NodeBlob = (await import("node:buffer")).Blob;
227
+ //@ts-ignore
228
+ global.WebSocket = ws.WebSocket;
229
+ }
234
230
 
235
- if (hf_token && space_id) {
236
- jwt = await get_jwt(space_id, hf_token);
237
- }
231
+ const { ws_protocol, http_protocol, host, space_id } =
232
+ await process_endpoint(app_reference, hf_token);
238
233
 
239
- async function config_success(_config: Config) {
240
- config = _config;
241
- api_map = map_names_to_ids(_config?.dependencies || []);
242
- try {
243
- api = await view_api(config);
244
- } catch (e) {
245
- console.error(`Could not get api details: ${e.message}`);
234
+ const session_hash = Math.random().toString(36).substring(2);
235
+ const last_status: Record<string, Status["stage"]> = {};
236
+ let config: Config;
237
+ let api_map: Record<string, number> = {};
238
+
239
+ let jwt: false | string = false;
240
+
241
+ if (hf_token && space_id) {
242
+ jwt = await get_jwt(space_id, hf_token);
246
243
  }
247
244
 
248
- return {
249
- config,
250
- ...return_obj
251
- };
252
- }
253
- let api: ApiInfo<JsApiData>;
254
- async function handle_space_sucess(status: SpaceStatus) {
255
- if (status_callback) status_callback(status);
256
- if (status.status === "running")
245
+ async function config_success(_config: Config) {
246
+ config = _config;
247
+ api_map = map_names_to_ids(_config?.dependencies || []);
257
248
  try {
258
- config = await resolve_config(`${http_protocol}//${host}`, hf_token);
259
-
260
- const _config = await config_success(config);
261
- res(_config);
249
+ api = await view_api(config);
262
250
  } catch (e) {
263
- if (status_callback) {
264
- status_callback({
265
- status: "error",
266
- message: "Could not load this space.",
267
- load_status: "error",
268
- detail: "NOT_FOUND"
269
- });
270
- }
251
+ console.error(`Could not get api details: ${e.message}`);
271
252
  }
272
- }
273
-
274
- try {
275
- config = await resolve_config(`${http_protocol}//${host}`, hf_token);
276
253
 
277
- const _config = await config_success(config);
278
- res(_config);
279
- } catch (e) {
280
- if (space_id) {
281
- check_space_status(
282
- space_id,
283
- RE_SPACE_NAME.test(space_id) ? "space_name" : "subdomain",
284
- handle_space_sucess
285
- );
286
- } else {
287
- if (status_callback)
288
- status_callback({
289
- status: "error",
290
- message: "Could not load this space.",
291
- load_status: "error",
292
- detail: "NOT_FOUND"
293
- });
254
+ return {
255
+ config,
256
+ ...return_obj
257
+ };
294
258
  }
295
- }
259
+ let api: ApiInfo<JsApiData>;
260
+ async function handle_space_sucess(status: SpaceStatus) {
261
+ if (status_callback) status_callback(status);
262
+ if (status.status === "running")
263
+ try {
264
+ config = await resolve_config(
265
+ fetch_implementation,
266
+ `${http_protocol}//${host}`,
267
+ hf_token
268
+ );
296
269
 
297
- /**
298
- * Run a prediction.
299
- * @param endpoint - The prediction endpoint to use.
300
- * @param status_callback - A function that is called with the current status of the prediction immediately and every time it updates.
301
- * @return Returns the data for the prediction or an error message.
302
- */
303
- function predict(endpoint: string, data: unknown[], event_data?: unknown) {
304
- let data_returned = false;
305
- let status_complete = false;
306
- return new Promise((res, rej) => {
307
- const app = submit(endpoint, data, event_data);
308
-
309
- app
310
- .on("data", (d) => {
311
- data_returned = true;
312
- if (status_complete) {
313
- app.destroy();
314
- }
315
- res(d);
316
- })
317
- .on("status", (status) => {
318
- if (status.stage === "error") rej(status);
319
- if (status.stage === "complete" && data_returned) {
320
- app.destroy();
321
- }
322
- if (status.stage === "complete") {
323
- status_complete = true;
270
+ const _config = await config_success(config);
271
+ res(_config);
272
+ } catch (e) {
273
+ console.error(e);
274
+ if (status_callback) {
275
+ status_callback({
276
+ status: "error",
277
+ message: "Could not load this space.",
278
+ load_status: "error",
279
+ detail: "NOT_FOUND"
280
+ });
324
281
  }
325
- });
326
- });
327
- }
282
+ }
283
+ }
328
284
 
329
- function submit(
330
- endpoint: string | number,
331
- data: unknown[],
332
- event_data?: unknown
333
- ): SubmitReturn {
334
- let fn_index: number;
335
- let api_info;
336
-
337
- if (typeof endpoint === "number") {
338
- fn_index = endpoint;
339
- api_info = api.unnamed_endpoints[fn_index];
340
- } else {
341
- const trimmed_endpoint = endpoint.replace(/^\//, "");
285
+ try {
286
+ config = await resolve_config(
287
+ fetch_implementation,
288
+ `${http_protocol}//${host}`,
289
+ hf_token
290
+ );
342
291
 
343
- fn_index = api_map[trimmed_endpoint];
344
- api_info = api.named_endpoints[endpoint.trim()];
292
+ const _config = await config_success(config);
293
+ res(_config);
294
+ } catch (e) {
295
+ console.error(e);
296
+ if (space_id) {
297
+ check_space_status(
298
+ space_id,
299
+ RE_SPACE_NAME.test(space_id) ? "space_name" : "subdomain",
300
+ handle_space_sucess
301
+ );
302
+ } else {
303
+ if (status_callback)
304
+ status_callback({
305
+ status: "error",
306
+ message: "Could not load this space.",
307
+ load_status: "error",
308
+ detail: "NOT_FOUND"
309
+ });
310
+ }
345
311
  }
346
312
 
347
- if (typeof fn_index !== "number") {
348
- throw new Error(
349
- "There is no endpoint matching that name of fn_index matching that number."
350
- );
313
+ /**
314
+ * Run a prediction.
315
+ * @param endpoint - The prediction endpoint to use.
316
+ * @param status_callback - A function that is called with the current status of the prediction immediately and every time it updates.
317
+ * @return Returns the data for the prediction or an error message.
318
+ */
319
+ function predict(
320
+ endpoint: string,
321
+ data: unknown[],
322
+ event_data?: unknown
323
+ ) {
324
+ let data_returned = false;
325
+ let status_complete = false;
326
+ return new Promise((res, rej) => {
327
+ const app = submit(endpoint, data, event_data);
328
+
329
+ app
330
+ .on("data", (d) => {
331
+ data_returned = true;
332
+ if (status_complete) {
333
+ app.destroy();
334
+ }
335
+ res(d);
336
+ })
337
+ .on("status", (status) => {
338
+ if (status.stage === "error") rej(status);
339
+ if (status.stage === "complete" && data_returned) {
340
+ app.destroy();
341
+ }
342
+ if (status.stage === "complete") {
343
+ status_complete = true;
344
+ }
345
+ });
346
+ });
351
347
  }
352
348
 
353
- let websocket: WebSocket;
349
+ function submit(
350
+ endpoint: string | number,
351
+ data: unknown[],
352
+ event_data?: unknown
353
+ ): SubmitReturn {
354
+ let fn_index: number;
355
+ let api_info;
356
+
357
+ if (typeof endpoint === "number") {
358
+ fn_index = endpoint;
359
+ api_info = api.unnamed_endpoints[fn_index];
360
+ } else {
361
+ const trimmed_endpoint = endpoint.replace(/^\//, "");
354
362
 
355
- const _endpoint = typeof endpoint === "number" ? "/predict" : endpoint;
356
- let payload: Payload;
357
- let complete: false | Record<string, any> = false;
358
- const listener_map: ListenerMap<EventType> = {};
363
+ fn_index = api_map[trimmed_endpoint];
364
+ api_info = api.named_endpoints[endpoint.trim()];
365
+ }
359
366
 
360
- handle_blob(
361
- `${http_protocol}//${host + config.path}`,
362
- data,
363
- api_info,
364
- hf_token
365
- ).then((_payload) => {
366
- payload = { data: _payload || [], event_data, fn_index };
367
- if (skip_queue(fn_index, config)) {
368
- fire_event({
369
- type: "status",
370
- endpoint: _endpoint,
371
- stage: "pending",
372
- queue: false,
373
- fn_index,
374
- time: new Date()
375
- });
367
+ if (typeof fn_index !== "number") {
368
+ throw new Error(
369
+ "There is no endpoint matching that name of fn_index matching that number."
370
+ );
371
+ }
376
372
 
377
- post_data(
378
- `${http_protocol}//${host + config.path}/run${
379
- _endpoint.startsWith("/") ? _endpoint : `/${_endpoint}`
380
- }`,
381
- {
382
- ...payload,
383
- session_hash
384
- },
385
- hf_token
386
- )
387
- .then(([output, status_code]) => {
388
- const data = transform_files
389
- ? transform_output(
390
- output.data,
391
- api_info,
392
- config.root,
393
- config.root_url
394
- )
395
- : output.data;
396
- if (status_code == 200) {
397
- fire_event({
398
- type: "data",
399
- endpoint: _endpoint,
400
- fn_index,
401
- data: data,
402
- time: new Date()
403
- });
373
+ let websocket: WebSocket;
374
+
375
+ const _endpoint = typeof endpoint === "number" ? "/predict" : endpoint;
376
+ let payload: Payload;
377
+ let complete: false | Record<string, any> = false;
378
+ const listener_map: ListenerMap<EventType> = {};
379
+
380
+ handle_blob(
381
+ `${http_protocol}//${host + config.path}`,
382
+ data,
383
+ api_info,
384
+ hf_token
385
+ ).then((_payload) => {
386
+ payload = { data: _payload || [], event_data, fn_index };
387
+ if (skip_queue(fn_index, config)) {
388
+ fire_event({
389
+ type: "status",
390
+ endpoint: _endpoint,
391
+ stage: "pending",
392
+ queue: false,
393
+ fn_index,
394
+ time: new Date()
395
+ });
404
396
 
405
- fire_event({
406
- type: "status",
407
- endpoint: _endpoint,
408
- fn_index,
409
- stage: "complete",
410
- eta: output.average_duration,
411
- queue: false,
412
- time: new Date()
413
- });
414
- } else {
397
+ post_data(
398
+ `${http_protocol}//${host + config.path}/run${
399
+ _endpoint.startsWith("/") ? _endpoint : `/${_endpoint}`
400
+ }`,
401
+ {
402
+ ...payload,
403
+ session_hash
404
+ },
405
+ hf_token
406
+ )
407
+ .then(([output, status_code]) => {
408
+ const data = transform_files
409
+ ? transform_output(
410
+ output.data,
411
+ api_info,
412
+ config.root,
413
+ config.root_url
414
+ )
415
+ : output.data;
416
+ if (status_code == 200) {
417
+ fire_event({
418
+ type: "data",
419
+ endpoint: _endpoint,
420
+ fn_index,
421
+ data: data,
422
+ time: new Date()
423
+ });
424
+
425
+ fire_event({
426
+ type: "status",
427
+ endpoint: _endpoint,
428
+ fn_index,
429
+ stage: "complete",
430
+ eta: output.average_duration,
431
+ queue: false,
432
+ time: new Date()
433
+ });
434
+ } else {
435
+ fire_event({
436
+ type: "status",
437
+ stage: "error",
438
+ endpoint: _endpoint,
439
+ fn_index,
440
+ message: output.error,
441
+ queue: false,
442
+ time: new Date()
443
+ });
444
+ }
445
+ })
446
+ .catch((e) => {
415
447
  fire_event({
416
448
  type: "status",
417
449
  stage: "error",
450
+ message: e.message,
418
451
  endpoint: _endpoint,
419
452
  fn_index,
420
- message: output.error,
421
453
  queue: false,
422
454
  time: new Date()
423
455
  });
424
- }
425
- })
426
- .catch((e) => {
427
- fire_event({
428
- type: "status",
429
- stage: "error",
430
- message: e.message,
431
- endpoint: _endpoint,
432
- fn_index,
433
- queue: false,
434
- time: new Date()
435
456
  });
457
+ } else {
458
+ fire_event({
459
+ type: "status",
460
+ stage: "pending",
461
+ queue: true,
462
+ endpoint: _endpoint,
463
+ fn_index,
464
+ time: new Date()
436
465
  });
437
- } else {
438
- fire_event({
439
- type: "status",
440
- stage: "pending",
441
- queue: true,
442
- endpoint: _endpoint,
443
- fn_index,
444
- time: new Date()
445
- });
446
-
447
- let url = new URL(`${ws_protocol}://${host}${config.path}
448
- /queue/join`);
449
466
 
450
- if (jwt) {
451
- url.searchParams.set("__sign", jwt);
452
- }
467
+ let url = new URL(`${ws_protocol}://${host}${config.path}
468
+ /queue/join`);
453
469
 
454
- websocket = new WebSocket(url);
455
-
456
- websocket.onclose = (evt) => {
457
- if (!evt.wasClean) {
458
- fire_event({
459
- type: "status",
460
- stage: "error",
461
- message: BROKEN_CONNECTION_MSG,
462
- queue: true,
463
- endpoint: _endpoint,
464
- fn_index,
465
- time: new Date()
466
- });
470
+ if (jwt) {
471
+ url.searchParams.set("__sign", jwt);
467
472
  }
468
- };
469
473
 
470
- websocket.onmessage = function (event) {
471
- const _data = JSON.parse(event.data);
472
- const { type, status, data } = handle_message(
473
- _data,
474
- last_status[fn_index]
475
- );
474
+ websocket = new WebSocket(url);
476
475
 
477
- if (type === "update" && status && !complete) {
478
- // call 'status' listeners
479
- fire_event({
480
- type: "status",
481
- endpoint: _endpoint,
482
- fn_index,
483
- time: new Date(),
484
- ...status
485
- });
486
- if (status.stage === "error") {
487
- websocket.close();
476
+ websocket.onclose = (evt) => {
477
+ if (!evt.wasClean) {
478
+ fire_event({
479
+ type: "status",
480
+ stage: "error",
481
+ message: BROKEN_CONNECTION_MSG,
482
+ queue: true,
483
+ endpoint: _endpoint,
484
+ fn_index,
485
+ time: new Date()
486
+ });
488
487
  }
489
- } else if (type === "hash") {
490
- websocket.send(JSON.stringify({ fn_index, session_hash }));
491
- return;
492
- } else if (type === "data") {
493
- websocket.send(JSON.stringify({ ...payload, session_hash }));
494
- } else if (type === "complete") {
495
- complete = status;
496
- } else if (type === "generating") {
497
- fire_event({
498
- type: "status",
499
- time: new Date(),
500
- ...status,
501
- stage: status?.stage!,
502
- queue: true,
503
- endpoint: _endpoint,
504
- fn_index
505
- });
506
- }
507
- if (data) {
508
- fire_event({
509
- type: "data",
510
- time: new Date(),
511
- data: transform_files
512
- ? transform_output(
513
- data.data,
514
- api_info,
515
- config.root,
516
- config.root_url
517
- )
518
- : data.data,
519
- endpoint: _endpoint,
520
- fn_index
521
- });
488
+ };
522
489
 
523
- if (complete) {
490
+ websocket.onmessage = function (event) {
491
+ const _data = JSON.parse(event.data);
492
+ const { type, status, data } = handle_message(
493
+ _data,
494
+ last_status[fn_index]
495
+ );
496
+
497
+ if (type === "update" && status && !complete) {
498
+ // call 'status' listeners
499
+ fire_event({
500
+ type: "status",
501
+ endpoint: _endpoint,
502
+ fn_index,
503
+ time: new Date(),
504
+ ...status
505
+ });
506
+ if (status.stage === "error") {
507
+ websocket.close();
508
+ }
509
+ } else if (type === "hash") {
510
+ websocket.send(JSON.stringify({ fn_index, session_hash }));
511
+ return;
512
+ } else if (type === "data") {
513
+ websocket.send(JSON.stringify({ ...payload, session_hash }));
514
+ } else if (type === "complete") {
515
+ complete = status;
516
+ } else if (type === "generating") {
524
517
  fire_event({
525
518
  type: "status",
526
519
  time: new Date(),
527
- ...complete,
520
+ ...status,
528
521
  stage: status?.stage!,
529
522
  queue: true,
530
523
  endpoint: _endpoint,
531
524
  fn_index
532
525
  });
533
- websocket.close();
534
526
  }
535
- }
536
- };
527
+ if (data) {
528
+ fire_event({
529
+ type: "data",
530
+ time: new Date(),
531
+ data: transform_files
532
+ ? transform_output(
533
+ data.data,
534
+ api_info,
535
+ config.root,
536
+ config.root_url
537
+ )
538
+ : data.data,
539
+ endpoint: _endpoint,
540
+ fn_index
541
+ });
537
542
 
538
- // different ws contract for gradio versions older than 3.6.0
539
- //@ts-ignore
540
- if (semiver(config.version || "2.0.0", "3.6") < 0) {
541
- addEventListener("open", () =>
542
- websocket.send(JSON.stringify({ hash: session_hash }))
543
- );
543
+ if (complete) {
544
+ fire_event({
545
+ type: "status",
546
+ time: new Date(),
547
+ ...complete,
548
+ stage: status?.stage!,
549
+ queue: true,
550
+ endpoint: _endpoint,
551
+ fn_index
552
+ });
553
+ websocket.close();
554
+ }
555
+ }
556
+ };
557
+
558
+ // different ws contract for gradio versions older than 3.6.0
559
+ //@ts-ignore
560
+ if (semiver(config.version || "2.0.0", "3.6") < 0) {
561
+ addEventListener("open", () =>
562
+ websocket.send(JSON.stringify({ hash: session_hash }))
563
+ );
564
+ }
544
565
  }
545
- }
546
- });
566
+ });
547
567
 
548
- function fire_event<K extends EventType>(event: Event<K>) {
549
- const narrowed_listener_map: ListenerMap<K> = listener_map;
550
- const listeners = narrowed_listener_map[event.type] || [];
551
- listeners?.forEach((l) => l(event));
552
- }
568
+ function fire_event<K extends EventType>(event: Event<K>) {
569
+ const narrowed_listener_map: ListenerMap<K> = listener_map;
570
+ const listeners = narrowed_listener_map[event.type] || [];
571
+ listeners?.forEach((l) => l(event));
572
+ }
553
573
 
554
- function on<K extends EventType>(
555
- eventType: K,
556
- listener: EventListener<K>
557
- ) {
558
- const narrowed_listener_map: ListenerMap<K> = listener_map;
559
- const listeners = narrowed_listener_map[eventType] || [];
560
- narrowed_listener_map[eventType] = listeners;
561
- listeners?.push(listener);
574
+ function on<K extends EventType>(
575
+ eventType: K,
576
+ listener: EventListener<K>
577
+ ) {
578
+ const narrowed_listener_map: ListenerMap<K> = listener_map;
579
+ const listeners = narrowed_listener_map[eventType] || [];
580
+ narrowed_listener_map[eventType] = listeners;
581
+ listeners?.push(listener);
562
582
 
563
- return { on, off, cancel, destroy };
564
- }
583
+ return { on, off, cancel, destroy };
584
+ }
565
585
 
566
- function off<K extends EventType>(
567
- eventType: K,
568
- listener: EventListener<K>
569
- ) {
570
- const narrowed_listener_map: ListenerMap<K> = listener_map;
571
- let listeners = narrowed_listener_map[eventType] || [];
572
- listeners = listeners?.filter((l) => l !== listener);
573
- narrowed_listener_map[eventType] = listeners;
586
+ function off<K extends EventType>(
587
+ eventType: K,
588
+ listener: EventListener<K>
589
+ ) {
590
+ const narrowed_listener_map: ListenerMap<K> = listener_map;
591
+ let listeners = narrowed_listener_map[eventType] || [];
592
+ listeners = listeners?.filter((l) => l !== listener);
593
+ narrowed_listener_map[eventType] = listeners;
574
594
 
575
- return { on, off, cancel, destroy };
576
- }
595
+ return { on, off, cancel, destroy };
596
+ }
577
597
 
578
- async function cancel() {
579
- const _status: Status = {
580
- stage: "complete",
581
- queue: false,
582
- time: new Date()
583
- };
584
- complete = _status;
585
- fire_event({
586
- ..._status,
587
- type: "status",
588
- endpoint: _endpoint,
589
- fn_index: fn_index
590
- });
598
+ async function cancel() {
599
+ const _status: Status = {
600
+ stage: "complete",
601
+ queue: false,
602
+ time: new Date()
603
+ };
604
+ complete = _status;
605
+ fire_event({
606
+ ..._status,
607
+ type: "status",
608
+ endpoint: _endpoint,
609
+ fn_index: fn_index
610
+ });
591
611
 
592
- if (websocket && websocket.readyState === 0) {
593
- websocket.addEventListener("open", () => {
612
+ if (websocket && websocket.readyState === 0) {
613
+ websocket.addEventListener("open", () => {
614
+ websocket.close();
615
+ });
616
+ } else {
594
617
  websocket.close();
595
- });
596
- } else {
597
- websocket.close();
618
+ }
619
+
620
+ try {
621
+ await fetch_implementation(
622
+ `${http_protocol}//${host + config.path}/reset`,
623
+ {
624
+ headers: { "Content-Type": "application/json" },
625
+ method: "POST",
626
+ body: JSON.stringify({ fn_index, session_hash })
627
+ }
628
+ );
629
+ } catch (e) {
630
+ console.warn(
631
+ "The `/reset` endpoint could not be called. Subsequent endpoint results may be unreliable."
632
+ );
633
+ }
598
634
  }
599
635
 
600
- try {
601
- await fetch(`${http_protocol}//${host + config.path}/reset`, {
602
- headers: { "Content-Type": "application/json" },
603
- method: "POST",
604
- body: JSON.stringify({ fn_index, session_hash })
605
- });
606
- } catch (e) {
607
- console.warn(
608
- "The `/reset` endpoint could not be called. Subsequent endpoint results may be unreliable."
609
- );
636
+ function destroy() {
637
+ for (const event_type in listener_map) {
638
+ listener_map[event_type as "data" | "status"].forEach((fn) => {
639
+ off(event_type as "data" | "status", fn);
640
+ });
641
+ }
610
642
  }
643
+
644
+ return {
645
+ on,
646
+ off,
647
+ cancel,
648
+ destroy
649
+ };
611
650
  }
612
651
 
613
- function destroy() {
614
- for (const event_type in listener_map) {
615
- listener_map[event_type as "data" | "status"].forEach((fn) => {
616
- off(event_type as "data" | "status", fn);
652
+ async function view_api(config?: Config): Promise<ApiInfo<JsApiData>> {
653
+ if (api) return api;
654
+
655
+ const headers: {
656
+ Authorization?: string;
657
+ "Content-Type": "application/json";
658
+ } = { "Content-Type": "application/json" };
659
+ if (hf_token) {
660
+ headers.Authorization = `Bearer ${hf_token}`;
661
+ }
662
+ let response: Response;
663
+ // @ts-ignore
664
+ if (semiver(config.version || "2.0.0", "3.30") < 0) {
665
+ response = await fetch_implementation(
666
+ "https://gradio-space-api-fetcher-v2.hf.space/api",
667
+ {
668
+ method: "POST",
669
+ body: JSON.stringify({
670
+ serialize: false,
671
+ config: JSON.stringify(config)
672
+ }),
673
+ headers
674
+ }
675
+ );
676
+ } else {
677
+ response = await fetch_implementation(`${config.root}/info`, {
678
+ headers
617
679
  });
618
680
  }
619
- }
620
681
 
621
- return {
622
- on,
623
- off,
624
- cancel,
625
- destroy
626
- };
627
- }
682
+ if (!response.ok) {
683
+ throw new Error(BROKEN_CONNECTION_MSG);
684
+ }
628
685
 
629
- async function view_api(config?: Config): Promise<ApiInfo<JsApiData>> {
630
- if (api) return api;
686
+ let api_info = (await response.json()) as
687
+ | ApiInfo<ApiData>
688
+ | { api: ApiInfo<ApiData> };
689
+ if ("api" in api_info) {
690
+ api_info = api_info.api;
691
+ }
631
692
 
632
- const headers: {
633
- Authorization?: string;
634
- "Content-Type": "application/json";
635
- } = { "Content-Type": "application/json" };
636
- if (hf_token) {
637
- headers.Authorization = `Bearer ${hf_token}`;
638
- }
639
- let response: Response;
640
- // @ts-ignore
641
- if (semiver(config.version || "2.0.0", "3.30") < 0) {
642
- response = await fetch(
643
- "https://gradio-space-api-fetcher-v2.hf.space/api",
644
- {
645
- method: "POST",
646
- body: JSON.stringify({
647
- serialize: false,
648
- config: JSON.stringify(config)
649
- }),
650
- headers
651
- }
652
- );
653
- } else {
654
- response = await fetch(`${config.root}/info`, {
655
- headers
656
- });
657
- }
693
+ if (
694
+ api_info.named_endpoints["/predict"] &&
695
+ !api_info.unnamed_endpoints["0"]
696
+ ) {
697
+ api_info.unnamed_endpoints[0] = api_info.named_endpoints["/predict"];
698
+ }
658
699
 
659
- if (!response.ok) {
660
- throw new Error(BROKEN_CONNECTION_MSG);
700
+ const x = transform_api_info(api_info, config, api_map);
701
+ return x;
661
702
  }
703
+ });
704
+ }
662
705
 
663
- let api_info = (await response.json()) as
664
- | ApiInfo<ApiData>
665
- | { api: ApiInfo<ApiData> };
666
- if ("api" in api_info) {
667
- api_info = api_info.api;
668
- }
706
+ async function handle_blob(
707
+ endpoint: string,
708
+ data: unknown[],
709
+ api_info,
710
+ token?: `hf_${string}`
711
+ ): Promise<unknown[]> {
712
+ const blob_refs = await walk_and_store_blobs(
713
+ data,
714
+ undefined,
715
+ [],
716
+ true,
717
+ api_info
718
+ );
669
719
 
670
- if (
671
- api_info.named_endpoints["/predict"] &&
672
- !api_info.unnamed_endpoints["0"]
673
- ) {
674
- api_info.unnamed_endpoints[0] = api_info.named_endpoints["/predict"];
675
- }
720
+ return Promise.all(
721
+ blob_refs.map(async ({ path, blob, data, type }) => {
722
+ if (blob) {
723
+ const file_url = (await upload_files(endpoint, [blob], token))
724
+ .files[0];
725
+ return { path, file_url, type };
726
+ } else {
727
+ return { path, base64: data, type };
728
+ }
729
+ })
730
+ ).then((r) => {
731
+ r.forEach(({ path, file_url, base64, type }) => {
732
+ if (base64) {
733
+ update_object(data, base64, path);
734
+ } else if (type === "Gallery") {
735
+ update_object(data, file_url, path);
736
+ } else if (file_url) {
737
+ const o = {
738
+ is_file: true,
739
+ name: `${file_url}`,
740
+ data: null
741
+ // orig_name: "file.csv"
742
+ };
743
+ update_object(data, o, path);
744
+ }
745
+ });
676
746
 
677
- const x = transform_api_info(api_info, config, api_map);
678
- return x;
679
- }
680
- });
747
+ return data;
748
+ });
749
+ }
681
750
  }
682
751
 
752
+ export const { post_data, upload_files, client, handle_blob } =
753
+ api_factory(fetch);
754
+
683
755
  function transform_output(
684
756
  data: any[],
685
757
  api_info: any,
@@ -902,50 +974,6 @@ async function get_jwt(
902
974
  }
903
975
  }
904
976
 
905
- export async function handle_blob(
906
- endpoint: string,
907
- data: unknown[],
908
- api_info,
909
- token?: `hf_${string}`
910
- ): Promise<unknown[]> {
911
- const blob_refs = await walk_and_store_blobs(
912
- data,
913
- undefined,
914
- [],
915
- true,
916
- api_info
917
- );
918
-
919
- return Promise.all(
920
- blob_refs.map(async ({ path, blob, data, type }) => {
921
- if (blob) {
922
- const file_url = (await upload_files(endpoint, [blob], token)).files[0];
923
- return { path, file_url, type };
924
- } else {
925
- return { path, base64: data, type };
926
- }
927
- })
928
- ).then((r) => {
929
- r.forEach(({ path, file_url, base64, type }) => {
930
- if (base64) {
931
- update_object(data, base64, path);
932
- } else if (type === "Gallery") {
933
- update_object(data, file_url, path);
934
- } else if (file_url) {
935
- const o = {
936
- is_file: true,
937
- name: `${file_url}`,
938
- data: null
939
- // orig_name: "file.csv"
940
- };
941
- update_object(data, o, path);
942
- }
943
- });
944
-
945
- return data;
946
- });
947
- }
948
-
949
977
  function update_object(object, newValue, stack) {
950
978
  while (stack.length > 1) {
951
979
  object = object[stack.shift()];
@@ -1051,6 +1079,7 @@ function skip_queue(id: number, config: Config) {
1051
1079
  }
1052
1080
 
1053
1081
  async function resolve_config(
1082
+ fetch_implementation: typeof fetch,
1054
1083
  endpoint?: string,
1055
1084
  token?: `hf_${string}`
1056
1085
  ): Promise<Config> {
@@ -1068,7 +1097,9 @@ async function resolve_config(
1068
1097
  config.root = endpoint + config.root;
1069
1098
  return { ...config, path: path };
1070
1099
  } else if (endpoint) {
1071
- let response = await fetch(`${endpoint}/config`, { headers });
1100
+ let response = await fetch_implementation(`${endpoint}/config`, {
1101
+ headers
1102
+ });
1072
1103
 
1073
1104
  if (response.status === 200) {
1074
1105
  const config = await response.json();