@gradio/client 0.15.1 → 0.17.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 +51 -0
- package/README.md +49 -43
- package/dist/client.d.ts +52 -70
- package/dist/client.d.ts.map +1 -1
- package/dist/constants.d.ts +17 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/helpers/api_info.d.ts +25 -0
- package/dist/helpers/api_info.d.ts.map +1 -0
- package/dist/helpers/data.d.ts +8 -0
- package/dist/helpers/data.d.ts.map +1 -0
- package/dist/{utils.d.ts → helpers/init_helpers.d.ts} +6 -17
- package/dist/helpers/init_helpers.d.ts.map +1 -0
- package/dist/helpers/spaces.d.ts +7 -0
- package/dist/helpers/spaces.d.ts.map +1 -0
- package/dist/index.d.ts +8 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1587 -1359
- package/dist/types.d.ts +152 -49
- package/dist/types.d.ts.map +1 -1
- package/dist/upload.d.ts +2 -2
- package/dist/upload.d.ts.map +1 -1
- package/dist/utils/duplicate.d.ts +4 -0
- package/dist/utils/duplicate.d.ts.map +1 -0
- package/dist/utils/handle_blob.d.ts +4 -0
- package/dist/utils/handle_blob.d.ts.map +1 -0
- package/dist/utils/post_data.d.ts +4 -0
- package/dist/utils/post_data.d.ts.map +1 -0
- package/dist/utils/predict.d.ts +4 -0
- package/dist/utils/predict.d.ts.map +1 -0
- package/dist/utils/stream.d.ts +8 -0
- package/dist/utils/stream.d.ts.map +1 -0
- package/dist/utils/submit.d.ts +4 -0
- package/dist/utils/submit.d.ts.map +1 -0
- package/dist/utils/upload_files.d.ts +4 -0
- package/dist/utils/upload_files.d.ts.map +1 -0
- package/dist/utils/view_api.d.ts +3 -0
- package/dist/utils/view_api.d.ts.map +1 -0
- package/dist/{wrapper-6f348d45.js → wrapper-CviSselG.js} +259 -17
- package/package.json +3 -2
- package/src/client.ts +290 -1652
- package/src/constants.ts +20 -0
- package/src/globals.d.ts +2 -21
- package/src/helpers/api_info.ts +300 -0
- package/src/helpers/data.ts +115 -0
- package/src/helpers/init_helpers.ts +134 -0
- package/src/helpers/spaces.ts +192 -0
- package/src/index.ts +16 -10
- package/src/types.ts +200 -59
- package/src/upload.ts +23 -15
- package/src/{client.node-test.ts → utils/client.node-test.ts} +11 -10
- package/src/utils/duplicate.ts +87 -0
- package/src/utils/handle_blob.ts +47 -0
- package/src/utils/post_data.ts +37 -0
- package/src/utils/predict.ts +56 -0
- package/src/utils/stream.ts +166 -0
- package/src/utils/submit.ts +689 -0
- package/src/utils/upload_files.ts +46 -0
- package/src/utils/view_api.ts +70 -0
- package/src/vite-env.d.ts +1 -0
- package/tsconfig.json +15 -2
- package/vite.config.js +4 -17
- package/dist/utils.d.ts.map +0 -1
- package/src/utils.ts +0 -300
@@ -0,0 +1,87 @@
|
|
1
|
+
import {
|
2
|
+
get_space_hardware,
|
3
|
+
hardware_types,
|
4
|
+
set_space_timeout
|
5
|
+
} from "../helpers/spaces";
|
6
|
+
import type { DuplicateOptions } from "../types";
|
7
|
+
import { Client } from "../client";
|
8
|
+
|
9
|
+
export async function duplicate(
|
10
|
+
app_reference: string,
|
11
|
+
options: DuplicateOptions
|
12
|
+
): Promise<Client> {
|
13
|
+
const { hf_token, private: _private, hardware, timeout } = options;
|
14
|
+
|
15
|
+
if (hardware && !hardware_types.includes(hardware)) {
|
16
|
+
throw new Error(
|
17
|
+
`Invalid hardware type provided. Valid types are: ${hardware_types
|
18
|
+
.map((v) => `"${v}"`)
|
19
|
+
.join(",")}.`
|
20
|
+
);
|
21
|
+
}
|
22
|
+
const headers = {
|
23
|
+
Authorization: `Bearer ${hf_token}`,
|
24
|
+
"Content-Type": "application/json"
|
25
|
+
};
|
26
|
+
|
27
|
+
const user = (
|
28
|
+
await (
|
29
|
+
await fetch(`https://huggingface.co/api/whoami-v2`, {
|
30
|
+
headers
|
31
|
+
})
|
32
|
+
).json()
|
33
|
+
).name;
|
34
|
+
|
35
|
+
const space_name = app_reference.split("/")[1];
|
36
|
+
const body: {
|
37
|
+
repository: string;
|
38
|
+
private?: boolean;
|
39
|
+
hardware?: string;
|
40
|
+
} = {
|
41
|
+
repository: `${user}/${space_name}`
|
42
|
+
};
|
43
|
+
|
44
|
+
if (_private) {
|
45
|
+
body.private = true;
|
46
|
+
}
|
47
|
+
|
48
|
+
let original_hardware;
|
49
|
+
|
50
|
+
if (!hardware) {
|
51
|
+
original_hardware = await get_space_hardware(app_reference, hf_token);
|
52
|
+
}
|
53
|
+
|
54
|
+
const requested_hardware = hardware || original_hardware || "cpu-basic";
|
55
|
+
|
56
|
+
body.hardware = requested_hardware;
|
57
|
+
|
58
|
+
try {
|
59
|
+
const response = await fetch(
|
60
|
+
`https://huggingface.co/api/spaces/${app_reference}/duplicate`,
|
61
|
+
{
|
62
|
+
method: "POST",
|
63
|
+
headers,
|
64
|
+
body: JSON.stringify(body)
|
65
|
+
}
|
66
|
+
);
|
67
|
+
|
68
|
+
if (response.status === 409) {
|
69
|
+
try {
|
70
|
+
const client = await Client.connect(`${user}/${space_name}`, options);
|
71
|
+
return client;
|
72
|
+
} catch (error) {
|
73
|
+
console.error("Failed to connect Client instance:", error);
|
74
|
+
throw error;
|
75
|
+
}
|
76
|
+
} else if (response.status !== 200) {
|
77
|
+
throw new Error(response.statusText);
|
78
|
+
}
|
79
|
+
|
80
|
+
const duplicated_space = await response.json();
|
81
|
+
|
82
|
+
await set_space_timeout(`${user}/${space_name}`, timeout || 300, hf_token);
|
83
|
+
return await Client.connect(duplicated_space.url, options);
|
84
|
+
} catch (e: any) {
|
85
|
+
throw new Error(e);
|
86
|
+
}
|
87
|
+
}
|
@@ -0,0 +1,47 @@
|
|
1
|
+
import { update_object, walk_and_store_blobs } from "../helpers/data";
|
2
|
+
import type { ApiData, EndpointInfo, JsApiData } from "../types";
|
3
|
+
import { FileData } from "../upload";
|
4
|
+
import type { Client } from "..";
|
5
|
+
|
6
|
+
export async function handle_blob(
|
7
|
+
this: Client,
|
8
|
+
endpoint: string,
|
9
|
+
data: unknown[],
|
10
|
+
api_info: EndpointInfo<JsApiData | ApiData>
|
11
|
+
): Promise<unknown[]> {
|
12
|
+
const self = this;
|
13
|
+
|
14
|
+
const blobRefs = await walk_and_store_blobs(
|
15
|
+
data,
|
16
|
+
undefined,
|
17
|
+
[],
|
18
|
+
true,
|
19
|
+
api_info
|
20
|
+
);
|
21
|
+
|
22
|
+
const results = await Promise.all(
|
23
|
+
blobRefs.map(async ({ path, blob, type }) => {
|
24
|
+
if (!blob) return { path, type };
|
25
|
+
|
26
|
+
const response = await self.upload_files(endpoint, [blob]);
|
27
|
+
const file_url = response.files && response.files[0];
|
28
|
+
return {
|
29
|
+
path,
|
30
|
+
file_url,
|
31
|
+
type,
|
32
|
+
name: blob?.name
|
33
|
+
};
|
34
|
+
})
|
35
|
+
);
|
36
|
+
|
37
|
+
results.forEach(({ path, file_url, type, name }) => {
|
38
|
+
if (type === "Gallery") {
|
39
|
+
update_object(data, file_url, path);
|
40
|
+
} else if (file_url) {
|
41
|
+
const file = new FileData({ path: file_url, orig_name: name });
|
42
|
+
update_object(data, file, path);
|
43
|
+
}
|
44
|
+
});
|
45
|
+
|
46
|
+
return data;
|
47
|
+
}
|
@@ -0,0 +1,37 @@
|
|
1
|
+
import { BROKEN_CONNECTION_MSG } from "../constants";
|
2
|
+
import type { PostResponse } from "../types";
|
3
|
+
import { Client } from "..";
|
4
|
+
|
5
|
+
export async function post_data(
|
6
|
+
this: Client,
|
7
|
+
url: string,
|
8
|
+
body: unknown,
|
9
|
+
additional_headers?: any
|
10
|
+
): Promise<[PostResponse, number]> {
|
11
|
+
const headers: {
|
12
|
+
Authorization?: string;
|
13
|
+
"Content-Type": "application/json";
|
14
|
+
} = { "Content-Type": "application/json" };
|
15
|
+
if (this.options.hf_token) {
|
16
|
+
headers.Authorization = `Bearer ${this.options.hf_token}`;
|
17
|
+
}
|
18
|
+
try {
|
19
|
+
var response = await this.fetch_implementation(url, {
|
20
|
+
method: "POST",
|
21
|
+
body: JSON.stringify(body),
|
22
|
+
headers: { ...headers, ...additional_headers }
|
23
|
+
});
|
24
|
+
} catch (e) {
|
25
|
+
return [{ error: BROKEN_CONNECTION_MSG }, 500];
|
26
|
+
}
|
27
|
+
let output: PostResponse;
|
28
|
+
let status: number;
|
29
|
+
try {
|
30
|
+
output = await response.json();
|
31
|
+
status = response.status;
|
32
|
+
} catch (e) {
|
33
|
+
output = { error: `Could not parse server response: ${e}` };
|
34
|
+
status = 500;
|
35
|
+
}
|
36
|
+
return [output, status];
|
37
|
+
}
|
@@ -0,0 +1,56 @@
|
|
1
|
+
import { Client } from "../client";
|
2
|
+
import type { Dependency, SubmitReturn } from "../types";
|
3
|
+
|
4
|
+
export async function predict(
|
5
|
+
this: Client,
|
6
|
+
endpoint: string | number,
|
7
|
+
data?: unknown[]
|
8
|
+
): Promise<SubmitReturn> {
|
9
|
+
let data_returned = false;
|
10
|
+
let status_complete = false;
|
11
|
+
let dependency: Dependency;
|
12
|
+
|
13
|
+
if (!this.config) {
|
14
|
+
throw new Error("Could not resolve app config");
|
15
|
+
}
|
16
|
+
|
17
|
+
if (typeof endpoint === "number") {
|
18
|
+
dependency = this.config.dependencies[endpoint];
|
19
|
+
} else {
|
20
|
+
const trimmed_endpoint = endpoint.replace(/^\//, "");
|
21
|
+
dependency = this.config.dependencies[this.api_map[trimmed_endpoint]];
|
22
|
+
}
|
23
|
+
|
24
|
+
if (dependency?.types.continuous) {
|
25
|
+
throw new Error(
|
26
|
+
"Cannot call predict on this function as it may run forever. Use submit instead"
|
27
|
+
);
|
28
|
+
}
|
29
|
+
|
30
|
+
return new Promise(async (resolve, reject) => {
|
31
|
+
const app = this.submit(endpoint, data || []);
|
32
|
+
let result: unknown;
|
33
|
+
|
34
|
+
app
|
35
|
+
.on("data", (d: unknown) => {
|
36
|
+
// if complete message comes before data, resolve here
|
37
|
+
if (status_complete) {
|
38
|
+
app.destroy();
|
39
|
+
resolve(d as SubmitReturn);
|
40
|
+
}
|
41
|
+
data_returned = true;
|
42
|
+
result = d;
|
43
|
+
})
|
44
|
+
.on("status", (status) => {
|
45
|
+
if (status.stage === "error") reject(status);
|
46
|
+
if (status.stage === "complete") {
|
47
|
+
status_complete = true;
|
48
|
+
// if complete message comes after data, resolve here
|
49
|
+
if (data_returned) {
|
50
|
+
app.destroy();
|
51
|
+
resolve(result as SubmitReturn);
|
52
|
+
}
|
53
|
+
}
|
54
|
+
});
|
55
|
+
});
|
56
|
+
}
|
@@ -0,0 +1,166 @@
|
|
1
|
+
import { BROKEN_CONNECTION_MSG } from "../constants";
|
2
|
+
import type { Client } from "../client";
|
3
|
+
|
4
|
+
export function open_stream(this: Client): void {
|
5
|
+
let {
|
6
|
+
event_callbacks,
|
7
|
+
unclosed_events,
|
8
|
+
pending_stream_messages,
|
9
|
+
stream_status,
|
10
|
+
config
|
11
|
+
} = this;
|
12
|
+
|
13
|
+
if (!config) {
|
14
|
+
throw new Error("Could not resolve app config");
|
15
|
+
}
|
16
|
+
|
17
|
+
stream_status.open = true;
|
18
|
+
|
19
|
+
let event_source: EventSource | null = null;
|
20
|
+
let params = new URLSearchParams({
|
21
|
+
session_hash: this.session_hash
|
22
|
+
}).toString();
|
23
|
+
|
24
|
+
let url = new URL(`${config.root}/queue/data?${params}`);
|
25
|
+
event_source = this.eventSource_factory(url);
|
26
|
+
|
27
|
+
if (!event_source) {
|
28
|
+
throw new Error("Cannot connect to sse endpoint: " + url.toString());
|
29
|
+
}
|
30
|
+
|
31
|
+
event_source.onmessage = async function (event) {
|
32
|
+
let _data = JSON.parse(event.data);
|
33
|
+
if (_data.msg === "close_stream") {
|
34
|
+
close_stream(stream_status, event_source);
|
35
|
+
return;
|
36
|
+
}
|
37
|
+
const event_id = _data.event_id;
|
38
|
+
if (!event_id) {
|
39
|
+
await Promise.all(
|
40
|
+
Object.keys(event_callbacks).map(
|
41
|
+
(event_id) =>
|
42
|
+
// @ts-ignore
|
43
|
+
event_callbacks[event_id](_data) // todo: check event_callbacks
|
44
|
+
)
|
45
|
+
);
|
46
|
+
} else if (event_callbacks[event_id] && config) {
|
47
|
+
if (
|
48
|
+
_data.msg === "process_completed" &&
|
49
|
+
["sse", "sse_v1", "sse_v2", "sse_v2.1"].includes(config.protocol)
|
50
|
+
) {
|
51
|
+
unclosed_events.delete(event_id);
|
52
|
+
if (unclosed_events.size === 0) {
|
53
|
+
close_stream(stream_status, event_source);
|
54
|
+
}
|
55
|
+
}
|
56
|
+
let fn = event_callbacks[event_id];
|
57
|
+
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
|
58
|
+
} else {
|
59
|
+
if (!pending_stream_messages[event_id]) {
|
60
|
+
pending_stream_messages[event_id] = [];
|
61
|
+
}
|
62
|
+
pending_stream_messages[event_id].push(_data);
|
63
|
+
}
|
64
|
+
};
|
65
|
+
event_source.onerror = async function () {
|
66
|
+
await Promise.all(
|
67
|
+
Object.keys(event_callbacks).map((event_id) =>
|
68
|
+
// @ts-ignore
|
69
|
+
event_callbacks[event_id]({
|
70
|
+
msg: "unexpected_error",
|
71
|
+
message: BROKEN_CONNECTION_MSG
|
72
|
+
})
|
73
|
+
)
|
74
|
+
);
|
75
|
+
close_stream(stream_status, event_source);
|
76
|
+
};
|
77
|
+
}
|
78
|
+
|
79
|
+
export function close_stream(
|
80
|
+
stream_status: { open: boolean },
|
81
|
+
event_source: EventSource | null
|
82
|
+
): void {
|
83
|
+
if (stream_status && event_source) {
|
84
|
+
stream_status.open = false;
|
85
|
+
event_source?.close();
|
86
|
+
}
|
87
|
+
}
|
88
|
+
|
89
|
+
export function apply_diff_stream(
|
90
|
+
pending_diff_streams: Record<string, any[][]>,
|
91
|
+
event_id: string,
|
92
|
+
data: any
|
93
|
+
): void {
|
94
|
+
let is_first_generation = !pending_diff_streams[event_id];
|
95
|
+
if (is_first_generation) {
|
96
|
+
pending_diff_streams[event_id] = [];
|
97
|
+
data.data.forEach((value: any, i: number) => {
|
98
|
+
pending_diff_streams[event_id][i] = value;
|
99
|
+
});
|
100
|
+
} else {
|
101
|
+
data.data.forEach((value: any, i: number) => {
|
102
|
+
let new_data = apply_diff(pending_diff_streams[event_id][i], value);
|
103
|
+
pending_diff_streams[event_id][i] = new_data;
|
104
|
+
data.data[i] = new_data;
|
105
|
+
});
|
106
|
+
}
|
107
|
+
}
|
108
|
+
|
109
|
+
export function apply_diff(
|
110
|
+
obj: any,
|
111
|
+
diff: [string, (number | string)[], any][]
|
112
|
+
): any {
|
113
|
+
diff.forEach(([action, path, value]) => {
|
114
|
+
obj = apply_edit(obj, path, action, value);
|
115
|
+
});
|
116
|
+
|
117
|
+
return obj;
|
118
|
+
}
|
119
|
+
|
120
|
+
function apply_edit(
|
121
|
+
target: any,
|
122
|
+
path: (number | string)[],
|
123
|
+
action: string,
|
124
|
+
value: any
|
125
|
+
): any {
|
126
|
+
if (path.length === 0) {
|
127
|
+
if (action === "replace") {
|
128
|
+
return value;
|
129
|
+
} else if (action === "append") {
|
130
|
+
return target + value;
|
131
|
+
}
|
132
|
+
throw new Error(`Unsupported action: ${action}`);
|
133
|
+
}
|
134
|
+
|
135
|
+
let current = target;
|
136
|
+
for (let i = 0; i < path.length - 1; i++) {
|
137
|
+
current = current[path[i]];
|
138
|
+
}
|
139
|
+
|
140
|
+
const last_path = path[path.length - 1];
|
141
|
+
switch (action) {
|
142
|
+
case "replace":
|
143
|
+
current[last_path] = value;
|
144
|
+
break;
|
145
|
+
case "append":
|
146
|
+
current[last_path] += value;
|
147
|
+
break;
|
148
|
+
case "add":
|
149
|
+
if (Array.isArray(current)) {
|
150
|
+
current.splice(Number(last_path), 0, value);
|
151
|
+
} else {
|
152
|
+
current[last_path] = value;
|
153
|
+
}
|
154
|
+
break;
|
155
|
+
case "delete":
|
156
|
+
if (Array.isArray(current)) {
|
157
|
+
current.splice(Number(last_path), 1);
|
158
|
+
} else {
|
159
|
+
delete current[last_path];
|
160
|
+
}
|
161
|
+
break;
|
162
|
+
default:
|
163
|
+
throw new Error(`Unknown action: ${action}`);
|
164
|
+
}
|
165
|
+
return target;
|
166
|
+
}
|