@gradio/client 0.20.1 → 1.1.1

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 (46) hide show
  1. package/CHANGELOG.md +201 -0
  2. package/README.md +21 -36
  3. package/dist/client.d.ts +6 -7
  4. package/dist/client.d.ts.map +1 -1
  5. package/dist/constants.d.ts +3 -0
  6. package/dist/constants.d.ts.map +1 -1
  7. package/dist/helpers/data.d.ts +15 -1
  8. package/dist/helpers/data.d.ts.map +1 -1
  9. package/dist/helpers/spaces.d.ts.map +1 -1
  10. package/dist/index.d.ts +2 -1
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +490 -114
  13. package/dist/test/handlers.d.ts +1 -0
  14. package/dist/test/handlers.d.ts.map +1 -1
  15. package/dist/test/test_data.d.ts.map +1 -1
  16. package/dist/types.d.ts +94 -24
  17. package/dist/types.d.ts.map +1 -1
  18. package/dist/utils/handle_blob.d.ts +2 -1
  19. package/dist/utils/handle_blob.d.ts.map +1 -1
  20. package/dist/utils/predict.d.ts +2 -2
  21. package/dist/utils/predict.d.ts.map +1 -1
  22. package/dist/utils/stream.d.ts +2 -1
  23. package/dist/utils/stream.d.ts.map +1 -1
  24. package/dist/utils/submit.d.ts +2 -2
  25. package/dist/utils/submit.d.ts.map +1 -1
  26. package/index.html +39 -0
  27. package/package.json +5 -2
  28. package/src/client.ts +40 -35
  29. package/src/constants.ts +4 -0
  30. package/src/helpers/api_info.ts +1 -1
  31. package/src/helpers/data.ts +124 -10
  32. package/src/helpers/spaces.ts +2 -1
  33. package/src/index.ts +6 -1
  34. package/src/test/api_info.test.ts +0 -1
  35. package/src/test/data.test.ts +201 -26
  36. package/src/test/handlers.ts +9 -1
  37. package/src/test/stream.test.ts +12 -10
  38. package/src/test/test_data.ts +8 -5
  39. package/src/test/upload_files.test.ts +1 -1
  40. package/src/types.ts +111 -26
  41. package/src/utils/handle_blob.ts +91 -1
  42. package/src/utils/predict.ts +15 -16
  43. package/src/utils/stream.ts +66 -13
  44. package/src/utils/submit.ts +156 -63
  45. package/src/utils/upload_files.ts +1 -1
  46. package/vite.config.js +37 -24
package/src/types.ts CHANGED
@@ -1,6 +1,8 @@
1
1
  // API Data Types
2
2
 
3
3
  import { hardware_types } from "./helpers/spaces";
4
+ import type { SvelteComponent } from "svelte";
5
+ import type { ComponentType } from "svelte";
4
6
 
5
7
  export interface ApiData {
6
8
  label: string;
@@ -49,6 +51,27 @@ export interface BlobRef {
49
51
 
50
52
  export type DataType = string | Buffer | Record<string, any> | any[];
51
53
 
54
+ // custom class used for uploading local files
55
+ export class Command {
56
+ type: string;
57
+ command: string;
58
+ meta: {
59
+ path: string;
60
+ name: string;
61
+ orig_path: string;
62
+ };
63
+ fileData?: FileData;
64
+
65
+ constructor(
66
+ command: string,
67
+ meta: { path: string; name: string; orig_path: string }
68
+ ) {
69
+ this.type = "command";
70
+ this.command = command;
71
+ this.meta = meta;
72
+ }
73
+ }
74
+
52
75
  // Function Signature Types
53
76
 
54
77
  export type SubmitFunction = (
@@ -56,20 +79,13 @@ export type SubmitFunction = (
56
79
  data: unknown[] | Record<string, unknown>,
57
80
  event_data?: unknown,
58
81
  trigger_id?: number | null
59
- ) => SubmitReturn;
82
+ ) => SubmitIterable<GradioEvent>;
60
83
 
61
84
  export type PredictFunction = (
62
85
  endpoint: string | number,
63
86
  data: unknown[] | Record<string, unknown>,
64
87
  event_data?: unknown
65
- ) => Promise<SubmitReturn>;
66
-
67
- // Event and Submission Types
68
-
69
- type event = <K extends EventType>(
70
- eventType: K,
71
- listener: EventListener<K>
72
- ) => SubmitReturn;
88
+ ) => Promise<PredictReturn>;
73
89
 
74
90
  export type client_return = {
75
91
  config: Config | undefined;
@@ -83,11 +99,17 @@ export type client_return = {
83
99
  view_api: (_fetch: typeof fetch) => Promise<ApiInfo<JsApiData>>;
84
100
  };
85
101
 
86
- export type SubmitReturn = {
87
- on: event;
88
- off: event;
102
+ export interface SubmitIterable<T> extends AsyncIterable<T> {
103
+ [Symbol.asyncIterator](): AsyncIterator<T>;
89
104
  cancel: () => Promise<void>;
90
- destroy: () => void;
105
+ }
106
+
107
+ export type PredictReturn = {
108
+ type: EventType;
109
+ time: Date;
110
+ data: unknown;
111
+ endpoint: string;
112
+ fn_index: number;
91
113
  };
92
114
 
93
115
  // Space Status Types
@@ -128,7 +150,7 @@ export interface Config {
128
150
  analytics_enabled: boolean;
129
151
  connect_heartbeat: boolean;
130
152
  auth_message: string;
131
- components: any[];
153
+ components: ComponentMeta[];
132
154
  css: string | null;
133
155
  js: string | null;
134
156
  head: string | null;
@@ -151,6 +173,46 @@ export interface Config {
151
173
  path: string;
152
174
  protocol: "sse_v3" | "sse_v2.1" | "sse_v2" | "sse_v1" | "sse" | "ws";
153
175
  max_file_size?: number;
176
+ theme_hash?: number;
177
+ }
178
+
179
+ // todo: DRY up types
180
+ export interface ComponentMeta {
181
+ type: string;
182
+ id: number;
183
+ has_modes: boolean;
184
+ props: SharedProps;
185
+ instance: SvelteComponent;
186
+ component: ComponentType<SvelteComponent>;
187
+ documentation?: Documentation;
188
+ children?: ComponentMeta[];
189
+ parent?: ComponentMeta;
190
+ value?: any;
191
+ component_class_id: string;
192
+ key: string | number | null;
193
+ rendered_in?: number;
194
+ }
195
+
196
+ interface SharedProps {
197
+ elem_id?: string;
198
+ elem_classes?: string[];
199
+ components?: string[];
200
+ server_fns?: string[];
201
+ interactive: boolean;
202
+ [key: string]: unknown;
203
+ root_url?: string;
204
+ }
205
+
206
+ export interface Documentation {
207
+ type?: TypeDescription;
208
+ description?: TypeDescription;
209
+ example_data?: string;
210
+ }
211
+
212
+ interface TypeDescription {
213
+ input_payload?: string;
214
+ response_object?: string;
215
+ payload?: string;
154
216
  }
155
217
 
156
218
  export interface Dependency {
@@ -186,6 +248,7 @@ export interface Dependency {
186
248
  export interface DependencyTypes {
187
249
  continuous: boolean;
188
250
  generator: boolean;
251
+ cancel: boolean;
189
252
  }
190
253
 
191
254
  export interface Payload {
@@ -218,6 +281,8 @@ export interface ClientOptions {
218
281
  hf_token?: `hf_${string}`;
219
282
  status_callback?: SpaceStatusCallback | null;
220
283
  auth?: [string, string] | null;
284
+ with_null_state?: boolean;
285
+ events?: EventType[];
221
286
  }
222
287
 
223
288
  export interface FileData {
@@ -236,25 +301,21 @@ export interface FileData {
236
301
  export type EventType = "data" | "status" | "log" | "render";
237
302
 
238
303
  export interface EventMap {
239
- data: Payload;
240
- status: Status;
304
+ data: PayloadMessage;
305
+ status: StatusMessage;
241
306
  log: LogMessage;
242
307
  render: RenderMessage;
243
308
  }
244
309
 
245
- export type Event<K extends EventType> = {
246
- [P in K]: EventMap[P] & { type: P; endpoint: string; fn_index: number };
247
- }[K];
248
- export type EventListener<K extends EventType> = (event: Event<K>) => void;
249
- export type ListenerMap<K extends EventType> = {
250
- [P in K]?: EventListener<K>[];
251
- };
252
- export interface LogMessage {
310
+ export type GradioEvent = {
311
+ [P in EventType]: EventMap[P];
312
+ }[EventType];
313
+
314
+ export interface Log {
253
315
  log: string;
254
316
  level: "warning" | "info";
255
317
  }
256
- export interface RenderMessage {
257
- fn_index: number;
318
+ export interface Render {
258
319
  data: {
259
320
  components: any[];
260
321
  layout: any;
@@ -283,3 +344,27 @@ export interface Status {
283
344
  time?: Date;
284
345
  changed_state_ids?: number[];
285
346
  }
347
+
348
+ export interface StatusMessage extends Status {
349
+ type: "status";
350
+ endpoint: string;
351
+ fn_index: number;
352
+ }
353
+
354
+ export interface PayloadMessage extends Payload {
355
+ type: "data";
356
+ endpoint: string;
357
+ fn_index: number;
358
+ }
359
+
360
+ export interface LogMessage extends Log {
361
+ type: "log";
362
+ endpoint: string;
363
+ fn_index: number;
364
+ }
365
+
366
+ export interface RenderMessage extends Render {
367
+ type: "render";
368
+ endpoint: string;
369
+ fn_index: number;
370
+ }
@@ -1,7 +1,17 @@
1
1
  import { update_object, walk_and_store_blobs } from "../helpers/data";
2
- import type { ApiData, EndpointInfo, JsApiData } from "../types";
2
+ import {
3
+ Command,
4
+ type ApiData,
5
+ type EndpointInfo,
6
+ type JsApiData
7
+ } from "../types";
3
8
  import { FileData } from "../upload";
4
9
  import type { Client } from "..";
10
+ import {
11
+ FILE_PROCESSING_ERROR_MSG,
12
+ NODEJS_FS_ERROR_MSG,
13
+ ROOT_URL_ERROR_MSG
14
+ } from "../constants";
5
15
 
6
16
  export async function handle_blob(
7
17
  this: Client,
@@ -11,6 +21,8 @@ export async function handle_blob(
11
21
  ): Promise<unknown[]> {
12
22
  const self = this;
13
23
 
24
+ await process_local_file_commands(self, data);
25
+
14
26
  const blobRefs = await walk_and_store_blobs(
15
27
  data,
16
28
  undefined,
@@ -45,3 +57,81 @@ export async function handle_blob(
45
57
 
46
58
  return data;
47
59
  }
60
+
61
+ export async function process_local_file_commands(
62
+ client: Client,
63
+ data: unknown[]
64
+ ): Promise<void> {
65
+ const root = client.config?.root || client.config?.root_url;
66
+
67
+ if (!root) {
68
+ throw new Error(ROOT_URL_ERROR_MSG);
69
+ }
70
+
71
+ await recursively_process_commands(client, data);
72
+ }
73
+
74
+ async function recursively_process_commands(
75
+ client: Client,
76
+ data: any,
77
+ path: string[] = []
78
+ ): Promise<void> {
79
+ for (const key in data) {
80
+ if (data[key] instanceof Command) {
81
+ await process_single_command(client, data, key);
82
+ } else if (typeof data[key] === "object" && data[key] !== null) {
83
+ await recursively_process_commands(client, data[key], [...path, key]);
84
+ }
85
+ }
86
+ }
87
+
88
+ async function process_single_command(
89
+ client: Client,
90
+ data: any,
91
+ key: string
92
+ ): Promise<void> {
93
+ let cmd_item = data[key] as Command;
94
+ const root = client.config?.root || client.config?.root_url;
95
+
96
+ if (!root) {
97
+ throw new Error(ROOT_URL_ERROR_MSG);
98
+ }
99
+
100
+ try {
101
+ let fileBuffer: Buffer;
102
+ let fullPath: string;
103
+
104
+ // check if running in a Node.js environment
105
+ if (
106
+ typeof process !== "undefined" &&
107
+ process.versions &&
108
+ process.versions.node
109
+ ) {
110
+ const fs = await import("fs/promises");
111
+ const path = await import("path");
112
+
113
+ fullPath = path.resolve(process.cwd(), cmd_item.meta.path);
114
+ fileBuffer = await fs.readFile(fullPath); // Read file from disk
115
+ } else {
116
+ throw new Error(NODEJS_FS_ERROR_MSG);
117
+ }
118
+
119
+ const file = new Blob([fileBuffer], { type: "application/octet-stream" });
120
+
121
+ const response = await client.upload_files(root, [file]);
122
+
123
+ const file_url = response.files && response.files[0];
124
+
125
+ if (file_url) {
126
+ const fileData = new FileData({
127
+ path: file_url,
128
+ orig_name: cmd_item.meta.name || ""
129
+ });
130
+
131
+ // replace the command object with the fileData object
132
+ data[key] = fileData;
133
+ }
134
+ } catch (error) {
135
+ console.error(FILE_PROCESSING_ERROR_MSG, error);
136
+ }
137
+ }
@@ -1,11 +1,11 @@
1
1
  import { Client } from "../client";
2
- import type { Dependency, SubmitReturn } from "../types";
2
+ import type { Dependency, PredictReturn } from "../types";
3
3
 
4
4
  export async function predict(
5
5
  this: Client,
6
6
  endpoint: string | number,
7
7
  data: unknown[] | Record<string, unknown>
8
- ): Promise<SubmitReturn> {
8
+ ): Promise<PredictReturn> {
9
9
  let data_returned = false;
10
10
  let status_complete = false;
11
11
  let dependency: Dependency;
@@ -30,29 +30,28 @@ export async function predict(
30
30
  }
31
31
 
32
32
  return new Promise(async (resolve, reject) => {
33
- const app = this.submit(endpoint, data);
33
+ const app = this.submit(endpoint, data, null, null, true);
34
34
  let result: unknown;
35
35
 
36
- app
37
- .on("data", (d: unknown) => {
38
- // if complete message comes before data, resolve here
36
+ for await (const message of app) {
37
+ if (message.type === "data") {
39
38
  if (status_complete) {
40
- app.destroy();
41
- resolve(d as SubmitReturn);
39
+ resolve(result as PredictReturn);
42
40
  }
43
41
  data_returned = true;
44
- result = d;
45
- })
46
- .on("status", (status) => {
47
- if (status.stage === "error") reject(status);
48
- if (status.stage === "complete") {
42
+ result = message;
43
+ }
44
+
45
+ if (message.type === "status") {
46
+ if (message.stage === "error") reject(message);
47
+ if (message.stage === "complete") {
49
48
  status_complete = true;
50
49
  // if complete message comes after data, resolve here
51
50
  if (data_returned) {
52
- app.destroy();
53
- resolve(result as SubmitReturn);
51
+ resolve(result as PredictReturn);
54
52
  }
55
53
  }
56
- });
54
+ }
55
+ }
57
56
  });
58
57
  }
@@ -1,5 +1,6 @@
1
1
  import { BROKEN_CONNECTION_MSG } from "../constants";
2
2
  import type { Client } from "../client";
3
+ import { stream } from "fetch-event-stream";
3
4
 
4
5
  export async function open_stream(this: Client): Promise<void> {
5
6
  let {
@@ -11,6 +12,8 @@ export async function open_stream(this: Client): Promise<void> {
11
12
  jwt
12
13
  } = this;
13
14
 
15
+ const that = this;
16
+
14
17
  if (!config) {
15
18
  throw new Error("Could not resolve app config");
16
19
  }
@@ -28,7 +31,7 @@ export async function open_stream(this: Client): Promise<void> {
28
31
  url.searchParams.set("__sign", jwt);
29
32
  }
30
33
 
31
- stream = await this.stream(url);
34
+ stream = this.stream(url);
32
35
 
33
36
  if (!stream) {
34
37
  console.warn("Cannot connect to SSE endpoint: " + url.toString());
@@ -38,7 +41,7 @@ export async function open_stream(this: Client): Promise<void> {
38
41
  stream.onmessage = async function (event: MessageEvent) {
39
42
  let _data = JSON.parse(event.data);
40
43
  if (_data.msg === "close_stream") {
41
- close_stream(stream_status, stream);
44
+ close_stream(stream_status, that.abort_controller);
42
45
  return;
43
46
  }
44
47
  const event_id = _data.event_id;
@@ -51,19 +54,19 @@ export async function open_stream(this: Client): Promise<void> {
51
54
  } else if (event_callbacks[event_id] && config) {
52
55
  if (
53
56
  _data.msg === "process_completed" &&
54
- ["sse", "sse_v1", "sse_v2", "sse_v2.1"].includes(config.protocol)
57
+ ["sse", "sse_v1", "sse_v2", "sse_v2.1", "sse_v3"].includes(
58
+ config.protocol
59
+ )
55
60
  ) {
56
61
  unclosed_events.delete(event_id);
57
- if (unclosed_events.size === 0) {
58
- close_stream(stream_status, stream);
59
- }
60
62
  }
61
63
  let fn: (data: any) => void = event_callbacks[event_id];
62
64
 
63
- if (typeof window !== "undefined") {
64
- window.setTimeout(fn, 0, _data); // need to do this to put the event on the end of the event loop, so the browser can refresh between callbacks and not freeze in case of quick generations. See https://github.com/gradio-app/gradio/pull/7055
65
+ if (typeof window !== "undefined" && typeof document !== "undefined") {
66
+ // fn(_data); // need to do this to put the event on the end of the event loop, so the browser can refresh between callbacks and not freeze in case of quick generations. See
67
+ setTimeout(fn, 0, _data); // need to do this to put the event on the end of the event loop, so the browser can refresh between callbacks and not freeze in case of quick generations. See https://github.com/gradio-app/gradio/pull/7055
65
68
  } else {
66
- setImmediate(fn, _data);
69
+ fn(_data);
67
70
  }
68
71
  } else {
69
72
  if (!pending_stream_messages[event_id]) {
@@ -81,17 +84,16 @@ export async function open_stream(this: Client): Promise<void> {
81
84
  })
82
85
  )
83
86
  );
84
- close_stream(stream_status, stream);
85
87
  };
86
88
  }
87
89
 
88
90
  export function close_stream(
89
91
  stream_status: { open: boolean },
90
- stream: EventSource | null
92
+ abort_controller: AbortController | null
91
93
  ): void {
92
- if (stream_status && stream) {
94
+ if (stream_status) {
93
95
  stream_status.open = false;
94
- stream?.close();
96
+ abort_controller?.abort();
95
97
  }
96
98
  }
97
99
 
@@ -173,3 +175,54 @@ function apply_edit(
173
175
  }
174
176
  return target;
175
177
  }
178
+
179
+ export function readable_stream(
180
+ input: RequestInfo | URL,
181
+ init: RequestInit = {}
182
+ ): EventSource {
183
+ const instance: EventSource & { readyState: number } = {
184
+ close: () => {
185
+ throw new Error("Method not implemented.");
186
+ },
187
+ onerror: null,
188
+ onmessage: null,
189
+ onopen: null,
190
+ readyState: 0,
191
+ url: input.toString(),
192
+ withCredentials: false,
193
+ CONNECTING: 0,
194
+ OPEN: 1,
195
+ CLOSED: 2,
196
+ addEventListener: () => {
197
+ throw new Error("Method not implemented.");
198
+ },
199
+ dispatchEvent: () => {
200
+ throw new Error("Method not implemented.");
201
+ },
202
+ removeEventListener: () => {
203
+ throw new Error("Method not implemented.");
204
+ }
205
+ };
206
+
207
+ stream(input, init)
208
+ .then(async (res) => {
209
+ instance.readyState = instance.OPEN;
210
+ try {
211
+ for await (const chunk of res) {
212
+ //@ts-ignore
213
+ instance.onmessage && instance.onmessage(chunk);
214
+ }
215
+ instance.readyState = instance.CLOSED;
216
+ } catch (e) {
217
+ instance.onerror && instance.onerror(e as Event);
218
+ instance.readyState = instance.CLOSED;
219
+ }
220
+ })
221
+ .catch((e) => {
222
+ console.error(e);
223
+ instance.onerror && instance.onerror(e as Event);
224
+ instance.readyState = instance.CLOSED;
225
+ });
226
+
227
+ return instance as EventSource;
228
+ }