@gradio/client 0.20.0 → 1.1.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 (48) 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/init_helpers.d.ts.map +1 -1
  10. package/dist/helpers/spaces.d.ts.map +1 -1
  11. package/dist/index.d.ts +2 -1
  12. package/dist/index.d.ts.map +1 -1
  13. package/dist/index.js +496 -114
  14. package/dist/test/handlers.d.ts +1 -0
  15. package/dist/test/handlers.d.ts.map +1 -1
  16. package/dist/test/test_data.d.ts.map +1 -1
  17. package/dist/types.d.ts +93 -24
  18. package/dist/types.d.ts.map +1 -1
  19. package/dist/utils/handle_blob.d.ts +2 -1
  20. package/dist/utils/handle_blob.d.ts.map +1 -1
  21. package/dist/utils/predict.d.ts +2 -2
  22. package/dist/utils/predict.d.ts.map +1 -1
  23. package/dist/utils/stream.d.ts +2 -1
  24. package/dist/utils/stream.d.ts.map +1 -1
  25. package/dist/utils/submit.d.ts +2 -2
  26. package/dist/utils/submit.d.ts.map +1 -1
  27. package/index.html +39 -0
  28. package/package.json +5 -2
  29. package/src/client.ts +40 -35
  30. package/src/constants.ts +4 -0
  31. package/src/helpers/api_info.ts +1 -1
  32. package/src/helpers/data.ts +124 -10
  33. package/src/helpers/init_helpers.ts +5 -0
  34. package/src/helpers/spaces.ts +2 -1
  35. package/src/index.ts +6 -1
  36. package/src/test/api_info.test.ts +0 -1
  37. package/src/test/data.test.ts +201 -26
  38. package/src/test/handlers.ts +9 -1
  39. package/src/test/stream.test.ts +12 -10
  40. package/src/test/test_data.ts +8 -5
  41. package/src/test/upload_files.test.ts +1 -1
  42. package/src/types.ts +110 -26
  43. package/src/utils/handle_blob.ts +91 -1
  44. package/src/utils/predict.ts +15 -16
  45. package/src/utils/stream.ts +66 -13
  46. package/src/utils/submit.ts +156 -63
  47. package/src/utils/upload_files.ts +1 -1
  48. 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;
@@ -153,6 +175,45 @@ export interface Config {
153
175
  max_file_size?: number;
154
176
  }
155
177
 
178
+ // todo: DRY up types
179
+ export interface ComponentMeta {
180
+ type: string;
181
+ id: number;
182
+ has_modes: boolean;
183
+ props: SharedProps;
184
+ instance: SvelteComponent;
185
+ component: ComponentType<SvelteComponent>;
186
+ documentation?: Documentation;
187
+ children?: ComponentMeta[];
188
+ parent?: ComponentMeta;
189
+ value?: any;
190
+ component_class_id: string;
191
+ key: string | number | null;
192
+ rendered_in?: number;
193
+ }
194
+
195
+ interface SharedProps {
196
+ elem_id?: string;
197
+ elem_classes?: string[];
198
+ components?: string[];
199
+ server_fns?: string[];
200
+ interactive: boolean;
201
+ [key: string]: unknown;
202
+ root_url?: string;
203
+ }
204
+
205
+ export interface Documentation {
206
+ type?: TypeDescription;
207
+ description?: TypeDescription;
208
+ example_data?: string;
209
+ }
210
+
211
+ interface TypeDescription {
212
+ input_payload?: string;
213
+ response_object?: string;
214
+ payload?: string;
215
+ }
216
+
156
217
  export interface Dependency {
157
218
  id: number;
158
219
  targets: [number, string][];
@@ -186,6 +247,7 @@ export interface Dependency {
186
247
  export interface DependencyTypes {
187
248
  continuous: boolean;
188
249
  generator: boolean;
250
+ cancel: boolean;
189
251
  }
190
252
 
191
253
  export interface Payload {
@@ -218,6 +280,8 @@ export interface ClientOptions {
218
280
  hf_token?: `hf_${string}`;
219
281
  status_callback?: SpaceStatusCallback | null;
220
282
  auth?: [string, string] | null;
283
+ with_null_state?: boolean;
284
+ events?: EventType[];
221
285
  }
222
286
 
223
287
  export interface FileData {
@@ -236,25 +300,21 @@ export interface FileData {
236
300
  export type EventType = "data" | "status" | "log" | "render";
237
301
 
238
302
  export interface EventMap {
239
- data: Payload;
240
- status: Status;
303
+ data: PayloadMessage;
304
+ status: StatusMessage;
241
305
  log: LogMessage;
242
306
  render: RenderMessage;
243
307
  }
244
308
 
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 {
309
+ export type GradioEvent = {
310
+ [P in EventType]: EventMap[P];
311
+ }[EventType];
312
+
313
+ export interface Log {
253
314
  log: string;
254
315
  level: "warning" | "info";
255
316
  }
256
- export interface RenderMessage {
257
- fn_index: number;
317
+ export interface Render {
258
318
  data: {
259
319
  components: any[];
260
320
  layout: any;
@@ -283,3 +343,27 @@ export interface Status {
283
343
  time?: Date;
284
344
  changed_state_ids?: number[];
285
345
  }
346
+
347
+ export interface StatusMessage extends Status {
348
+ type: "status";
349
+ endpoint: string;
350
+ fn_index: number;
351
+ }
352
+
353
+ export interface PayloadMessage extends Payload {
354
+ type: "data";
355
+ endpoint: string;
356
+ fn_index: number;
357
+ }
358
+
359
+ export interface LogMessage extends Log {
360
+ type: "log";
361
+ endpoint: string;
362
+ fn_index: number;
363
+ }
364
+
365
+ export interface RenderMessage extends Render {
366
+ type: "render";
367
+ endpoint: string;
368
+ fn_index: number;
369
+ }
@@ -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
+ }