@gradio/client 0.17.0 → 0.18.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.
Files changed (62) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/client.d.ts +15 -4
  3. package/dist/client.d.ts.map +1 -1
  4. package/dist/constants.d.ts +8 -2
  5. package/dist/constants.d.ts.map +1 -1
  6. package/dist/helpers/data.d.ts +2 -2
  7. package/dist/helpers/data.d.ts.map +1 -1
  8. package/dist/helpers/init_helpers.d.ts.map +1 -1
  9. package/dist/helpers/spaces.d.ts.map +1 -1
  10. package/dist/index.d.ts +1 -1
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +219 -176
  13. package/dist/test/handlers.d.ts +3 -0
  14. package/dist/test/handlers.d.ts.map +1 -0
  15. package/dist/test/mock_eventsource.d.ts +2 -0
  16. package/dist/test/mock_eventsource.d.ts.map +1 -0
  17. package/dist/test/server.d.ts +2 -0
  18. package/dist/test/server.d.ts.map +1 -0
  19. package/dist/test/test_data.d.ts +76 -0
  20. package/dist/test/test_data.d.ts.map +1 -0
  21. package/dist/types.d.ts +4 -3
  22. package/dist/types.d.ts.map +1 -1
  23. package/dist/upload.d.ts +2 -2
  24. package/dist/upload.d.ts.map +1 -1
  25. package/dist/utils/duplicate.d.ts.map +1 -1
  26. package/dist/utils/stream.d.ts +1 -1
  27. package/dist/utils/stream.d.ts.map +1 -1
  28. package/dist/utils/submit.d.ts.map +1 -1
  29. package/dist/utils/upload_files.d.ts.map +1 -1
  30. package/dist/utils/view_api.d.ts.map +1 -1
  31. package/package.json +8 -2
  32. package/src/client.ts +50 -24
  33. package/src/constants.ts +9 -2
  34. package/src/helpers/api_info.ts +4 -4
  35. package/src/helpers/data.ts +46 -28
  36. package/src/helpers/init_helpers.ts +7 -11
  37. package/src/helpers/spaces.ts +8 -3
  38. package/src/index.ts +1 -1
  39. package/src/test/api_info.test.ts +456 -0
  40. package/src/test/data.test.ts +281 -0
  41. package/src/test/handlers.ts +438 -0
  42. package/src/test/init.test.ts +139 -0
  43. package/src/test/init_helpers.test.ts +94 -0
  44. package/src/test/mock_eventsource.ts +11 -0
  45. package/src/test/post_data.test.ts +45 -0
  46. package/src/test/server.ts +6 -0
  47. package/src/test/spaces.test.ts +145 -0
  48. package/src/test/stream.test.ts +67 -0
  49. package/src/test/test_data.ts +557 -0
  50. package/src/test/upload_files.test.ts +42 -0
  51. package/src/test/view_api.test.ts +53 -0
  52. package/src/types.ts +4 -3
  53. package/src/upload.ts +4 -8
  54. package/src/utils/duplicate.ts +20 -3
  55. package/src/utils/handle_blob.ts +1 -1
  56. package/src/utils/post_data.ts +1 -1
  57. package/src/utils/stream.ts +29 -20
  58. package/src/utils/submit.ts +23 -15
  59. package/src/utils/upload_files.ts +11 -6
  60. package/src/utils/view_api.ts +4 -7
  61. package/vite.config.js +7 -0
  62. package/src/utils/client.node-test.ts +0 -173
@@ -0,0 +1,557 @@
1
+ // @ts-nocheck
2
+ import { ApiData, ApiInfo, Config, EndpointInfo } from "../types";
3
+
4
+ export const runtime_response = {
5
+ stage: "RUNNING",
6
+ hardware: {
7
+ current: "cpu-basic",
8
+ requested: "cpu-basic"
9
+ },
10
+ storage: {
11
+ current: null,
12
+ requested: null
13
+ },
14
+ gcTimeout: 86400,
15
+ replicas: {
16
+ current: 1,
17
+ requested: 1
18
+ },
19
+ devMode: false,
20
+ domains: [
21
+ {
22
+ domain: "hmb-hello-world.hf.space",
23
+ isCustom: false,
24
+ stage: "READY"
25
+ }
26
+ ]
27
+ };
28
+
29
+ export const transformed_api_info: ApiInfo<ApiData> = {
30
+ named_endpoints: {
31
+ "/predict": {
32
+ parameters: [
33
+ {
34
+ label: "name",
35
+ type: "string",
36
+ python_type: { type: "str", description: "" },
37
+ component: "Textbox",
38
+ example_input: "Hello!!"
39
+ }
40
+ ],
41
+ returns: [
42
+ {
43
+ label: "output",
44
+ type: "string",
45
+ python_type: { type: "str", description: "" },
46
+ component: "Textbox"
47
+ }
48
+ ],
49
+ type: { continuous: false, generator: false }
50
+ }
51
+ },
52
+ unnamed_endpoints: {
53
+ "0": {
54
+ parameters: [
55
+ {
56
+ label: "name",
57
+ type: "string",
58
+ python_type: { type: "str", description: "" },
59
+ component: "Textbox",
60
+ example_input: "Hello!!"
61
+ }
62
+ ],
63
+ returns: [
64
+ {
65
+ label: "output",
66
+ type: "string",
67
+ python_type: { type: "str", description: "" },
68
+ component: "Textbox"
69
+ }
70
+ ],
71
+ type: { continuous: false, generator: false }
72
+ }
73
+ }
74
+ };
75
+
76
+ export const response_api_info: ApiInfo<ApiData> = {
77
+ named_endpoints: {
78
+ "/predict": {
79
+ parameters: [
80
+ {
81
+ label: "name",
82
+ type: {
83
+ type: "string"
84
+ },
85
+ python_type: {
86
+ type: "str",
87
+ description: ""
88
+ },
89
+ component: "Textbox",
90
+ example_input: "Hello!!"
91
+ }
92
+ ],
93
+ returns: [
94
+ {
95
+ label: "output",
96
+ type: {
97
+ type: "string"
98
+ },
99
+ python_type: {
100
+ type: "str",
101
+ description: ""
102
+ },
103
+ component: "Textbox"
104
+ }
105
+ ]
106
+ }
107
+ },
108
+ unnamed_endpoints: {}
109
+ };
110
+
111
+ export const config_response: Config = {
112
+ version: "4.27.0",
113
+ mode: "interface",
114
+ app_id: 123,
115
+ dev_mode: false,
116
+ analytics_enabled: true,
117
+ components: [
118
+ {
119
+ id: 3,
120
+ type: "row",
121
+ props: {
122
+ variant: "default",
123
+ visible: true,
124
+ equal_height: false,
125
+ name: "row"
126
+ },
127
+ skip_api: true,
128
+ component_class_id: ""
129
+ },
130
+ {
131
+ id: 4,
132
+ type: "column",
133
+ props: {
134
+ scale: 1,
135
+ min_width: 320,
136
+ variant: "panel",
137
+ visible: true,
138
+ name: "column"
139
+ },
140
+ skip_api: true,
141
+ component_class_id: ""
142
+ },
143
+ {
144
+ id: 5,
145
+ type: "column",
146
+ props: {
147
+ scale: 1,
148
+ min_width: 320,
149
+ variant: "default",
150
+ visible: true,
151
+ name: "column"
152
+ },
153
+ skip_api: true,
154
+ component_class_id: ""
155
+ },
156
+ {
157
+ id: 1,
158
+ type: "textbox",
159
+ props: {
160
+ lines: 1,
161
+ max_lines: 20,
162
+ label: "name",
163
+ show_label: true,
164
+ container: true,
165
+ min_width: 160,
166
+ visible: true,
167
+ autofocus: false,
168
+ autoscroll: true,
169
+ elem_classes: [],
170
+ type: "text",
171
+ rtl: false,
172
+ show_copy_button: false,
173
+ name: "textbox",
174
+ _selectable: false
175
+ },
176
+ skip_api: false,
177
+ component_class_id: "",
178
+ api_info: {
179
+ type: "string"
180
+ },
181
+ example_inputs: "Hello!!"
182
+ },
183
+ {
184
+ id: 6,
185
+ type: "form",
186
+ props: {
187
+ scale: 0,
188
+ min_width: 0,
189
+ name: "form"
190
+ },
191
+ skip_api: true,
192
+ component_class_id: ""
193
+ },
194
+ {
195
+ id: 7,
196
+ type: "row",
197
+ props: {
198
+ variant: "default",
199
+ visible: true,
200
+ equal_height: true,
201
+ name: "row"
202
+ },
203
+ skip_api: true,
204
+ component_class_id: ""
205
+ },
206
+ {
207
+ id: 8,
208
+ type: "button",
209
+ props: {
210
+ value: "Clear",
211
+ variant: "secondary",
212
+ visible: true,
213
+ interactive: true,
214
+ elem_classes: [],
215
+ show_api: false,
216
+ name: "button",
217
+ _selectable: false
218
+ },
219
+ skip_api: true,
220
+ component_class_id: ""
221
+ },
222
+ {
223
+ id: 9,
224
+ type: "button",
225
+ props: {
226
+ value: "Submit",
227
+ variant: "primary",
228
+ visible: true,
229
+ interactive: true,
230
+ elem_classes: [],
231
+ name: "button",
232
+ _selectable: false
233
+ },
234
+ skip_api: true,
235
+ component_class_id: ""
236
+ },
237
+ {
238
+ id: 10,
239
+ type: "column",
240
+ props: {
241
+ scale: 1,
242
+ min_width: 320,
243
+ variant: "panel",
244
+ visible: true,
245
+ name: "column"
246
+ },
247
+ skip_api: true,
248
+ component_class_id: ""
249
+ },
250
+ {
251
+ id: 2,
252
+ type: "textbox",
253
+ props: {
254
+ lines: 1,
255
+ max_lines: 20,
256
+ label: "output",
257
+ show_label: true,
258
+ container: true,
259
+ min_width: 160,
260
+ interactive: false,
261
+ visible: true,
262
+ autofocus: false,
263
+ autoscroll: true,
264
+ elem_classes: [],
265
+ type: "text",
266
+ rtl: false,
267
+ show_copy_button: false,
268
+ name: "textbox",
269
+ _selectable: false
270
+ },
271
+ skip_api: false,
272
+ component_class_id: "",
273
+ api_info: {
274
+ type: "string"
275
+ },
276
+ example_inputs: "Hello!!"
277
+ },
278
+ {
279
+ id: 11,
280
+ type: "row",
281
+ props: {
282
+ variant: "default",
283
+ visible: true,
284
+ equal_height: true,
285
+ name: "row"
286
+ },
287
+ skip_api: true,
288
+ component_class_id: ""
289
+ },
290
+ {
291
+ id: 12,
292
+ type: "form",
293
+ props: {
294
+ scale: 0,
295
+ min_width: 0,
296
+ name: "form"
297
+ },
298
+ skip_api: true,
299
+ component_class_id: ""
300
+ }
301
+ ],
302
+ css: null,
303
+ js: null,
304
+ head: null,
305
+ title: "Gradio",
306
+ space_id: "hmb/hello_world",
307
+ enable_queue: true,
308
+ show_error: false,
309
+ show_api: true,
310
+ is_colab: false,
311
+ stylesheets: [],
312
+ theme: "default",
313
+ protocol: "sse_v3",
314
+ body_css: {
315
+ body_background_fill: "white",
316
+ body_text_color: "#1f2937",
317
+ body_background_fill_dark: "#0b0f19",
318
+ body_text_color_dark: "#f3f4f6"
319
+ },
320
+ fill_height: false,
321
+ layout: {
322
+ id: 0,
323
+ children: [
324
+ {
325
+ id: 3,
326
+ children: [
327
+ {
328
+ id: 4,
329
+ children: [
330
+ {
331
+ id: 5,
332
+ children: [
333
+ {
334
+ id: 6,
335
+ children: [
336
+ {
337
+ id: 1
338
+ }
339
+ ]
340
+ }
341
+ ]
342
+ },
343
+ {
344
+ id: 7,
345
+ children: [
346
+ {
347
+ id: 8
348
+ },
349
+ {
350
+ id: 9
351
+ }
352
+ ]
353
+ }
354
+ ]
355
+ },
356
+ {
357
+ id: 10,
358
+ children: [
359
+ {
360
+ id: 12,
361
+ children: [
362
+ {
363
+ id: 2
364
+ }
365
+ ]
366
+ },
367
+ {
368
+ id: 11,
369
+ children: []
370
+ }
371
+ ]
372
+ }
373
+ ]
374
+ }
375
+ ]
376
+ },
377
+ dependencies: [
378
+ {
379
+ targets: [
380
+ [9, "click"],
381
+ [1, "submit"]
382
+ ],
383
+ inputs: [1],
384
+ outputs: [2],
385
+ backend_fn: true,
386
+ js: null,
387
+ queue: null,
388
+ api_name: "predict",
389
+ scroll_to_output: false,
390
+ show_progress: "full",
391
+ every: null,
392
+ batch: false,
393
+ max_batch_size: 4,
394
+ cancels: [],
395
+ types: {
396
+ continuous: false,
397
+ generator: false
398
+ },
399
+ collects_event_data: false,
400
+ trigger_after: null,
401
+ trigger_only_on_success: false,
402
+ trigger_mode: "once",
403
+ show_api: true,
404
+ zerogpu: false
405
+ },
406
+ {
407
+ targets: [[8, "click"]],
408
+ inputs: [],
409
+ outputs: [1, 2],
410
+ backend_fn: false,
411
+ js: "() => [null, null]",
412
+ queue: false,
413
+ api_name: "js_fn",
414
+ scroll_to_output: false,
415
+ show_progress: "full",
416
+ every: null,
417
+ batch: false,
418
+ max_batch_size: 4,
419
+ cancels: [],
420
+ types: {
421
+ continuous: false,
422
+ generator: false
423
+ },
424
+ collects_event_data: false,
425
+ trigger_after: null,
426
+ trigger_only_on_success: false,
427
+ trigger_mode: "once",
428
+ show_api: false,
429
+ zerogpu: false
430
+ },
431
+ {
432
+ targets: [[8, "click"]],
433
+ inputs: [],
434
+ outputs: [5],
435
+ backend_fn: false,
436
+ js: '() => [{"variant": null, "visible": true, "__type__": "update"}]\n ',
437
+ queue: false,
438
+ api_name: "js_fn_1",
439
+ scroll_to_output: false,
440
+ show_progress: "full",
441
+ every: null,
442
+ batch: false,
443
+ max_batch_size: 4,
444
+ cancels: [],
445
+ types: {
446
+ continuous: false,
447
+ generator: false
448
+ },
449
+ collects_event_data: false,
450
+ trigger_after: null,
451
+ trigger_only_on_success: false,
452
+ trigger_mode: "once",
453
+ show_api: false,
454
+ zerogpu: false
455
+ }
456
+ ],
457
+ root: "https://hmb-hello-world.hf.space",
458
+ path: ""
459
+ };
460
+
461
+ export const whoami_response = {
462
+ type: "user",
463
+ id: "123",
464
+ name: "hmb",
465
+ fullname: "jerry",
466
+ email: "jerry@gradio.com",
467
+ emailVerified: true,
468
+ canPay: true,
469
+ periodEnd: 123,
470
+ isPro: false,
471
+ avatarUrl: "",
472
+ orgs: [],
473
+ auth: {
474
+ type: "access_token",
475
+ accessToken: {
476
+ displayName: "Gradio Client",
477
+ role: "write"
478
+ }
479
+ }
480
+ };
481
+
482
+ export const duplicate_response = {
483
+ url: "https://huggingface.co/spaces/hmb/hello_world"
484
+ };
485
+
486
+ export const hardware_sleeptime_response = {
487
+ stage: "RUNNING",
488
+ hardware: {
489
+ current: "cpu-basic",
490
+ requested: "cpu-upgrade"
491
+ },
492
+ storage: null,
493
+ gcTimeout: 300,
494
+ replicas: {
495
+ current: 1,
496
+ requested: 1
497
+ },
498
+ devMode: false,
499
+ domains: [
500
+ {
501
+ domain: "hmb-hello-world.hf.space",
502
+ isCustom: false,
503
+ stage: "READY"
504
+ }
505
+ ]
506
+ };
507
+
508
+ export const endpoint_info: EndpointInfo<ApiData> = {
509
+ parameters: [
510
+ {
511
+ label: "parameter_2",
512
+ parameter_name: "im",
513
+ parameter_has_default: false,
514
+ parameter_default: null,
515
+ type: "",
516
+ python_type: {
517
+ type: "Dict(background: filepath | None, layers: List[filepath], composite: filepath | None, id: str | None)",
518
+ description: ""
519
+ },
520
+ component: "Imageeditor",
521
+ example_input: {
522
+ background: {
523
+ path: "",
524
+ meta: {
525
+ _type: "gradio.FileData"
526
+ },
527
+ orig_name: "bus.png",
528
+ url: ""
529
+ },
530
+ layers: [],
531
+ composite: null
532
+ }
533
+ }
534
+ ],
535
+ returns: [
536
+ {
537
+ label: "value_3",
538
+ type: "string",
539
+ python_type: {
540
+ type: "filepath",
541
+ description: ""
542
+ },
543
+ component: "Image"
544
+ }
545
+ ],
546
+ type: {
547
+ continuous: false,
548
+ generator: false
549
+ }
550
+ };
551
+
552
+ export const discussions_response = {
553
+ discussions: [],
554
+ count: 0,
555
+ start: 0,
556
+ numClosedDiscussions: 0
557
+ };
@@ -0,0 +1,42 @@
1
+ import { describe, it, expect, afterEach, beforeAll, afterAll } from "vitest";
2
+
3
+ import { Client } from "..";
4
+ import { initialise_server } from "./server";
5
+
6
+ const server = initialise_server();
7
+
8
+ beforeAll(() => server.listen());
9
+ afterEach(() => server.resetHandlers());
10
+ afterAll(() => server.close());
11
+
12
+ describe("upload_files", () => {
13
+ it("should upload files successfully", async () => {
14
+ const root_url = "https://hmb-hello-world.hf.space";
15
+
16
+ const client = await Client.connect("hmb/hello_world", {
17
+ hf_token: "hf_token"
18
+ });
19
+
20
+ const files = [new Blob([], { type: "image/jpeg" })];
21
+
22
+ const response = await client.upload_files(root_url, files);
23
+
24
+ if (!response.files) {
25
+ throw new Error("No files returned");
26
+ }
27
+
28
+ expect(response.files).toHaveLength(1);
29
+ expect(response.files[0]).toBe("lion.jpg");
30
+ });
31
+
32
+ it("should handle a server error when connected to a running app and uploading files", async () => {
33
+ const client = await Client.connect("hmb/server_test");
34
+
35
+ const root_url = "https://hmb-server-test.hf.space";
36
+ const files = [new Blob([""], { type: "text/plain" })];
37
+
38
+ await expect(client.upload_files(root_url, files)).rejects.toThrow(
39
+ "Connection errored out. Failed to fetch"
40
+ );
41
+ });
42
+ });
@@ -0,0 +1,53 @@
1
+ import { describe, beforeAll, afterEach, afterAll, test, expect } from "vitest";
2
+
3
+ import { Client, client, duplicate } from "..";
4
+ import { transformed_api_info, config_response } from "./test_data";
5
+ import { initialise_server } from "./server";
6
+
7
+ const app_reference = "hmb/hello_world";
8
+ const secret_app_reference = "hmb/secret_world";
9
+ const secret_direct_app_reference = "https://hmb-secret-world.hf.space";
10
+
11
+ const server = initialise_server();
12
+
13
+ beforeAll(() => server.listen());
14
+ afterEach(() => server.resetHandlers());
15
+ afterAll(() => server.close());
16
+
17
+ describe("view_api", () => {
18
+ test("viewing the api of a running, public app", async () => {
19
+ const app = await Client.connect(app_reference);
20
+
21
+ expect(await app.view_api()).toEqual(transformed_api_info);
22
+ });
23
+
24
+ test("viewing the api of a running, private app", async () => {
25
+ const app = await Client.connect(secret_app_reference, {
26
+ hf_token: "hf_123"
27
+ });
28
+
29
+ expect(app.config).toEqual({
30
+ ...config_response,
31
+ root: secret_direct_app_reference
32
+ });
33
+
34
+ expect(await app.view_api()).toEqual({
35
+ ...transformed_api_info
36
+ });
37
+ });
38
+
39
+ test("viewing the api of a running, private app with a direct app URL", async () => {
40
+ const app = await Client.connect(secret_direct_app_reference, {
41
+ hf_token: "hf_123"
42
+ });
43
+
44
+ expect(app.config).toEqual({
45
+ ...config_response,
46
+ root: secret_direct_app_reference
47
+ });
48
+
49
+ expect(await app.view_api()).toEqual({
50
+ ...transformed_api_info
51
+ });
52
+ });
53
+ });
package/src/types.ts CHANGED
@@ -38,10 +38,10 @@ export interface ApiInfo<T extends ApiData | JsApiData> {
38
38
  export interface BlobRef {
39
39
  path: string[];
40
40
  type: string | undefined;
41
- blob: Blob | false;
41
+ blob: Blob | File | false;
42
42
  }
43
43
 
44
- export type ParamType = string | Buffer | Record<string, any> | any[];
44
+ export type DataType = string | Buffer | Record<string, any> | any[];
45
45
 
46
46
  // Event and Submission Types
47
47
 
@@ -70,7 +70,7 @@ export type client_return = {
70
70
  fn_name: string,
71
71
  data: unknown[]
72
72
  ) => any;
73
- view_api: (fetch_implementation: typeof fetch) => Promise<ApiInfo<JsApiData>>;
73
+ view_api: (_fetch: typeof fetch) => Promise<ApiInfo<JsApiData>>;
74
74
  };
75
75
 
76
76
  export type SubmitReturn = {
@@ -116,6 +116,7 @@ export type SpaceStatusCallback = (a: SpaceStatus) => void;
116
116
  export interface Config {
117
117
  auth_required: boolean;
118
118
  analytics_enabled: boolean;
119
+ connect_heartbeat: boolean;
119
120
  auth_message: string;
120
121
  components: any[];
121
122
  css: string | null;
package/src/upload.ts CHANGED
@@ -1,16 +1,12 @@
1
1
  import type { UploadResponse } from "./types";
2
- import { upload_files } from ".";
2
+ import type { Client } from "./client";
3
3
 
4
4
  export async function upload(
5
+ this: Client,
5
6
  file_data: FileData[],
6
7
  root_url: string,
7
8
  upload_id?: string,
8
- max_file_size?: number,
9
- upload_fn: (
10
- root_url: string,
11
- files: (Blob | File)[],
12
- upload_id?: string
13
- ) => Promise<UploadResponse> = upload_files
9
+ max_file_size?: number
14
10
  ): Promise<(FileData | null)[] | null> {
15
11
  let files = (Array.isArray(file_data) ? file_data : [file_data]).map(
16
12
  (file_data) => file_data.blob!
@@ -28,7 +24,7 @@ export async function upload(
28
24
  }
29
25
 
30
26
  return await Promise.all(
31
- await upload_fn(root_url, files, upload_id).then(
27
+ await this.upload_files(root_url, files, upload_id).then(
32
28
  async (response: { files?: string[]; error?: string }) => {
33
29
  if (response.error) {
34
30
  throw new Error(response.error);