mirror of
https://github.com/gradio-app/gradio.git
synced 2025-03-31 12:20:26 +08:00
JS client take 2 (#3388)
* start * change api * integrate into gradio * log * try this * format * changes * format * fix css * fix file
This commit is contained in:
parent
47d6231680
commit
fddf376784
Binary file not shown.
@ -1 +1 @@
|
||||
{"cells": [{"cell_type": "markdown", "id": 302934307671667531413257853548643485645, "metadata": {}, "source": ["# Gradio Demo: stream_frames"]}, {"cell_type": "code", "execution_count": null, "id": 272996653310673477252411125948039410165, "metadata": {}, "outputs": [], "source": ["!pip install -q gradio "]}, {"cell_type": "code", "execution_count": null, "id": 288918539441861185822528903084949547379, "metadata": {}, "outputs": [], "source": ["import gradio as gr\n", "import numpy as np\n", "\n", "def flip(im):\n", " return np.flipud(im)\n", "\n", "demo = gr.Interface(\n", " flip, \n", " gr.Image(source=\"webcam\", streaming=True), \n", " \"image\",\n", " live=True\n", ")\n", "if __name__ == \"__main__\":\n", " demo.launch()"]}], "metadata": {}, "nbformat": 4, "nbformat_minor": 5}
|
||||
{"cells": [{"cell_type": "markdown", "id": 302934307671667531413257853548643485645, "metadata": {}, "source": ["# Gradio Demo: stream_frames"]}, {"cell_type": "code", "execution_count": null, "id": 272996653310673477252411125948039410165, "metadata": {}, "outputs": [], "source": ["!pip install -q gradio "]}, {"cell_type": "code", "execution_count": null, "id": 288918539441861185822528903084949547379, "metadata": {}, "outputs": [], "source": ["import gradio as gr\n", "import numpy as np\n", "\n", "def flip(im):\n", " return np.flipud(im)\n", "\n", "demo = gr.Interface(\n", " flip, \n", " gr.Image(source=\"webcam\", streaming=True), \n", " \"image\",\n", " live=True\n", ")\n", "if __name__ == \"__main__\":\n", " demo.launch()\n", " "]}], "metadata": {}, "nbformat": 4, "nbformat_minor": 5}
|
@ -11,4 +11,5 @@ demo = gr.Interface(
|
||||
live=True
|
||||
)
|
||||
if __name__ == "__main__":
|
||||
demo.launch()
|
||||
demo.launch()
|
||||
|
@ -21,6 +21,7 @@
|
||||
"@gradio/button": "workspace:^0.0.1",
|
||||
"@gradio/chart": "workspace:^0.0.1",
|
||||
"@gradio/chatbot": "workspace:^0.0.1",
|
||||
"@gradio/client": "workspace:^0.0.1",
|
||||
"@gradio/file": "workspace:^0.0.1",
|
||||
"@gradio/form": "workspace:^0.0.1",
|
||||
"@gradio/gallery": "workspace:^0.0.1",
|
||||
|
@ -1,6 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { tick } from "svelte";
|
||||
import { _ } from "svelte-i18n";
|
||||
import type { client } from "@gradio/client";
|
||||
|
||||
import { component_map } from "./components/directory";
|
||||
import {
|
||||
@ -15,7 +16,6 @@
|
||||
LayoutNode,
|
||||
Documentation
|
||||
} from "./components/types";
|
||||
import type { fn as api_fn } from "./api";
|
||||
import { setupi18n } from "./i18n";
|
||||
import Render from "./Render.svelte";
|
||||
import { ApiDocs } from "./api_docs/";
|
||||
@ -26,12 +26,10 @@
|
||||
setupi18n();
|
||||
|
||||
export let root: string;
|
||||
export let fn: ReturnType<typeof api_fn>;
|
||||
export let components: Array<ComponentMeta>;
|
||||
export let layout: LayoutNode;
|
||||
export let dependencies: Array<Dependency>;
|
||||
|
||||
export let enable_queue: boolean;
|
||||
export let title: string = "Gradio";
|
||||
export let analytics_enabled: boolean = false;
|
||||
export let target: HTMLElement;
|
||||
@ -41,6 +39,7 @@
|
||||
export let control_page_title = false;
|
||||
export let app_mode: boolean;
|
||||
export let theme: string;
|
||||
export let app: Awaited<ReturnType<typeof client>>;
|
||||
|
||||
let loading_status = create_loading_status_store();
|
||||
|
||||
@ -210,6 +209,36 @@
|
||||
});
|
||||
});
|
||||
|
||||
function handle_update(data: any, fn_index: number) {
|
||||
const outputs = dependencies[fn_index].outputs;
|
||||
data.forEach((value: any, i: number) => {
|
||||
if (
|
||||
typeof value === "object" &&
|
||||
value !== null &&
|
||||
value.__type__ === "update"
|
||||
) {
|
||||
for (const [update_key, update_value] of Object.entries(value)) {
|
||||
if (update_key === "__type__") {
|
||||
continue;
|
||||
} else {
|
||||
instance_map[outputs[i]].props[update_key] = update_value;
|
||||
}
|
||||
}
|
||||
rootNode = rootNode;
|
||||
} else {
|
||||
instance_map[outputs[i]].props.value = value;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
app.on("data", ({ data, fn_index }) => {
|
||||
handle_update(data, fn_index);
|
||||
});
|
||||
|
||||
app.on("status", ({ fn_index, ...status }) => {
|
||||
loading_status.update({ ...status, fn_index });
|
||||
});
|
||||
|
||||
function set_prop<T extends ComponentMeta>(obj: T, prop: string, val: any) {
|
||||
if (!obj?.props) {
|
||||
obj.props = {};
|
||||
@ -217,7 +246,6 @@
|
||||
obj.props[prop] = val;
|
||||
rootNode = rootNode;
|
||||
}
|
||||
|
||||
let handled_dependencies: Array<number[]> = [];
|
||||
|
||||
async function handle_mount() {
|
||||
@ -259,45 +287,36 @@
|
||||
outputs.every((v) => instance_map?.[v].instance) &&
|
||||
inputs.every((v) => instance_map?.[v].instance)
|
||||
) {
|
||||
const req = fn({
|
||||
action: "predict",
|
||||
backend_fn,
|
||||
frontend_fn,
|
||||
payload: {
|
||||
fn_index: i,
|
||||
data: inputs.map((id) => instance_map[id].props.value)
|
||||
},
|
||||
queue: queue === null ? enable_queue : queue,
|
||||
queue_callback: handle_update,
|
||||
loading_status: loading_status,
|
||||
cancels
|
||||
});
|
||||
cancels &&
|
||||
cancels.forEach((fn_index) => {
|
||||
app.cancel("/predict", fn_index);
|
||||
});
|
||||
|
||||
function handle_update(output: any) {
|
||||
output.data.forEach((value: any, i: number) => {
|
||||
if (
|
||||
typeof value === "object" &&
|
||||
value !== null &&
|
||||
value.__type__ === "update"
|
||||
) {
|
||||
for (const [update_key, update_value] of Object.entries(
|
||||
value
|
||||
)) {
|
||||
if (update_key === "__type__") {
|
||||
continue;
|
||||
} else {
|
||||
instance_map[outputs[i]].props[update_key] = update_value;
|
||||
}
|
||||
}
|
||||
rootNode = rootNode;
|
||||
let payload = {
|
||||
fn_index: i,
|
||||
data: inputs.map((id) => instance_map[id].props.value)
|
||||
};
|
||||
|
||||
if (frontend_fn) {
|
||||
frontend_fn(
|
||||
payload.data.concat(
|
||||
outputs.map((id) => instance_map[id].props.value)
|
||||
)
|
||||
).then((v: []) => {
|
||||
if (backend_fn) {
|
||||
payload.data = v;
|
||||
make_prediction();
|
||||
} else {
|
||||
instance_map[outputs[i]].props.value = value;
|
||||
handle_update(v, i);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
if (backend_fn) {
|
||||
make_prediction();
|
||||
}
|
||||
}
|
||||
|
||||
if (!(queue === null ? enable_queue : queue)) {
|
||||
req.then(handle_update);
|
||||
function make_prediction() {
|
||||
app.predict("/predict", payload);
|
||||
}
|
||||
|
||||
handled_dependencies[i] = [-1];
|
||||
@ -308,54 +327,49 @@
|
||||
.forEach(([id, { instance }]: [number, ComponentMeta]) => {
|
||||
if (handled_dependencies[i]?.includes(id) || !instance) return;
|
||||
instance?.$on(trigger, () => {
|
||||
if (loading_status.get_status_for_fn(i) === "pending") {
|
||||
const current_status = loading_status.get_status_for_fn(i);
|
||||
if (
|
||||
current_status === "pending" ||
|
||||
current_status === "generating"
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
// page events
|
||||
const req = fn({
|
||||
action: "predict",
|
||||
backend_fn,
|
||||
frontend_fn,
|
||||
payload: {
|
||||
fn_index: i,
|
||||
data: inputs.map((id) => instance_map[id].props.value)
|
||||
},
|
||||
output_data: outputs.map((id) => instance_map[id].props.value),
|
||||
queue: queue === null ? enable_queue : queue,
|
||||
queue_callback: handle_update,
|
||||
loading_status: loading_status,
|
||||
cancels
|
||||
});
|
||||
if (cancels) {
|
||||
cancels.forEach((fn_index) => {
|
||||
app.cancel("/predict", fn_index);
|
||||
});
|
||||
}
|
||||
|
||||
if (!(queue === null ? enable_queue : queue)) {
|
||||
req.then(handle_update);
|
||||
let payload = {
|
||||
fn_index: i,
|
||||
data: inputs.map((id) => instance_map[id].props.value)
|
||||
};
|
||||
|
||||
if (frontend_fn) {
|
||||
frontend_fn(
|
||||
payload.data.concat(
|
||||
outputs.map((id) => instance_map[id].props.value)
|
||||
)
|
||||
).then((v: []) => {
|
||||
if (backend_fn) {
|
||||
payload.data = v;
|
||||
make_prediction();
|
||||
} else {
|
||||
handle_update(v, i);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
if (backend_fn) {
|
||||
make_prediction();
|
||||
}
|
||||
}
|
||||
|
||||
function make_prediction() {
|
||||
app.predict("/predict", payload);
|
||||
}
|
||||
});
|
||||
|
||||
function handle_update(output: any) {
|
||||
output.data.forEach((value: any, i: number) => {
|
||||
if (
|
||||
typeof value === "object" &&
|
||||
value !== null &&
|
||||
value.__type__ === "update"
|
||||
) {
|
||||
for (const [update_key, update_value] of Object.entries(
|
||||
value
|
||||
)) {
|
||||
if (update_key === "__type__") {
|
||||
continue;
|
||||
} else {
|
||||
instance_map[outputs[i]].props[update_key] = update_value;
|
||||
}
|
||||
}
|
||||
rootNode = rootNode;
|
||||
} else {
|
||||
instance_map[outputs[i]].props.value = value;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (!handled_dependencies[i]) handled_dependencies[i] = [];
|
||||
handled_dependencies[i].push(id);
|
||||
});
|
||||
|
@ -1,6 +1,5 @@
|
||||
<script context="module" lang="ts">
|
||||
import { writable } from "svelte/store";
|
||||
import { fn } from "./api";
|
||||
import { mount_css } from "./main";
|
||||
|
||||
import type {
|
||||
@ -19,11 +18,9 @@
|
||||
dependencies: Dependency[];
|
||||
dev_mode: boolean;
|
||||
enable_queue: boolean;
|
||||
fn: ReturnType<typeof fn>;
|
||||
layout: LayoutNode;
|
||||
mode: "blocks" | "interface";
|
||||
root: string;
|
||||
target: HTMLElement;
|
||||
theme: string;
|
||||
title: string;
|
||||
version: string;
|
||||
@ -62,6 +59,7 @@
|
||||
|
||||
<script lang="ts">
|
||||
import { onMount } from "svelte";
|
||||
import { client, SpaceStatus } from "@gradio/client";
|
||||
|
||||
import Embed from "./Embed.svelte";
|
||||
import { Component as Loader } from "./components/StatusTracker";
|
||||
@ -83,78 +81,25 @@
|
||||
|
||||
let _id = id++;
|
||||
|
||||
let status: "pending" | "error" | "success" | "login" = "pending";
|
||||
let loader_status: "pending" | "error" | "complete" | "generating" =
|
||||
"pending";
|
||||
let app_id: string | null = null;
|
||||
let wrapper: HTMLDivElement;
|
||||
let ready: boolean = false;
|
||||
let root: string;
|
||||
let config: Config;
|
||||
let loading_text: string = "Loading...";
|
||||
|
||||
async function handle_config(target: HTMLElement, source: string | null) {
|
||||
let config;
|
||||
try {
|
||||
let _config = await get_config(source);
|
||||
config = _config;
|
||||
} catch (e) {
|
||||
if (BUILD_MODE === "dev") {
|
||||
console.error(e);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
if (root === undefined) {
|
||||
root = BACKEND_URL;
|
||||
config.root = BACKEND_URL;
|
||||
}
|
||||
|
||||
mount_css(config.root + "theme.css", document.head);
|
||||
mount_custom_css(target, config.css);
|
||||
window.__is_colab__ = config.is_colab;
|
||||
|
||||
if (config.dev_mode) {
|
||||
reload_check(root);
|
||||
}
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
async function get_config(source: string | null) {
|
||||
if (BUILD_MODE === "dev" || location.origin === "http://localhost:9876") {
|
||||
let config = await fetch(BACKEND_URL + "config");
|
||||
const result = await config.json();
|
||||
return result;
|
||||
} else if (source) {
|
||||
if (!source.endsWith("/")) {
|
||||
source += "/";
|
||||
}
|
||||
const config = await get_source_config(source);
|
||||
return config;
|
||||
} else if (window.gradio_config) {
|
||||
return window.gradio_config;
|
||||
} else {
|
||||
throw new Error("Config not found");
|
||||
}
|
||||
}
|
||||
|
||||
async function get_source_config(source: string): Promise<Config> {
|
||||
let config = await (await fetch(source + "config")).json();
|
||||
root = source;
|
||||
config.root = source;
|
||||
return config;
|
||||
}
|
||||
|
||||
function mount_custom_css(target: HTMLElement, css_string?: string) {
|
||||
function mount_custom_css(target: HTMLElement, css_string: string | null) {
|
||||
if (css_string) {
|
||||
let style = document.createElement("style");
|
||||
style.innerHTML = css_string;
|
||||
target.appendChild(style);
|
||||
}
|
||||
mount_css(config.root + "/theme.css", document.head);
|
||||
}
|
||||
|
||||
async function reload_check(root: string) {
|
||||
const result = await (await fetch(root + "app_id")).text();
|
||||
const result = await (await fetch(root + "/app_id")).text();
|
||||
|
||||
if (app_id === null) {
|
||||
app_id = result;
|
||||
@ -215,194 +160,51 @@
|
||||
return "dark";
|
||||
}
|
||||
|
||||
export const RE_SPACE_NAME = /^[^\/]*\/[^\/]*$/;
|
||||
async function check_space_status(
|
||||
space_id: string,
|
||||
source: string,
|
||||
cb: Function = () => {}
|
||||
) {
|
||||
const endpoint = RE_SPACE_NAME.test(space_id) ? "" : "by-subdomain/";
|
||||
let _space_id = "";
|
||||
let response;
|
||||
let _status;
|
||||
try {
|
||||
response = await fetch(
|
||||
`https://huggingface.co/api/spaces/${endpoint}${space_id}`
|
||||
);
|
||||
_status = response.status;
|
||||
let status: SpaceStatus = {
|
||||
message: "",
|
||||
load_status: "pending",
|
||||
status: "sleeping",
|
||||
detail: "SLEEPING"
|
||||
};
|
||||
|
||||
if (_status !== 200) {
|
||||
space = "";
|
||||
source = "";
|
||||
throw new Error();
|
||||
}
|
||||
response = await response.json();
|
||||
_space_id = response.id;
|
||||
cb(response.id);
|
||||
} catch (e) {
|
||||
space = "";
|
||||
source = "";
|
||||
status = "error";
|
||||
error_detail = {
|
||||
type: "space_error",
|
||||
detail: {
|
||||
description: "This space is experiencing an issue.",
|
||||
discussions_enabled: await discussions_enabled(_space_id || space_id)
|
||||
}
|
||||
};
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!response || _status !== 200) return;
|
||||
const {
|
||||
runtime: { stage }
|
||||
} = response;
|
||||
|
||||
switch (stage) {
|
||||
case "STOPPED":
|
||||
case "SLEEPING":
|
||||
status = "pending";
|
||||
loading_text = "Space is asleep. Waking it up...";
|
||||
setTimeout(() => {
|
||||
check_space_status(_space_id, source);
|
||||
}, 1000);
|
||||
break;
|
||||
// poll for status
|
||||
case "RUNNING":
|
||||
case "RUNNING_BUILDING":
|
||||
status = "success";
|
||||
load_config(source);
|
||||
// launch
|
||||
break;
|
||||
case "BUILDING":
|
||||
status = "pending";
|
||||
loading_text = "Space is building...";
|
||||
setTimeout(() => {
|
||||
check_space_status(_space_id, source);
|
||||
}, 1000);
|
||||
break;
|
||||
|
||||
case "NO_APP_FILE":
|
||||
case "CONFIG_ERROR":
|
||||
case "BUILD_ERROR":
|
||||
case "RUNTIME_ERROR":
|
||||
status = "error";
|
||||
error_detail = {
|
||||
type: "space_error",
|
||||
detail: {
|
||||
description: "This space is experiencing an issue.",
|
||||
discussions_enabled: await discussions_enabled(_space_id),
|
||||
stage
|
||||
}
|
||||
};
|
||||
break;
|
||||
default:
|
||||
status = "error";
|
||||
error_detail = {
|
||||
type: "space_error",
|
||||
detail: {
|
||||
description: "This space is experiencing an issue.",
|
||||
discussions_enabled: await discussions_enabled(_space_id)
|
||||
}
|
||||
};
|
||||
}
|
||||
let app: Awaited<ReturnType<typeof client>>;
|
||||
function handle_status(_status: SpaceStatus) {
|
||||
status = _status;
|
||||
}
|
||||
|
||||
const RE_DISABLED_DISCUSSION =
|
||||
/^(?=[^]*\b[dD]iscussions{0,1}\b)(?=[^]*\b[dD]isabled\b)[^]*$/;
|
||||
|
||||
async function discussions_enabled(space_id: string) {
|
||||
try {
|
||||
const r = await fetch(
|
||||
`https://huggingface.co/api/spaces/${space_id}/discussions`,
|
||||
{
|
||||
method: "HEAD"
|
||||
}
|
||||
);
|
||||
const error = r.headers.get("x-error-message");
|
||||
|
||||
if (error && RE_DISABLED_DISCUSSION.test(error)) return false;
|
||||
else return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const session_hash = Math.random().toString(36).substring(2);
|
||||
|
||||
let error_detail: null | {
|
||||
type: "not_found" | "space_error";
|
||||
detail?: Record<string, any>;
|
||||
} = null;
|
||||
|
||||
onMount(async () => {
|
||||
if (window.__gradio_mode__ !== "website") {
|
||||
theme = handle_darkmode(wrapper);
|
||||
}
|
||||
|
||||
let source;
|
||||
if (space) {
|
||||
try {
|
||||
const r = await fetch(
|
||||
`https://huggingface.co/api/spaces/${space.trim()}/host`
|
||||
);
|
||||
const api_url =
|
||||
BUILD_MODE === "dev"
|
||||
? "http://localhost:7860"
|
||||
: host || space || src || location.origin;
|
||||
|
||||
if (r.status !== 200) {
|
||||
source = "";
|
||||
space = "";
|
||||
}
|
||||
app = await client(api_url, handle_status);
|
||||
config = app.config;
|
||||
|
||||
source = (await r.json()).host;
|
||||
} catch (e) {
|
||||
source = "";
|
||||
space = "";
|
||||
}
|
||||
} else {
|
||||
source = (host ? `https://${host}` : src)?.trim();
|
||||
status = {
|
||||
message: "",
|
||||
load_status: "complete",
|
||||
status: "running",
|
||||
detail: "RUNNING"
|
||||
};
|
||||
|
||||
mount_custom_css(wrapper, config.css);
|
||||
window.__is_colab__ = config.is_colab;
|
||||
|
||||
if (config.dev_mode) {
|
||||
reload_check(config.root);
|
||||
}
|
||||
|
||||
if (space) {
|
||||
check_space_status(space?.trim(), source);
|
||||
} else if (src?.endsWith(".hf.space")) {
|
||||
check_space_status(
|
||||
src.replace(".hf.space", "").replace("https://", ""),
|
||||
source,
|
||||
(id: string) => (space = id)
|
||||
);
|
||||
space = " ";
|
||||
}
|
||||
load_config(source);
|
||||
});
|
||||
|
||||
async function load_config(source: string) {
|
||||
const _config: Config | null = await handle_config(wrapper, source);
|
||||
|
||||
if (_config) {
|
||||
status = _config.auth_required ? "login" : "pending";
|
||||
_config.fn = _config.fn = fn(
|
||||
session_hash,
|
||||
root + "run/",
|
||||
_config.is_space,
|
||||
is_embed
|
||||
);
|
||||
config = _config;
|
||||
} else {
|
||||
create_not_found_error();
|
||||
}
|
||||
}
|
||||
|
||||
function create_not_found_error() {
|
||||
status = "error";
|
||||
error_detail = {
|
||||
type: "not_found",
|
||||
detail: {
|
||||
description: "This gradio app is experiencing an issue."
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
$: status = ready ? "success" : status;
|
||||
$: loader_status =
|
||||
!ready && status.load_status !== "error"
|
||||
? "pending"
|
||||
: !ready && status.load_status === "error"
|
||||
? "error"
|
||||
: status.load_status;
|
||||
|
||||
$: config && (eager || $intersecting[_id]) && load_demo();
|
||||
|
||||
@ -450,7 +252,7 @@
|
||||
}
|
||||
};
|
||||
|
||||
onMount(() => {
|
||||
onMount(async () => {
|
||||
intersecting.register(_id, wrapper);
|
||||
});
|
||||
</script>
|
||||
@ -462,27 +264,27 @@
|
||||
{version}
|
||||
{initial_height}
|
||||
{space}
|
||||
loaded={status === "success"}
|
||||
loaded={loader_status === "complete"}
|
||||
bind:wrapper
|
||||
>
|
||||
{#if status === "pending" || status === "error"}
|
||||
{#if (loader_status === "pending" || loader_status === "error") && !(config && config?.auth_required)}
|
||||
<Loader
|
||||
absolute={!is_embed}
|
||||
{status}
|
||||
status={loader_status}
|
||||
timer={false}
|
||||
queue_position={null}
|
||||
queue_size={null}
|
||||
{loading_text}
|
||||
>
|
||||
<div class="error" slot="error">
|
||||
<p><strong>{error_detail?.detail?.description || ""}</strong></p>
|
||||
{#if error_detail?.detail?.discussions_enabled}
|
||||
<p><strong>{status?.message || ""}</strong></p>
|
||||
{#if status.status === "space_error" && status.discussions_enabled}
|
||||
<p>
|
||||
Please <a
|
||||
href="https://huggingface.co/spaces/{space}/discussions/new?title={discussion_message.title(
|
||||
error_detail.detail.stage
|
||||
status?.detail
|
||||
)}&description={discussion_message.description(
|
||||
error_detail.detail.stage,
|
||||
status?.detail,
|
||||
location.origin
|
||||
)}"
|
||||
>
|
||||
@ -494,16 +296,17 @@
|
||||
{/if}
|
||||
</div>
|
||||
</Loader>
|
||||
{:else if status === "login" && Login}
|
||||
{/if}
|
||||
{#if config?.auth_required && Login}
|
||||
<Login
|
||||
auth_message={config.auth_message}
|
||||
root={config.root}
|
||||
is_space={config.is_space}
|
||||
{app_mode}
|
||||
/>
|
||||
{/if}
|
||||
{#if config && Blocks}
|
||||
{:else if config && Blocks}
|
||||
<Blocks
|
||||
{app}
|
||||
{...config}
|
||||
{theme}
|
||||
{control_page_title}
|
||||
|
@ -17,7 +17,7 @@
|
||||
formData.append("username", username);
|
||||
formData.append("password", password);
|
||||
|
||||
let response = await fetch(root + "login", {
|
||||
let response = await fetch(root + "/login", {
|
||||
method: "POST",
|
||||
body: formData
|
||||
});
|
||||
|
@ -1,327 +0,0 @@
|
||||
import { get } from "svelte/store";
|
||||
import type { LoadingStatusType } from "./stores";
|
||||
|
||||
type StatusResponse =
|
||||
| {
|
||||
status: "COMPLETE";
|
||||
data: {
|
||||
duration: number;
|
||||
average_duration: number;
|
||||
data: Array<unknown>;
|
||||
};
|
||||
}
|
||||
| {
|
||||
status: "QUEUED";
|
||||
data: number;
|
||||
}
|
||||
| {
|
||||
status: "PENDING";
|
||||
data: null;
|
||||
}
|
||||
| {
|
||||
status: "FAILED";
|
||||
data: Record<string, unknown>;
|
||||
};
|
||||
|
||||
interface Payload {
|
||||
data: Array<unknown>;
|
||||
fn_index: number;
|
||||
session_hash?: string;
|
||||
}
|
||||
|
||||
declare let BUILD_MODE: string;
|
||||
declare let BACKEND_URL: string;
|
||||
|
||||
interface PostResponse {
|
||||
error?: string;
|
||||
[x: string]: any;
|
||||
}
|
||||
export interface UploadResponse {
|
||||
error?: string;
|
||||
files?: Array<string>;
|
||||
}
|
||||
const QUEUE_FULL_MSG = "This application is too busy. Keep trying!";
|
||||
const BROKEN_CONNECTION_MSG = "Connection errored out.";
|
||||
|
||||
export async function post_data(
|
||||
url: string,
|
||||
body: unknown
|
||||
): Promise<[PostResponse, number]> {
|
||||
try {
|
||||
var response = await fetch(url, {
|
||||
method: "POST",
|
||||
body: JSON.stringify(body),
|
||||
headers: { "Content-Type": "application/json" }
|
||||
});
|
||||
} catch (e) {
|
||||
return [{ error: BROKEN_CONNECTION_MSG }, 500];
|
||||
}
|
||||
const output: PostResponse = await response.json();
|
||||
return [output, response.status];
|
||||
}
|
||||
|
||||
export async function upload_files(
|
||||
root: string,
|
||||
files: Array<File>
|
||||
): Promise<UploadResponse> {
|
||||
const formData = new FormData();
|
||||
files.forEach((file) => {
|
||||
formData.append("files", file);
|
||||
});
|
||||
try {
|
||||
var response = await fetch(`${root}upload`, {
|
||||
method: "POST",
|
||||
body: formData
|
||||
});
|
||||
} catch (e) {
|
||||
return { error: BROKEN_CONNECTION_MSG };
|
||||
}
|
||||
const output: UploadResponse["files"] = await response.json();
|
||||
return { files: output };
|
||||
}
|
||||
|
||||
interface UpdateOutput {
|
||||
__type__: string;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
type Output = {
|
||||
data: Array<UpdateOutput | unknown>;
|
||||
duration?: number;
|
||||
average_duration?: number;
|
||||
};
|
||||
|
||||
const ws_map = new Map<number, WebSocket>();
|
||||
export const fn =
|
||||
(
|
||||
session_hash: string,
|
||||
api_endpoint: string,
|
||||
is_space: boolean,
|
||||
is_embed: boolean
|
||||
) =>
|
||||
async ({
|
||||
action,
|
||||
payload,
|
||||
queue,
|
||||
backend_fn,
|
||||
frontend_fn,
|
||||
output_data,
|
||||
queue_callback,
|
||||
loading_status,
|
||||
cancels
|
||||
}: {
|
||||
action: string;
|
||||
payload: Payload;
|
||||
queue: boolean;
|
||||
backend_fn: boolean;
|
||||
frontend_fn: Function | undefined;
|
||||
output_data?: Output["data"];
|
||||
queue_callback: Function;
|
||||
loading_status: LoadingStatusType;
|
||||
cancels: Array<number>;
|
||||
}): Promise<unknown> => {
|
||||
const fn_index = payload.fn_index;
|
||||
|
||||
payload.session_hash = session_hash;
|
||||
if (frontend_fn !== undefined) {
|
||||
payload.data = await frontend_fn(payload.data.concat(output_data));
|
||||
}
|
||||
if (backend_fn == false) {
|
||||
return payload;
|
||||
}
|
||||
|
||||
if (queue && ["predict", "interpret"].includes(action)) {
|
||||
loading_status.update(
|
||||
fn_index as number,
|
||||
"pending",
|
||||
queue,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
);
|
||||
|
||||
function send_message(fn: number, data: any) {
|
||||
ws_map.get(fn)?.send(JSON.stringify(data));
|
||||
}
|
||||
|
||||
let WS_ENDPOINT = "";
|
||||
|
||||
if (is_embed) {
|
||||
WS_ENDPOINT = `wss://${new URL(api_endpoint).host}/queue/join`;
|
||||
} else {
|
||||
var ws_endpoint =
|
||||
api_endpoint === "run/" ? location.href : api_endpoint;
|
||||
var ws_protocol = ws_endpoint.startsWith("https") ? "wss:" : "ws:";
|
||||
var ws_path = location.pathname === "/" ? "/" : location.pathname;
|
||||
var ws_host =
|
||||
BUILD_MODE === "dev" || location.origin === "http://localhost:9876"
|
||||
? BACKEND_URL.replace("http://", "").slice(0, -1)
|
||||
: location.host;
|
||||
WS_ENDPOINT = `${ws_protocol}//${ws_host}${ws_path}queue/join`;
|
||||
}
|
||||
|
||||
var websocket = new WebSocket(WS_ENDPOINT);
|
||||
ws_map.set(fn_index, websocket);
|
||||
|
||||
websocket.onclose = (evt) => {
|
||||
if (!evt.wasClean) {
|
||||
loading_status.update(
|
||||
fn_index,
|
||||
"error",
|
||||
queue,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
BROKEN_CONNECTION_MSG
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
websocket.onmessage = async function (event) {
|
||||
const data = JSON.parse(event.data);
|
||||
|
||||
switch (data.msg) {
|
||||
case "send_data":
|
||||
send_message(fn_index, payload);
|
||||
break;
|
||||
case "send_hash":
|
||||
ws_map.get(fn_index)?.send(
|
||||
JSON.stringify({
|
||||
session_hash: session_hash,
|
||||
fn_index: fn_index
|
||||
})
|
||||
);
|
||||
break;
|
||||
case "queue_full":
|
||||
loading_status.update(
|
||||
fn_index,
|
||||
"error",
|
||||
queue,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
QUEUE_FULL_MSG
|
||||
);
|
||||
websocket.close();
|
||||
return;
|
||||
case "estimation":
|
||||
loading_status.update(
|
||||
fn_index,
|
||||
get(loading_status)[data.fn_index]?.status || "pending",
|
||||
queue,
|
||||
data.queue_size,
|
||||
data.rank,
|
||||
data.rank_eta,
|
||||
null
|
||||
);
|
||||
break;
|
||||
case "progress":
|
||||
loading_status.update(
|
||||
fn_index,
|
||||
"pending",
|
||||
queue,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
data.progress_data
|
||||
);
|
||||
break;
|
||||
case "process_generating":
|
||||
loading_status.update(
|
||||
fn_index,
|
||||
data.success ? "generating" : "error",
|
||||
queue,
|
||||
null,
|
||||
null,
|
||||
data.output.average_duration,
|
||||
!data.success ? data.output.error : null
|
||||
);
|
||||
if (data.success) {
|
||||
queue_callback(data.output);
|
||||
}
|
||||
break;
|
||||
case "process_completed":
|
||||
loading_status.update(
|
||||
fn_index,
|
||||
data.success ? "complete" : "error",
|
||||
queue,
|
||||
null,
|
||||
null,
|
||||
data.output.average_duration,
|
||||
!data.success ? data.output.error : null
|
||||
);
|
||||
if (data.success) {
|
||||
queue_callback(data.output);
|
||||
}
|
||||
websocket.close();
|
||||
return;
|
||||
case "process_starts":
|
||||
loading_status.update(
|
||||
fn_index,
|
||||
"pending",
|
||||
queue,
|
||||
data.rank,
|
||||
0,
|
||||
null,
|
||||
null
|
||||
);
|
||||
break;
|
||||
}
|
||||
};
|
||||
} else {
|
||||
loading_status.update(
|
||||
fn_index as number,
|
||||
"pending",
|
||||
queue,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
);
|
||||
|
||||
var [output, status_code] = await post_data(api_endpoint + action + "/", {
|
||||
...payload,
|
||||
session_hash
|
||||
});
|
||||
if (status_code == 200) {
|
||||
loading_status.update(
|
||||
fn_index,
|
||||
"complete",
|
||||
queue,
|
||||
null,
|
||||
null,
|
||||
output.average_duration as number,
|
||||
null
|
||||
);
|
||||
// Cancelled jobs are set to complete
|
||||
if (cancels.length > 0) {
|
||||
cancels.forEach((fn_index) => {
|
||||
loading_status.update(
|
||||
fn_index,
|
||||
"complete",
|
||||
queue,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
);
|
||||
ws_map.get(fn_index)?.close();
|
||||
});
|
||||
}
|
||||
} else {
|
||||
loading_status.update(
|
||||
fn_index,
|
||||
"error",
|
||||
queue,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
output.error
|
||||
);
|
||||
throw output.error || "API Error";
|
||||
}
|
||||
return output;
|
||||
}
|
||||
};
|
@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { onMount, createEventDispatcher } from "svelte";
|
||||
import type { ComponentMeta, Dependency } from "../components/types";
|
||||
import { post_data } from "../api";
|
||||
import { post_data } from "@gradio/client";
|
||||
import NoApi from "./NoApi.svelte";
|
||||
|
||||
import { represent_value } from "./utils";
|
||||
@ -72,7 +72,7 @@
|
||||
return;
|
||||
}
|
||||
let [response, status_code] = await post_data(
|
||||
`${root}run/${dependency.api_name}`,
|
||||
`${root}/run/${dependency.api_name}`,
|
||||
{
|
||||
data: inputs
|
||||
}
|
||||
|
@ -18,8 +18,8 @@
|
||||
const dispatch = createEventDispatcher<{ click: number }>();
|
||||
|
||||
let samples_dir: string = root_url
|
||||
? "proxy=" + root_url + "file="
|
||||
: root + "file=";
|
||||
? "proxy=" + root_url + "/file="
|
||||
: root + "/file=";
|
||||
let page = 0;
|
||||
$: gallery = components.length < 2;
|
||||
let paginate = samples.length > samples_per_page;
|
||||
|
@ -5,7 +5,7 @@
|
||||
import { normalise_file } from "@gradio/upload";
|
||||
import { Block } from "@gradio/atoms";
|
||||
import UploadText from "../UploadText.svelte";
|
||||
import { upload_files } from "../../api";
|
||||
import { upload_files } from "@gradio/client";
|
||||
|
||||
import StatusTracker from "../StatusTracker/StatusTracker.svelte";
|
||||
import type { LoadingStatus } from "../StatusTracker/types";
|
||||
|
@ -3,7 +3,7 @@ import { cleanup, render } from "@gradio/tootils";
|
||||
|
||||
import File from "./File.svelte";
|
||||
import type { LoadingStatus } from "../StatusTracker/types";
|
||||
import { upload_files } from "../../api";
|
||||
import { upload_files } from "@gradio/client";
|
||||
|
||||
const loading_status = {
|
||||
eta: 0,
|
||||
@ -22,13 +22,13 @@ describe("File", () => {
|
||||
});
|
||||
|
||||
test("Uploads with blob", async () => {
|
||||
vi.mock("../../api", async () => {
|
||||
vi.mock("@gradio/client", async () => {
|
||||
return {
|
||||
upload_files: vi.fn((f) => new Promise((res) => res({})))
|
||||
};
|
||||
});
|
||||
|
||||
const api = await import("../../api");
|
||||
const api = await import("@gradio/client");
|
||||
|
||||
render(File, {
|
||||
loading_status,
|
||||
|
@ -3,7 +3,7 @@
|
||||
import type { Styles } from "@gradio/utils";
|
||||
import type { FileData } from "@gradio/upload";
|
||||
import { UploadButton } from "@gradio/upload-button";
|
||||
import { upload_files } from "../../api";
|
||||
import { upload_files } from "@gradio/client";
|
||||
import { blobToBase64 } from "@gradio/upload";
|
||||
import { _ } from "svelte-i18n";
|
||||
|
||||
|
@ -1,11 +1,11 @@
|
||||
import { writable } from "svelte/store";
|
||||
import { writable, get } from "svelte/store";
|
||||
|
||||
export interface LoadingStatus {
|
||||
eta: number | null;
|
||||
status: "pending" | "error" | "complete" | "generating";
|
||||
queue: boolean;
|
||||
queue_position: number | null;
|
||||
queue_size: number | null;
|
||||
queue_size?: number;
|
||||
fn_index: number;
|
||||
message?: string | null;
|
||||
scroll_to_output?: boolean;
|
||||
@ -32,16 +32,25 @@ export function create_loading_status_store() {
|
||||
const inputs_to_update = new Map<number, string>();
|
||||
const fn_status: Array<LoadingStatus["status"]> = [];
|
||||
|
||||
function update(
|
||||
fn_index: LoadingStatus["fn_index"],
|
||||
status: LoadingStatus["status"],
|
||||
queue: LoadingStatus["queue"],
|
||||
size: LoadingStatus["queue_size"],
|
||||
position: LoadingStatus["queue_position"],
|
||||
eta: LoadingStatus["eta"],
|
||||
message: LoadingStatus["message"],
|
||||
progress?: LoadingStatus["progress"]
|
||||
) {
|
||||
function update({
|
||||
fn_index,
|
||||
status,
|
||||
queue = true,
|
||||
size,
|
||||
position = null,
|
||||
eta = null,
|
||||
message = null,
|
||||
progress
|
||||
}: {
|
||||
fn_index: LoadingStatus["fn_index"];
|
||||
status: LoadingStatus["status"];
|
||||
queue?: LoadingStatus["queue"];
|
||||
size?: LoadingStatus["queue_size"];
|
||||
position?: LoadingStatus["queue_position"];
|
||||
eta?: LoadingStatus["eta"];
|
||||
message?: LoadingStatus["message"];
|
||||
progress?: LoadingStatus["progress"];
|
||||
}) {
|
||||
const outputs = fn_outputs[fn_index];
|
||||
const inputs = fn_inputs[fn_index];
|
||||
const last_status = fn_status[fn_index];
|
||||
@ -98,7 +107,7 @@ export function create_loading_status_store() {
|
||||
}
|
||||
});
|
||||
|
||||
store.update((outputs) => {
|
||||
store.update((outputs: LoadingStatusCollection) => {
|
||||
outputs_to_update.forEach(
|
||||
({
|
||||
id,
|
||||
@ -114,7 +123,7 @@ export function create_loading_status_store() {
|
||||
queue_size: queue_size,
|
||||
queue_position: queue_position,
|
||||
eta: eta,
|
||||
message,
|
||||
message: message,
|
||||
progress,
|
||||
status,
|
||||
fn_index
|
||||
@ -124,7 +133,6 @@ export function create_loading_status_store() {
|
||||
|
||||
return outputs;
|
||||
});
|
||||
|
||||
fn_status[fn_index] = status;
|
||||
}
|
||||
|
||||
|
@ -12,7 +12,7 @@ function mock_demo(page: Page, demo: string) {
|
||||
}
|
||||
|
||||
function mock_api(page: Page, body: Array<unknown>) {
|
||||
return page.route("**/run/predict/", (route) => {
|
||||
return page.route("**/run/predict", (route) => {
|
||||
const id = JSON.parse(route.request().postData()!).fn_index;
|
||||
return route.fulfill({
|
||||
headers: {
|
||||
@ -39,7 +39,7 @@ test("renders the correct elements", async ({ page }) => {
|
||||
await textboxTwo.fill("dawood");
|
||||
await Promise.all([
|
||||
page.click('text="Submit"'),
|
||||
page.waitForResponse("**/run/predict/")
|
||||
page.waitForResponse("**/run/predict")
|
||||
]);
|
||||
|
||||
await expect(await page.getByLabel("Output")).toHaveValue("hi dawood");
|
||||
|
@ -12,7 +12,7 @@ function mock_demo(page: Page, demo: string) {
|
||||
}
|
||||
|
||||
function mock_api(page: Page, body: Array<unknown>) {
|
||||
return page.route("**/run/predict/", (route) => {
|
||||
return page.route("**/run/predict", (route) => {
|
||||
const id = JSON.parse(route.request().postData()!).fn_index;
|
||||
return route.fulfill({
|
||||
headers: {
|
||||
@ -32,6 +32,6 @@ test("renders the correct elements", async ({ page }) => {
|
||||
|
||||
await Promise.all([
|
||||
page.click("button:has-text('Run')"),
|
||||
page.waitForResponse("**/run/predict/")
|
||||
page.waitForResponse("**/run/predict")
|
||||
]);
|
||||
});
|
||||
|
@ -12,7 +12,7 @@ function mock_demo(page: Page, demo: string) {
|
||||
}
|
||||
|
||||
function mock_api(page: Page, body: Array<unknown>) {
|
||||
return page.route("**/run/predict/", (route) => {
|
||||
return page.route("**/run/predict", (route) => {
|
||||
const id = JSON.parse(route.request().postData()!).fn_index;
|
||||
return route.fulfill({
|
||||
headers: {
|
||||
|
@ -12,7 +12,7 @@ function mock_demo(page: Page, demo: string) {
|
||||
}
|
||||
|
||||
function mock_api(page: Page, body: Array<unknown>) {
|
||||
return page.route("**/run/predict/", (route) => {
|
||||
return page.route("**/run/predict", (route) => {
|
||||
const id = JSON.parse(route.request().postData()!).fn_index;
|
||||
return route.fulfill({
|
||||
headers: {
|
||||
@ -65,7 +65,7 @@ test("can run an api request and display the data", async ({ page }) => {
|
||||
|
||||
await Promise.all([
|
||||
run_button.click(),
|
||||
page.waitForResponse("**/run/predict/")
|
||||
page.waitForResponse("**/run/predict")
|
||||
]);
|
||||
|
||||
const json = await page.getByTestId("json").first();
|
||||
|
@ -12,7 +12,7 @@ function mock_demo(page: Page, demo: string) {
|
||||
}
|
||||
|
||||
function mock_api(page: Page, body: Array<unknown>) {
|
||||
return page.route("**/run/predict/", (route) => {
|
||||
return page.route("**/run/predict", (route) => {
|
||||
const id = JSON.parse(route.request().postData()!).fn_index;
|
||||
return route.fulfill({
|
||||
headers: {
|
||||
@ -35,7 +35,7 @@ test("a component acts as both input and output", async ({ page }) => {
|
||||
await textbox.fill("test");
|
||||
await Promise.all([
|
||||
page.click("button"),
|
||||
page.waitForResponse("**/run/predict/")
|
||||
page.waitForResponse("**/run/predict")
|
||||
]);
|
||||
await expect(await textbox).toHaveValue("tset");
|
||||
});
|
||||
|
@ -13,7 +13,7 @@ function mock_demo(page: Page, demo: string) {
|
||||
}
|
||||
|
||||
function mock_api(page: Page, body: Array<unknown>) {
|
||||
return page.route("**/run/predict/", (route) => {
|
||||
return page.route("**/run/predict", (route) => {
|
||||
const id = JSON.parse(route.request().postData()!).fn_index;
|
||||
return route.fulfill({
|
||||
headers: {
|
||||
@ -215,7 +215,7 @@ test("test outputs", async ({ page }) => {
|
||||
|
||||
await Promise.all([
|
||||
submit_button.click(),
|
||||
page.waitForResponse("**/run/predict/")
|
||||
page.waitForResponse("**/run/predict")
|
||||
]);
|
||||
|
||||
const textbox = await page.getByLabel("Textbox").nth(2);
|
||||
|
@ -1,383 +0,0 @@
|
||||
function cheap_clone(obj: object) {
|
||||
return JSON.parse(JSON.stringify(obj));
|
||||
}
|
||||
|
||||
export const RUNNING = {
|
||||
_test: true,
|
||||
_id: "63d2d66613d86bc148071e75",
|
||||
id: "pngwn/music-visualizer",
|
||||
author: "pngwn",
|
||||
sha: "4090039c565c73d5a0485d14bc3067ca6b25967c",
|
||||
lastModified: "2023-01-26T19:37:53.000Z",
|
||||
private: false,
|
||||
cardData: {
|
||||
title: "Music Visualizer",
|
||||
emoji: "🐨",
|
||||
colorFrom: "gray",
|
||||
colorTo: "indigo",
|
||||
sdk: "gradio",
|
||||
sdk_version: "3.16.2",
|
||||
app_file: "app.py",
|
||||
pinned: false,
|
||||
duplicated_from: "nateraw/music-visualizer"
|
||||
},
|
||||
gated: false,
|
||||
disabled: false,
|
||||
subdomain: "pngwn-music-visualizer",
|
||||
tags: ["gradio"],
|
||||
likes: 1,
|
||||
datasets: ["nateraw/misc"],
|
||||
sdk: "gradio",
|
||||
runtime: {
|
||||
stage: "RUNNING",
|
||||
sdk: "gradio",
|
||||
sdkVersion: "3.16.2",
|
||||
hardware: {
|
||||
current: "cpu-basic",
|
||||
requested: "cpu-basic"
|
||||
},
|
||||
spaceId: "pngwn/music-visualizer"
|
||||
},
|
||||
siblings: [
|
||||
{
|
||||
rfilename: ".gitattributes"
|
||||
},
|
||||
{
|
||||
rfilename: "README.md"
|
||||
},
|
||||
{
|
||||
rfilename: "app.py"
|
||||
},
|
||||
{
|
||||
rfilename: "requirements.txt"
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
export const BUILDING = {
|
||||
_test: true,
|
||||
_id: "621ffddf36468d709f184f2b",
|
||||
id: "pngwn/test",
|
||||
author: "pngwn",
|
||||
sha: "5d35bea1c37f0b727798d9325532a3a47cedb8d8",
|
||||
lastModified: "2022-01-10T11:02:33.000Z",
|
||||
private: false,
|
||||
cardData: {
|
||||
title: "Test",
|
||||
emoji: "🌖",
|
||||
colorFrom: "purple",
|
||||
colorTo: "indigo",
|
||||
sdk: "gradio",
|
||||
app_file: "app.py",
|
||||
pinned: false
|
||||
},
|
||||
gated: false,
|
||||
disabled: false,
|
||||
subdomain: "pngwn-test",
|
||||
tags: ["gradio"],
|
||||
likes: 0,
|
||||
sdk: "gradio",
|
||||
runtime: {
|
||||
stage: "BUILDING",
|
||||
hardware: {
|
||||
current: "cpu-basic",
|
||||
requested: "cpu-basic"
|
||||
},
|
||||
resources: {
|
||||
requests: {
|
||||
cpu: 0.2,
|
||||
memory: "1G",
|
||||
gpu: "0",
|
||||
gpuModel: "",
|
||||
ephemeral: "0G"
|
||||
},
|
||||
limits: {
|
||||
cpu: 8,
|
||||
memory: "16G",
|
||||
gpu: "0",
|
||||
gpuModel: "",
|
||||
ephemeral: "50G"
|
||||
},
|
||||
replicas: 1,
|
||||
throttled: false,
|
||||
is_custom: false,
|
||||
ports: []
|
||||
},
|
||||
gcTimeout: null
|
||||
},
|
||||
siblings: [
|
||||
{
|
||||
rfilename: ".gitattributes"
|
||||
},
|
||||
{
|
||||
rfilename: "README.md"
|
||||
},
|
||||
{
|
||||
rfilename: "app.py"
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
export const RUNNING_BUILDING = {
|
||||
_test: true,
|
||||
_id: "621ffddf36468d709f184f2b",
|
||||
id: "pngwn/test",
|
||||
author: "pngwn",
|
||||
sha: "5d35bea1c37f0b727798d9325532a3a47cedb8d8",
|
||||
lastModified: "2022-01-10T11:02:33.000Z",
|
||||
private: false,
|
||||
cardData: {
|
||||
title: "Test",
|
||||
emoji: "🌖",
|
||||
colorFrom: "purple",
|
||||
colorTo: "indigo",
|
||||
sdk: "gradio",
|
||||
app_file: "app.py",
|
||||
pinned: false
|
||||
},
|
||||
gated: false,
|
||||
disabled: false,
|
||||
subdomain: "pngwn-test",
|
||||
tags: ["gradio"],
|
||||
likes: 0,
|
||||
sdk: "gradio",
|
||||
runtime: {
|
||||
stage: "RUNNING_BUILDING",
|
||||
sdk: "gradio",
|
||||
sdkVersion: "3.18.0",
|
||||
hardware: {
|
||||
current: "cpu-basic",
|
||||
requested: "cpu-basic"
|
||||
},
|
||||
resources: {
|
||||
requests: {
|
||||
cpu: "0.2",
|
||||
memory: "1G",
|
||||
gpu: "0",
|
||||
gpuModel: null,
|
||||
ephemeral: "0G"
|
||||
},
|
||||
limits: {
|
||||
cpu: "2.0",
|
||||
memory: "16G",
|
||||
gpu: "0",
|
||||
gpuModel: null,
|
||||
ephemeral: "50G"
|
||||
},
|
||||
replicas: 1,
|
||||
throttled: false,
|
||||
is_custom: false,
|
||||
ports: []
|
||||
},
|
||||
gcTimeout: null
|
||||
},
|
||||
siblings: [
|
||||
{
|
||||
rfilename: ".gitattributes"
|
||||
},
|
||||
{
|
||||
rfilename: "README.md"
|
||||
},
|
||||
{
|
||||
rfilename: "app.py"
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
export const NO_APP_FILE = {
|
||||
_test: true,
|
||||
_id: "627a52a6f652717251c6f238",
|
||||
id: "pngwn/AnimeGANv2_v3",
|
||||
author: "pngwn",
|
||||
sha: "977cfd1d60a3dcdb18577c37210257f8702b58dd",
|
||||
lastModified: "2022-05-10T11:55:18.000Z",
|
||||
private: false,
|
||||
cardData: {
|
||||
title: "AnimeGANv2_v3",
|
||||
emoji: "🔥",
|
||||
colorFrom: "green",
|
||||
colorTo: "pink",
|
||||
sdk: "gradio",
|
||||
sdk_version: "2.9.4",
|
||||
app_file: "app.py",
|
||||
pinned: false
|
||||
},
|
||||
gated: false,
|
||||
disabled: false,
|
||||
subdomain: "pngwn-animeganv2-v3",
|
||||
tags: ["gradio"],
|
||||
likes: 0,
|
||||
sdk: "gradio",
|
||||
runtime: {
|
||||
stage: "NO_APP_FILE",
|
||||
hardware: {
|
||||
current: null,
|
||||
requested: "cpu-basic"
|
||||
},
|
||||
resources: {
|
||||
replicas: 1,
|
||||
ports: []
|
||||
},
|
||||
gcTimeout: null
|
||||
},
|
||||
siblings: [
|
||||
{
|
||||
rfilename: ".gitattributes"
|
||||
},
|
||||
{
|
||||
rfilename: "README.md"
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
export const CONFIG_ERROR = {
|
||||
_test: true,
|
||||
_id: "627d3fd94918f2e0b4333354",
|
||||
id: "pngwn/clear-inputs",
|
||||
author: "pngwn",
|
||||
sha: "d8fe24c4d35a195feacdfa9d0cbf6cfa9a7efdfc",
|
||||
lastModified: "2022-05-12T17:38:44.000Z",
|
||||
private: false,
|
||||
cardData: {
|
||||
title: "Clear Inputs",
|
||||
emoji: "🐢",
|
||||
colorFrom: "indigo",
|
||||
colorTo: "gray",
|
||||
sdk: "gradio",
|
||||
sdk_version: "2.9b26",
|
||||
app_file: "app.py",
|
||||
pinned: false
|
||||
},
|
||||
gated: false,
|
||||
disabled: false,
|
||||
subdomain: "pngwn-clear-inputs",
|
||||
tags: ["gradio"],
|
||||
likes: 0,
|
||||
sdk: "gradio",
|
||||
runtime: {
|
||||
stage: "CONFIG_ERROR",
|
||||
errorMessage: "ConfigError: Gradio version does not exist",
|
||||
hardware: {
|
||||
current: null,
|
||||
requested: "cpu-basic"
|
||||
},
|
||||
spaceId: "pngwn/clear-inputs"
|
||||
},
|
||||
siblings: [
|
||||
{
|
||||
rfilename: ".gitattributes"
|
||||
},
|
||||
{
|
||||
rfilename: "README.md"
|
||||
},
|
||||
{
|
||||
rfilename: "app.py"
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
export const RUNTIME_ERROR = {
|
||||
_test: true,
|
||||
_id: "634825a6c241cb6791d3472f",
|
||||
id: "pngwn/altair-charts",
|
||||
author: "pngwn",
|
||||
sha: "f304b7c624bbd9e47a3a6dc59b03986416aa22fa",
|
||||
lastModified: "2022-10-13T17:27:52.000Z",
|
||||
private: false,
|
||||
cardData: {
|
||||
title: "Altair Charts",
|
||||
emoji: "🚀",
|
||||
colorFrom: "green",
|
||||
colorTo: "gray",
|
||||
sdk: "gradio",
|
||||
sdk_version: "3.4.1",
|
||||
app_file: "app.py",
|
||||
pinned: false
|
||||
},
|
||||
gated: false,
|
||||
disabled: false,
|
||||
subdomain: "pngwn-altair-charts",
|
||||
tags: ["gradio"],
|
||||
likes: 0,
|
||||
sdk: "gradio",
|
||||
runtime: {
|
||||
stage: "RUNTIME_ERROR",
|
||||
errorMessage: "launch timed out, space was not healthy after 30 min",
|
||||
hardware: {
|
||||
current: null,
|
||||
requested: "cpu-basic"
|
||||
},
|
||||
resources: {
|
||||
replicas: 1,
|
||||
ports: []
|
||||
},
|
||||
gcTimeout: null
|
||||
},
|
||||
siblings: [
|
||||
{
|
||||
rfilename: ".gitattributes"
|
||||
},
|
||||
{
|
||||
rfilename: "README.md"
|
||||
},
|
||||
{
|
||||
rfilename: "app.py"
|
||||
},
|
||||
{
|
||||
rfilename: "requirements.txt"
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
export const BUILD_ERROR = {
|
||||
_test: true,
|
||||
_id: "63ec0a88a86bb0294d3fbe41",
|
||||
id: "Gaborandi/PubMed_Downloader",
|
||||
author: "Gaborandi",
|
||||
sha: "3f2a988756800e4f39e96cabda6ccbf2c9c25783",
|
||||
lastModified: "2023-02-15T02:26:49.000Z",
|
||||
private: false,
|
||||
cardData: {
|
||||
title: "PubMed Downloader",
|
||||
emoji: "🌖",
|
||||
colorFrom: "gray",
|
||||
colorTo: "indigo",
|
||||
sdk: "gradio",
|
||||
sdk_version: "3.18.0",
|
||||
app_file: "app.py",
|
||||
pinned: false
|
||||
},
|
||||
gated: false,
|
||||
disabled: false,
|
||||
subdomain: "gaborandi-pubmed-downloader",
|
||||
tags: ["gradio"],
|
||||
likes: 0,
|
||||
sdk: "gradio",
|
||||
runtime: {
|
||||
stage: "BUILD_ERROR",
|
||||
errorMessage: "build failed with exit code: 1, message: None",
|
||||
hardware: {
|
||||
current: null,
|
||||
requested: "cpu-basic"
|
||||
},
|
||||
resources: {
|
||||
replicas: 1,
|
||||
ports: []
|
||||
},
|
||||
gcTimeout: null
|
||||
},
|
||||
siblings: [
|
||||
{
|
||||
rfilename: ".gitattributes"
|
||||
},
|
||||
{
|
||||
rfilename: "README.md"
|
||||
},
|
||||
{
|
||||
rfilename: "app.py"
|
||||
},
|
||||
{
|
||||
rfilename: "requirements.txt"
|
||||
}
|
||||
]
|
||||
};
|
@ -24,7 +24,7 @@ function mock_theme(page: Page) {
|
||||
}
|
||||
|
||||
function mock_api(page: Page, body: Array<unknown>) {
|
||||
return page.route("**/run/predict/", (route) => {
|
||||
return page.route("**/run/predict", (route) => {
|
||||
const id = JSON.parse(route.request().postData()!).fn_index;
|
||||
return route.fulfill({
|
||||
headers: {
|
||||
@ -51,7 +51,7 @@ test("matplotlib", async ({ page }) => {
|
||||
|
||||
await Promise.all([
|
||||
page.click("text=Submit"),
|
||||
page.waitForResponse("**/run/predict/")
|
||||
page.waitForResponse("**/run/predict")
|
||||
]);
|
||||
|
||||
const matplotlib_img = await page.locator("img").nth(0);
|
||||
@ -80,7 +80,7 @@ test("plotly", async ({ page }) => {
|
||||
|
||||
await Promise.all([
|
||||
page.click("text=Submit"),
|
||||
page.waitForResponse("**/run/predict/")
|
||||
page.waitForResponse("**/run/predict")
|
||||
]);
|
||||
await expect(page.locator(".js-plotly-plot")).toHaveCount(1);
|
||||
});
|
||||
|
@ -32,19 +32,44 @@ async function changeSlider(
|
||||
await page.mouse.up();
|
||||
}
|
||||
|
||||
function mock_demo(page: Page, demo: string) {
|
||||
return page.route("**/config", (route) => {
|
||||
return route.fulfill({
|
||||
headers: {
|
||||
"Access-Control-Allow-Origin": "*"
|
||||
},
|
||||
path: `../../../demo/${demo}/config.json`
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function mock_api(page: Page) {
|
||||
return page.route("**/run/predict", (route) => {
|
||||
return route.fulfill({
|
||||
headers: {
|
||||
"Access-Control-Allow-Origin": "*"
|
||||
},
|
||||
body: JSON.stringify({
|
||||
data: [70, null, 1]
|
||||
})
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
test("slider release", async ({ page }) => {
|
||||
await page.goto("http://127.0.0.1:7888/");
|
||||
await mock_demo(page, "slider_release");
|
||||
await mock_api(page);
|
||||
await page.goto("http://localhost:9876");
|
||||
|
||||
const slider = page.getByLabel("Slider");
|
||||
|
||||
const responsePromise = page.waitForResponse(
|
||||
"http://127.0.0.1:7888/run/predict/"
|
||||
);
|
||||
await changeSlider(page, slider, slider, 0.7);
|
||||
const response = await responsePromise;
|
||||
const responseData = await response.json();
|
||||
|
||||
expect(responseData.data[0]).toBeGreaterThan(69.5);
|
||||
expect(responseData.data[0]).toBeLessThan(71.0);
|
||||
expect(responseData.data[2]).toEqual(1);
|
||||
const value = page.getByLabel("On release");
|
||||
const events = page.getByLabel("Number of events fired");
|
||||
|
||||
const val = await slider.inputValue();
|
||||
expect(parseInt(val)).toBeCloseTo(70);
|
||||
expect(value).toHaveValue("70");
|
||||
expect(events).toHaveValue("1");
|
||||
});
|
||||
|
@ -14,7 +14,7 @@
|
||||
|
||||
const dispatch = createEventDispatcher<{ change: undefined }>();
|
||||
const redirect_src_url = (src: string) =>
|
||||
src.replace('src="/file', `src="${root}file`);
|
||||
src.replace('src="/file', `src="${root}/file`);
|
||||
|
||||
$: _value = value
|
||||
? value.map(([user_msg, bot_msg]) => [
|
||||
|
46
ui/packages/client/README.md
Normal file
46
ui/packages/client/README.md
Normal file
@ -0,0 +1,46 @@
|
||||
# `@gradio/client`
|
||||
|
||||
A javascript client to call Gradio APIs.
|
||||
|
||||
**usage**
|
||||
|
||||
```ts
|
||||
import { client } from "@gradio/client";
|
||||
|
||||
const app = client();
|
||||
|
||||
const prediction = app.predict(endpoint, payload);
|
||||
|
||||
// listen for predictions
|
||||
prediction.on("data", (event: { data: Array<unknown>; type: "data" }) => {});
|
||||
|
||||
// listen for status updates
|
||||
prediction.on("status", (event: { data: Status; type: "data" }) => {});
|
||||
|
||||
interface Status {
|
||||
status: "pending" | "error" | "complete" | "generating";
|
||||
size: number;
|
||||
position?: number;
|
||||
eta?: number;
|
||||
message?: string;
|
||||
progress?: Array<{
|
||||
progress: number | null;
|
||||
index: number | null;
|
||||
length: number | null;
|
||||
unit: string | null;
|
||||
desc: string | null;
|
||||
}>;
|
||||
}
|
||||
|
||||
// stop listening
|
||||
prediction.off("data");
|
||||
|
||||
// cancel a prediction if it is a generator
|
||||
prediction.cancel();
|
||||
|
||||
// chainable
|
||||
const prediction_two = app
|
||||
.predict(endpoint, payload)
|
||||
.on("data", data_callback)
|
||||
.on("status", status_callback);
|
||||
```
|
10
ui/packages/client/package.json
Normal file
10
ui/packages/client/package.json
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"name": "@gradio/client",
|
||||
"version": "0.0.1",
|
||||
"description": "Gradio UI packages",
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"private": true
|
||||
}
|
533
ui/packages/client/src/client.ts
Normal file
533
ui/packages/client/src/client.ts
Normal file
@ -0,0 +1,533 @@
|
||||
import {
|
||||
process_endpoint,
|
||||
RE_SPACE_NAME,
|
||||
map_names_to_ids,
|
||||
discussions_enabled
|
||||
} from "./utils";
|
||||
|
||||
import type {
|
||||
EventType,
|
||||
EventListener,
|
||||
ListenerMap,
|
||||
Event,
|
||||
Config,
|
||||
Payload,
|
||||
PostResponse,
|
||||
UploadResponse,
|
||||
Status,
|
||||
SpaceStatus,
|
||||
SpaceStatusCallback
|
||||
} from "./types";
|
||||
|
||||
type event = <K extends EventType>(
|
||||
eventType: K,
|
||||
listener: EventListener<K>
|
||||
) => ReturnType<predict>;
|
||||
type predict = (endpoint: string, payload: Payload) => {};
|
||||
|
||||
type client_return = {
|
||||
predict: predict;
|
||||
config: Config;
|
||||
on: event;
|
||||
off: event;
|
||||
cancel: (endpoint: string, fn_index?: number) => void;
|
||||
};
|
||||
|
||||
const QUEUE_FULL_MSG = "This application is too busy. Keep trying!";
|
||||
const BROKEN_CONNECTION_MSG = "Connection errored out.";
|
||||
|
||||
export async function post_data(
|
||||
url: string,
|
||||
body: unknown
|
||||
): Promise<[PostResponse, number]> {
|
||||
try {
|
||||
var response = await fetch(url, {
|
||||
method: "POST",
|
||||
body: JSON.stringify(body),
|
||||
headers: { "Content-Type": "application/json" }
|
||||
});
|
||||
} catch (e) {
|
||||
return [{ error: BROKEN_CONNECTION_MSG }, 500];
|
||||
}
|
||||
const output: PostResponse = await response.json();
|
||||
return [output, response.status];
|
||||
}
|
||||
|
||||
export async function upload_files(
|
||||
root: string,
|
||||
files: Array<File>
|
||||
): Promise<UploadResponse> {
|
||||
const formData = new FormData();
|
||||
files.forEach((file) => {
|
||||
formData.append("files", file);
|
||||
});
|
||||
try {
|
||||
var response = await fetch(`${root}/upload`, {
|
||||
method: "POST",
|
||||
body: formData
|
||||
});
|
||||
} catch (e) {
|
||||
return { error: BROKEN_CONNECTION_MSG };
|
||||
}
|
||||
const output: UploadResponse["files"] = await response.json();
|
||||
return { files: output };
|
||||
}
|
||||
|
||||
export async function client(
|
||||
app_reference: string,
|
||||
space_status_callback?: SpaceStatusCallback
|
||||
): Promise<client_return> {
|
||||
return new Promise(async (res, rej) => {
|
||||
const return_obj = {
|
||||
predict,
|
||||
on,
|
||||
off,
|
||||
cancel
|
||||
};
|
||||
|
||||
const listener_map: ListenerMap<EventType> = {};
|
||||
const { ws_protocol, http_protocol, host, space_id } =
|
||||
await process_endpoint(app_reference);
|
||||
const session_hash = Math.random().toString(36).substring(2);
|
||||
const ws_map = new Map<number, WebSocket>();
|
||||
const last_status: Record<string, Status["status"]> = {};
|
||||
let config: Config;
|
||||
let api_map: Record<string, number> = {};
|
||||
|
||||
function config_success(_config: Config) {
|
||||
config = _config;
|
||||
api_map = map_names_to_ids(_config?.dependencies || []);
|
||||
return {
|
||||
config,
|
||||
...return_obj
|
||||
};
|
||||
}
|
||||
|
||||
function on<K extends EventType>(eventType: K, listener: EventListener<K>) {
|
||||
const narrowed_listener_map: ListenerMap<K> = listener_map;
|
||||
let listeners = narrowed_listener_map[eventType] || [];
|
||||
narrowed_listener_map[eventType] = listeners;
|
||||
listeners?.push(listener);
|
||||
|
||||
return { ...return_obj, config };
|
||||
}
|
||||
|
||||
function off<K extends EventType>(
|
||||
eventType: K,
|
||||
listener: EventListener<K>
|
||||
) {
|
||||
const narrowed_listener_map: ListenerMap<K> = listener_map;
|
||||
let listeners = narrowed_listener_map[eventType] || [];
|
||||
listeners = listeners?.filter((l) => l !== listener);
|
||||
narrowed_listener_map[eventType] = listeners;
|
||||
|
||||
return { ...return_obj, config };
|
||||
}
|
||||
|
||||
function cancel(endpoint: string, fn_index?: number) {
|
||||
const _index =
|
||||
typeof fn_index === "number" ? fn_index : api_map[endpoint];
|
||||
|
||||
fire_event({
|
||||
type: "status",
|
||||
endpoint,
|
||||
fn_index: _index,
|
||||
status: "complete",
|
||||
queue: false
|
||||
});
|
||||
|
||||
ws_map.get(_index)?.close();
|
||||
}
|
||||
|
||||
function fire_event<K extends EventType>(event: Event<K>) {
|
||||
const narrowed_listener_map: ListenerMap<K> = listener_map;
|
||||
let listeners = narrowed_listener_map[event.type] || [];
|
||||
listeners?.forEach((l) => l(event));
|
||||
}
|
||||
|
||||
async function handle_space_sucess(status: SpaceStatus) {
|
||||
if (space_status_callback) space_status_callback(status);
|
||||
if (status.status === "running")
|
||||
try {
|
||||
config = await resolve_config(`${http_protocol}//${host}`);
|
||||
res(config_success(config));
|
||||
} catch (e) {
|
||||
if (space_status_callback) {
|
||||
space_status_callback({
|
||||
status: "error",
|
||||
message: "Could not load this space.",
|
||||
load_status: "error",
|
||||
detail: "NOT_FOUND"
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
config = await resolve_config(`${http_protocol}//${host}`);
|
||||
res(config_success(config));
|
||||
} catch (e) {
|
||||
if (space_id) {
|
||||
check_space_status(
|
||||
space_id,
|
||||
RE_SPACE_NAME.test(space_id) ? "space_name" : "subdomain",
|
||||
handle_space_sucess
|
||||
);
|
||||
} else {
|
||||
if (space_status_callback)
|
||||
space_status_callback({
|
||||
status: "error",
|
||||
message: "Could not load this space.",
|
||||
load_status: "error",
|
||||
detail: "NOT_FOUND"
|
||||
});
|
||||
}
|
||||
}
|
||||
function make_predict(endpoint: string, payload: Payload) {
|
||||
return new Promise((res, rej) => {
|
||||
const trimmed_endpoint = endpoint.replace(/^\//, "");
|
||||
let fn_index =
|
||||
typeof payload.fn_index === "number"
|
||||
? payload.fn_index
|
||||
: api_map[trimmed_endpoint];
|
||||
|
||||
if (skip_queue(fn_index, config)) {
|
||||
fire_event({
|
||||
type: "status",
|
||||
endpoint,
|
||||
status: "pending",
|
||||
queue: false,
|
||||
fn_index
|
||||
});
|
||||
|
||||
post_data(
|
||||
`${http_protocol}//${host}/run${
|
||||
endpoint.startsWith("/") ? endpoint : `/${endpoint}`
|
||||
}`,
|
||||
{
|
||||
...payload,
|
||||
session_hash
|
||||
}
|
||||
)
|
||||
.then(([output, status_code]) => {
|
||||
if (status_code == 200) {
|
||||
fire_event({
|
||||
type: "status",
|
||||
endpoint,
|
||||
fn_index,
|
||||
status: "complete",
|
||||
eta: output.average_duration,
|
||||
queue: false
|
||||
});
|
||||
|
||||
fire_event({
|
||||
type: "data",
|
||||
endpoint,
|
||||
fn_index,
|
||||
data: output.data
|
||||
});
|
||||
} else {
|
||||
fire_event({
|
||||
type: "status",
|
||||
status: "error",
|
||||
endpoint,
|
||||
fn_index,
|
||||
message: output.error,
|
||||
queue: false
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch((e) => {
|
||||
fire_event({
|
||||
type: "status",
|
||||
status: "error",
|
||||
message: e.message,
|
||||
endpoint,
|
||||
fn_index,
|
||||
queue: false
|
||||
});
|
||||
throw new Error(e.message);
|
||||
});
|
||||
} else {
|
||||
fire_event({
|
||||
type: "status",
|
||||
status: "pending",
|
||||
queue: true,
|
||||
endpoint,
|
||||
fn_index
|
||||
});
|
||||
|
||||
const ws_endpoint = `${ws_protocol}://${host}/queue/join`;
|
||||
|
||||
const websocket = new WebSocket(ws_endpoint);
|
||||
|
||||
ws_map.set(fn_index, websocket);
|
||||
websocket.onclose = (evt) => {
|
||||
if (!evt.wasClean) {
|
||||
fire_event({
|
||||
type: "status",
|
||||
status: "error",
|
||||
message: BROKEN_CONNECTION_MSG,
|
||||
queue: true,
|
||||
endpoint,
|
||||
fn_index
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
websocket.onmessage = function (event) {
|
||||
const _data = JSON.parse(event.data);
|
||||
const { type, status, data } = handle_message(
|
||||
_data,
|
||||
last_status[fn_index]
|
||||
);
|
||||
|
||||
if (type === "update" && status) {
|
||||
// call 'status' listeners
|
||||
fire_event({ type: "status", endpoint, fn_index, ...status });
|
||||
if (status.status === "error") {
|
||||
websocket.close();
|
||||
rej(status);
|
||||
}
|
||||
} else if (type === "hash") {
|
||||
websocket.send(JSON.stringify({ fn_index, session_hash }));
|
||||
return;
|
||||
} else if (type === "data") {
|
||||
websocket.send(JSON.stringify({ ...payload, session_hash }));
|
||||
} else if (type === "complete") {
|
||||
fire_event({
|
||||
type: "status",
|
||||
...status,
|
||||
status: status?.status!,
|
||||
queue: true,
|
||||
endpoint,
|
||||
fn_index
|
||||
});
|
||||
websocket.close();
|
||||
} else if (type === "generating") {
|
||||
fire_event({
|
||||
type: "status",
|
||||
...status,
|
||||
status: status?.status!,
|
||||
queue: true,
|
||||
endpoint,
|
||||
fn_index
|
||||
});
|
||||
}
|
||||
if (data) {
|
||||
fire_event({
|
||||
type: "data",
|
||||
data: data.data,
|
||||
endpoint,
|
||||
fn_index
|
||||
});
|
||||
res({ data: data.data });
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Run a prediction.
|
||||
* @param endpoint - The prediction endpoint to use.
|
||||
* @param status_callback - A function that is called with the current status of the prediction immediately and every time it updates.
|
||||
* @return Returns the data for the prediction or an error message.
|
||||
*/
|
||||
function predict(endpoint: string, payload: Payload) {
|
||||
return make_predict(endpoint, payload);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function skip_queue(id: number, config: Config) {
|
||||
return (
|
||||
!(config?.dependencies?.[id].queue === null
|
||||
? config.enable_queue
|
||||
: config?.dependencies?.[id].queue) || false
|
||||
);
|
||||
}
|
||||
|
||||
async function resolve_config(endpoint?: string): Promise<Config> {
|
||||
if (window.gradio_config && location.origin !== "http://localhost:9876") {
|
||||
return { ...window.gradio_config, root: endpoint };
|
||||
} else if (endpoint) {
|
||||
let response = await fetch(`${endpoint}/config`);
|
||||
|
||||
if (response.status === 200) {
|
||||
const config = await response.json();
|
||||
config.root = endpoint;
|
||||
return config;
|
||||
} else {
|
||||
throw new Error("Could not get config.");
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error("No config or app endpoint found");
|
||||
}
|
||||
|
||||
async function check_space_status(
|
||||
id: string,
|
||||
type: "subdomain" | "space_name",
|
||||
space_status_callback: SpaceStatusCallback
|
||||
) {
|
||||
let endpoint =
|
||||
type === "subdomain"
|
||||
? `https://huggingface.co/api/spaces/by-subdomain/${id}`
|
||||
: `https://huggingface.co/api/spaces/${id}`;
|
||||
let response;
|
||||
let _status;
|
||||
try {
|
||||
response = await fetch(endpoint);
|
||||
_status = response.status;
|
||||
if (_status !== 200) {
|
||||
throw new Error();
|
||||
}
|
||||
response = await response.json();
|
||||
} catch (e) {
|
||||
space_status_callback({
|
||||
status: "error",
|
||||
load_status: "error",
|
||||
message: "Could not get space status",
|
||||
detail: "NOT_FOUND"
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (!response || _status !== 200) return;
|
||||
const {
|
||||
runtime: { stage },
|
||||
id: space_name
|
||||
} = response;
|
||||
|
||||
switch (stage) {
|
||||
case "STOPPED":
|
||||
case "SLEEPING":
|
||||
space_status_callback({
|
||||
status: "sleeping",
|
||||
load_status: "pending",
|
||||
message: "Space is asleep. Waking it up...",
|
||||
detail: stage
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
check_space_status(id, type, space_status_callback);
|
||||
}, 1000);
|
||||
break;
|
||||
// poll for status
|
||||
case "RUNNING":
|
||||
case "RUNNING_BUILDING":
|
||||
space_status_callback({
|
||||
status: "running",
|
||||
load_status: "complete",
|
||||
message: "",
|
||||
detail: stage
|
||||
});
|
||||
// load_config(source);
|
||||
// launch
|
||||
break;
|
||||
case "BUILDING":
|
||||
space_status_callback({
|
||||
status: "building",
|
||||
load_status: "pending",
|
||||
message: "Space is building...",
|
||||
detail: stage
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
check_space_status(id, type, space_status_callback);
|
||||
}, 1000);
|
||||
break;
|
||||
default:
|
||||
space_status_callback({
|
||||
status: "space_error",
|
||||
load_status: "error",
|
||||
message: "This space is experiencing an issue.",
|
||||
detail: stage,
|
||||
discussions_enabled: await discussions_enabled(space_name)
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function handle_message(
|
||||
data: any,
|
||||
last_status: Status["status"]
|
||||
): {
|
||||
type: "hash" | "data" | "update" | "complete" | "generating" | "none";
|
||||
data?: any;
|
||||
status?: Status;
|
||||
} {
|
||||
const queue = true;
|
||||
switch (data.msg) {
|
||||
case "send_data":
|
||||
return { type: "data" };
|
||||
case "send_hash":
|
||||
return { type: "hash" };
|
||||
case "queue_full":
|
||||
return {
|
||||
type: "update",
|
||||
status: {
|
||||
queue,
|
||||
message: QUEUE_FULL_MSG,
|
||||
status: "error"
|
||||
}
|
||||
};
|
||||
case "estimation":
|
||||
return {
|
||||
type: "update",
|
||||
status: {
|
||||
queue,
|
||||
status: last_status || "pending",
|
||||
size: data.queue_size,
|
||||
position: data.rank,
|
||||
eta: data.rank_eta
|
||||
}
|
||||
};
|
||||
case "progress":
|
||||
return {
|
||||
type: "update",
|
||||
status: {
|
||||
queue,
|
||||
status: "pending",
|
||||
progress: data.progress_data
|
||||
}
|
||||
};
|
||||
case "process_generating":
|
||||
return {
|
||||
type: "generating",
|
||||
status: {
|
||||
queue,
|
||||
message: !data.success ? data.output.error : null,
|
||||
status: data.success ? "generating" : "error",
|
||||
progress: data.progress_data,
|
||||
eta: data.average_duration
|
||||
},
|
||||
data: data.success ? data.output : null
|
||||
};
|
||||
case "process_completed":
|
||||
return {
|
||||
type: "complete",
|
||||
status: {
|
||||
queue,
|
||||
message: !data.success ? data.output.error : undefined,
|
||||
status: data.success ? "complete" : "error",
|
||||
progress: data.progress_data,
|
||||
eta: data.output.average_duration
|
||||
},
|
||||
data: data.success ? data.output : null
|
||||
};
|
||||
case "process_starts":
|
||||
return {
|
||||
type: "update",
|
||||
status: {
|
||||
queue,
|
||||
status: "pending",
|
||||
size: data.rank,
|
||||
position: 0
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return { type: "none", status: { status: "error", queue } };
|
||||
}
|
2
ui/packages/client/src/index.ts
Normal file
2
ui/packages/client/src/index.ts
Normal file
@ -0,0 +1,2 @@
|
||||
export { client, post_data, upload_files } from "./client";
|
||||
export type { SpaceStatus } from "./types";
|
86
ui/packages/client/src/types.ts
Normal file
86
ui/packages/client/src/types.ts
Normal file
@ -0,0 +1,86 @@
|
||||
export interface Config {
|
||||
auth_required: boolean | undefined;
|
||||
auth_message: string;
|
||||
components: any[];
|
||||
css: string | null;
|
||||
dependencies: any[];
|
||||
dev_mode: boolean;
|
||||
enable_queue: boolean;
|
||||
layout: any;
|
||||
mode: "blocks" | "interface";
|
||||
root: string;
|
||||
theme: string;
|
||||
title: string;
|
||||
version: string;
|
||||
is_space: boolean;
|
||||
is_colab: boolean;
|
||||
show_api: boolean;
|
||||
}
|
||||
|
||||
export interface Payload {
|
||||
data: Array<unknown>;
|
||||
fn_index: number;
|
||||
}
|
||||
|
||||
export interface PostResponse {
|
||||
error?: string;
|
||||
[x: string]: any;
|
||||
}
|
||||
export interface UploadResponse {
|
||||
error?: string;
|
||||
files?: Array<string>;
|
||||
}
|
||||
|
||||
export interface Status {
|
||||
queue: boolean;
|
||||
status: "pending" | "error" | "complete" | "generating";
|
||||
size?: number;
|
||||
position?: number;
|
||||
eta?: number;
|
||||
message?: string;
|
||||
progress?: Array<{
|
||||
progress: number | null;
|
||||
index: number | null;
|
||||
length: number | null;
|
||||
unit: string | null;
|
||||
desc: string | null;
|
||||
}>;
|
||||
}
|
||||
|
||||
export interface SpaceStatusNormal {
|
||||
status: "sleeping" | "running" | "building" | "error" | "stopped";
|
||||
detail:
|
||||
| "SLEEPING"
|
||||
| "RUNNING"
|
||||
| "RUNNING_BUILDING"
|
||||
| "BUILDING"
|
||||
| "NOT_FOUND";
|
||||
load_status: "pending" | "error" | "complete" | "generating";
|
||||
message: string;
|
||||
}
|
||||
export interface SpaceStatusError {
|
||||
status: "space_error";
|
||||
detail: "NO_APP_FILE" | "CONFIG_ERROR" | "BUILD_ERROR" | "RUNTIME_ERROR";
|
||||
load_status: "error";
|
||||
message: string;
|
||||
discussions_enabled: boolean;
|
||||
}
|
||||
export type SpaceStatus = SpaceStatusNormal | SpaceStatusError;
|
||||
|
||||
export type status_callback_function = (a: Status) => void;
|
||||
export type SpaceStatusCallback = (a: SpaceStatus) => void;
|
||||
|
||||
export type EventType = "data" | "status";
|
||||
|
||||
export interface EventMap {
|
||||
data: Record<string, any>;
|
||||
status: Status;
|
||||
}
|
||||
|
||||
export type Event<K extends EventType> = {
|
||||
[P in K]: EventMap[P] & { type: P; endpoint: string; fn_index: number };
|
||||
}[K];
|
||||
export type EventListener<K extends EventType> = (event: Event<K>) => void;
|
||||
export type ListenerMap<K extends EventType> = {
|
||||
[P in K]?: EventListener<K>[];
|
||||
};
|
101
ui/packages/client/src/utils.ts
Normal file
101
ui/packages/client/src/utils.ts
Normal file
@ -0,0 +1,101 @@
|
||||
import type { Config } from "./types";
|
||||
|
||||
export function determine_protocol(endpoint: string): {
|
||||
ws_protocol: "ws" | "wss";
|
||||
http_protocol: "http:" | "https:";
|
||||
host: string;
|
||||
} {
|
||||
if (endpoint.startsWith("http")) {
|
||||
const { protocol, host } = new URL(endpoint);
|
||||
|
||||
if (host.endsWith("hf.space")) {
|
||||
return {
|
||||
ws_protocol: "wss",
|
||||
host: host,
|
||||
http_protocol: protocol as "http:" | "https:"
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
ws_protocol: protocol === "https:" ? "wss" : "ws",
|
||||
http_protocol: protocol as "http:" | "https:",
|
||||
host
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// default to secure if no protocol is provided
|
||||
return {
|
||||
ws_protocol: "wss",
|
||||
http_protocol: "https:",
|
||||
host: endpoint
|
||||
};
|
||||
}
|
||||
|
||||
export const RE_SPACE_NAME = /^[^\/]*\/[^\/]*$/;
|
||||
export const RE_SPACE_DOMAIN = /.*hf\.space\/{0,1}$/;
|
||||
export async function process_endpoint(app_reference: string): Promise<{
|
||||
space_id: string | false;
|
||||
host: string;
|
||||
ws_protocol: "ws" | "wss";
|
||||
http_protocol: "http:" | "https:";
|
||||
}> {
|
||||
const _app_reference = app_reference.trim();
|
||||
|
||||
if (RE_SPACE_NAME.test(_app_reference)) {
|
||||
const _host = (
|
||||
await (
|
||||
await fetch(`https://huggingface.co/api/spaces/${_app_reference}/host`)
|
||||
).json()
|
||||
).host;
|
||||
return {
|
||||
space_id: app_reference,
|
||||
...determine_protocol(_host)
|
||||
};
|
||||
}
|
||||
|
||||
if (RE_SPACE_DOMAIN.test(_app_reference)) {
|
||||
const { ws_protocol, http_protocol, host } =
|
||||
determine_protocol(_app_reference);
|
||||
|
||||
return {
|
||||
space_id: host.replace(".hf.space", ""),
|
||||
ws_protocol,
|
||||
http_protocol,
|
||||
host
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
space_id: false,
|
||||
...determine_protocol(_app_reference)
|
||||
};
|
||||
}
|
||||
|
||||
export function map_names_to_ids(fns: Config["dependencies"]) {
|
||||
let apis: Record<string, number> = {};
|
||||
|
||||
fns.forEach(({ api_name }, i) => {
|
||||
if (api_name) apis[api_name] = i;
|
||||
});
|
||||
|
||||
return apis;
|
||||
}
|
||||
|
||||
const RE_DISABLED_DISCUSSION =
|
||||
/^(?=[^]*\b[dD]iscussions{0,1}\b)(?=[^]*\b[dD]isabled\b)[^]*$/;
|
||||
export async function discussions_enabled(space_id: string) {
|
||||
try {
|
||||
const r = await fetch(
|
||||
`https://huggingface.co/api/spaces/${space_id}/discussions`,
|
||||
{
|
||||
method: "HEAD"
|
||||
}
|
||||
);
|
||||
const error = r.headers.get("x-error-message");
|
||||
|
||||
if (error && RE_DISABLED_DISCUSSION.test(error)) return false;
|
||||
else return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
@ -28,9 +28,9 @@ export function normalise_file(
|
||||
}
|
||||
} else if (file.is_file) {
|
||||
if (root_url == null) {
|
||||
file.data = root + "file=" + file.name;
|
||||
file.data = root + "/file=" + file.name;
|
||||
} else {
|
||||
file.data = "proxy=" + root_url + "file=" + file.name;
|
||||
file.data = "/proxy=" + root_url + "/file=" + file.name;
|
||||
}
|
||||
}
|
||||
return file;
|
||||
|
@ -4,12 +4,5 @@ export default {
|
||||
trace: "retain-on-failure"
|
||||
},
|
||||
globalSetup: "./playwright-setup.js",
|
||||
workers: 1,
|
||||
webServer: [
|
||||
{
|
||||
command: "GRADIO_SERVER_PORT=7888 python ../demo/slider_release/run.py",
|
||||
port: 7888,
|
||||
timeout: 120 * 1000
|
||||
}
|
||||
]
|
||||
workers: 1
|
||||
};
|
||||
|
98
ui/pnpm-lock.yaml
generated
98
ui/pnpm-lock.yaml
generated
@ -1,4 +1,4 @@
|
||||
lockfileVersion: 5.3
|
||||
lockfileVersion: 5.4
|
||||
|
||||
importers:
|
||||
|
||||
@ -48,7 +48,7 @@ importers:
|
||||
'@tailwindcss/forms': 0.5.0_tailwindcss@3.1.6
|
||||
'@testing-library/dom': 8.11.3
|
||||
'@testing-library/svelte': 3.1.0_svelte@3.49.0
|
||||
'@testing-library/user-event': 13.5.0_@testing-library+dom@8.11.3
|
||||
'@testing-library/user-event': 13.5.0_gzufz4q333be4gqfrvipwvqt6a
|
||||
autoprefixer: 10.4.4_postcss@8.4.6
|
||||
babylonjs: 5.18.0
|
||||
babylonjs-loaders: 5.18.0
|
||||
@ -65,15 +65,15 @@ importers:
|
||||
postcss-nested: 5.0.6_postcss@8.4.6
|
||||
postcss-prefix-selector: 1.16.0_postcss@8.4.6
|
||||
prettier: 2.6.2
|
||||
prettier-plugin-css-order: 1.3.0_postcss@8.4.6+prettier@2.6.2
|
||||
prettier-plugin-svelte: 2.7.0_prettier@2.6.2+svelte@3.49.0
|
||||
prettier-plugin-css-order: 1.3.0_ob5okuz2s5mlecytbeo2erc43a
|
||||
prettier-plugin-svelte: 2.7.0_3cyj5wbackxvw67rnaarcmbw7y
|
||||
sirv: 2.0.2
|
||||
sirv-cli: 2.0.2
|
||||
svelte: 3.49.0
|
||||
svelte-check: 2.8.0_postcss@8.4.6+svelte@3.49.0
|
||||
svelte-check: 2.8.0_mgmdnb6x5rpawk37gozc2sbtta
|
||||
svelte-i18n: 3.3.13_svelte@3.49.0
|
||||
svelte-preprocess: 4.10.6_62d50a01257de5eec5be08cad9d3ed66
|
||||
tailwindcss: 3.1.6
|
||||
svelte-preprocess: 4.10.6_mlkquajfpxs65rn6bdfntu7nmy
|
||||
tailwindcss: 3.1.6_postcss@8.4.6
|
||||
tinyspy: 0.3.0
|
||||
typescript: 4.7.4
|
||||
vite: 2.9.9
|
||||
@ -98,6 +98,7 @@ importers:
|
||||
'@gradio/button': workspace:^0.0.1
|
||||
'@gradio/chart': workspace:^0.0.1
|
||||
'@gradio/chatbot': workspace:^0.0.1
|
||||
'@gradio/client': workspace:^0.0.1
|
||||
'@gradio/file': workspace:^0.0.1
|
||||
'@gradio/form': workspace:^0.0.1
|
||||
'@gradio/gallery': workspace:^0.0.1
|
||||
@ -130,6 +131,7 @@ importers:
|
||||
'@gradio/button': link:../button
|
||||
'@gradio/chart': link:../chart
|
||||
'@gradio/chatbot': link:../chatbot
|
||||
'@gradio/client': link:../client
|
||||
'@gradio/file': link:../file
|
||||
'@gradio/form': link:../form
|
||||
'@gradio/gallery': link:../gallery
|
||||
@ -219,6 +221,9 @@ importers:
|
||||
'@gradio/theme': link:../theme
|
||||
'@gradio/utils': link:../utils
|
||||
|
||||
packages/client:
|
||||
specifiers: {}
|
||||
|
||||
packages/file:
|
||||
specifiers:
|
||||
'@gradio/atoms': workspace:^0.0.1
|
||||
@ -334,7 +339,7 @@ importers:
|
||||
'@gradio/utils': link:../utils
|
||||
'@rollup/plugin-json': 5.0.2
|
||||
plotly.js-dist-min: 2.11.1
|
||||
svelte-vega: 1.2.0_vega-lite@5.6.0+vega@5.22.1
|
||||
svelte-vega: 1.2.0_36sthfwhgi34qytpvkzggbhnle
|
||||
vega: 5.22.1
|
||||
vega-lite: 5.6.0_vega@5.22.1
|
||||
|
||||
@ -466,14 +471,14 @@ importers:
|
||||
'@gradio/video': link:../video
|
||||
svelte: 3.49.0
|
||||
devDependencies:
|
||||
'@sveltejs/adapter-auto': 1.0.0-next.91_@sveltejs+kit@1.0.0-next.318
|
||||
'@sveltejs/adapter-auto': 1.0.0-next.91_b2bjiolq6much32vueqoio7eoy
|
||||
'@sveltejs/kit': 1.0.0-next.318_svelte@3.49.0
|
||||
autoprefixer: 10.4.2_postcss@8.4.6
|
||||
postcss: 8.4.6
|
||||
postcss-load-config: 3.1.1
|
||||
svelte-check: 2.4.1_736abba5ed1eb6f8ecf70b1d49ead14b
|
||||
svelte-preprocess: 4.10.2_d50790bb30dd88cc44babe7efa52bace
|
||||
tailwindcss: 3.1.6
|
||||
svelte-check: 2.4.1_onvlxjpnd23pr3hxbmout2wrjm
|
||||
svelte-preprocess: 4.10.2_2udzbozq3wemyrf2xz7puuv2zy
|
||||
tailwindcss: 3.1.6_postcss@8.4.6
|
||||
tslib: 2.3.1
|
||||
typescript: 4.5.5
|
||||
|
||||
@ -675,7 +680,7 @@ packages:
|
||||
picomatch: 2.3.1
|
||||
dev: false
|
||||
|
||||
/@sveltejs/adapter-auto/1.0.0-next.91_@sveltejs+kit@1.0.0-next.318:
|
||||
/@sveltejs/adapter-auto/1.0.0-next.91_b2bjiolq6much32vueqoio7eoy:
|
||||
resolution: {integrity: sha512-U57tQdzTfFINim8tzZSARC9ztWPzwOoHwNOpGdb2o6XrD0mEQwU9DsII7dBblvzg+xCnmd0pw7PDtXz5c5t96w==}
|
||||
peerDependencies:
|
||||
'@sveltejs/kit': ^1.0.0-next.587
|
||||
@ -731,7 +736,7 @@ packages:
|
||||
tailwindcss: '>=3.0.0 || >= 3.0.0-alpha.1'
|
||||
dependencies:
|
||||
mini-svg-data-uri: 1.4.4
|
||||
tailwindcss: 3.1.6
|
||||
tailwindcss: 3.1.6_postcss@8.4.6
|
||||
dev: false
|
||||
|
||||
/@testing-library/dom/7.31.2:
|
||||
@ -772,7 +777,7 @@ packages:
|
||||
svelte: 3.49.0
|
||||
dev: false
|
||||
|
||||
/@testing-library/user-event/13.5.0_@testing-library+dom@8.11.3:
|
||||
/@testing-library/user-event/13.5.0_gzufz4q333be4gqfrvipwvqt6a:
|
||||
resolution: {integrity: sha512-5Kwtbo3Y/NowpkbRuSepbyMFkZmHgD+vPzYB/RJ4oxt5Gj/avFFBYjhw27cqSVPVw/3a67NK1PbiIr9k4Gwmdg==}
|
||||
engines: {node: '>=10', npm: '>=6'}
|
||||
peerDependencies:
|
||||
@ -2977,25 +2982,25 @@ packages:
|
||||
postcss-value-parser: 4.2.0
|
||||
dev: false
|
||||
|
||||
/postcss-import/14.1.0_postcss@8.4.21:
|
||||
/postcss-import/14.1.0_postcss@8.4.6:
|
||||
resolution: {integrity: sha512-flwI+Vgm4SElObFVPpTIT7SU7R3qk2L7PyduMcokiaVKuWv9d/U+Gm/QAd8NDLuykTWTkcrjOeD2Pp1rMeBTGw==}
|
||||
engines: {node: '>=10.0.0'}
|
||||
peerDependencies:
|
||||
postcss: ^8.0.0
|
||||
dependencies:
|
||||
postcss: 8.4.21
|
||||
postcss: 8.4.6
|
||||
postcss-value-parser: 4.2.0
|
||||
read-cache: 1.0.0
|
||||
resolve: 1.22.1
|
||||
|
||||
/postcss-js/4.0.0_postcss@8.4.21:
|
||||
/postcss-js/4.0.0_postcss@8.4.6:
|
||||
resolution: {integrity: sha512-77QESFBwgX4irogGVPgQ5s07vLvFqWr228qZY+w6lW599cRlK/HmnlivnnVUxkjHnCu4J16PDMHcH+e+2HbvTQ==}
|
||||
engines: {node: ^12 || ^14 || >= 16}
|
||||
peerDependencies:
|
||||
postcss: ^8.3.3
|
||||
dependencies:
|
||||
camelcase-css: 2.0.1
|
||||
postcss: 8.4.21
|
||||
postcss: 8.4.6
|
||||
|
||||
/postcss-less/6.0.0_postcss@8.4.6:
|
||||
resolution: {integrity: sha512-FPX16mQLyEjLzEuuJtxA8X3ejDLNGGEG503d2YGZR5Ask1SpDN8KmZUMpzCvyalWRywAn1n1VOA5dcqfCLo5rg==}
|
||||
@ -3019,7 +3024,7 @@ packages:
|
||||
yaml: 1.10.2
|
||||
dev: true
|
||||
|
||||
/postcss-load-config/3.1.4_postcss@8.4.21:
|
||||
/postcss-load-config/3.1.4_postcss@8.4.6:
|
||||
resolution: {integrity: sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==}
|
||||
engines: {node: '>= 10'}
|
||||
peerDependencies:
|
||||
@ -3032,18 +3037,9 @@ packages:
|
||||
optional: true
|
||||
dependencies:
|
||||
lilconfig: 2.0.6
|
||||
postcss: 8.4.21
|
||||
postcss: 8.4.6
|
||||
yaml: 1.10.2
|
||||
|
||||
/postcss-nested/5.0.6_postcss@8.4.21:
|
||||
resolution: {integrity: sha512-rKqm2Fk0KbA8Vt3AdGN0FB9OBOMDVajMG6ZCf/GoHgdxUJ4sBFp0A/uMIRm+MJUdo33YXEtjqIz8u7DAp8B7DA==}
|
||||
engines: {node: '>=12.0'}
|
||||
peerDependencies:
|
||||
postcss: ^8.2.14
|
||||
dependencies:
|
||||
postcss: 8.4.21
|
||||
postcss-selector-parser: 6.0.9
|
||||
|
||||
/postcss-nested/5.0.6_postcss@8.4.6:
|
||||
resolution: {integrity: sha512-rKqm2Fk0KbA8Vt3AdGN0FB9OBOMDVajMG6ZCf/GoHgdxUJ4sBFp0A/uMIRm+MJUdo33YXEtjqIz8u7DAp8B7DA==}
|
||||
engines: {node: '>=12.0'}
|
||||
@ -3052,7 +3048,6 @@ packages:
|
||||
dependencies:
|
||||
postcss: 8.4.6
|
||||
postcss-selector-parser: 6.0.9
|
||||
dev: false
|
||||
|
||||
/postcss-prefix-selector/1.16.0_postcss@8.4.21:
|
||||
resolution: {integrity: sha512-rdVMIi7Q4B0XbXqNUEI+Z4E+pueiu/CS5E6vRCQommzdQ/sgsS4dK42U7GX8oJR+TJOtT+Qv3GkNo6iijUMp3Q==}
|
||||
@ -3111,6 +3106,7 @@ packages:
|
||||
nanoid: 3.3.4
|
||||
picocolors: 1.0.0
|
||||
source-map-js: 1.0.2
|
||||
dev: false
|
||||
|
||||
/postcss/8.4.6:
|
||||
resolution: {integrity: sha512-OovjwIzs9Te46vlEx7+uXB0PLijpwjXGKXjVGGPIGubGpq7uh5Xgf6D6FiJ/SzJMBosHDp6a2hiXOS97iBXcaA==}
|
||||
@ -3120,7 +3116,7 @@ packages:
|
||||
picocolors: 1.0.0
|
||||
source-map-js: 1.0.2
|
||||
|
||||
/prettier-plugin-css-order/1.3.0_postcss@8.4.6+prettier@2.6.2:
|
||||
/prettier-plugin-css-order/1.3.0_ob5okuz2s5mlecytbeo2erc43a:
|
||||
resolution: {integrity: sha512-wOS4qlbUARCoiiuOG0TiB/j751soC3+gUnMMva5HVWKvHJdLNYqh+jXK3MvvixR6xkJVPxHSF7rIIhkHIuHTFg==}
|
||||
engines: {node: '>=14'}
|
||||
peerDependencies:
|
||||
@ -3135,7 +3131,7 @@ packages:
|
||||
- postcss
|
||||
dev: false
|
||||
|
||||
/prettier-plugin-svelte/2.7.0_prettier@2.6.2+svelte@3.49.0:
|
||||
/prettier-plugin-svelte/2.7.0_3cyj5wbackxvw67rnaarcmbw7y:
|
||||
resolution: {integrity: sha512-fQhhZICprZot2IqEyoiUYLTRdumULGRvw0o4dzl5jt0jfzVWdGqeYW27QTWAeXhoupEZJULmNoH3ueJwUWFLIA==}
|
||||
peerDependencies:
|
||||
prettier: ^1.16.4 || ^2.0.0
|
||||
@ -3593,7 +3589,7 @@ packages:
|
||||
resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
/svelte-check/2.4.1_736abba5ed1eb6f8ecf70b1d49ead14b:
|
||||
/svelte-check/2.4.1_onvlxjpnd23pr3hxbmout2wrjm:
|
||||
resolution: {integrity: sha512-xhf3ShP5rnRwBokrgTBJ/0cO9QIc1DAVu1NWNRTfCDsDBNjGmkS3HgitgUadRuoMKj1+irZR/yHJ+Uqobnkbrw==}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
@ -3607,7 +3603,7 @@ packages:
|
||||
sade: 1.8.1
|
||||
source-map: 0.7.3
|
||||
svelte: 3.49.0
|
||||
svelte-preprocess: 4.10.2_d50790bb30dd88cc44babe7efa52bace
|
||||
svelte-preprocess: 4.10.2_2udzbozq3wemyrf2xz7puuv2zy
|
||||
typescript: 4.5.5
|
||||
transitivePeerDependencies:
|
||||
- '@babel/core'
|
||||
@ -3622,7 +3618,7 @@ packages:
|
||||
- sugarss
|
||||
dev: true
|
||||
|
||||
/svelte-check/2.8.0_postcss@8.4.6+svelte@3.49.0:
|
||||
/svelte-check/2.8.0_mgmdnb6x5rpawk37gozc2sbtta:
|
||||
resolution: {integrity: sha512-HRL66BxffMAZusqe5I5k26mRWQ+BobGd9Rxm3onh7ZVu0nTk8YTKJ9vu3LVPjUGLU9IX7zS+jmwPVhJYdXJ8vg==}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
@ -3635,7 +3631,7 @@ packages:
|
||||
picocolors: 1.0.0
|
||||
sade: 1.8.1
|
||||
svelte: 3.49.0
|
||||
svelte-preprocess: 4.10.6_62d50a01257de5eec5be08cad9d3ed66
|
||||
svelte-preprocess: 4.10.6_mlkquajfpxs65rn6bdfntu7nmy
|
||||
typescript: 4.7.4
|
||||
transitivePeerDependencies:
|
||||
- '@babel/core'
|
||||
@ -3673,7 +3669,7 @@ packages:
|
||||
tiny-glob: 0.2.9
|
||||
dev: false
|
||||
|
||||
/svelte-preprocess/4.10.2_d50790bb30dd88cc44babe7efa52bace:
|
||||
/svelte-preprocess/4.10.2_2udzbozq3wemyrf2xz7puuv2zy:
|
||||
resolution: {integrity: sha512-aPpkCreSo8EL/y8kJSa1trhiX0oyAtTjlNNM7BNjRAsMJ8Yy2LtqHt0zyd4pQPXt+D4PzbO3qTjjio3kwOxDlA==}
|
||||
engines: {node: '>= 9.11.2'}
|
||||
requiresBuild: true
|
||||
@ -3726,7 +3722,7 @@ packages:
|
||||
typescript: 4.5.5
|
||||
dev: true
|
||||
|
||||
/svelte-preprocess/4.10.6_62d50a01257de5eec5be08cad9d3ed66:
|
||||
/svelte-preprocess/4.10.6_mlkquajfpxs65rn6bdfntu7nmy:
|
||||
resolution: {integrity: sha512-I2SV1w/AveMvgIQlUF/ZOO3PYVnhxfcpNyGt8pxpUVhPfyfL/CZBkkw/KPfuFix5FJ9TnnNYMhACK3DtSaYVVQ==}
|
||||
engines: {node: '>= 9.11.2'}
|
||||
requiresBuild: true
|
||||
@ -3782,7 +3778,7 @@ packages:
|
||||
resolution: {integrity: sha512-VTWHOdwDyWbndGZnI0PQJY9DO7hgQlNubtCcCL6Wlypv5dU4vEsc4A1sX9TWMuvebEe4332SgsQQHzOdZ+guhQ==}
|
||||
dev: false
|
||||
|
||||
/svelte-vega/1.2.0_vega-lite@5.6.0+vega@5.22.1:
|
||||
/svelte-vega/1.2.0_36sthfwhgi34qytpvkzggbhnle:
|
||||
resolution: {integrity: sha512-MsDdO+l7o/d9d4mVkh8MBDhqZvJ45lpuprBaTj0V/ZilIG902QERHFQlam3ZFcR9C9OIKSpmPqINssWNPkDdcA==}
|
||||
peerDependencies:
|
||||
vega: '*'
|
||||
@ -3790,7 +3786,7 @@ packages:
|
||||
dependencies:
|
||||
fast-deep-equal: 3.1.3
|
||||
vega: 5.22.1
|
||||
vega-embed: 6.21.0_vega-lite@5.6.0+vega@5.22.1
|
||||
vega-embed: 6.21.0_36sthfwhgi34qytpvkzggbhnle
|
||||
vega-lite: 5.6.0_vega@5.22.1
|
||||
dev: false
|
||||
|
||||
@ -3817,10 +3813,12 @@ packages:
|
||||
resolution: {integrity: sha512-hIdwt/c/e1ONnr2RJmfBxEAj/J6KQQWKdToF3Qw8ZNRsTNNteGkOe63rQy9I7J5UNlr8Yl0wkzIr9wgLY94x0Q==}
|
||||
dev: false
|
||||
|
||||
/tailwindcss/3.1.6:
|
||||
/tailwindcss/3.1.6_postcss@8.4.6:
|
||||
resolution: {integrity: sha512-7skAOY56erZAFQssT1xkpk+kWt2NrO45kORlxFPXUt3CiGsVPhH1smuH5XoDH6sGPXLyBv+zgCKA2HWBsgCytg==}
|
||||
engines: {node: '>=12.13.0'}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
postcss: ^8.0.9
|
||||
dependencies:
|
||||
arg: 5.0.2
|
||||
chokidar: 3.5.3
|
||||
@ -3835,11 +3833,11 @@ packages:
|
||||
normalize-path: 3.0.0
|
||||
object-hash: 3.0.0
|
||||
picocolors: 1.0.0
|
||||
postcss: 8.4.21
|
||||
postcss-import: 14.1.0_postcss@8.4.21
|
||||
postcss-js: 4.0.0_postcss@8.4.21
|
||||
postcss-load-config: 3.1.4_postcss@8.4.21
|
||||
postcss-nested: 5.0.6_postcss@8.4.21
|
||||
postcss: 8.4.6
|
||||
postcss-import: 14.1.0_postcss@8.4.6
|
||||
postcss-js: 4.0.0_postcss@8.4.6
|
||||
postcss-load-config: 3.1.4_postcss@8.4.6
|
||||
postcss-nested: 5.0.6_postcss@8.4.6
|
||||
postcss-selector-parser: 6.0.10
|
||||
postcss-value-parser: 4.2.0
|
||||
quick-lru: 5.1.1
|
||||
@ -4022,7 +4020,7 @@ packages:
|
||||
- encoding
|
||||
dev: false
|
||||
|
||||
/vega-embed/6.21.0_vega-lite@5.6.0+vega@5.22.1:
|
||||
/vega-embed/6.21.0_36sthfwhgi34qytpvkzggbhnle:
|
||||
resolution: {integrity: sha512-Tzo9VAfgNRb6XpxSFd7uphSeK2w5OxDY2wDtmpsQ+rQlPSEEI9TE6Jsb2nHRLD5J4FrmXKLrTcORqidsNQSXEg==}
|
||||
peerDependencies:
|
||||
vega: ^5.21.0
|
||||
@ -4036,7 +4034,7 @@ packages:
|
||||
vega-interpreter: 1.0.4
|
||||
vega-lite: 5.6.0_vega@5.22.1
|
||||
vega-schema-url-parser: 2.2.0
|
||||
vega-themes: 2.12.0_vega-lite@5.6.0+vega@5.22.1
|
||||
vega-themes: 2.12.0_36sthfwhgi34qytpvkzggbhnle
|
||||
vega-tooltip: 0.28.0
|
||||
dev: false
|
||||
bundledDependencies:
|
||||
@ -4255,7 +4253,7 @@ packages:
|
||||
d3-array: 3.1.1
|
||||
dev: false
|
||||
|
||||
/vega-themes/2.12.0_vega-lite@5.6.0+vega@5.22.1:
|
||||
/vega-themes/2.12.0_36sthfwhgi34qytpvkzggbhnle:
|
||||
resolution: {integrity: sha512-gHNYCzDgexSQDmGzQsxH57OYgFVbAOmvhIYN3MPOvVucyI+zhbUawBVIVNzG9ftucRp0MaaMVXi6ctC5HLnBsg==}
|
||||
peerDependencies:
|
||||
vega: '*'
|
||||
|
Loading…
x
Reference in New Issue
Block a user