diff --git a/.changeset/yellow-rooms-dream.md b/.changeset/yellow-rooms-dream.md new file mode 100644 index 000000000000..43e2962d1972 --- /dev/null +++ b/.changeset/yellow-rooms-dream.md @@ -0,0 +1,9 @@ +--- +"@gradio/app": minor +"@gradio/client": minor +"gradio": minor +"gradio_client": minor +"gradio_test": minor +--- + +feat:Swap websockets for SSE diff --git a/client/js/src/client.ts b/client/js/src/client.ts index 38741031be3b..9511ab753cde 100644 --- a/client/js/src/client.ts +++ b/client/js/src/client.ts @@ -429,9 +429,12 @@ export function api_factory( } let websocket: WebSocket; + let eventSource: EventSource; + let protocol = config.protocol ?? "sse"; const _endpoint = typeof endpoint === "number" ? "/predict" : endpoint; let payload: Payload; + let event_id: string | null = null; let complete: false | Record = false; const listener_map: ListenerMap = {}; let url_params = ""; @@ -516,7 +519,7 @@ export function api_factory( time: new Date() }); }); - } else { + } else if (protocol == "ws") { fire_event({ type: "status", stage: "pending", @@ -636,6 +639,126 @@ export function api_factory( websocket.send(JSON.stringify({ hash: session_hash })) ); } + } else { + fire_event({ + type: "status", + stage: "pending", + queue: true, + endpoint: _endpoint, + fn_index, + time: new Date() + }); + var params = new URLSearchParams({ + fn_index: fn_index.toString(), + session_hash: session_hash + }).toString(); + let url = new URL( + `${http_protocol}//${resolve_root( + host, + config.path, + true + )}/queue/join?${params}` + ); + + eventSource = new EventSource(url); + + eventSource.onmessage = async function (event) { + const _data = JSON.parse(event.data); + const { type, status, data } = handle_message( + _data, + last_status[fn_index] + ); + + if (type === "update" && status && !complete) { + // call 'status' listeners + fire_event({ + type: "status", + endpoint: _endpoint, + fn_index, + time: new Date(), + ...status + }); + if (status.stage === "error") { + eventSource.close(); + } + } else if (type === "data") { + event_id = _data.event_id as string; + let [_, status] = await post_data( + `${http_protocol}//${resolve_root( + host, + config.path, + true + )}/queue/data`, + { + ...payload, + session_hash, + event_id + }, + hf_token + ); + if (status !== 200) { + fire_event({ + type: "status", + stage: "error", + message: BROKEN_CONNECTION_MSG, + queue: true, + endpoint: _endpoint, + fn_index, + time: new Date() + }); + eventSource.close(); + } + } else if (type === "complete") { + complete = status; + } else if (type === "log") { + fire_event({ + type: "log", + log: data.log, + level: data.level, + endpoint: _endpoint, + fn_index + }); + } else if (type === "generating") { + fire_event({ + type: "status", + time: new Date(), + ...status, + stage: status?.stage!, + queue: true, + endpoint: _endpoint, + fn_index + }); + } + if (data) { + fire_event({ + type: "data", + time: new Date(), + data: transform_files + ? transform_output( + data.data, + api_info, + config.root, + config.root_url + ) + : data.data, + endpoint: _endpoint, + fn_index + }); + + if (complete) { + fire_event({ + type: "status", + time: new Date(), + ...complete, + stage: status?.stage!, + queue: true, + endpoint: _endpoint, + fn_index + }); + eventSource.close(); + } + } + }; } }); @@ -683,12 +806,19 @@ export function api_factory( fn_index: fn_index }); - if (websocket && websocket.readyState === 0) { - websocket.addEventListener("open", () => { + let cancel_request = {}; + if (protocol === "ws") { + if (websocket && websocket.readyState === 0) { + websocket.addEventListener("open", () => { + websocket.close(); + }); + } else { websocket.close(); - }); + } + cancel_request = { fn_index, session_hash }; } else { - websocket.close(); + eventSource.close(); + cancel_request = { event_id }; } try { @@ -701,7 +831,7 @@ export function api_factory( { headers: { "Content-Type": "application/json" }, method: "POST", - body: JSON.stringify({ fn_index, session_hash }) + body: JSON.stringify(cancel_request) } ); } catch (e) { diff --git a/client/js/src/types.ts b/client/js/src/types.ts index d77c22869f19..7229c44ff2f1 100644 --- a/client/js/src/types.ts +++ b/client/js/src/types.ts @@ -18,6 +18,7 @@ export interface Config { show_api: boolean; stylesheets: string[]; path: string; + protocol?: "sse" | "ws"; } export interface Payload { diff --git a/client/python/gradio_client/client.py b/client/python/gradio_client/client.py index 4ef0910ec56b..7d210803ba00 100644 --- a/client/python/gradio_client/client.py +++ b/client/python/gradio_client/client.py @@ -19,6 +19,7 @@ from threading import Lock from typing import Any, Callable, Literal +import httpx import huggingface_hub import requests import websockets @@ -74,6 +75,7 @@ def __init__( serialize: bool = True, output_dir: str | Path = DEFAULT_TEMP_DIR, verbose: bool = True, + auth: tuple[str, str] | None = None, ): """ Parameters: @@ -93,6 +95,7 @@ def __init__( library_version=utils.__version__, ) self.space_id = None + self.cookies: dict[str, str] = {} self.output_dir = ( str(output_dir) if isinstance(output_dir, Path) else output_dir ) @@ -123,21 +126,22 @@ def __init__( print(f"Loaded as API: {self.src} ✔") self.api_url = urllib.parse.urljoin(self.src, utils.API_URL) + self.sse_url = urllib.parse.urljoin(self.src, utils.SSE_URL) + self.sse_data_url = urllib.parse.urljoin(self.src, utils.SSE_DATA_URL) self.ws_url = urllib.parse.urljoin( self.src.replace("http", "ws", 1), utils.WS_URL ) self.upload_url = urllib.parse.urljoin(self.src, utils.UPLOAD_URL) self.reset_url = urllib.parse.urljoin(self.src, utils.RESET_URL) + if auth is not None: + self._login(auth) self.config = self._get_config() self.app_version = version.parse(self.config.get("version", "2.0")) self._info = self._get_api_info() self.session_hash = str(uuid.uuid4()) - endpoint_class = Endpoint - if self.app_version < version.Version("4.0.0") and not str( - self.app_version - ).startswith("4.0.0"): - endpoint_class = EndpointV3Compatibility + protocol = self.config.get("protocol") + endpoint_class = Endpoint if protocol == "sse" else EndpointV3Compatibility self.endpoints = [ endpoint_class(self, fn_index, dependency) for fn_index, dependency in enumerate(self.config["dependencies"]) @@ -300,6 +304,14 @@ def predict( ) return self.submit(*args, api_name=api_name, fn_index=fn_index).result() + def new_helper(self, fn_index: int) -> Communicator: + return Communicator( + Lock(), + JobStatus(), + self.endpoints[fn_index].process_predictions, + self.reset_url, + ) + def submit( self, *args, @@ -329,13 +341,8 @@ def submit( inferred_fn_index = self._infer_fn_index(api_name, fn_index) helper = None - if self.endpoints[inferred_fn_index].use_ws: - helper = Communicator( - Lock(), - JobStatus(), - self.endpoints[inferred_fn_index].process_predictions, - self.reset_url, - ) + if self.endpoints[inferred_fn_index].protocol in ("ws", "sse"): + helper = self.new_helper(inferred_fn_index) end_to_end_fn = self.endpoints[inferred_fn_index].make_end_to_end_fn(helper) future = self.executor.submit(end_to_end_fn, *args) @@ -368,11 +375,11 @@ def _get_api_info(self): api_info_url = urllib.parse.urljoin(self.src, utils.RAW_API_INFO_URL) if self.app_version > version.Version("3.36.1"): - r = requests.get(api_info_url, headers=self.headers) + r = requests.get(api_info_url, headers=self.headers, cookies=self.cookies) if r.ok: info = r.json() else: - raise ValueError(f"Could not fetch api info for {self.src}") + raise ValueError(f"Could not fetch api info for {self.src}: {r.text}") else: fetch = requests.post( utils.SPACE_FETCHER_URL, @@ -381,7 +388,9 @@ def _get_api_info(self): if fetch.ok: info = fetch.json()["api"] else: - raise ValueError(f"Could not fetch api info for {self.src}") + raise ValueError( + f"Could not fetch api info for {self.src}: {fetch.text}" + ) return info @@ -604,14 +613,33 @@ def __del__(self): def _space_name_to_src(self, space) -> str | None: return huggingface_hub.space_info(space, token=self.hf_token).host # type: ignore + def _login(self, auth: tuple[str, str]): + resp = requests.post( + urllib.parse.urljoin(self.src, utils.LOGIN_URL), + data={"username": auth[0], "password": auth[1]}, + ) + if not resp.ok: + raise ValueError(f"Could not login to {self.src}") + self.cookies = { + cookie.name: cookie.value + for cookie in resp.cookies + if cookie.value is not None + } + def _get_config(self) -> dict: r = requests.get( - urllib.parse.urljoin(self.src, utils.CONFIG_URL), headers=self.headers + urllib.parse.urljoin(self.src, utils.CONFIG_URL), + headers=self.headers, + cookies=self.cookies, ) if r.ok: return r.json() + elif r.status_code == 401: + raise ValueError(f"Could not load {self.src}. Please login.") else: # to support older versions of Gradio - r = requests.get(self.src, headers=self.headers) + r = requests.get(self.src, headers=self.headers, cookies=self.cookies) + if not r.ok: + raise ValueError(f"Could not fetch config for {self.src}") # some basic regex to extract the config result = re.search(r"window.gradio_config = (.*?);[\s]*", r.text) try: @@ -786,7 +814,7 @@ def __init__(self, client: Client, fn_index: int, dependency: dict): self.api_name: str | Literal[False] | None = ( "/" + api_name if isinstance(api_name, str) else api_name ) - self.use_ws = self._use_websocket(self.dependency) + self.protocol = "sse" self.input_component_types = [ self._get_component_type(id_) for id_ in dependency["inputs"] ] @@ -852,29 +880,21 @@ def _inner(*data): def make_predict(self, helper: Communicator | None = None): def _predict(*data) -> tuple: - data = json.dumps( - { - "data": data, - "fn_index": self.fn_index, - "session_hash": self.client.session_hash, - } - ) - hash_data = json.dumps( - { - "fn_index": self.fn_index, - "session_hash": self.client.session_hash, - } - ) + data = { + "data": data, + "fn_index": self.fn_index, + "session_hash": self.client.session_hash, + } + + hash_data = { + "fn_index": self.fn_index, + "session_hash": self.client.session_hash, + } + + result = utils.synchronize_async(self._sse_fn, data, hash_data, helper) + if "error" in result: + raise ValueError(result["error"]) - if self.use_ws: - result = utils.synchronize_async(self._ws_fn, data, hash_data, helper) - if "error" in result: - raise ValueError(result["error"]) - else: - response = requests.post( - self.client.api_url, headers=self.client.headers, data=data - ) - result = json.loads(response.content.decode("utf-8")) try: output = result["data"] except KeyError as ke: @@ -1050,22 +1070,17 @@ def process_predictions(self, *predictions): predictions = self.reduce_singleton_output(*predictions) return predictions - def _use_websocket(self, dependency: dict) -> bool: - queue_enabled = self.client.config.get("enable_queue", False) - queue_uses_websocket = version.parse( - self.client.config.get("version", "2.0") - ) >= version.Version("3.2") - dependency_uses_queue = dependency.get("queue", False) is not False - return queue_enabled and queue_uses_websocket and dependency_uses_queue - - async def _ws_fn(self, data, hash_data, helper: Communicator): - async with websockets.connect( # type: ignore - self.client.ws_url, - open_timeout=10, - extra_headers=self.client.headers, - max_size=1024 * 1024 * 1024, - ) as websocket: - return await utils.get_pred_from_ws(websocket, data, hash_data, helper) + async def _sse_fn(self, data: dict, hash_data: dict, helper: Communicator): + async with httpx.AsyncClient(timeout=httpx.Timeout(timeout=None)) as client: + return await utils.get_pred_from_sse( + client, + data, + hash_data, + helper, + self.client.sse_url, + self.client.sse_data_url, + self.client.cookies, + ) class EndpointV3Compatibility: @@ -1080,6 +1095,7 @@ def __init__(self, client: Client, fn_index: int, dependency: dict): "/" + api_name if isinstance(api_name, str) else api_name ) self.use_ws = self._use_websocket(self.dependency) + self.protocol = "ws" if self.use_ws else "http" self.input_component_types = [] self.output_component_types = [] self.root_url = client.src + "/" if not client.src.endswith("/") else client.src @@ -1413,13 +1429,9 @@ def __next__(self) -> tuple | Any: if not self.communicator: raise StopIteration() - with self.communicator.lock: - if self.communicator.job.latest_status.code == Status.FINISHED: - raise StopIteration() - while True: with self.communicator.lock: - if len(self.communicator.job.outputs) == self._counter + 1: + if len(self.communicator.job.outputs) >= self._counter + 1: o = self.communicator.job.outputs[self._counter] self._counter += 1 return o diff --git a/client/python/gradio_client/utils.py b/client/python/gradio_client/utils.py index 3b862b3fcaa7..ad2c0ef8cbe9 100644 --- a/client/python/gradio_client/utils.py +++ b/client/python/gradio_client/utils.py @@ -27,8 +27,11 @@ from websockets.legacy.protocol import WebSocketCommonProtocol API_URL = "api/predict/" +SSE_URL = "queue/join" +SSE_DATA_URL = "queue/data" WS_URL = "queue/join" UPLOAD_URL = "upload" +LOGIN_URL = "login" CONFIG_URL = "config" API_INFO_URL = "info" RAW_API_INFO_URL = "info?serialize=False" @@ -143,7 +146,7 @@ class ProgressUnit: desc: Optional[str] @classmethod - def from_ws_msg(cls, data: list[dict]) -> list[ProgressUnit]: + def from_msg(cls, data: list[dict]) -> list[ProgressUnit]: return [ cls( index=d.get("index"), @@ -201,6 +204,7 @@ class Communicator: prediction_processor: Callable[..., tuple] reset_url: str should_cancel: bool = False + event_id: str | None = None ######################## @@ -282,7 +286,7 @@ async def get_pred_from_ws( success=resp.get("success"), time=datetime.now(), eta=resp.get("rank_eta"), - progress_data=ProgressUnit.from_ws_msg(resp["progress_data"]) + progress_data=ProgressUnit.from_msg(resp["progress_data"]) if has_progress else None, ) @@ -304,6 +308,111 @@ async def get_pred_from_ws( return resp["output"] +async def get_pred_from_sse( + client: httpx.AsyncClient, + data: dict, + hash_data: dict, + helper: Communicator, + sse_url: str, + sse_data_url: str, + cookies: dict[str, str] | None = None, +) -> dict[str, Any] | None: + done, pending = await asyncio.wait( + [ + asyncio.create_task(check_for_cancel(helper, cookies)), + asyncio.create_task( + stream_sse( + client, data, hash_data, helper, sse_url, sse_data_url, cookies + ) + ), + ], + return_when=asyncio.FIRST_COMPLETED, + ) + + for task in pending: + task.cancel() + try: + await task + except asyncio.CancelledError: + pass + + assert len(done) == 1 + for task in done: + return task.result() + + +async def check_for_cancel(helper: Communicator, cookies: dict[str, str] | None): + while True: + await asyncio.sleep(0.05) + with helper.lock: + if helper.should_cancel: + break + if helper.event_id: + async with httpx.AsyncClient() as http: + await http.post( + helper.reset_url, json={"event_id": helper.event_id}, cookies=cookies + ) + raise CancelledError() + + +async def stream_sse( + client: httpx.AsyncClient, + data: dict, + hash_data: dict, + helper: Communicator, + sse_url: str, + sse_data_url: str, + cookies: dict[str, str] | None = None, +) -> dict[str, Any]: + try: + async with client.stream( + "GET", sse_url, params=hash_data, cookies=cookies + ) as response: + async for line in response.aiter_text(): + if line.startswith("data:"): + resp = json.loads(line[5:]) + with helper.lock: + has_progress = "progress_data" in resp + status_update = StatusUpdate( + code=Status.msg_to_status(resp["msg"]), + queue_size=resp.get("queue_size"), + rank=resp.get("rank", None), + success=resp.get("success"), + time=datetime.now(), + eta=resp.get("rank_eta"), + progress_data=ProgressUnit.from_msg(resp["progress_data"]) + if has_progress + else None, + ) + output = resp.get("output", {}).get("data", []) + if output and status_update.code != Status.FINISHED: + try: + result = helper.prediction_processor(*output) + except Exception as e: + result = [e] + helper.job.outputs.append(result) + helper.job.latest_status = status_update + + if resp["msg"] == "queue_full": + raise QueueError("Queue is full! Please try again.") + elif resp["msg"] == "send_data": + event_id = resp["event_id"] + helper.event_id = event_id + req = await client.post( + sse_data_url, + json={"event_id": event_id, **data, **hash_data}, + cookies=cookies, + ) + req.raise_for_status() + elif resp["msg"] == "process_completed": + return resp["output"] + else: + raise ValueError(f"Unexpected message: {line}") + raise ValueError("Did not receive process_completed message.") + except asyncio.CancelledError: + raise + + ######################## # Data processing utils ######################## diff --git a/client/python/test/conftest.py b/client/python/test/conftest.py index 0ecb1f89c6ab..1c2843315a7e 100644 --- a/client/python/test/conftest.py +++ b/client/python/test/conftest.py @@ -102,7 +102,7 @@ def spell(x): time.sleep(0.5) yield x[:i] - return gr.Interface(spell, "textbox", "textbox").queue() + return gr.Interface(spell, "textbox", "textbox") @pytest.fixture @@ -132,6 +132,7 @@ def long_process(): @pytest.fixture def sentiment_classification_demo(): def classifier(text): + time.sleep(1) return {label: random.random() for label in ["POSITIVE", "NEGATIVE", "NEUTRAL"]} def sleep_for_test(): diff --git a/client/python/test/test_client.py b/client/python/test/test_client.py index 8d7c35debf2a..da5e7aeea0a4 100644 --- a/client/python/test/test_client.py +++ b/client/python/test/test_client.py @@ -69,7 +69,7 @@ def test_numerical_to_label_space(self): @pytest.mark.flaky def test_numerical_to_label_space_v4(self): - client = Client("gradio-tests/titanic-survival-v4") + client = Client("gradio-tests/titanic-survivalv4-sse") label = client.predict("male", 77, 10, api_name="/predict") assert label["label"] == "Perishes" @@ -81,7 +81,9 @@ def test_private_space(self): @pytest.mark.flaky def test_private_space_v4(self): - client = Client("gradio-tests/not-actually-private-space-v4", hf_token=HF_TOKEN) + client = Client( + "gradio-tests/not-actually-private-spacev4-sse", hf_token=HF_TOKEN + ) output = client.predict("abc", api_name="/predict") assert output == "abc" @@ -121,18 +123,6 @@ def test_job_status(self, calculator_demo): s.code for s in statuses if s ] - @pytest.mark.flaky - def test_job_status_queue_disabled(self, sentiment_classification_demo): - with connect(sentiment_classification_demo) as client: - statuses = [] - job = client.submit("I love the gradio python client", api_name="/classify") - while not job.done(): - time.sleep(0.02) - statuses.append(job.status()) - statuses.append(job.status()) - assert all(s.code in [Status.PROCESSING, Status.FINISHED] for s in statuses) - assert not any(s.progress_data for s in statuses) - @pytest.mark.flaky def test_intermediate_outputs(self, count_generator_demo): with connect(count_generator_demo) as client: @@ -282,10 +272,12 @@ def test_cancel_subsequent_jobs_state_reset(self, yield_demo): time.sleep(3) job1.cancel() + assert len(job1.outputs()) > 0 assert len(job1.outputs()) < len("abcdefefadsadfs") assert job1.status().code == Status.CANCELLED job2 = client.submit("abcd", api_name="/predict") + assert len(job2.outputs()) == 0 while not job2.done(): time.sleep(0.1) # Ran all iterations from scratch @@ -311,7 +303,7 @@ def test_stream_audio(self, stream_audio): @pytest.mark.xfail def test_upload_file_private_space_v4(self): client = Client( - src="gradio-tests/not-actually-private-file-upload-v4", hf_token=HF_TOKEN + src="gradio-tests/not-actually-private-file-uploadv4-sse", hf_token=HF_TOKEN ) with patch.object( @@ -1059,7 +1051,7 @@ def test_upload(self): def test_upload_v4(self): client = Client( - src="gradio-tests/not-actually-private-file-upload-v4", hf_token=HF_TOKEN + src="gradio-tests/not-actually-private-file-uploadv4-sse", hf_token=HF_TOKEN ) response = MagicMock(status_code=200) response.json.return_value = [ diff --git a/client/python/test/test_utils.py b/client/python/test/test_utils.py index 40badd5da366..d5faf42fb845 100644 --- a/client/python/test/test_utils.py +++ b/client/python/test/test_utils.py @@ -68,7 +68,7 @@ def test_decode_base64_to_file(): def test_download_private_file(gradio_temp_dir): url_path = ( - "https://gradio-tests-not-actually-private-space-v4.hf.space/file=lion.jpg" + "https://gradio-tests-not-actually-private-spacev4-sse.hf.space/file=lion.jpg" ) hf_token = "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" # Intentionally revealing this key for testing purposes file = utils.download_file( @@ -119,8 +119,8 @@ async def test_get_pred_from_ws(): json.dumps({"msg": "process_completed", "output": {"data": ["result!"]}}), ] mock_ws.recv.side_effect = messages - data = json.dumps({"data": ["foo"], "fn_index": "foo"}) - hash_data = json.dumps({"session_hash": "daslskdf", "fn_index": "foo"}) + data = {"data": ["foo"], "fn_index": "foo"} + hash_data = {"session_hash": "daslskdf", "fn_index": "foo"} output = await utils.get_pred_from_ws(mock_ws, data, hash_data) assert output == {"data": ["result!"]} mock_ws.send.assert_called_once_with(data) diff --git a/demo/blocks_essay/run.ipynb b/demo/blocks_essay/run.ipynb index 5664f8bd40e0..cc1930faa7be 100644 --- a/demo/blocks_essay/run.ipynb +++ b/demo/blocks_essay/run.ipynb @@ -1 +1 @@ -{"cells": [{"cell_type": "markdown", "id": "302934307671667531413257853548643485645", "metadata": {}, "source": ["# Gradio Demo: blocks_essay"]}, {"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", "\n", "\n", "def change_textbox(choice):\n", " if choice == \"short\":\n", " return gr.Textbox(lines=2, visible=True)\n", " elif choice == \"long\":\n", " return gr.Textbox(lines=8, visible=True, value=\"Lorem ipsum dolor sit amet\")\n", " else:\n", " return gr.Textbox(visible=False)\n", "\n", "\n", "with gr.Blocks() as demo:\n", " radio = gr.Radio(\n", " [\"short\", \"long\", \"none\"], label=\"What kind of essay would you like to write?\"\n", " )\n", " text = gr.Textbox(lines=2, interactive=True, show_copy_button=True)\n", " radio.change(fn=change_textbox, inputs=radio, outputs=text)\n", "\n", " with gr.Row():\n", " num = gr.Number(minimum=0, maximum=100, label=\"input\")\n", " out = gr.Number(label=\"output\")\n", " minimum_slider = gr.Slider(0, 100, 0, label=\"min\")\n", " maximum_slider = gr.Slider(0, 100, 100, label=\"max\")\n", "\n", " def reset_bounds(minimum, maximum):\n", " return gr.Number(minimum=minimum, maximum=maximum)\n", " \n", " minimum_slider.change(reset_bounds, [minimum_slider, maximum_slider], outputs=num)\n", " maximum_slider.change(reset_bounds, [minimum_slider, maximum_slider], outputs=num)\n", " num.submit(lambda x:x, num, out)\n", "\n", "\n", "if __name__ == \"__main__\":\n", " demo.launch()\n"]}], "metadata": {}, "nbformat": 4, "nbformat_minor": 5} \ No newline at end of file +{"cells": [{"cell_type": "markdown", "id": "302934307671667531413257853548643485645", "metadata": {}, "source": ["# Gradio Demo: blocks_essay"]}, {"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", "\n", "\n", "def change_textbox(choice):\n", " if choice == \"short\":\n", " return gr.Textbox(lines=2, visible=True)\n", " elif choice == \"long\":\n", " return gr.Textbox(lines=8, visible=True, value=\"Lorem ipsum dolor sit amet\")\n", " else:\n", " return gr.Textbox(visible=False)\n", "\n", "\n", "with gr.Blocks() as demo:\n", " radio = gr.Radio(\n", " [\"short\", \"long\", \"none\"], label=\"What kind of essay would you like to write?\"\n", " )\n", " text = gr.Textbox(lines=2, interactive=True, show_copy_button=True)\n", " radio.change(fn=change_textbox, inputs=radio, outputs=text)\n", "\n", " with gr.Row():\n", " num = gr.Number(minimum=0, maximum=100, label=\"input\")\n", " out = gr.Number(label=\"output\")\n", " minimum_slider = gr.Slider(0, 100, 0, label=\"min\")\n", " maximum_slider = gr.Slider(0, 100, 100, label=\"max\")\n", "\n", " def reset_bounds(minimum, maximum):\n", " return gr.Number(minimum=minimum, maximum=maximum)\n", "\n", " gr.on(\n", " [minimum_slider.change, maximum_slider.change],\n", " reset_bounds,\n", " [minimum_slider, maximum_slider],\n", " outputs=num,\n", " )\n", " num.submit(lambda x: x, num, out)\n", "\n", "\n", "if __name__ == \"__main__\":\n", " demo.launch()\n"]}], "metadata": {}, "nbformat": 4, "nbformat_minor": 5} \ No newline at end of file diff --git a/demo/blocks_essay/run.py b/demo/blocks_essay/run.py index d38a7f652ffc..98160bb1d511 100644 --- a/demo/blocks_essay/run.py +++ b/demo/blocks_essay/run.py @@ -25,10 +25,14 @@ def change_textbox(choice): def reset_bounds(minimum, maximum): return gr.Number(minimum=minimum, maximum=maximum) - - minimum_slider.change(reset_bounds, [minimum_slider, maximum_slider], outputs=num) - maximum_slider.change(reset_bounds, [minimum_slider, maximum_slider], outputs=num) - num.submit(lambda x:x, num, out) + + gr.on( + [minimum_slider.change, maximum_slider.change], + reset_bounds, + [minimum_slider, maximum_slider], + outputs=num, + ) + num.submit(lambda x: x, num, out) if __name__ == "__main__": diff --git a/demo/cancel_events/run.ipynb b/demo/cancel_events/run.ipynb index e5dd7c95b7ad..e595c9532015 100644 --- a/demo/cancel_events/run.ipynb +++ b/demo/cancel_events/run.ipynb @@ -1 +1 @@ -{"cells": [{"cell_type": "markdown", "id": "302934307671667531413257853548643485645", "metadata": {}, "source": ["# Gradio Demo: cancel_events"]}, {"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 time\n", "import gradio as gr\n", "\n", "\n", "def fake_diffusion(steps):\n", " for i in range(steps):\n", " print(f\"Current step: {i}\")\n", " time.sleep(0.2)\n", " yield str(i)\n", "\n", "\n", "def long_prediction(*args, **kwargs):\n", " time.sleep(10)\n", " return 42\n", "\n", "\n", "with gr.Blocks() as demo:\n", " with gr.Row():\n", " with gr.Column():\n", " n = gr.Slider(1, 10, value=9, step=1, label=\"Number Steps\")\n", " run = gr.Button(value=\"Start Iterating\")\n", " output = gr.Textbox(label=\"Iterative Output\")\n", " stop = gr.Button(value=\"Stop Iterating\")\n", " with gr.Column():\n", " textbox = gr.Textbox(label=\"Prompt\")\n", " prediction = gr.Number(label=\"Expensive Calculation\")\n", " run_pred = gr.Button(value=\"Run Expensive Calculation\")\n", " with gr.Column():\n", " cancel_on_change = gr.Textbox(label=\"Cancel Iteration and Expensive Calculation on Change\")\n", " cancel_on_submit = gr.Textbox(label=\"Cancel Iteration and Expensive Calculation on Submit\")\n", " echo = gr.Textbox(label=\"Echo\")\n", " with gr.Row():\n", " with gr.Column():\n", " image = gr.Image(sources=[\"webcam\"], tool=\"editor\", label=\"Cancel on edit\", interactive=True)\n", " with gr.Column():\n", " video = gr.Video(source=\"webcam\", label=\"Cancel on play\", interactive=True)\n", "\n", " click_event = run.click(fake_diffusion, n, output)\n", " stop.click(fn=None, inputs=None, outputs=None, cancels=[click_event])\n", " pred_event = run_pred.click(fn=long_prediction, inputs=[textbox], outputs=prediction)\n", "\n", " cancel_on_change.change(None, None, None, cancels=[click_event, pred_event])\n", " cancel_on_submit.submit(lambda s: s, cancel_on_submit, echo, cancels=[click_event, pred_event])\n", " image.edit(None, None, None, cancels=[click_event, pred_event])\n", " video.play(None, None, None, cancels=[click_event, pred_event])\n", "\n", " demo.queue(concurrency_count=2, max_size=20)\n", "\n", "if __name__ == \"__main__\":\n", " demo.launch()\n"]}], "metadata": {}, "nbformat": 4, "nbformat_minor": 5} \ No newline at end of file +{"cells": [{"cell_type": "markdown", "id": "302934307671667531413257853548643485645", "metadata": {}, "source": ["# Gradio Demo: cancel_events"]}, {"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 time\n", "import gradio as gr\n", "\n", "\n", "def fake_diffusion(steps):\n", " for i in range(steps):\n", " print(f\"Current step: {i}\")\n", " time.sleep(1)\n", " yield str(i)\n", "\n", "\n", "def long_prediction(*args, **kwargs):\n", " time.sleep(10)\n", " return 42\n", "\n", "\n", "with gr.Blocks() as demo:\n", " with gr.Row():\n", " with gr.Column():\n", " n = gr.Slider(1, 10, value=9, step=1, label=\"Number Steps\")\n", " run = gr.Button(value=\"Start Iterating\")\n", " output = gr.Textbox(label=\"Iterative Output\")\n", " stop = gr.Button(value=\"Stop Iterating\")\n", " with gr.Column():\n", " textbox = gr.Textbox(label=\"Prompt\")\n", " prediction = gr.Number(label=\"Expensive Calculation\")\n", " run_pred = gr.Button(value=\"Run Expensive Calculation\")\n", " with gr.Column():\n", " cancel_on_change = gr.Textbox(label=\"Cancel Iteration and Expensive Calculation on Change\")\n", " cancel_on_submit = gr.Textbox(label=\"Cancel Iteration and Expensive Calculation on Submit\")\n", " echo = gr.Textbox(label=\"Echo\")\n", " with gr.Row():\n", " with gr.Column():\n", " image = gr.Image(sources=[\"webcam\"], tool=\"editor\", label=\"Cancel on edit\", interactive=True)\n", " with gr.Column():\n", " video = gr.Video(source=\"webcam\", label=\"Cancel on play\", interactive=True)\n", "\n", " click_event = run.click(fake_diffusion, n, output)\n", " stop.click(fn=None, inputs=None, outputs=None, cancels=[click_event])\n", " pred_event = run_pred.click(fn=long_prediction, inputs=[textbox], outputs=prediction)\n", "\n", " cancel_on_change.change(None, None, None, cancels=[click_event, pred_event])\n", " cancel_on_submit.submit(lambda s: s, cancel_on_submit, echo, cancels=[click_event, pred_event])\n", " image.edit(None, None, None, cancels=[click_event, pred_event])\n", " video.play(None, None, None, cancels=[click_event, pred_event])\n", "\n", " demo.queue(concurrency_count=2, max_size=20)\n", "\n", "if __name__ == \"__main__\":\n", " demo.launch()\n"]}], "metadata": {}, "nbformat": 4, "nbformat_minor": 5} \ No newline at end of file diff --git a/demo/cancel_events/run.py b/demo/cancel_events/run.py index 451a0262c02d..aa5da35533ba 100644 --- a/demo/cancel_events/run.py +++ b/demo/cancel_events/run.py @@ -5,7 +5,7 @@ def fake_diffusion(steps): for i in range(steps): print(f"Current step: {i}") - time.sleep(0.2) + time.sleep(1) yield str(i) diff --git a/gradio/blocks.py b/gradio/blocks.py index e84e74326f97..8a46e2039edd 100644 --- a/gradio/blocks.py +++ b/gradio/blocks.py @@ -500,7 +500,7 @@ def __init__( self.stylesheets = theme._stylesheets self.encrypt = False self.share = False - self.enable_queue = None + self.enable_queue = True self.max_threads = 40 self.pending_streams = defaultdict(dict) self.show_error = True @@ -534,7 +534,7 @@ def __init__( self.share_url = None self.width = None self.height = None - self.api_open = False + self.api_open = utils.get_space() is None self.space_id = utils.get_space() self.favicon_path = None @@ -573,6 +573,8 @@ def __init__( } analytics.initiated_analytics(data) + self.queue() + def get_component(self, id: int) -> Component: comp = self.blocks[id] assert isinstance(comp, components.Component), f"{comp}" @@ -1090,8 +1092,6 @@ async def call_function( prediction = None if inspect.isgeneratorfunction(fn) or inspect.isasyncgenfunction(fn): - if not self.enable_queue: - raise ValueError("Need to enable queue to use generators.") try: if iterator is None: iterator = cast(AsyncIterator[Any], prediction) @@ -1395,7 +1395,7 @@ async def process_api( inputs: list[Any], state: SessionState | None = None, request: routes.Request | list[routes.Request] | None = None, - iterators: dict[int, Any] | None = None, + iterator: AsyncIterator | None = None, session_hash: str | None = None, event_id: str | None = None, event_data: EventData | None = None, @@ -1453,7 +1453,7 @@ async def process_api( data = list(zip(*data)) is_generating, iterator = None, None else: - old_iterator = iterators.get(fn_index, None) if iterators else None + old_iterator = iterator if old_iterator: inputs = [] else: @@ -1509,12 +1509,13 @@ def get_config_file(self): "css": self.css, "title": self.title or "Gradio", "space_id": self.space_id, - "enable_queue": getattr(self, "enable_queue", False), # launch attributes + "enable_queue": True, # launch attributes "show_error": getattr(self, "show_error", False), "show_api": self.show_api, "is_colab": utils.colab_check(), "stylesheets": self.stylesheets, "theme": self.theme.name, + "protocol": "sse", } def get_layout(block): @@ -1584,15 +1585,15 @@ def clear(self): @document() def queue( self, - concurrency_count: int = 1, + concurrency_count: int | None = None, status_update_rate: float | Literal["auto"] = "auto", - api_open: bool = False, + api_open: bool | None = None, max_size: int | None = None, ): """ By enabling the queue you can control the rate of processed requests, let users know their position in the queue, and set a limit on maximum number of events allowed. Parameters: - concurrency_count: Number of worker threads that will be processing requests from the queue concurrently. Increasing this number will increase the rate at which requests are processed, but will also increase the memory usage of the queue. + concurrency_count: Number of worker threads that will be processing requests from the queue concurrently. Default is 40 when running locally, and 1 in Spaces. status_update_rate: If "auto", Queue will send status estimations to all clients whenever a job is finished. Otherwise Queue will send status at regular intervals set by this parameter as the number of seconds. api_open: If True, the REST routes of the backend will be open, allowing requests made directly to those endpoints to skip the queue. max_size: The maximum number of events the queue will store at any given moment. If the queue is full, new events will not be added and a user will receive a message saying that the queue is full. If None, the queue size will be unlimited. @@ -1607,8 +1608,10 @@ def queue( demo.queue(max_size=20) demo.launch() """ - self.enable_queue = True - self.api_open = api_open + if concurrency_count is None: + concurrency_count = 1 if utils.get_space() else 40 + if api_open is not None: + self.api_open = api_open if utils.is_zero_gpu_space(): concurrency_count = self.max_threads max_size = 1 if max_size is None else max_size @@ -1624,17 +1627,7 @@ def queue( return self def validate_queue_settings(self): - if not self.enable_queue and self.progress_tracking: - raise ValueError("Progress tracking requires queuing to be enabled.") - - for fn_index, dep in enumerate(self.dependencies): - if not self.enable_queue and self.queue_enabled_for_fn(fn_index): - raise ValueError( - f"The queue is enabled for event {dep['api_name'] if dep['api_name'] else fn_index} " - "but the queue has not been enabled for the app. Please call .queue() " - "on your app. Consult https://gradio.app/docs/#blocks-queue for information on how " - "to configure the queue." - ) + for dep in self.dependencies: for i in dep["cancels"]: if not self.queue_enabled_for_fn(i): raise ValueError( @@ -1644,10 +1637,7 @@ def validate_queue_settings(self): "another event without enabling the queue. Both can be solved by calling .queue() " "before .launch()" ) - if dep["batch"] and ( - dep["queue"] is False - or (dep["queue"] is None and not self.enable_queue) - ): + if dep["batch"] and dep["queue"] is False: raise ValueError("In order to use batching, the queue must be enabled.") def launch( @@ -1760,12 +1750,6 @@ def reverse(text): self.root_path = os.environ.get("GRADIO_ROOT_PATH", "") else: self.root_path = root_path - if self.space_id: - self.enable_queue = self.enable_queue is not False - else: - self.enable_queue = self.enable_queue is True - if self.enable_queue and not hasattr(self, "_queue"): - self.queue() self.show_api = show_api @@ -1780,9 +1764,7 @@ def reverse(text): self.validate_queue_settings() self.config = self.get_config_file() - self.max_threads = max( - self._queue.max_thread_count if self.enable_queue else 0, max_threads - ) + self.max_threads = max(self._queue.max_thread_count, max_threads) if self.is_running: if not isinstance(self.local_url, str): @@ -1850,8 +1832,7 @@ def reverse(text): ) ) - if self.enable_queue: - self._queue.set_server_app(self.server_app) + self._queue.set_server_app(self.server_app) if not wasm_utils.IS_WASM: # Cannot run async functions in background other than app's scope. @@ -1904,18 +1885,13 @@ def reverse(text): "When localhost is not accessible, a shareable link must be created. Please set share=True or check your proxy settings to allow access to localhost." ) - if self.is_colab: - if not quiet: - if debug: - print(strings.en["COLAB_DEBUG_TRUE"]) - else: - print(strings.en["COLAB_DEBUG_FALSE"]) - if not self.share: - print(strings.en["COLAB_WARNING"].format(self.server_port)) - if self.enable_queue and not self.share: - raise ValueError( - "When using queueing in Colab, a shareable link must be created. Please set share=True." - ) + if self.is_colab and not quiet: + if debug: + print(strings.en["COLAB_DEBUG_TRUE"]) + else: + print(strings.en["COLAB_DEBUG_FALSE"]) + if not self.share: + print(strings.en["COLAB_WARNING"].format(self.server_port)) if self.share: if self.space_id: @@ -2034,7 +2010,7 @@ def reverse(text): "is_google_colab": self.is_colab, "is_sharing_on": self.share, "share_url": self.share_url, - "enable_queue": self.enable_queue, + "enable_queue": True, "server_name": server_name, "server_port": server_port, "is_space": self.space_id is not None, @@ -2128,11 +2104,9 @@ def close(self, verbose: bool = True) -> None: # However, in the Wasm env, we don't have the `server` and # all async tasks are running in the same event loop, `pyodide.webloop.WebLoop` in the main thread, # so we have to cancel them explicitly so that these tasks won't run after a new app is launched. - if self.enable_queue: - self._queue._cancel_asyncio_tasks() + self._queue._cancel_asyncio_tasks() self.server_app._cancel_asyncio_tasks() - if self.enable_queue: - self._queue.close() + self._queue.close() if self.server: self.server.close() self.is_running = False @@ -2186,17 +2160,13 @@ def attach_load_events(self): def startup_events(self): """Events that should be run when the app containing this block starts up.""" - - if self.enable_queue: - self._queue.start() - # So that processing can resume in case the queue was stopped - self._queue.stopped = False + self._queue.start() + # So that processing can resume in case the queue was stopped + self._queue.stopped = False self.create_limiter() def queue_enabled_for_fn(self, fn_index: int): - if self.dependencies[fn_index]["queue"] is None: - return self.enable_queue - return self.dependencies[fn_index]["queue"] + return self.dependencies[fn_index]["queue"] is not False def get_api_info(self): """ diff --git a/gradio/data_classes.py b/gradio/data_classes.py index 113b38e5c7ac..f46386940e08 100644 --- a/gradio/data_classes.py +++ b/gradio/data_classes.py @@ -9,12 +9,16 @@ from enum import Enum, auto from typing import Any, List, Optional, Union +from fastapi import Request from gradio_client.utils import traverse from pydantic import BaseModel, RootModel, ValidationError from typing_extensions import Literal class PredictBody(BaseModel): + class Config: + arbitrary_types_allowed = True + session_hash: Optional[str] = None event_id: Optional[str] = None data: List[Any] @@ -24,13 +28,12 @@ class PredictBody(BaseModel): bool ] = False # Whether the data is a batch of samples (i.e. called from the queue if batch=True) or a single sample (i.e. called from the UI) request: Optional[ - Union[dict, List[dict]] + Request ] = None # dictionary of request headers, query parameters, url, etc. (used to to pass in request for queuing) class ResetBody(BaseModel): - session_hash: str - fn_index: int + event_id: str class ComponentServerBody(BaseModel): @@ -48,7 +51,6 @@ class InterfaceTypes(Enum): class Estimation(BaseModel): - msg: Optional[str] = "estimation" rank: Optional[int] = None queue_size: int avg_event_process_time: Optional[float] = None @@ -66,12 +68,10 @@ class ProgressUnit(BaseModel): class Progress(BaseModel): - msg: str = "progress" progress_data: List[ProgressUnit] = [] class LogMessage(BaseModel): - msg: str = "log" log: str level: Literal["info", "warning"] diff --git a/gradio/external.py b/gradio/external.py index 8705cfa8da80..23e2eff4efa7 100644 --- a/gradio/external.py +++ b/gradio/external.py @@ -14,6 +14,7 @@ import requests from gradio_client import Client from gradio_client import utils as client_utils +from gradio_client.client import Endpoint from gradio_client.documentation import document, set_documentation_group from packaging import version @@ -522,13 +523,19 @@ def from_spaces( def from_spaces_blocks(space: str, hf_token: str | None) -> Blocks: client = Client(space, hf_token=hf_token) - if client.app_version < version.Version("4.0.0"): + if client.app_version < version.Version("4.0.0b14"): raise GradioVersionIncompatibleError( f"Gradio version 4.x cannot load spaces with versions less than 4.x ({client.app_version})." "Please downgrade to version 3 to load this space." ) # Use end_to_end_fn here to properly upload/download all files - predict_fns = [endpoint.make_end_to_end_fn() for endpoint in client.endpoints] + predict_fns = [] + for fn_index, endpoint in enumerate(client.endpoints): + assert isinstance(endpoint, Endpoint) + helper = None + if endpoint.protocol in ("ws", "sse"): + helper = client.new_helper(fn_index) + predict_fns.append(endpoint.make_end_to_end_fn(helper)) return gradio.Blocks.from_config(client.config, predict_fns, client.src) diff --git a/gradio/helpers.py b/gradio/helpers.py index 56b9dcbe698a..61a74944bd64 100644 --- a/gradio/helpers.py +++ b/gradio/helpers.py @@ -1082,11 +1082,6 @@ def log_message(message: str, level: Literal["info", "warning"] = "info"): elif level == "warning": warnings.warn(message) return - if not blocks.enable_queue: - warnings.warn( - f"Queueing must be enabled to issue {level.capitalize()}: '{message}'." - ) - return blocks._queue.log_message(event_id=event_id, log=message, level=level) diff --git a/gradio/networking.py b/gradio/networking.py index e7229fd70fe1..b4169b90191b 100644 --- a/gradio/networking.py +++ b/gradio/networking.py @@ -184,7 +184,6 @@ def start_server( ssl_keyfile=ssl_keyfile, ssl_certfile=ssl_certfile, ssl_keyfile_password=ssl_keyfile_password, - ws_max_size=1024 * 1024 * 1024, # Setting max websocket size to be 1 GB ) reloader = None if GRADIO_WATCH_DIRS: diff --git a/gradio/queueing.py b/gradio/queueing.py index 5bcc51e1ba7b..8bd318a04364 100644 --- a/gradio/queueing.py +++ b/gradio/queueing.py @@ -5,9 +5,9 @@ import json import time import traceback -from asyncio import TimeoutError as AsyncTimeOutError +import uuid from collections import deque -from typing import Any +from queue import Queue as ThreadQueue import fastapi from typing_extensions import Literal @@ -28,23 +28,43 @@ class Event: def __init__( self, - websocket: fastapi.WebSocket, session_hash: str, fn_index: int, + request: fastapi.Request, + username: str | None, ): - self.websocket = websocket - self.session_hash: str = session_hash - self.fn_index: int = fn_index - self._id = f"{self.session_hash}_{self.fn_index}" + self.message_queue = ThreadQueue() + self.session_hash = session_hash + self.fn_index = fn_index + self.request = request + self.username = username + self._id = uuid.uuid4().hex self.data: PredictBody | None = None - self.lost_connection_time: float | None = None - self.username: str | None = None self.progress: Progress | None = None self.progress_pending: bool = False - self.log_messages: deque[LogMessage] = deque() + self.alive = True - async def disconnect(self, code: int = 1000): - await self.websocket.close(code=code) + def send_message( + self, + message_type: str, + data: dict | None = None, + final: bool = False, + ): + data = {} if data is None else data + self.message_queue.put_nowait({"msg": message_type, **data}) + if final: + self.message_queue.put_nowait(None) + + async def get_data(self, timeout=5) -> bool: + self.send_message("send_data", {"event_id": self._id}) + sleep_interval = 0.05 + wait_time = 0 + while wait_time < timeout and self.alive: + if self.data is not None: + break + await asyncio.sleep(sleep_interval) + wait_time += sleep_interval + return self.data is not None class Queue: @@ -57,7 +77,7 @@ def __init__( blocks_dependencies: list, ): self.event_queue: deque[Event] = deque() - self.events_pending_reconnection = [] + self.awaiting_data_events: dict[str, Event] = {} self.stopped = False self.max_thread_count = concurrency_count self.update_intervals = update_intervals @@ -79,15 +99,20 @@ def __init__( def start(self): run_coro_in_background(self.start_processing) - run_coro_in_background(self.start_log_and_progress_updates) + run_coro_in_background(self.start_progress_updates) if not self.live_updates: run_coro_in_background(self.notify_clients) def close(self): self.stopped = True - def resume(self): - self.stopped = False + def attach_data(self, body: PredictBody): + event_id = body.event_id + if event_id in self.awaiting_data_events: + event = self.awaiting_data_events[event_id] + event.data = body + else: + raise ValueError("Event not found", event_id) def _cancel_asyncio_tasks(self): for task in self._asyncio_tasks: @@ -148,14 +173,20 @@ async def start_processing(self) -> None: events[0].fn_index, batch, ) - broadcast_live_estimations_task = run_coro_in_background( - self.broadcast_live_estimations - ) self._asyncio_tasks.append(process_event_task) - self._asyncio_tasks.append(broadcast_live_estimations_task) + if self.live_updates: + broadcast_live_estimations_task = run_coro_in_background( + self.broadcast_estimations + ) + self._asyncio_tasks.append(broadcast_live_estimations_task) - async def start_log_and_progress_updates(self) -> None: + async def start_progress_updates(self) -> None: + """ + Because progress updates can be very frequent, we do not necessarily want to send a message per update. + Rather, we check for progress updates at regular intervals, and send a message if there is a pending update. + Consecutive progress updates between sends will overwrite each other so only the most recent update will be sent. + """ while not self.stopped: events = [ evt for job in self.active_jobs if job is not None for evt in job @@ -168,23 +199,10 @@ async def start_log_and_progress_updates(self) -> None: for event in events: if event.progress_pending and event.progress: event.progress_pending = False - client_awake = await self.send_message(event, event.progress.dict()) - if not client_awake: - await self.clean_event(event) - await self.send_log_updates_for_event(event) + event.send_message("progress", event.progress.model_dump()) await asyncio.sleep(self.progress_update_sleep_when_free) - async def send_log_updates_for_event(self, event: Event) -> None: - while True: - try: - message = event.log_messages.popleft() - except IndexError: - break - client_awake = await self.send_message(event, message.dict()) - if not client_awake: - await self.clean_event(event) - def set_progress( self, event_id: str, @@ -225,7 +243,7 @@ def log_message( log=log, level=level, ) - event.log_messages.append(log_message) + event.send_message("log", log_message.model_dump()) def push(self, event: Event) -> int | None: """ @@ -241,45 +259,21 @@ def push(self, event: Event) -> int | None: self.event_queue.append(event) return queue_len - async def clean_event(self, event: Event) -> None: + async def clean_event(self, event: Event | str) -> None: + if isinstance(event, str): + for job_set in self.active_jobs: + if job_set: + for job in job_set: + if job._id == event: + event = job + break + if isinstance(event, str): + raise ValueError("Event not found", event) + event.alive = False if event in self.event_queue: async with self.delete_lock: self.event_queue.remove(event) - async def broadcast_live_estimations(self) -> None: - """ - Runs 2 functions sequentially instead of concurrently. Otherwise dced clients are tried to get deleted twice. - """ - if self.live_updates: - await self.broadcast_estimations() - - async def gather_event_data(self, event: Event, receive_timeout=60) -> bool: - """ - Gather data for the event - Parameters: - event: the Event to gather data for - receive_timeout: how long to wait for data to be received from frontend - """ - if not event.data: - client_awake = await self.send_message(event, {"msg": "send_data"}) - if not client_awake: - return False - data, client_awake = await self.get_message(event, timeout=receive_timeout) - if not client_awake: - # In the event, we timeout due to large data size - # Let the client know, otherwise will hang - await self.send_message( - event, - { - "msg": "process_completed", - "output": {"error": "Time out uploading data to server"}, - "success": False, - }, - ) - return False - event.data = data - return True - async def notify_clients(self) -> None: """ Notify clients about events statuses in the queue periodically. @@ -320,9 +314,7 @@ async def send_estimation( if None not in self.active_jobs: # Add estimated amount of time for a thread to get empty estimation.rank_eta += self.avg_concurrent_process_time - client_awake = await self.send_message(event, estimation.dict()) - if not client_awake: - await self.clean_event(event) + event.send_message("estimation", estimation.model_dump()) return estimation def update_estimation(self, duration: float) -> None: @@ -350,22 +342,6 @@ def get_estimation(self) -> Estimation: queue_eta=self.queue_duration, ) - def get_request_params(self, websocket: fastapi.WebSocket) -> dict[str, Any]: - params = { - "url": str(websocket.url), - "headers": dict(websocket.headers), - "query_params": dict(websocket.query_params), - "path_params": dict(websocket.path_params), - "client": {"host": websocket.client.host, "port": websocket.client.port}, # type: ignore - } - try: - params[ - "session" - ] = websocket.session # forward OAuth information if available - except Exception: - pass - return params - async def call_prediction(self, events: list[Event], batch: bool): body = events[0].data if body is None: @@ -373,17 +349,13 @@ async def call_prediction(self, events: list[Event], batch: bool): username = events[0].username body.event_id = events[0]._id if not batch else None try: - body.request = self.get_request_params(events[0].websocket) + body.request = events[0].request except ValueError: pass if batch: body.data = list(zip(*[event.data.data for event in events if event.data])) - body.request = [ - self.get_request_params(event.websocket) - for event in events - if event.data - ] + body.request = events[0].request body.batched = True app = self.server_app @@ -436,13 +408,14 @@ async def process_events(self, events: list[Event], batch: bool) -> None: awake_events: list[Event] = [] try: for event in events: - client_awake = await self.gather_event_data(event) - if client_awake: - client_awake = await self.send_message( - event, {"msg": "process_starts"} - ) + self.awaiting_data_events[event._id] = event + client_awake = await event.get_data() + del self.awaiting_data_events[event._id] if client_awake: + event.send_message("process_starts") awake_events.append(event) + else: + await self.clean_event(event) if not awake_events: return begin_time = time.time() @@ -450,13 +423,13 @@ async def process_events(self, events: list[Event], batch: bool) -> None: response = await self.call_prediction(awake_events, batch) err = None except Exception as e: + traceback.print_exc() response = None err = e for event in awake_events: - await self.send_message( - event, + event.send_message( + "process_completed", { - "msg": "process_completed", "output": { "error": None if len(e.args) and e.args[0] is None @@ -464,6 +437,7 @@ async def process_events(self, events: list[Event], batch: bool) -> None: }, "success": False, }, + final=True, ) if response and response.get("is_generating", False): old_response = response @@ -471,20 +445,15 @@ async def process_events(self, events: list[Event], batch: bool) -> None: while response and response.get("is_generating", False): old_response = response old_err = err - open_ws = [] for event in awake_events: - open = await self.send_message( - event, + event.send_message( + "process_generating", { - "msg": "process_generating", "output": old_response, "success": old_response is not None, }, ) - open_ws.append(open) - awake_events = [ - e for e, is_open in zip(awake_events, open_ws) if is_open - ] + awake_events = [event for event in awake_events if event.alive] if not awake_events: return try: @@ -498,45 +467,36 @@ async def process_events(self, events: list[Event], batch: bool) -> None: relevant_response = err else: relevant_response = old_response or old_err - await self.send_log_updates_for_event(event) - await self.send_message( - event, + event.send_message( + "process_completed", { - "msg": "process_completed", "output": {"error": str(relevant_response)} if isinstance(relevant_response, Exception) else relevant_response, "success": relevant_response and not isinstance(relevant_response, Exception), }, + final=True, ) elif response: output = copy.deepcopy(response) for e, event in enumerate(awake_events): if batch and "data" in output: output["data"] = list(zip(*response.get("data")))[e] - await self.send_log_updates_for_event( - event - ) # clean out pending log updates first - await self.send_message( - event, + event.send_message( + "process_completed", { - "msg": "process_completed", "output": output, "success": response is not None, }, + final=True, ) end_time = time.time() if response is not None: self.update_estimation(end_time - begin_time) except Exception as e: - print(e) + traceback.print_exc() finally: - for event in awake_events: - try: - await event.disconnect() - except Exception: - pass try: self.active_jobs[self.active_jobs.index(events)] = None except ValueError: @@ -546,42 +506,21 @@ async def process_events(self, events: list[Event], batch: bool) -> None: # https://github.com/gradio-app/gradio/blob/f09aea34d6bd18c1e2fef80c86ab2476a6d1dd83/gradio/routes.py#L594-L596 pass for event in events: - await self.clean_event(event) # Always reset the state of the iterator # If the job finished successfully, this has no effect # If the job is cancelled, this will enable future runs # to start "from scratch" - await self.reset_iterators(event.session_hash, event.fn_index) - - async def send_message(self, event, data: dict, timeout: float | int = 1) -> bool: - try: - await asyncio.wait_for( - event.websocket.send_json(data=data), timeout=timeout - ) - return True - except Exception: - await self.clean_event(event) - return False - - async def get_message(self, event, timeout=5) -> tuple[PredictBody | None, bool]: - try: - data = await asyncio.wait_for( - event.websocket.receive_json(), timeout=timeout - ) - return PredictBody(**data), True - except AsyncTimeOutError: - await self.clean_event(event) - return None, False + await self.reset_iterators(event._id) - async def reset_iterators(self, session_hash: str, fn_index: int): + async def reset_iterators(self, event_id: str): # Do the same thing as the /reset route app = self.server_app if app is None: raise Exception("Server app has not been set.") - if session_hash not in app.iterators: + if event_id not in app.iterators: # Failure, but don't raise an error return async with app.lock: - app.iterators[session_hash][fn_index] = None - app.iterators_to_reset[session_hash].add(fn_index) + del app.iterators[event_id] + app.iterators_to_reset.add(event_id) return diff --git a/gradio/route_utils.py b/gradio/route_utils.py index 1e17e6a65867..074ae618fc88 100644 --- a/gradio/route_utils.py +++ b/gradio/route_utils.py @@ -104,7 +104,7 @@ def __init__( ): """ Can be instantiated with either a fastapi.Request or by manually passing in - attributes (needed for websocket-based queueing). + attributes (needed for queueing). Parameters: request: A fastapi.Request """ @@ -159,10 +159,9 @@ def compile_gr_request( body.data = [body.session_hash] if body.request: if body.batched: - gr_request = [Request(username=username, **req) for req in body.request] + gr_request = [Request(username=username, request=request)] else: - assert isinstance(body.request, dict) - gr_request = Request(username=username, **body.request) + gr_request = Request(username=username, request=body.request) else: if request is None: raise ValueError("request must be provided if body.request is None") @@ -172,7 +171,7 @@ def compile_gr_request( def restore_session_state(app: App, body: PredictBody): - fn_index = body.fn_index + event_id = body.event_id session_hash = getattr(body, "session_hash", None) if session_hash is not None: session_state = app.state_holder[session_hash] @@ -181,16 +180,18 @@ def restore_session_state(app: App, body: PredictBody): # the /reset route will mark the jobs as having been reset. # That way if the cancel job finishes BEFORE the job being cancelled # the job being cancelled will not overwrite the state of the iterator. - if fn_index in app.iterators_to_reset[session_hash]: - iterators = {} - app.iterators_to_reset[session_hash].remove(fn_index) + if event_id is None: + iterator = None + elif event_id in app.iterators_to_reset: + iterator = None + app.iterators_to_reset.remove(event_id) else: - iterators = app.iterators[session_hash] + iterator = app.iterators.get(event_id) else: session_state = SessionState(app.get_blocks()) - iterators = {} + iterator = None - return session_state, iterators + return session_state, iterator def prepare_event_data( @@ -213,13 +214,12 @@ async def call_process_api( gr_request: Union[Request, list[Request]], fn_index_inferred: int, ): - session_state, iterators = restore_session_state(app=app, body=body) + session_state, iterator = restore_session_state(app=app, body=body) dependency = app.get_blocks().dependencies[fn_index_inferred] event_data = prepare_event_data(app.get_blocks(), body, fn_index_inferred) - event_id = getattr(body, "event_id", None) + event_id = body.event_id - fn_index = body.fn_index session_hash = getattr(body, "session_hash", None) inputs = body.data @@ -234,19 +234,19 @@ async def call_process_api( inputs=inputs, request=gr_request, state=session_state, - iterators=iterators, + iterator=iterator, session_hash=session_hash, event_id=event_id, event_data=event_data, in_event_listener=True, ) iterator = output.pop("iterator", None) - if hasattr(body, "session_hash"): - app.iterators[body.session_hash][fn_index] = iterator + if event_id is not None: + app.iterators[event_id] = iterator if isinstance(output, Error): raise output except BaseException: - iterator = iterators.get(fn_index, None) + iterator = app.iterators.get(event_id) if event_id is not None else None if iterator is not None: # close off any streams that are still open run_id = id(iterator) pending_streams: dict[int, list] = ( diff --git a/gradio/routes.py b/gradio/routes.py index f5d8c07bfbbc..5146027c8d03 100644 --- a/gradio/routes.py +++ b/gradio/routes.py @@ -11,6 +11,7 @@ else: from importlib_resources import files import inspect +import json import mimetypes import os import posixpath @@ -20,17 +21,16 @@ import threading import time import traceback -from asyncio import TimeoutError as AsyncTimeOutError -from collections import defaultdict from pathlib import Path -from typing import TYPE_CHECKING, Any, Dict, List, Optional, Type +from queue import Empty as EmptyQueue +from typing import TYPE_CHECKING, Any, AsyncIterator, Dict, List, Optional, Type import anyio import fastapi import httpx import markupsafe import orjson -from fastapi import Depends, FastAPI, HTTPException, WebSocket, status +from fastapi import Depends, FastAPI, HTTPException, status from fastapi.middleware.cors import CORSMiddleware from fastapi.responses import ( FileResponse, @@ -46,7 +46,6 @@ from multipart.multipart import parse_options_header from starlette.background import BackgroundTask from starlette.responses import RedirectResponse, StreamingResponse -from starlette.websockets import WebSocketState import gradio import gradio.ranged_response as ranged_response @@ -127,8 +126,8 @@ def __init__(self, **kwargs): self.auth = None self.blocks: gradio.Blocks | None = None self.state_holder = StateHolder() - self.iterators = defaultdict(dict) - self.iterators_to_reset = defaultdict(set) + self.iterators: dict[str, AsyncIterator] = {} + self.iterators_to_reset: set[str] = set() self.lock = utils.safe_get_lock() self.cookie_id = secrets.token_urlsafe(32) self.queue_token = secrets.token_urlsafe(32) @@ -222,12 +221,6 @@ def login_check(user: str = Depends(get_current_user)): status_code=status.HTTP_401_UNAUTHORIZED, detail="Not authenticated" ) - async def ws_login_check(websocket: WebSocket) -> Optional[str]: - token = websocket.cookies.get( - f"access-token-{app.cookie_id}" - ) or websocket.cookies.get(f"access-token-unsecure-{app.cookie_id}") - return token # token is returned to authenticate the websocket connection in the endpoint handler. - @app.get("/token") @app.get("/token/") def get_token(request: fastapi.Request) -> dict: @@ -239,38 +232,32 @@ def get_token(request: fastapi.Request) -> dict: def app_id(request: fastapi.Request) -> dict: return {"app_id": app.get_blocks().app_id} - async def send_ping_periodically(websocket: WebSocket): - while True: - await websocket.send_text("PING") - await asyncio.sleep(1) - - async def listen_for_changes(websocket: WebSocket): - assert app.change_event - while True: - if app.change_event.is_set(): - await websocket.send_text("CHANGE") - app.change_event.clear() - await asyncio.sleep(0.1) # Short sleep to not make this a tight loop - - @app.websocket("/dev/reload") - async def notify_changes(websocket: WebSocket): - await websocket.accept() - - ping = asyncio.create_task(send_ping_periodically(websocket)) - notify = asyncio.create_task(listen_for_changes(websocket)) - tasks = {ping, notify} - ping.add_done_callback(tasks.remove) - notify.add_done_callback(tasks.remove) - done, pending = await asyncio.wait( - [ping, notify], - return_when=asyncio.FIRST_COMPLETED, - ) + @app.get("/dev/reload", dependencies=[Depends(login_check)]) + async def notify_changes( + request: fastapi.Request, + ): + async def reload_checker(request: fastapi.Request): + heartbeat_rate = 15 + check_rate = 0.05 + last_heartbeat = time.perf_counter() - for task in pending: - task.cancel() + while True: + if await request.is_disconnected(): + return + + if app.change_event and app.change_event.is_set(): + app.change_event.clear() + yield """data: CHANGE\n\n""" - if any(isinstance(task.exception(), Exception) for task in done): - await websocket.close() + await asyncio.sleep(check_rate) + if time.perf_counter() - last_heartbeat > heartbeat_rate: + yield """data: HEARTBEAT\n\n""" + last_heartbeat = time.time() + + return StreamingResponse( + reload_checker(request), + media_type="text/event-stream", + ) @app.post("/login") @app.post("/login/") @@ -526,11 +513,12 @@ async def file_deprecated(path: str, request: fastapi.Request): @app.post("/reset/") @app.post("/reset") async def reset_iterator(body: ResetBody): - if body.session_hash not in app.iterators: + if body.event_id not in app.iterators: return {"success": False} async with app.lock: - app.iterators[body.session_hash][body.fn_index] = None - app.iterators_to_reset[body.session_hash].add(body.fn_index) + del app.iterators[body.event_id] + app.iterators_to_reset.add(body.event_id) + await app.get_blocks()._queue.clean_event(body.event_id) return {"success": True} # had to use '/run' endpoint for Colab compatibility, '/api' supported for backwards compatibility @@ -580,46 +568,24 @@ async def predict( ) return output - @app.websocket("/queue/join") - async def join_queue( - websocket: WebSocket, - token: Optional[str] = Depends(ws_login_check), + @app.get("/queue/join", dependencies=[Depends(login_check)]) + async def queue_join( + fn_index: int, + session_hash: str, + request: fastapi.Request, + username: str = Depends(get_current_user), ): blocks = app.get_blocks() - if app.auth is not None and token is None: - await websocket.close(code=status.WS_1008_POLICY_VIOLATION) - return if blocks._queue.server_app is None: blocks._queue.set_server_app(app) - await websocket.accept() - # In order to cancel jobs, we need the session_hash and fn_index - # to create a unique id for each job - try: - await asyncio.wait_for( - websocket.send_json({"msg": "send_hash"}), timeout=5 - ) - except AsyncTimeOutError: - return - try: - session_info = await asyncio.wait_for( - websocket.receive_json(), timeout=5 - ) - except AsyncTimeOutError: - return + event = Event(session_hash, fn_index, request, username) - event = Event( - websocket, session_info["session_hash"], session_info["fn_index"] - ) - # set the username into Event to allow using the same username for call_prediction - event.username = app.tokens.get(token) - event.session_hash = session_info["session_hash"] - - # Continuous events are not put in the queue so that they do not + # Continuous events are not put in the queue so that they do not # occupy the queue's resource as they are expected to run forever if blocks.dependencies[event.fn_index].get("every", 0): await cancel_tasks({f"{event.session_hash}_{event.fn_index}"}) - await blocks._queue.reset_iterators(event.session_hash, event.fn_index) + await blocks._queue.reset_iterators(event._id) blocks._queue.continuous_tasks.append(event) task = run_coro_in_background( blocks._queue.process_events, [event], False @@ -628,17 +594,49 @@ async def join_queue( app._asyncio_tasks.append(task) else: rank = blocks._queue.push(event) - if rank is None: - await blocks._queue.send_message(event, {"msg": "queue_full"}) - await event.disconnect() - return - estimation = blocks._queue.get_estimation() - await blocks._queue.send_estimation(event, estimation, rank) - while True: - await asyncio.sleep(1) - if websocket.application_state == WebSocketState.DISCONNECTED: - return + event.send_message("queue_full", final=True) + else: + estimation = blocks._queue.get_estimation() + await blocks._queue.send_estimation(event, estimation, rank) + + async def sse_stream(request: fastapi.Request): + last_heartbeat = time.perf_counter() + while True: + if await request.is_disconnected(): + await blocks._queue.clean_event(event) + if not event.alive: + return + + heartbeat_rate = 15 + check_rate = 0.05 + message = None + try: + message = event.message_queue.get_nowait() + if message is None: # end of stream marker + return + except EmptyQueue: + await asyncio.sleep(check_rate) + if time.perf_counter() - last_heartbeat > heartbeat_rate: + message = {"msg": "heartbeat"} + last_heartbeat = time.time() + + if message: + yield f"data: {json.dumps(message)}\n\n" + + return StreamingResponse( + sse_stream(request), + media_type="text/event-stream", + ) + + @app.post("/queue/data", dependencies=[Depends(login_check)]) + async def queue_data( + body: PredictBody, + request: fastapi.Request, + username: str = Depends(get_current_user), + ): + blocks = app.get_blocks() + blocks._queue.attach_data(body) @app.post("/component_server", dependencies=[Depends(login_check)]) @app.post("/component_server/", dependencies=[Depends(login_check)]) @@ -803,8 +801,7 @@ def read_main(): @app.on_event("startup") async def start_queue(): - if gradio_app.get_blocks().enable_queue: - gradio_app.get_blocks().startup_events() + gradio_app.get_blocks().startup_events() app.mount(path, gradio_app) return app diff --git a/gradio/utils.py b/gradio/utils.py index cd64bf34df63..84aecb658d9f 100644 --- a/gradio/utils.py +++ b/gradio/utils.py @@ -95,7 +95,7 @@ def swap_blocks(self, demo: Blocks): assert self.running_app.blocks # Copy over the blocks to get new components and events but # not a new queue - if hasattr(self.running_app.blocks, "_queue"): + if self.running_app.blocks._queue: self.running_app.blocks._queue.blocks_dependencies = demo.dependencies demo._queue = self.running_app.blocks._queue self.running_app.blocks = demo diff --git a/js/app/src/Index.svelte b/js/app/src/Index.svelte index 9ad582bd3bac..489cb2ca4f95 100644 --- a/js/app/src/Index.svelte +++ b/js/app/src/Index.svelte @@ -81,7 +81,7 @@ export let container: boolean; export let info: boolean; export let eager: boolean; - let websocket: WebSocket; + let eventSource: EventSource; // These utilities are exported to be injectable for the Wasm version. export let mount_css: typeof default_mount_css = default_mount_css; @@ -190,7 +190,7 @@ message: "", load_status: "pending", status: "sleeping", - detail: "SLEEPING" + detail: "SLEEPING", }; let app: Awaited>; @@ -217,7 +217,7 @@ app = await client(api_url, { status_callback: handle_status, - normalise_files: false + normalise_files: false, }); config = app.config; window.__gradio_space__ = config.space_id; @@ -226,7 +226,7 @@ message: "", load_status: "complete", status: "running", - detail: "RUNNING" + detail: "RUNNING", }; await mount_custom_css(wrapper, config.css); @@ -236,18 +236,30 @@ if (config.dev_mode) { setTimeout(() => { const { host } = new URL(api_url); - let url = new URL(`ws://${host}/dev/reload`); - websocket = new WebSocket(url); - websocket.onmessage = async function (event) { + let url = new URL(`http://${host}/dev/reload`); + eventSource = new EventSource(url); + eventSource.onmessage = async function (event) { if (event.data === "CHANGE") { app = await client(api_url, { status_callback: handle_status, - normalise_files: false + normalise_files: false, }); config = app.config; window.__gradio_space__ = config.space_id; } }; + + // websocket = new WebSocket(url); + // websocket.onmessage = async function (event) { + // if (event.data === "CHANGE") { + // app = await client(api_url, { + // status_callback: handle_status, + // normalise_files: false + // }); + // config = app.config; + // window.__gradio_space__ = config.space_id; + // } + // }; }, 200); } }); @@ -292,7 +304,7 @@ CONFIG_ERROR: $_("errors.config_error"), BUILD_ERROR: $_("errors.build_error"), RUNTIME_ERROR: $_("errors.runtime_error"), - PAUSED: $_("errors.space_paused") + PAUSED: $_("errors.space_paused"), } as const, title(error: error_types): string { return encodeURIComponent($_("errors.space_not_working")); @@ -303,7 +315,7 @@ this.readable_error[error] || "an error" }.\n\nIt would be great if you could take a look at this because this space is being embedded on ${site}.\n\nThanks!` ); - } + }, }; onMount(async () => { @@ -315,7 +327,7 @@ new CustomEvent("render", { bubbles: true, cancelable: false, - composed: true + composed: true, }) ); } diff --git a/js/app/test/blocks_chained_events.spec.ts b/js/app/test/blocks_chained_events.spec.ts index 159ccdcef775..2299f7b8af33 100644 --- a/js/app/test/blocks_chained_events.spec.ts +++ b/js/app/test/blocks_chained_events.spec.ts @@ -81,10 +81,10 @@ test("ValueError makes the toast show up when show_error=True", async ({ test("gr.Info makes the toast show up", async ({ page }) => { await page.click("text=Trigger Info"); - const toast = page.getByTestId("toast-body"); + const toast = await page.getByTestId("toast-body"); expect(toast).toContainText("This is some info"); - const close = page.getByTestId("toast-close"); + const close = await page.getByTestId("toast-close"); await close.click(); await expect(page.getByTestId("toast-body")).toHaveCount(0); }); diff --git a/js/app/test/blocks_essay.spec.ts b/js/app/test/blocks_essay.spec.ts index 1aefd6df7b6b..4382f004e9e9 100644 --- a/js/app/test/blocks_essay.spec.ts +++ b/js/app/test/blocks_essay.spec.ts @@ -14,6 +14,7 @@ test("updates frontend correctly", async ({ page }) => { textbox.fill("hello world"); await short_btn.check(); await expect(textbox).toHaveValue("hello world"); + await expect(textbox).toHaveAttribute("rows", "2"); await hidden_btn.check(); await expect(textbox).toBeHidden(); diff --git a/js/app/test/blocks_inputs.spec.ts b/js/app/test/blocks_inputs.spec.ts index db29ce45dfeb..13ca196b41ba 100644 --- a/js/app/test/blocks_inputs.spec.ts +++ b/js/app/test/blocks_inputs.spec.ts @@ -8,10 +8,7 @@ test("renders the correct elements", async ({ page }) => { await textboxOne.fill("hi"); await textboxTwo.fill("dawood"); - await Promise.all([ - page.click('text="Submit"'), - page.waitForResponse("**/run/predict") - ]); + await page.click('text="Submit"'); await expect(await page.getByLabel("Output")).toHaveValue("hi dawood"); }); diff --git a/js/app/test/blocks_xray.spec.ts b/js/app/test/blocks_xray.spec.ts index 108abbf49692..2678e58500e4 100644 --- a/js/app/test/blocks_xray.spec.ts +++ b/js/app/test/blocks_xray.spec.ts @@ -16,11 +16,7 @@ test("can run an api request and display the data", async ({ page }) => { await page.getByTitle("Lung Cancer").check(); const run_button = await page.locator("button", { hasText: /Run/ }).first(); - - await Promise.all([ - run_button.click(), - page.waitForResponse("**/run/predict") - ]); + await run_button.click(); const json = await page.getByTestId("json").first(); await expect(json).toContainText(`Covid: 0.25, Lung Cancer: 0.5`); diff --git a/js/app/test/clear_components.spec.ts b/js/app/test/clear_components.spec.ts index e0672e3087a6..8a19ef6a418d 100644 --- a/js/app/test/clear_components.spec.ts +++ b/js/app/test/clear_components.spec.ts @@ -14,15 +14,9 @@ test("Components value can be set via callable to a non-None value", async ({ }); test("gr.ClearButton clears every component's value", async ({ page }) => { - await Promise.all([ - page.waitForResponse("**/run/predict"), - page.click("text=Get Values") - ]); + await page.click("text=Get Values"); await expect(page.getByLabel("Are all cleared?")).toHaveValue("False"); await page.click("text=Clear"); - await Promise.all([ - page.waitForResponse("**/run/predict"), - page.click("text=Get Values") - ]); + await page.click("text=Get Values"); await expect(page.getByLabel("Are all cleared?")).toHaveValue("True"); }); diff --git a/js/app/test/input_output.spec.ts b/js/app/test/input_output.spec.ts index 82e04729e2f1..20401d9ada91 100644 --- a/js/app/test/input_output.spec.ts +++ b/js/app/test/input_output.spec.ts @@ -4,9 +4,6 @@ test("a component acts as both input and output", async ({ page }) => { const textbox = await page.getByLabel("Input-Output"); await textbox.fill("test"); - await Promise.all([ - page.click("button"), - page.waitForResponse("**/run/predict") - ]); + await page.click("button"); await expect(await textbox).toHaveValue("tset"); }); diff --git a/js/app/test/outbreak_forecast.spec.ts b/js/app/test/outbreak_forecast.spec.ts index 4db0385da6a0..d937353ee9f2 100644 --- a/js/app/test/outbreak_forecast.spec.ts +++ b/js/app/test/outbreak_forecast.spec.ts @@ -9,10 +9,7 @@ test("selecting matplotlib should show matplotlib image and pressing clear shoul await page.getByRole("option", { name: "January" }).click(); await page.getByLabel("Social Distancing?").check(); - await Promise.all([ - page.click("text=Submit"), - page.waitForResponse("**/run/predict") - ]); + await page.click("text=Submit"); const matplotlib_img = await page.getByTestId("matplotlib").getByRole("img"); const matplotlib_img_data = await matplotlib_img.getAttribute("src"); @@ -31,10 +28,7 @@ test("selecting plotly should show plotly plot and pressing clear should clear o await page.getByRole("option", { name: "January" }).click(); await page.getByLabel("Social Distancing?").check(); - await Promise.all([ - page.click("text=Submit"), - page.waitForResponse("**/run/predict") - ]); + await page.click("text=Submit"); await expect(page.locator(".js-plotly-plot")).toHaveCount(1); await page.getByRole("button", { name: "Clear" }).click(); await expect(page.locator(".js-plotly-plot")).toHaveCount(0); @@ -49,10 +43,7 @@ test("selecting altair should show altair plot and pressing clear should clear o await page.getByRole("option", { name: "January" }).click(); await page.getByLabel("Social Distancing?").check(); - await Promise.all([ - page.click("text=Submit"), - page.waitForResponse("**/run/predict") - ]); + await page.click("text=Submit"); const altair = await page.getByTestId("altair"); await expect(altair).toHaveCount(1); @@ -71,10 +62,7 @@ test("switching between all 3 plot types and pressing submit should update outpu await page.getByRole("option", { name: "January" }).click(); await page.getByLabel("Social Distancing?").check(); - await Promise.all([ - page.click("text=Submit"), - page.waitForResponse("**/run/predict") - ]); + await page.click("text=Submit"); const matplotlib_img = await page.getByTestId("matplotlib").getByRole("img"); const matplotlib_img_data = await matplotlib_img.getAttribute("src"); @@ -84,20 +72,14 @@ test("switching between all 3 plot types and pressing submit should update outpu await page.getByLabel("Plot Type").click(); await page.getByRole("option", { name: "Plotly" }).click(); - await Promise.all([ - page.click("text=Submit"), - page.waitForResponse("**/run/predict") - ]); + await page.click("text=Submit"); await expect(page.locator(".js-plotly-plot")).toHaveCount(1); //Altair await page.getByLabel("Plot Type").click(); await page.getByRole("option", { name: "Altair" }).click(); - await Promise.all([ - page.click("text=Submit"), - page.waitForResponse("**/run/predict") - ]); + await page.click("text=Submit"); const altair = await page.getByTestId("altair"); await expect(altair).toHaveCount(1); }); diff --git a/js/preview/test/test/frontend/package-lock.json b/js/preview/test/test/frontend/package-lock.json index b75a26b4ecfc..efe271779126 100644 --- a/js/preview/test/test/frontend/package-lock.json +++ b/js/preview/test/test/frontend/package-lock.json @@ -1,1574 +1,1574 @@ { - "name": "gradio_test", - "version": "0.2.0-beta.6", - "lockfileVersion": 2, - "requires": true, - "packages": { - "": { - "name": "gradio_test", - "version": "0.2.0-beta.6", - "license": "ISC", - "dependencies": { - "@gradio/atoms": "0.2.0-beta.4", - "@gradio/statustracker": "0.3.0-beta.6", - "@gradio/utils": "0.2.0-beta.4", - "@zerodevx/svelte-json-view": "^1.0.7" - } - }, - "node_modules/@ampproject/remapping": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", - "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", - "peer": true, - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.0", - "@jridgewell/trace-mapping": "^0.3.9" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@esbuild/android-arm": { - "version": "0.19.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.5.tgz", - "integrity": "sha512-bhvbzWFF3CwMs5tbjf3ObfGqbl/17ict2/uwOSfr3wmxDE6VdS2GqY/FuzIPe0q0bdhj65zQsvqfArI9MY6+AA==", - "cpu": [ - "arm" - ], - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/android-arm64": { - "version": "0.19.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.5.tgz", - "integrity": "sha512-5d1OkoJxnYQfmC+Zd8NBFjkhyCNYwM4n9ODrycTFY6Jk1IGiZ+tjVJDDSwDt77nK+tfpGP4T50iMtVi4dEGzhQ==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/android-x64": { - "version": "0.19.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.5.tgz", - "integrity": "sha512-9t+28jHGL7uBdkBjL90QFxe7DVA+KGqWlHCF8ChTKyaKO//VLuoBricQCgwhOjA1/qOczsw843Fy4cbs4H3DVA==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.19.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.5.tgz", - "integrity": "sha512-mvXGcKqqIqyKoxq26qEDPHJuBYUA5KizJncKOAf9eJQez+L9O+KfvNFu6nl7SCZ/gFb2QPaRqqmG0doSWlgkqw==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.19.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.5.tgz", - "integrity": "sha512-Ly8cn6fGLNet19s0X4unjcniX24I0RqjPv+kurpXabZYSXGM4Pwpmf85WHJN3lAgB8GSth7s5A0r856S+4DyiA==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.19.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.5.tgz", - "integrity": "sha512-GGDNnPWTmWE+DMchq1W8Sd0mUkL+APvJg3b11klSGUDvRXh70JqLAO56tubmq1s2cgpVCSKYywEiKBfju8JztQ==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.19.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.5.tgz", - "integrity": "sha512-1CCwDHnSSoA0HNwdfoNY0jLfJpd7ygaLAp5EHFos3VWJCRX9DMwWODf96s9TSse39Br7oOTLryRVmBoFwXbuuQ==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-arm": { - "version": "0.19.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.5.tgz", - "integrity": "sha512-lrWXLY/vJBzCPC51QN0HM71uWgIEpGSjSZZADQhq7DKhPcI6NH1IdzjfHkDQws2oNpJKpR13kv7/pFHBbDQDwQ==", - "cpu": [ - "arm" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.19.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.5.tgz", - "integrity": "sha512-o3vYippBmSrjjQUCEEiTZ2l+4yC0pVJD/Dl57WfPwwlvFkrxoSO7rmBZFii6kQB3Wrn/6GwJUPLU5t52eq2meA==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.19.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.5.tgz", - "integrity": "sha512-MkjHXS03AXAkNp1KKkhSKPOCYztRtK+KXDNkBa6P78F8Bw0ynknCSClO/ztGszILZtyO/lVKpa7MolbBZ6oJtQ==", - "cpu": [ - "ia32" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.19.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.5.tgz", - "integrity": "sha512-42GwZMm5oYOD/JHqHska3Jg0r+XFb/fdZRX+WjADm3nLWLcIsN27YKtqxzQmGNJgu0AyXg4HtcSK9HuOk3v1Dw==", - "cpu": [ - "loong64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.19.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.5.tgz", - "integrity": "sha512-kcjndCSMitUuPJobWCnwQ9lLjiLZUR3QLQmlgaBfMX23UEa7ZOrtufnRds+6WZtIS9HdTXqND4yH8NLoVVIkcg==", - "cpu": [ - "mips64el" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.19.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.5.tgz", - "integrity": "sha512-yJAxJfHVm0ZbsiljbtFFP1BQKLc8kUF6+17tjQ78QjqjAQDnhULWiTA6u0FCDmYT1oOKS9PzZ2z0aBI+Mcyj7Q==", - "cpu": [ - "ppc64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.19.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.5.tgz", - "integrity": "sha512-5u8cIR/t3gaD6ad3wNt1MNRstAZO+aNyBxu2We8X31bA8XUNyamTVQwLDA1SLoPCUehNCymhBhK3Qim1433Zag==", - "cpu": [ - "riscv64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.19.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.5.tgz", - "integrity": "sha512-Z6JrMyEw/EmZBD/OFEFpb+gao9xJ59ATsoTNlj39jVBbXqoZm4Xntu6wVmGPB/OATi1uk/DB+yeDPv2E8PqZGw==", - "cpu": [ - "s390x" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.19.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.5.tgz", - "integrity": "sha512-psagl+2RlK1z8zWZOmVdImisMtrUxvwereIdyJTmtmHahJTKb64pAcqoPlx6CewPdvGvUKe2Jw+0Z/0qhSbG1A==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.19.5", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.5.tgz", - "integrity": "sha512-kL2l+xScnAy/E/3119OggX8SrWyBEcqAh8aOY1gr4gPvw76la2GlD4Ymf832UCVbmuWeTf2adkZDK+h0Z/fB4g==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.19.5", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.5.tgz", - "integrity": "sha512-sPOfhtzFufQfTBgRnE1DIJjzsXukKSvZxloZbkJDG383q0awVAq600pc1nfqBcl0ice/WN9p4qLc39WhBShRTA==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.19.5", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.5.tgz", - "integrity": "sha512-dGZkBXaafuKLpDSjKcB0ax0FL36YXCvJNnztjKV+6CO82tTYVDSH2lifitJ29jxRMoUhgkg9a+VA/B03WK5lcg==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.19.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.5.tgz", - "integrity": "sha512-dWVjD9y03ilhdRQ6Xig1NWNgfLtf2o/STKTS+eZuF90fI2BhbwD6WlaiCGKptlqXlURVB5AUOxUj09LuwKGDTg==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.19.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.5.tgz", - "integrity": "sha512-4liggWIA4oDgUxqpZwrDhmEfAH4d0iljanDOK7AnVU89T6CzHon/ony8C5LeOdfgx60x5cnQJFZwEydVlYx4iw==", - "cpu": [ - "ia32" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.19.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.5.tgz", - "integrity": "sha512-czTrygUsB/jlM8qEW5MD8bgYU2Xg14lo6kBDXW6HdxKjh8M5PzETGiSHaz9MtbXBYDloHNUAUW2tMiKW4KM9Mw==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@formatjs/ecma402-abstract": { - "version": "1.11.4", - "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.11.4.tgz", - "integrity": "sha512-EBikYFp2JCdIfGEb5G9dyCkTGDmC57KSHhRQOC3aYxoPWVZvfWCDjZwkGYHN7Lis/fmuWl906bnNTJifDQ3sXw==", - "dependencies": { - "@formatjs/intl-localematcher": "0.2.25", - "tslib": "^2.1.0" - } - }, - "node_modules/@formatjs/fast-memoize": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-1.2.1.tgz", - "integrity": "sha512-Rg0e76nomkz3vF9IPlKeV+Qynok0r7YZjL6syLz4/urSg0IbjPZCB/iYUMNsYA643gh4mgrX3T7KEIFIxJBQeg==", - "dependencies": { - "tslib": "^2.1.0" - } - }, - "node_modules/@formatjs/icu-messageformat-parser": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.1.0.tgz", - "integrity": "sha512-Qxv/lmCN6hKpBSss2uQ8IROVnta2r9jd3ymUEIjm2UyIkUCHVcbUVRGL/KS/wv7876edvsPe+hjHVJ4z8YuVaw==", - "dependencies": { - "@formatjs/ecma402-abstract": "1.11.4", - "@formatjs/icu-skeleton-parser": "1.3.6", - "tslib": "^2.1.0" - } - }, - "node_modules/@formatjs/icu-skeleton-parser": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.3.6.tgz", - "integrity": "sha512-I96mOxvml/YLrwU2Txnd4klA7V8fRhb6JG/4hm3VMNmeJo1F03IpV2L3wWt7EweqNLES59SZ4d6hVOPCSf80Bg==", - "dependencies": { - "@formatjs/ecma402-abstract": "1.11.4", - "tslib": "^2.1.0" - } - }, - "node_modules/@formatjs/intl-localematcher": { - "version": "0.2.25", - "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.2.25.tgz", - "integrity": "sha512-YmLcX70BxoSopLFdLr1Ds99NdlTI2oWoLbaUW2M406lxOIPzE1KQhRz2fPUkq34xVZQaihCoU29h0KK7An3bhA==", - "dependencies": { - "tslib": "^2.1.0" - } - }, - "node_modules/@gradio/atoms": { - "version": "0.2.0-beta.4", - "resolved": "https://registry.npmjs.org/@gradio/atoms/-/atoms-0.2.0-beta.4.tgz", - "integrity": "sha512-xZfP9oPmb7iiuGl7KB4vVELSVk9f3w5Y9KRIxkAaMb+oeRpmb5uDtKQPAxntpm0W9rKAZmYG+DIWhInlu1eeKA==", - "dependencies": { - "@gradio/icons": "^0.2.0-beta.1", - "@gradio/utils": "^0.2.0-beta.4" - } - }, - "node_modules/@gradio/column": { - "version": "0.1.0-beta.2", - "resolved": "https://registry.npmjs.org/@gradio/column/-/column-0.1.0-beta.2.tgz", - "integrity": "sha512-vL0GECdNL4wAaO/o0JcF3fm2xyMrx5DJWXUiPq/sUwqZwwB95srPGKBVTmVja3HproVXCBEnTzPQmRlrwWK67w==" - }, - "node_modules/@gradio/icons": { - "version": "0.2.0-beta.1", - "resolved": "https://registry.npmjs.org/@gradio/icons/-/icons-0.2.0-beta.1.tgz", - "integrity": "sha512-6nwP1NIi0u4YQoSoaqC/rY0wuCvJHsnK+8aHDOE37070JpzBGuxB/VUlEgO7trNz5zI/EJy2htIRYsqz1vKmXA==" - }, - "node_modules/@gradio/statustracker": { - "version": "0.3.0-beta.6", - "resolved": "https://registry.npmjs.org/@gradio/statustracker/-/statustracker-0.3.0-beta.6.tgz", - "integrity": "sha512-AIhaMCnr2uibHdqRrs4K8ZUvZK0q5e430TcvoduLOkaoOrkfnqetrHaHdOLNBz+H4kJlXJRsmt7ZZYV4wwMXRQ==", - "dependencies": { - "@gradio/atoms": "^0.2.0-beta.4", - "@gradio/column": "^0.1.0-beta.2", - "@gradio/icons": "^0.2.0-beta.1", - "@gradio/utils": "^0.2.0-beta.4" - } - }, - "node_modules/@gradio/theme": { - "version": "0.2.0-beta.2", - "resolved": "https://registry.npmjs.org/@gradio/theme/-/theme-0.2.0-beta.2.tgz", - "integrity": "sha512-yKrA8eE02URtXUC9w98lBW8tqZk5oGumbBH7bFKOAhsrv1sbVZKir18P4a2/EL4XJ6Um36MwhPB3D5ipMniV5g==" - }, - "node_modules/@gradio/utils": { - "version": "0.2.0-beta.4", - "resolved": "https://registry.npmjs.org/@gradio/utils/-/utils-0.2.0-beta.4.tgz", - "integrity": "sha512-jaOY3IQs1MnWRagXBICHXl5ZDKFqgF4XMfgsZNjTQxTG6THFOCsrUc14X1BNmXWkh9zVXJJTZcXifekj8O6LZQ==", - "dependencies": { - "@gradio/theme": "^0.2.0-beta.2", - "svelte-i18n": "^3.6.0" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", - "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", - "peer": true, - "dependencies": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", - "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", - "peer": true, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/set-array": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", - "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", - "peer": true, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", - "peer": true - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.20", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz", - "integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==", - "peer": true, - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@types/estree": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.3.tgz", - "integrity": "sha512-CS2rOaoQ/eAgAfcTfq6amKG7bsN+EMcgGY4FAFQdvSj2y1ixvOZTUA9mOtCai7E1SYu283XNw7urKK30nP3wkQ==", - "peer": true - }, - "node_modules/@zerodevx/svelte-json-view": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/@zerodevx/svelte-json-view/-/svelte-json-view-1.0.7.tgz", - "integrity": "sha512-yW0MV+9BCKOwzt3h86y3xDqYdI5st+Rxk+L5pa0Utq7nlPD+VvxyhL7R1gJoLxQvWwjyAvY/fyUCFTdwDyI14w==", - "peerDependencies": { - "svelte": "^3.57.0 || ^4.0.0" - } - }, - "node_modules/acorn": { - "version": "8.10.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", - "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==", - "peer": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/aria-query": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", - "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", - "peer": true, - "dependencies": { - "dequal": "^2.0.3" - } - }, - "node_modules/axobject-query": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.2.1.tgz", - "integrity": "sha512-jsyHu61e6N4Vbz/v18DHwWYKK0bSWLqn47eeDSKPB7m8tqMHF9YJ+mhIk2lVteyZrY8tnSj/jHOv4YiTCuCJgg==", - "peer": true, - "dependencies": { - "dequal": "^2.0.3" - } - }, - "node_modules/cli-color": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/cli-color/-/cli-color-2.0.3.tgz", - "integrity": "sha512-OkoZnxyC4ERN3zLzZaY9Emb7f/MhBOIpePv0Ycok0fJYT+Ouo00UBEIwsVsr0yoow++n5YWlSUgST9GKhNHiRQ==", - "dependencies": { - "d": "^1.0.1", - "es5-ext": "^0.10.61", - "es6-iterator": "^2.0.3", - "memoizee": "^0.4.15", - "timers-ext": "^0.1.7" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/code-red": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/code-red/-/code-red-1.0.4.tgz", - "integrity": "sha512-7qJWqItLA8/VPVlKJlFXU+NBlo/qyfs39aJcuMT/2ere32ZqvF5OSxgdM5xOfJJ7O429gg2HM47y8v9P+9wrNw==", - "peer": true, - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.4.15", - "@types/estree": "^1.0.1", - "acorn": "^8.10.0", - "estree-walker": "^3.0.3", - "periscopic": "^3.1.0" - } - }, - "node_modules/css-tree": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz", - "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==", - "peer": true, - "dependencies": { - "mdn-data": "2.0.30", - "source-map-js": "^1.0.1" - }, - "engines": { - "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" - } - }, - "node_modules/d": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", - "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", - "dependencies": { - "es5-ext": "^0.10.50", - "type": "^1.0.1" - } - }, - "node_modules/deepmerge": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", - "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/dequal": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", - "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", - "peer": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/es5-ext": { - "version": "0.10.62", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.62.tgz", - "integrity": "sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA==", - "hasInstallScript": true, - "dependencies": { - "es6-iterator": "^2.0.3", - "es6-symbol": "^3.1.3", - "next-tick": "^1.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/es6-iterator": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", - "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==", - "dependencies": { - "d": "1", - "es5-ext": "^0.10.35", - "es6-symbol": "^3.1.1" - } - }, - "node_modules/es6-symbol": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", - "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", - "dependencies": { - "d": "^1.0.1", - "ext": "^1.1.2" - } - }, - "node_modules/es6-weak-map": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.3.tgz", - "integrity": "sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA==", - "dependencies": { - "d": "1", - "es5-ext": "^0.10.46", - "es6-iterator": "^2.0.3", - "es6-symbol": "^3.1.1" - } - }, - "node_modules/esbuild": { - "version": "0.19.5", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.5.tgz", - "integrity": "sha512-bUxalY7b1g8vNhQKdB24QDmHeY4V4tw/s6Ak5z+jJX9laP5MoQseTOMemAr0gxssjNcH0MCViG8ONI2kksvfFQ==", - "hasInstallScript": true, - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=12" - }, - "optionalDependencies": { - "@esbuild/android-arm": "0.19.5", - "@esbuild/android-arm64": "0.19.5", - "@esbuild/android-x64": "0.19.5", - "@esbuild/darwin-arm64": "0.19.5", - "@esbuild/darwin-x64": "0.19.5", - "@esbuild/freebsd-arm64": "0.19.5", - "@esbuild/freebsd-x64": "0.19.5", - "@esbuild/linux-arm": "0.19.5", - "@esbuild/linux-arm64": "0.19.5", - "@esbuild/linux-ia32": "0.19.5", - "@esbuild/linux-loong64": "0.19.5", - "@esbuild/linux-mips64el": "0.19.5", - "@esbuild/linux-ppc64": "0.19.5", - "@esbuild/linux-riscv64": "0.19.5", - "@esbuild/linux-s390x": "0.19.5", - "@esbuild/linux-x64": "0.19.5", - "@esbuild/netbsd-x64": "0.19.5", - "@esbuild/openbsd-x64": "0.19.5", - "@esbuild/sunos-x64": "0.19.5", - "@esbuild/win32-arm64": "0.19.5", - "@esbuild/win32-ia32": "0.19.5", - "@esbuild/win32-x64": "0.19.5" - } - }, - "node_modules/estree-walker": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", - "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", - "peer": true, - "dependencies": { - "@types/estree": "^1.0.0" - } - }, - "node_modules/event-emitter": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", - "integrity": "sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==", - "dependencies": { - "d": "1", - "es5-ext": "~0.10.14" - } - }, - "node_modules/ext": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz", - "integrity": "sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==", - "dependencies": { - "type": "^2.7.2" - } - }, - "node_modules/ext/node_modules/type": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/type/-/type-2.7.2.tgz", - "integrity": "sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==" - }, - "node_modules/globalyzer": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/globalyzer/-/globalyzer-0.1.0.tgz", - "integrity": "sha512-40oNTM9UfG6aBmuKxk/giHn5nQ8RVz/SS4Ir6zgzOv9/qC3kKZ9v4etGTcJbEl/NyVQH7FGU7d+X1egr57Md2Q==" - }, - "node_modules/globrex": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/globrex/-/globrex-0.1.2.tgz", - "integrity": "sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==" - }, - "node_modules/intl-messageformat": { - "version": "9.13.0", - "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-9.13.0.tgz", - "integrity": "sha512-7sGC7QnSQGa5LZP7bXLDhVDtQOeKGeBFGHF2Y8LVBwYZoQZCgWeKoPGTa5GMG8g/TzDgeXuYJQis7Ggiw2xTOw==", - "dependencies": { - "@formatjs/ecma402-abstract": "1.11.4", - "@formatjs/fast-memoize": "1.2.1", - "@formatjs/icu-messageformat-parser": "2.1.0", - "tslib": "^2.1.0" - } - }, - "node_modules/is-promise": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", - "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==" - }, - "node_modules/is-reference": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.2.tgz", - "integrity": "sha512-v3rht/LgVcsdZa3O2Nqs+NMowLOxeOm7Ay9+/ARQ2F+qEoANRcqrjAZKGN0v8ymUetZGgkp26LTnGT7H0Qo9Pg==", - "peer": true, - "dependencies": { - "@types/estree": "*" - } - }, - "node_modules/locate-character": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-character/-/locate-character-3.0.0.tgz", - "integrity": "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==", - "peer": true - }, - "node_modules/lru-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/lru-queue/-/lru-queue-0.1.0.tgz", - "integrity": "sha512-BpdYkt9EvGl8OfWHDQPISVpcl5xZthb+XPsbELj5AQXxIC8IriDZIQYjBJPEm5rS420sjZ0TLEzRcq5KdBhYrQ==", - "dependencies": { - "es5-ext": "~0.10.2" - } - }, - "node_modules/magic-string": { - "version": "0.30.5", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.5.tgz", - "integrity": "sha512-7xlpfBaQaP/T6Vh8MO/EqXSW5En6INHEvEXQiuff7Gku0PWjU3uf6w/j9o7O+SpB5fOAkrI5HeoNgwjEO0pFsA==", - "peer": true, - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.4.15" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/mdn-data": { - "version": "2.0.30", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", - "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==", - "peer": true - }, - "node_modules/memoizee": { - "version": "0.4.15", - "resolved": "https://registry.npmjs.org/memoizee/-/memoizee-0.4.15.tgz", - "integrity": "sha512-UBWmJpLZd5STPm7PMUlOw/TSy972M+z8gcyQ5veOnSDRREz/0bmpyTfKt3/51DhEBqCZQn1udM/5flcSPYhkdQ==", - "dependencies": { - "d": "^1.0.1", - "es5-ext": "^0.10.53", - "es6-weak-map": "^2.0.3", - "event-emitter": "^0.3.5", - "is-promise": "^2.2.2", - "lru-queue": "^0.1.0", - "next-tick": "^1.1.0", - "timers-ext": "^0.1.7" - } - }, - "node_modules/mri": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", - "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", - "engines": { - "node": ">=4" - } - }, - "node_modules/next-tick": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", - "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==" - }, - "node_modules/periscopic": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/periscopic/-/periscopic-3.1.0.tgz", - "integrity": "sha512-vKiQ8RRtkl9P+r/+oefh25C3fhybptkHKCZSPlcXiJux2tJF55GnEj3BVn4A5gKfq9NWWXXrxkHBwVPUfH0opw==", - "peer": true, - "dependencies": { - "@types/estree": "^1.0.0", - "estree-walker": "^3.0.0", - "is-reference": "^3.0.0" - } - }, - "node_modules/sade": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz", - "integrity": "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==", - "dependencies": { - "mri": "^1.1.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/source-map-js": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", - "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", - "peer": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/svelte": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/svelte/-/svelte-4.2.2.tgz", - "integrity": "sha512-My2tytF2e2NnHSpn2M7/3VdXT4JdTglYVUuSuK/mXL2XtulPYbeBfl8Dm1QiaKRn0zoULRnL+EtfZHHP0k4H3A==", - "peer": true, - "dependencies": { - "@ampproject/remapping": "^2.2.1", - "@jridgewell/sourcemap-codec": "^1.4.15", - "@jridgewell/trace-mapping": "^0.3.18", - "acorn": "^8.9.0", - "aria-query": "^5.3.0", - "axobject-query": "^3.2.1", - "code-red": "^1.0.3", - "css-tree": "^2.3.1", - "estree-walker": "^3.0.3", - "is-reference": "^3.0.1", - "locate-character": "^3.0.0", - "magic-string": "^0.30.4", - "periscopic": "^3.1.0" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/svelte-i18n": { - "version": "3.7.4", - "resolved": "https://registry.npmjs.org/svelte-i18n/-/svelte-i18n-3.7.4.tgz", - "integrity": "sha512-yGRCNo+eBT4cPuU7IVsYTYjxB7I2V8qgUZPlHnNctJj5IgbJgV78flsRzpjZ/8iUYZrS49oCt7uxlU3AZv/N5Q==", - "dependencies": { - "cli-color": "^2.0.3", - "deepmerge": "^4.2.2", - "esbuild": "^0.19.2", - "estree-walker": "^2", - "intl-messageformat": "^9.13.0", - "sade": "^1.8.1", - "tiny-glob": "^0.2.9" - }, - "bin": { - "svelte-i18n": "dist/cli.js" - }, - "engines": { - "node": ">= 16" - }, - "peerDependencies": { - "svelte": "^3 || ^4" - } - }, - "node_modules/svelte-i18n/node_modules/estree-walker": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", - "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==" - }, - "node_modules/timers-ext": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/timers-ext/-/timers-ext-0.1.7.tgz", - "integrity": "sha512-b85NUNzTSdodShTIbky6ZF02e8STtVVfD+fu4aXXShEELpozH+bCpJLYMPZbsABN2wDH7fJpqIoXxJpzbf0NqQ==", - "dependencies": { - "es5-ext": "~0.10.46", - "next-tick": "1" - } - }, - "node_modules/tiny-glob": { - "version": "0.2.9", - "resolved": "https://registry.npmjs.org/tiny-glob/-/tiny-glob-0.2.9.tgz", - "integrity": "sha512-g/55ssRPUjShh+xkfx9UPDXqhckHEsHr4Vd9zX55oSdGZc/MD0m3sferOkwWtp98bv+kcVfEHtRJgBVJzelrzg==", - "dependencies": { - "globalyzer": "0.1.0", - "globrex": "^0.1.2" - } - }, - "node_modules/tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" - }, - "node_modules/type": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", - "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==" - } - }, - "dependencies": { - "@ampproject/remapping": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", - "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", - "peer": true, - "requires": { - "@jridgewell/gen-mapping": "^0.3.0", - "@jridgewell/trace-mapping": "^0.3.9" - } - }, - "@esbuild/android-arm": { - "version": "0.19.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.5.tgz", - "integrity": "sha512-bhvbzWFF3CwMs5tbjf3ObfGqbl/17ict2/uwOSfr3wmxDE6VdS2GqY/FuzIPe0q0bdhj65zQsvqfArI9MY6+AA==", - "optional": true - }, - "@esbuild/android-arm64": { - "version": "0.19.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.5.tgz", - "integrity": "sha512-5d1OkoJxnYQfmC+Zd8NBFjkhyCNYwM4n9ODrycTFY6Jk1IGiZ+tjVJDDSwDt77nK+tfpGP4T50iMtVi4dEGzhQ==", - "optional": true - }, - "@esbuild/android-x64": { - "version": "0.19.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.5.tgz", - "integrity": "sha512-9t+28jHGL7uBdkBjL90QFxe7DVA+KGqWlHCF8ChTKyaKO//VLuoBricQCgwhOjA1/qOczsw843Fy4cbs4H3DVA==", - "optional": true - }, - "@esbuild/darwin-arm64": { - "version": "0.19.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.5.tgz", - "integrity": "sha512-mvXGcKqqIqyKoxq26qEDPHJuBYUA5KizJncKOAf9eJQez+L9O+KfvNFu6nl7SCZ/gFb2QPaRqqmG0doSWlgkqw==", - "optional": true - }, - "@esbuild/darwin-x64": { - "version": "0.19.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.5.tgz", - "integrity": "sha512-Ly8cn6fGLNet19s0X4unjcniX24I0RqjPv+kurpXabZYSXGM4Pwpmf85WHJN3lAgB8GSth7s5A0r856S+4DyiA==", - "optional": true - }, - "@esbuild/freebsd-arm64": { - "version": "0.19.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.5.tgz", - "integrity": "sha512-GGDNnPWTmWE+DMchq1W8Sd0mUkL+APvJg3b11klSGUDvRXh70JqLAO56tubmq1s2cgpVCSKYywEiKBfju8JztQ==", - "optional": true - }, - "@esbuild/freebsd-x64": { - "version": "0.19.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.5.tgz", - "integrity": "sha512-1CCwDHnSSoA0HNwdfoNY0jLfJpd7ygaLAp5EHFos3VWJCRX9DMwWODf96s9TSse39Br7oOTLryRVmBoFwXbuuQ==", - "optional": true - }, - "@esbuild/linux-arm": { - "version": "0.19.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.5.tgz", - "integrity": "sha512-lrWXLY/vJBzCPC51QN0HM71uWgIEpGSjSZZADQhq7DKhPcI6NH1IdzjfHkDQws2oNpJKpR13kv7/pFHBbDQDwQ==", - "optional": true - }, - "@esbuild/linux-arm64": { - "version": "0.19.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.5.tgz", - "integrity": "sha512-o3vYippBmSrjjQUCEEiTZ2l+4yC0pVJD/Dl57WfPwwlvFkrxoSO7rmBZFii6kQB3Wrn/6GwJUPLU5t52eq2meA==", - "optional": true - }, - "@esbuild/linux-ia32": { - "version": "0.19.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.5.tgz", - "integrity": "sha512-MkjHXS03AXAkNp1KKkhSKPOCYztRtK+KXDNkBa6P78F8Bw0ynknCSClO/ztGszILZtyO/lVKpa7MolbBZ6oJtQ==", - "optional": true - }, - "@esbuild/linux-loong64": { - "version": "0.19.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.5.tgz", - "integrity": "sha512-42GwZMm5oYOD/JHqHska3Jg0r+XFb/fdZRX+WjADm3nLWLcIsN27YKtqxzQmGNJgu0AyXg4HtcSK9HuOk3v1Dw==", - "optional": true - }, - "@esbuild/linux-mips64el": { - "version": "0.19.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.5.tgz", - "integrity": "sha512-kcjndCSMitUuPJobWCnwQ9lLjiLZUR3QLQmlgaBfMX23UEa7ZOrtufnRds+6WZtIS9HdTXqND4yH8NLoVVIkcg==", - "optional": true - }, - "@esbuild/linux-ppc64": { - "version": "0.19.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.5.tgz", - "integrity": "sha512-yJAxJfHVm0ZbsiljbtFFP1BQKLc8kUF6+17tjQ78QjqjAQDnhULWiTA6u0FCDmYT1oOKS9PzZ2z0aBI+Mcyj7Q==", - "optional": true - }, - "@esbuild/linux-riscv64": { - "version": "0.19.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.5.tgz", - "integrity": "sha512-5u8cIR/t3gaD6ad3wNt1MNRstAZO+aNyBxu2We8X31bA8XUNyamTVQwLDA1SLoPCUehNCymhBhK3Qim1433Zag==", - "optional": true - }, - "@esbuild/linux-s390x": { - "version": "0.19.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.5.tgz", - "integrity": "sha512-Z6JrMyEw/EmZBD/OFEFpb+gao9xJ59ATsoTNlj39jVBbXqoZm4Xntu6wVmGPB/OATi1uk/DB+yeDPv2E8PqZGw==", - "optional": true - }, - "@esbuild/linux-x64": { - "version": "0.19.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.5.tgz", - "integrity": "sha512-psagl+2RlK1z8zWZOmVdImisMtrUxvwereIdyJTmtmHahJTKb64pAcqoPlx6CewPdvGvUKe2Jw+0Z/0qhSbG1A==", - "optional": true - }, - "@esbuild/netbsd-x64": { - "version": "0.19.5", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.5.tgz", - "integrity": "sha512-kL2l+xScnAy/E/3119OggX8SrWyBEcqAh8aOY1gr4gPvw76la2GlD4Ymf832UCVbmuWeTf2adkZDK+h0Z/fB4g==", - "optional": true - }, - "@esbuild/openbsd-x64": { - "version": "0.19.5", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.5.tgz", - "integrity": "sha512-sPOfhtzFufQfTBgRnE1DIJjzsXukKSvZxloZbkJDG383q0awVAq600pc1nfqBcl0ice/WN9p4qLc39WhBShRTA==", - "optional": true - }, - "@esbuild/sunos-x64": { - "version": "0.19.5", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.5.tgz", - "integrity": "sha512-dGZkBXaafuKLpDSjKcB0ax0FL36YXCvJNnztjKV+6CO82tTYVDSH2lifitJ29jxRMoUhgkg9a+VA/B03WK5lcg==", - "optional": true - }, - "@esbuild/win32-arm64": { - "version": "0.19.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.5.tgz", - "integrity": "sha512-dWVjD9y03ilhdRQ6Xig1NWNgfLtf2o/STKTS+eZuF90fI2BhbwD6WlaiCGKptlqXlURVB5AUOxUj09LuwKGDTg==", - "optional": true - }, - "@esbuild/win32-ia32": { - "version": "0.19.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.5.tgz", - "integrity": "sha512-4liggWIA4oDgUxqpZwrDhmEfAH4d0iljanDOK7AnVU89T6CzHon/ony8C5LeOdfgx60x5cnQJFZwEydVlYx4iw==", - "optional": true - }, - "@esbuild/win32-x64": { - "version": "0.19.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.5.tgz", - "integrity": "sha512-czTrygUsB/jlM8qEW5MD8bgYU2Xg14lo6kBDXW6HdxKjh8M5PzETGiSHaz9MtbXBYDloHNUAUW2tMiKW4KM9Mw==", - "optional": true - }, - "@formatjs/ecma402-abstract": { - "version": "1.11.4", - "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.11.4.tgz", - "integrity": "sha512-EBikYFp2JCdIfGEb5G9dyCkTGDmC57KSHhRQOC3aYxoPWVZvfWCDjZwkGYHN7Lis/fmuWl906bnNTJifDQ3sXw==", - "requires": { - "@formatjs/intl-localematcher": "0.2.25", - "tslib": "^2.1.0" - } - }, - "@formatjs/fast-memoize": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-1.2.1.tgz", - "integrity": "sha512-Rg0e76nomkz3vF9IPlKeV+Qynok0r7YZjL6syLz4/urSg0IbjPZCB/iYUMNsYA643gh4mgrX3T7KEIFIxJBQeg==", - "requires": { - "tslib": "^2.1.0" - } - }, - "@formatjs/icu-messageformat-parser": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.1.0.tgz", - "integrity": "sha512-Qxv/lmCN6hKpBSss2uQ8IROVnta2r9jd3ymUEIjm2UyIkUCHVcbUVRGL/KS/wv7876edvsPe+hjHVJ4z8YuVaw==", - "requires": { - "@formatjs/ecma402-abstract": "1.11.4", - "@formatjs/icu-skeleton-parser": "1.3.6", - "tslib": "^2.1.0" - } - }, - "@formatjs/icu-skeleton-parser": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.3.6.tgz", - "integrity": "sha512-I96mOxvml/YLrwU2Txnd4klA7V8fRhb6JG/4hm3VMNmeJo1F03IpV2L3wWt7EweqNLES59SZ4d6hVOPCSf80Bg==", - "requires": { - "@formatjs/ecma402-abstract": "1.11.4", - "tslib": "^2.1.0" - } - }, - "@formatjs/intl-localematcher": { - "version": "0.2.25", - "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.2.25.tgz", - "integrity": "sha512-YmLcX70BxoSopLFdLr1Ds99NdlTI2oWoLbaUW2M406lxOIPzE1KQhRz2fPUkq34xVZQaihCoU29h0KK7An3bhA==", - "requires": { - "tslib": "^2.1.0" - } - }, - "@gradio/atoms": { - "version": "0.2.0-beta.4", - "resolved": "https://registry.npmjs.org/@gradio/atoms/-/atoms-0.2.0-beta.4.tgz", - "integrity": "sha512-xZfP9oPmb7iiuGl7KB4vVELSVk9f3w5Y9KRIxkAaMb+oeRpmb5uDtKQPAxntpm0W9rKAZmYG+DIWhInlu1eeKA==", - "requires": { - "@gradio/icons": "^0.2.0-beta.1", - "@gradio/utils": "^0.2.0-beta.4" - } - }, - "@gradio/column": { - "version": "0.1.0-beta.2", - "resolved": "https://registry.npmjs.org/@gradio/column/-/column-0.1.0-beta.2.tgz", - "integrity": "sha512-vL0GECdNL4wAaO/o0JcF3fm2xyMrx5DJWXUiPq/sUwqZwwB95srPGKBVTmVja3HproVXCBEnTzPQmRlrwWK67w==" - }, - "@gradio/icons": { - "version": "0.2.0-beta.1", - "resolved": "https://registry.npmjs.org/@gradio/icons/-/icons-0.2.0-beta.1.tgz", - "integrity": "sha512-6nwP1NIi0u4YQoSoaqC/rY0wuCvJHsnK+8aHDOE37070JpzBGuxB/VUlEgO7trNz5zI/EJy2htIRYsqz1vKmXA==" - }, - "@gradio/statustracker": { - "version": "0.3.0-beta.6", - "resolved": "https://registry.npmjs.org/@gradio/statustracker/-/statustracker-0.3.0-beta.6.tgz", - "integrity": "sha512-AIhaMCnr2uibHdqRrs4K8ZUvZK0q5e430TcvoduLOkaoOrkfnqetrHaHdOLNBz+H4kJlXJRsmt7ZZYV4wwMXRQ==", - "requires": { - "@gradio/atoms": "^0.2.0-beta.4", - "@gradio/column": "^0.1.0-beta.2", - "@gradio/icons": "^0.2.0-beta.1", - "@gradio/utils": "^0.2.0-beta.4" - } - }, - "@gradio/theme": { - "version": "0.2.0-beta.2", - "resolved": "https://registry.npmjs.org/@gradio/theme/-/theme-0.2.0-beta.2.tgz", - "integrity": "sha512-yKrA8eE02URtXUC9w98lBW8tqZk5oGumbBH7bFKOAhsrv1sbVZKir18P4a2/EL4XJ6Um36MwhPB3D5ipMniV5g==" - }, - "@gradio/utils": { - "version": "0.2.0-beta.4", - "resolved": "https://registry.npmjs.org/@gradio/utils/-/utils-0.2.0-beta.4.tgz", - "integrity": "sha512-jaOY3IQs1MnWRagXBICHXl5ZDKFqgF4XMfgsZNjTQxTG6THFOCsrUc14X1BNmXWkh9zVXJJTZcXifekj8O6LZQ==", - "requires": { - "@gradio/theme": "^0.2.0-beta.2", - "svelte-i18n": "^3.6.0" - } - }, - "@jridgewell/gen-mapping": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", - "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", - "peer": true, - "requires": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" - } - }, - "@jridgewell/resolve-uri": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", - "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", - "peer": true - }, - "@jridgewell/set-array": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", - "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", - "peer": true - }, - "@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", - "peer": true - }, - "@jridgewell/trace-mapping": { - "version": "0.3.20", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz", - "integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==", - "peer": true, - "requires": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "@types/estree": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.3.tgz", - "integrity": "sha512-CS2rOaoQ/eAgAfcTfq6amKG7bsN+EMcgGY4FAFQdvSj2y1ixvOZTUA9mOtCai7E1SYu283XNw7urKK30nP3wkQ==", - "peer": true - }, - "@zerodevx/svelte-json-view": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/@zerodevx/svelte-json-view/-/svelte-json-view-1.0.7.tgz", - "integrity": "sha512-yW0MV+9BCKOwzt3h86y3xDqYdI5st+Rxk+L5pa0Utq7nlPD+VvxyhL7R1gJoLxQvWwjyAvY/fyUCFTdwDyI14w==", - "requires": {} - }, - "acorn": { - "version": "8.10.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", - "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==", - "peer": true - }, - "aria-query": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", - "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", - "peer": true, - "requires": { - "dequal": "^2.0.3" - } - }, - "axobject-query": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.2.1.tgz", - "integrity": "sha512-jsyHu61e6N4Vbz/v18DHwWYKK0bSWLqn47eeDSKPB7m8tqMHF9YJ+mhIk2lVteyZrY8tnSj/jHOv4YiTCuCJgg==", - "peer": true, - "requires": { - "dequal": "^2.0.3" - } - }, - "cli-color": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/cli-color/-/cli-color-2.0.3.tgz", - "integrity": "sha512-OkoZnxyC4ERN3zLzZaY9Emb7f/MhBOIpePv0Ycok0fJYT+Ouo00UBEIwsVsr0yoow++n5YWlSUgST9GKhNHiRQ==", - "requires": { - "d": "^1.0.1", - "es5-ext": "^0.10.61", - "es6-iterator": "^2.0.3", - "memoizee": "^0.4.15", - "timers-ext": "^0.1.7" - } - }, - "code-red": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/code-red/-/code-red-1.0.4.tgz", - "integrity": "sha512-7qJWqItLA8/VPVlKJlFXU+NBlo/qyfs39aJcuMT/2ere32ZqvF5OSxgdM5xOfJJ7O429gg2HM47y8v9P+9wrNw==", - "peer": true, - "requires": { - "@jridgewell/sourcemap-codec": "^1.4.15", - "@types/estree": "^1.0.1", - "acorn": "^8.10.0", - "estree-walker": "^3.0.3", - "periscopic": "^3.1.0" - } - }, - "css-tree": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz", - "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==", - "peer": true, - "requires": { - "mdn-data": "2.0.30", - "source-map-js": "^1.0.1" - } - }, - "d": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", - "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", - "requires": { - "es5-ext": "^0.10.50", - "type": "^1.0.1" - } - }, - "deepmerge": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", - "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==" - }, - "dequal": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", - "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", - "peer": true - }, - "es5-ext": { - "version": "0.10.62", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.62.tgz", - "integrity": "sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA==", - "requires": { - "es6-iterator": "^2.0.3", - "es6-symbol": "^3.1.3", - "next-tick": "^1.1.0" - } - }, - "es6-iterator": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", - "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==", - "requires": { - "d": "1", - "es5-ext": "^0.10.35", - "es6-symbol": "^3.1.1" - } - }, - "es6-symbol": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", - "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", - "requires": { - "d": "^1.0.1", - "ext": "^1.1.2" - } - }, - "es6-weak-map": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.3.tgz", - "integrity": "sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA==", - "requires": { - "d": "1", - "es5-ext": "^0.10.46", - "es6-iterator": "^2.0.3", - "es6-symbol": "^3.1.1" - } - }, - "esbuild": { - "version": "0.19.5", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.5.tgz", - "integrity": "sha512-bUxalY7b1g8vNhQKdB24QDmHeY4V4tw/s6Ak5z+jJX9laP5MoQseTOMemAr0gxssjNcH0MCViG8ONI2kksvfFQ==", - "requires": { - "@esbuild/android-arm": "0.19.5", - "@esbuild/android-arm64": "0.19.5", - "@esbuild/android-x64": "0.19.5", - "@esbuild/darwin-arm64": "0.19.5", - "@esbuild/darwin-x64": "0.19.5", - "@esbuild/freebsd-arm64": "0.19.5", - "@esbuild/freebsd-x64": "0.19.5", - "@esbuild/linux-arm": "0.19.5", - "@esbuild/linux-arm64": "0.19.5", - "@esbuild/linux-ia32": "0.19.5", - "@esbuild/linux-loong64": "0.19.5", - "@esbuild/linux-mips64el": "0.19.5", - "@esbuild/linux-ppc64": "0.19.5", - "@esbuild/linux-riscv64": "0.19.5", - "@esbuild/linux-s390x": "0.19.5", - "@esbuild/linux-x64": "0.19.5", - "@esbuild/netbsd-x64": "0.19.5", - "@esbuild/openbsd-x64": "0.19.5", - "@esbuild/sunos-x64": "0.19.5", - "@esbuild/win32-arm64": "0.19.5", - "@esbuild/win32-ia32": "0.19.5", - "@esbuild/win32-x64": "0.19.5" - } - }, - "estree-walker": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", - "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", - "peer": true, - "requires": { - "@types/estree": "^1.0.0" - } - }, - "event-emitter": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", - "integrity": "sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==", - "requires": { - "d": "1", - "es5-ext": "~0.10.14" - } - }, - "ext": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz", - "integrity": "sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==", - "requires": { - "type": "^2.7.2" - }, - "dependencies": { - "type": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/type/-/type-2.7.2.tgz", - "integrity": "sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==" - } - } - }, - "globalyzer": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/globalyzer/-/globalyzer-0.1.0.tgz", - "integrity": "sha512-40oNTM9UfG6aBmuKxk/giHn5nQ8RVz/SS4Ir6zgzOv9/qC3kKZ9v4etGTcJbEl/NyVQH7FGU7d+X1egr57Md2Q==" - }, - "globrex": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/globrex/-/globrex-0.1.2.tgz", - "integrity": "sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==" - }, - "intl-messageformat": { - "version": "9.13.0", - "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-9.13.0.tgz", - "integrity": "sha512-7sGC7QnSQGa5LZP7bXLDhVDtQOeKGeBFGHF2Y8LVBwYZoQZCgWeKoPGTa5GMG8g/TzDgeXuYJQis7Ggiw2xTOw==", - "requires": { - "@formatjs/ecma402-abstract": "1.11.4", - "@formatjs/fast-memoize": "1.2.1", - "@formatjs/icu-messageformat-parser": "2.1.0", - "tslib": "^2.1.0" - } - }, - "is-promise": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", - "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==" - }, - "is-reference": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.2.tgz", - "integrity": "sha512-v3rht/LgVcsdZa3O2Nqs+NMowLOxeOm7Ay9+/ARQ2F+qEoANRcqrjAZKGN0v8ymUetZGgkp26LTnGT7H0Qo9Pg==", - "peer": true, - "requires": { - "@types/estree": "*" - } - }, - "locate-character": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-character/-/locate-character-3.0.0.tgz", - "integrity": "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==", - "peer": true - }, - "lru-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/lru-queue/-/lru-queue-0.1.0.tgz", - "integrity": "sha512-BpdYkt9EvGl8OfWHDQPISVpcl5xZthb+XPsbELj5AQXxIC8IriDZIQYjBJPEm5rS420sjZ0TLEzRcq5KdBhYrQ==", - "requires": { - "es5-ext": "~0.10.2" - } - }, - "magic-string": { - "version": "0.30.5", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.5.tgz", - "integrity": "sha512-7xlpfBaQaP/T6Vh8MO/EqXSW5En6INHEvEXQiuff7Gku0PWjU3uf6w/j9o7O+SpB5fOAkrI5HeoNgwjEO0pFsA==", - "peer": true, - "requires": { - "@jridgewell/sourcemap-codec": "^1.4.15" - } - }, - "mdn-data": { - "version": "2.0.30", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", - "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==", - "peer": true - }, - "memoizee": { - "version": "0.4.15", - "resolved": "https://registry.npmjs.org/memoizee/-/memoizee-0.4.15.tgz", - "integrity": "sha512-UBWmJpLZd5STPm7PMUlOw/TSy972M+z8gcyQ5veOnSDRREz/0bmpyTfKt3/51DhEBqCZQn1udM/5flcSPYhkdQ==", - "requires": { - "d": "^1.0.1", - "es5-ext": "^0.10.53", - "es6-weak-map": "^2.0.3", - "event-emitter": "^0.3.5", - "is-promise": "^2.2.2", - "lru-queue": "^0.1.0", - "next-tick": "^1.1.0", - "timers-ext": "^0.1.7" - } - }, - "mri": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", - "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==" - }, - "next-tick": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", - "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==" - }, - "periscopic": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/periscopic/-/periscopic-3.1.0.tgz", - "integrity": "sha512-vKiQ8RRtkl9P+r/+oefh25C3fhybptkHKCZSPlcXiJux2tJF55GnEj3BVn4A5gKfq9NWWXXrxkHBwVPUfH0opw==", - "peer": true, - "requires": { - "@types/estree": "^1.0.0", - "estree-walker": "^3.0.0", - "is-reference": "^3.0.0" - } - }, - "sade": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz", - "integrity": "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==", - "requires": { - "mri": "^1.1.0" - } - }, - "source-map-js": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", - "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", - "peer": true - }, - "svelte": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/svelte/-/svelte-4.2.2.tgz", - "integrity": "sha512-My2tytF2e2NnHSpn2M7/3VdXT4JdTglYVUuSuK/mXL2XtulPYbeBfl8Dm1QiaKRn0zoULRnL+EtfZHHP0k4H3A==", - "peer": true, - "requires": { - "@ampproject/remapping": "^2.2.1", - "@jridgewell/sourcemap-codec": "^1.4.15", - "@jridgewell/trace-mapping": "^0.3.18", - "acorn": "^8.9.0", - "aria-query": "^5.3.0", - "axobject-query": "^3.2.1", - "code-red": "^1.0.3", - "css-tree": "^2.3.1", - "estree-walker": "^3.0.3", - "is-reference": "^3.0.1", - "locate-character": "^3.0.0", - "magic-string": "^0.30.4", - "periscopic": "^3.1.0" - } - }, - "svelte-i18n": { - "version": "3.7.4", - "resolved": "https://registry.npmjs.org/svelte-i18n/-/svelte-i18n-3.7.4.tgz", - "integrity": "sha512-yGRCNo+eBT4cPuU7IVsYTYjxB7I2V8qgUZPlHnNctJj5IgbJgV78flsRzpjZ/8iUYZrS49oCt7uxlU3AZv/N5Q==", - "requires": { - "cli-color": "^2.0.3", - "deepmerge": "^4.2.2", - "esbuild": "^0.19.2", - "estree-walker": "^2", - "intl-messageformat": "^9.13.0", - "sade": "^1.8.1", - "tiny-glob": "^0.2.9" - }, - "dependencies": { - "estree-walker": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", - "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==" - } - } - }, - "timers-ext": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/timers-ext/-/timers-ext-0.1.7.tgz", - "integrity": "sha512-b85NUNzTSdodShTIbky6ZF02e8STtVVfD+fu4aXXShEELpozH+bCpJLYMPZbsABN2wDH7fJpqIoXxJpzbf0NqQ==", - "requires": { - "es5-ext": "~0.10.46", - "next-tick": "1" - } - }, - "tiny-glob": { - "version": "0.2.9", - "resolved": "https://registry.npmjs.org/tiny-glob/-/tiny-glob-0.2.9.tgz", - "integrity": "sha512-g/55ssRPUjShh+xkfx9UPDXqhckHEsHr4Vd9zX55oSdGZc/MD0m3sferOkwWtp98bv+kcVfEHtRJgBVJzelrzg==", - "requires": { - "globalyzer": "0.1.0", - "globrex": "^0.1.2" - } - }, - "tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" - }, - "type": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", - "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==" - } - } + "name": "gradio_test", + "version": "0.2.0-beta.6", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "gradio_test", + "version": "0.2.0-beta.6", + "license": "ISC", + "dependencies": { + "@gradio/atoms": "0.2.0-beta.4", + "@gradio/statustracker": "0.3.0-beta.6", + "@gradio/utils": "0.2.0-beta.4", + "@zerodevx/svelte-json-view": "^1.0.7" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", + "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", + "peer": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.5.tgz", + "integrity": "sha512-bhvbzWFF3CwMs5tbjf3ObfGqbl/17ict2/uwOSfr3wmxDE6VdS2GqY/FuzIPe0q0bdhj65zQsvqfArI9MY6+AA==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.5.tgz", + "integrity": "sha512-5d1OkoJxnYQfmC+Zd8NBFjkhyCNYwM4n9ODrycTFY6Jk1IGiZ+tjVJDDSwDt77nK+tfpGP4T50iMtVi4dEGzhQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.5.tgz", + "integrity": "sha512-9t+28jHGL7uBdkBjL90QFxe7DVA+KGqWlHCF8ChTKyaKO//VLuoBricQCgwhOjA1/qOczsw843Fy4cbs4H3DVA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.5.tgz", + "integrity": "sha512-mvXGcKqqIqyKoxq26qEDPHJuBYUA5KizJncKOAf9eJQez+L9O+KfvNFu6nl7SCZ/gFb2QPaRqqmG0doSWlgkqw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.5.tgz", + "integrity": "sha512-Ly8cn6fGLNet19s0X4unjcniX24I0RqjPv+kurpXabZYSXGM4Pwpmf85WHJN3lAgB8GSth7s5A0r856S+4DyiA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.5.tgz", + "integrity": "sha512-GGDNnPWTmWE+DMchq1W8Sd0mUkL+APvJg3b11klSGUDvRXh70JqLAO56tubmq1s2cgpVCSKYywEiKBfju8JztQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.5.tgz", + "integrity": "sha512-1CCwDHnSSoA0HNwdfoNY0jLfJpd7ygaLAp5EHFos3VWJCRX9DMwWODf96s9TSse39Br7oOTLryRVmBoFwXbuuQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.5.tgz", + "integrity": "sha512-lrWXLY/vJBzCPC51QN0HM71uWgIEpGSjSZZADQhq7DKhPcI6NH1IdzjfHkDQws2oNpJKpR13kv7/pFHBbDQDwQ==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.5.tgz", + "integrity": "sha512-o3vYippBmSrjjQUCEEiTZ2l+4yC0pVJD/Dl57WfPwwlvFkrxoSO7rmBZFii6kQB3Wrn/6GwJUPLU5t52eq2meA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.5.tgz", + "integrity": "sha512-MkjHXS03AXAkNp1KKkhSKPOCYztRtK+KXDNkBa6P78F8Bw0ynknCSClO/ztGszILZtyO/lVKpa7MolbBZ6oJtQ==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.5.tgz", + "integrity": "sha512-42GwZMm5oYOD/JHqHska3Jg0r+XFb/fdZRX+WjADm3nLWLcIsN27YKtqxzQmGNJgu0AyXg4HtcSK9HuOk3v1Dw==", + "cpu": [ + "loong64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.5.tgz", + "integrity": "sha512-kcjndCSMitUuPJobWCnwQ9lLjiLZUR3QLQmlgaBfMX23UEa7ZOrtufnRds+6WZtIS9HdTXqND4yH8NLoVVIkcg==", + "cpu": [ + "mips64el" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.5.tgz", + "integrity": "sha512-yJAxJfHVm0ZbsiljbtFFP1BQKLc8kUF6+17tjQ78QjqjAQDnhULWiTA6u0FCDmYT1oOKS9PzZ2z0aBI+Mcyj7Q==", + "cpu": [ + "ppc64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.5.tgz", + "integrity": "sha512-5u8cIR/t3gaD6ad3wNt1MNRstAZO+aNyBxu2We8X31bA8XUNyamTVQwLDA1SLoPCUehNCymhBhK3Qim1433Zag==", + "cpu": [ + "riscv64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.5.tgz", + "integrity": "sha512-Z6JrMyEw/EmZBD/OFEFpb+gao9xJ59ATsoTNlj39jVBbXqoZm4Xntu6wVmGPB/OATi1uk/DB+yeDPv2E8PqZGw==", + "cpu": [ + "s390x" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.5.tgz", + "integrity": "sha512-psagl+2RlK1z8zWZOmVdImisMtrUxvwereIdyJTmtmHahJTKb64pAcqoPlx6CewPdvGvUKe2Jw+0Z/0qhSbG1A==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.5.tgz", + "integrity": "sha512-kL2l+xScnAy/E/3119OggX8SrWyBEcqAh8aOY1gr4gPvw76la2GlD4Ymf832UCVbmuWeTf2adkZDK+h0Z/fB4g==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.5.tgz", + "integrity": "sha512-sPOfhtzFufQfTBgRnE1DIJjzsXukKSvZxloZbkJDG383q0awVAq600pc1nfqBcl0ice/WN9p4qLc39WhBShRTA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.5.tgz", + "integrity": "sha512-dGZkBXaafuKLpDSjKcB0ax0FL36YXCvJNnztjKV+6CO82tTYVDSH2lifitJ29jxRMoUhgkg9a+VA/B03WK5lcg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.5.tgz", + "integrity": "sha512-dWVjD9y03ilhdRQ6Xig1NWNgfLtf2o/STKTS+eZuF90fI2BhbwD6WlaiCGKptlqXlURVB5AUOxUj09LuwKGDTg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.5.tgz", + "integrity": "sha512-4liggWIA4oDgUxqpZwrDhmEfAH4d0iljanDOK7AnVU89T6CzHon/ony8C5LeOdfgx60x5cnQJFZwEydVlYx4iw==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.5.tgz", + "integrity": "sha512-czTrygUsB/jlM8qEW5MD8bgYU2Xg14lo6kBDXW6HdxKjh8M5PzETGiSHaz9MtbXBYDloHNUAUW2tMiKW4KM9Mw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@formatjs/ecma402-abstract": { + "version": "1.11.4", + "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.11.4.tgz", + "integrity": "sha512-EBikYFp2JCdIfGEb5G9dyCkTGDmC57KSHhRQOC3aYxoPWVZvfWCDjZwkGYHN7Lis/fmuWl906bnNTJifDQ3sXw==", + "dependencies": { + "@formatjs/intl-localematcher": "0.2.25", + "tslib": "^2.1.0" + } + }, + "node_modules/@formatjs/fast-memoize": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-1.2.1.tgz", + "integrity": "sha512-Rg0e76nomkz3vF9IPlKeV+Qynok0r7YZjL6syLz4/urSg0IbjPZCB/iYUMNsYA643gh4mgrX3T7KEIFIxJBQeg==", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/@formatjs/icu-messageformat-parser": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.1.0.tgz", + "integrity": "sha512-Qxv/lmCN6hKpBSss2uQ8IROVnta2r9jd3ymUEIjm2UyIkUCHVcbUVRGL/KS/wv7876edvsPe+hjHVJ4z8YuVaw==", + "dependencies": { + "@formatjs/ecma402-abstract": "1.11.4", + "@formatjs/icu-skeleton-parser": "1.3.6", + "tslib": "^2.1.0" + } + }, + "node_modules/@formatjs/icu-skeleton-parser": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.3.6.tgz", + "integrity": "sha512-I96mOxvml/YLrwU2Txnd4klA7V8fRhb6JG/4hm3VMNmeJo1F03IpV2L3wWt7EweqNLES59SZ4d6hVOPCSf80Bg==", + "dependencies": { + "@formatjs/ecma402-abstract": "1.11.4", + "tslib": "^2.1.0" + } + }, + "node_modules/@formatjs/intl-localematcher": { + "version": "0.2.25", + "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.2.25.tgz", + "integrity": "sha512-YmLcX70BxoSopLFdLr1Ds99NdlTI2oWoLbaUW2M406lxOIPzE1KQhRz2fPUkq34xVZQaihCoU29h0KK7An3bhA==", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/@gradio/atoms": { + "version": "0.2.0-beta.4", + "resolved": "https://registry.npmjs.org/@gradio/atoms/-/atoms-0.2.0-beta.4.tgz", + "integrity": "sha512-xZfP9oPmb7iiuGl7KB4vVELSVk9f3w5Y9KRIxkAaMb+oeRpmb5uDtKQPAxntpm0W9rKAZmYG+DIWhInlu1eeKA==", + "dependencies": { + "@gradio/icons": "^0.2.0-beta.1", + "@gradio/utils": "^0.2.0-beta.4" + } + }, + "node_modules/@gradio/column": { + "version": "0.1.0-beta.2", + "resolved": "https://registry.npmjs.org/@gradio/column/-/column-0.1.0-beta.2.tgz", + "integrity": "sha512-vL0GECdNL4wAaO/o0JcF3fm2xyMrx5DJWXUiPq/sUwqZwwB95srPGKBVTmVja3HproVXCBEnTzPQmRlrwWK67w==" + }, + "node_modules/@gradio/icons": { + "version": "0.2.0-beta.1", + "resolved": "https://registry.npmjs.org/@gradio/icons/-/icons-0.2.0-beta.1.tgz", + "integrity": "sha512-6nwP1NIi0u4YQoSoaqC/rY0wuCvJHsnK+8aHDOE37070JpzBGuxB/VUlEgO7trNz5zI/EJy2htIRYsqz1vKmXA==" + }, + "node_modules/@gradio/statustracker": { + "version": "0.3.0-beta.6", + "resolved": "https://registry.npmjs.org/@gradio/statustracker/-/statustracker-0.3.0-beta.6.tgz", + "integrity": "sha512-AIhaMCnr2uibHdqRrs4K8ZUvZK0q5e430TcvoduLOkaoOrkfnqetrHaHdOLNBz+H4kJlXJRsmt7ZZYV4wwMXRQ==", + "dependencies": { + "@gradio/atoms": "^0.2.0-beta.4", + "@gradio/column": "^0.1.0-beta.2", + "@gradio/icons": "^0.2.0-beta.1", + "@gradio/utils": "^0.2.0-beta.4" + } + }, + "node_modules/@gradio/theme": { + "version": "0.2.0-beta.2", + "resolved": "https://registry.npmjs.org/@gradio/theme/-/theme-0.2.0-beta.2.tgz", + "integrity": "sha512-yKrA8eE02URtXUC9w98lBW8tqZk5oGumbBH7bFKOAhsrv1sbVZKir18P4a2/EL4XJ6Um36MwhPB3D5ipMniV5g==" + }, + "node_modules/@gradio/utils": { + "version": "0.2.0-beta.4", + "resolved": "https://registry.npmjs.org/@gradio/utils/-/utils-0.2.0-beta.4.tgz", + "integrity": "sha512-jaOY3IQs1MnWRagXBICHXl5ZDKFqgF4XMfgsZNjTQxTG6THFOCsrUc14X1BNmXWkh9zVXJJTZcXifekj8O6LZQ==", + "dependencies": { + "@gradio/theme": "^0.2.0-beta.2", + "svelte-i18n": "^3.6.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", + "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "peer": true, + "dependencies": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", + "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", + "peer": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "peer": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "peer": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.20", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz", + "integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==", + "peer": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@types/estree": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.3.tgz", + "integrity": "sha512-CS2rOaoQ/eAgAfcTfq6amKG7bsN+EMcgGY4FAFQdvSj2y1ixvOZTUA9mOtCai7E1SYu283XNw7urKK30nP3wkQ==", + "peer": true + }, + "node_modules/@zerodevx/svelte-json-view": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@zerodevx/svelte-json-view/-/svelte-json-view-1.0.7.tgz", + "integrity": "sha512-yW0MV+9BCKOwzt3h86y3xDqYdI5st+Rxk+L5pa0Utq7nlPD+VvxyhL7R1gJoLxQvWwjyAvY/fyUCFTdwDyI14w==", + "peerDependencies": { + "svelte": "^3.57.0 || ^4.0.0" + } + }, + "node_modules/acorn": { + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", + "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==", + "peer": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/aria-query": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", + "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", + "peer": true, + "dependencies": { + "dequal": "^2.0.3" + } + }, + "node_modules/axobject-query": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.2.1.tgz", + "integrity": "sha512-jsyHu61e6N4Vbz/v18DHwWYKK0bSWLqn47eeDSKPB7m8tqMHF9YJ+mhIk2lVteyZrY8tnSj/jHOv4YiTCuCJgg==", + "peer": true, + "dependencies": { + "dequal": "^2.0.3" + } + }, + "node_modules/cli-color": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/cli-color/-/cli-color-2.0.3.tgz", + "integrity": "sha512-OkoZnxyC4ERN3zLzZaY9Emb7f/MhBOIpePv0Ycok0fJYT+Ouo00UBEIwsVsr0yoow++n5YWlSUgST9GKhNHiRQ==", + "dependencies": { + "d": "^1.0.1", + "es5-ext": "^0.10.61", + "es6-iterator": "^2.0.3", + "memoizee": "^0.4.15", + "timers-ext": "^0.1.7" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/code-red": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/code-red/-/code-red-1.0.4.tgz", + "integrity": "sha512-7qJWqItLA8/VPVlKJlFXU+NBlo/qyfs39aJcuMT/2ere32ZqvF5OSxgdM5xOfJJ7O429gg2HM47y8v9P+9wrNw==", + "peer": true, + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.4.15", + "@types/estree": "^1.0.1", + "acorn": "^8.10.0", + "estree-walker": "^3.0.3", + "periscopic": "^3.1.0" + } + }, + "node_modules/css-tree": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz", + "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==", + "peer": true, + "dependencies": { + "mdn-data": "2.0.30", + "source-map-js": "^1.0.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" + } + }, + "node_modules/d": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", + "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", + "dependencies": { + "es5-ext": "^0.10.50", + "type": "^1.0.1" + } + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "peer": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/es5-ext": { + "version": "0.10.62", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.62.tgz", + "integrity": "sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA==", + "hasInstallScript": true, + "dependencies": { + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.3", + "next-tick": "^1.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/es6-iterator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", + "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==", + "dependencies": { + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" + } + }, + "node_modules/es6-symbol": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", + "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", + "dependencies": { + "d": "^1.0.1", + "ext": "^1.1.2" + } + }, + "node_modules/es6-weak-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.3.tgz", + "integrity": "sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA==", + "dependencies": { + "d": "1", + "es5-ext": "^0.10.46", + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.1" + } + }, + "node_modules/esbuild": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.5.tgz", + "integrity": "sha512-bUxalY7b1g8vNhQKdB24QDmHeY4V4tw/s6Ak5z+jJX9laP5MoQseTOMemAr0gxssjNcH0MCViG8ONI2kksvfFQ==", + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/android-arm": "0.19.5", + "@esbuild/android-arm64": "0.19.5", + "@esbuild/android-x64": "0.19.5", + "@esbuild/darwin-arm64": "0.19.5", + "@esbuild/darwin-x64": "0.19.5", + "@esbuild/freebsd-arm64": "0.19.5", + "@esbuild/freebsd-x64": "0.19.5", + "@esbuild/linux-arm": "0.19.5", + "@esbuild/linux-arm64": "0.19.5", + "@esbuild/linux-ia32": "0.19.5", + "@esbuild/linux-loong64": "0.19.5", + "@esbuild/linux-mips64el": "0.19.5", + "@esbuild/linux-ppc64": "0.19.5", + "@esbuild/linux-riscv64": "0.19.5", + "@esbuild/linux-s390x": "0.19.5", + "@esbuild/linux-x64": "0.19.5", + "@esbuild/netbsd-x64": "0.19.5", + "@esbuild/openbsd-x64": "0.19.5", + "@esbuild/sunos-x64": "0.19.5", + "@esbuild/win32-arm64": "0.19.5", + "@esbuild/win32-ia32": "0.19.5", + "@esbuild/win32-x64": "0.19.5" + } + }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "peer": true, + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/event-emitter": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", + "integrity": "sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==", + "dependencies": { + "d": "1", + "es5-ext": "~0.10.14" + } + }, + "node_modules/ext": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz", + "integrity": "sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==", + "dependencies": { + "type": "^2.7.2" + } + }, + "node_modules/ext/node_modules/type": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/type/-/type-2.7.2.tgz", + "integrity": "sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==" + }, + "node_modules/globalyzer": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/globalyzer/-/globalyzer-0.1.0.tgz", + "integrity": "sha512-40oNTM9UfG6aBmuKxk/giHn5nQ8RVz/SS4Ir6zgzOv9/qC3kKZ9v4etGTcJbEl/NyVQH7FGU7d+X1egr57Md2Q==" + }, + "node_modules/globrex": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/globrex/-/globrex-0.1.2.tgz", + "integrity": "sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==" + }, + "node_modules/intl-messageformat": { + "version": "9.13.0", + "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-9.13.0.tgz", + "integrity": "sha512-7sGC7QnSQGa5LZP7bXLDhVDtQOeKGeBFGHF2Y8LVBwYZoQZCgWeKoPGTa5GMG8g/TzDgeXuYJQis7Ggiw2xTOw==", + "dependencies": { + "@formatjs/ecma402-abstract": "1.11.4", + "@formatjs/fast-memoize": "1.2.1", + "@formatjs/icu-messageformat-parser": "2.1.0", + "tslib": "^2.1.0" + } + }, + "node_modules/is-promise": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", + "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==" + }, + "node_modules/is-reference": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.2.tgz", + "integrity": "sha512-v3rht/LgVcsdZa3O2Nqs+NMowLOxeOm7Ay9+/ARQ2F+qEoANRcqrjAZKGN0v8ymUetZGgkp26LTnGT7H0Qo9Pg==", + "peer": true, + "dependencies": { + "@types/estree": "*" + } + }, + "node_modules/locate-character": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-character/-/locate-character-3.0.0.tgz", + "integrity": "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==", + "peer": true + }, + "node_modules/lru-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/lru-queue/-/lru-queue-0.1.0.tgz", + "integrity": "sha512-BpdYkt9EvGl8OfWHDQPISVpcl5xZthb+XPsbELj5AQXxIC8IriDZIQYjBJPEm5rS420sjZ0TLEzRcq5KdBhYrQ==", + "dependencies": { + "es5-ext": "~0.10.2" + } + }, + "node_modules/magic-string": { + "version": "0.30.5", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.5.tgz", + "integrity": "sha512-7xlpfBaQaP/T6Vh8MO/EqXSW5En6INHEvEXQiuff7Gku0PWjU3uf6w/j9o7O+SpB5fOAkrI5HeoNgwjEO0pFsA==", + "peer": true, + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.4.15" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/mdn-data": { + "version": "2.0.30", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", + "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==", + "peer": true + }, + "node_modules/memoizee": { + "version": "0.4.15", + "resolved": "https://registry.npmjs.org/memoizee/-/memoizee-0.4.15.tgz", + "integrity": "sha512-UBWmJpLZd5STPm7PMUlOw/TSy972M+z8gcyQ5veOnSDRREz/0bmpyTfKt3/51DhEBqCZQn1udM/5flcSPYhkdQ==", + "dependencies": { + "d": "^1.0.1", + "es5-ext": "^0.10.53", + "es6-weak-map": "^2.0.3", + "event-emitter": "^0.3.5", + "is-promise": "^2.2.2", + "lru-queue": "^0.1.0", + "next-tick": "^1.1.0", + "timers-ext": "^0.1.7" + } + }, + "node_modules/mri": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", + "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", + "engines": { + "node": ">=4" + } + }, + "node_modules/next-tick": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", + "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==" + }, + "node_modules/periscopic": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/periscopic/-/periscopic-3.1.0.tgz", + "integrity": "sha512-vKiQ8RRtkl9P+r/+oefh25C3fhybptkHKCZSPlcXiJux2tJF55GnEj3BVn4A5gKfq9NWWXXrxkHBwVPUfH0opw==", + "peer": true, + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^3.0.0", + "is-reference": "^3.0.0" + } + }, + "node_modules/sade": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz", + "integrity": "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==", + "dependencies": { + "mri": "^1.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/source-map-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/svelte": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/svelte/-/svelte-4.2.2.tgz", + "integrity": "sha512-My2tytF2e2NnHSpn2M7/3VdXT4JdTglYVUuSuK/mXL2XtulPYbeBfl8Dm1QiaKRn0zoULRnL+EtfZHHP0k4H3A==", + "peer": true, + "dependencies": { + "@ampproject/remapping": "^2.2.1", + "@jridgewell/sourcemap-codec": "^1.4.15", + "@jridgewell/trace-mapping": "^0.3.18", + "acorn": "^8.9.0", + "aria-query": "^5.3.0", + "axobject-query": "^3.2.1", + "code-red": "^1.0.3", + "css-tree": "^2.3.1", + "estree-walker": "^3.0.3", + "is-reference": "^3.0.1", + "locate-character": "^3.0.0", + "magic-string": "^0.30.4", + "periscopic": "^3.1.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/svelte-i18n": { + "version": "3.7.4", + "resolved": "https://registry.npmjs.org/svelte-i18n/-/svelte-i18n-3.7.4.tgz", + "integrity": "sha512-yGRCNo+eBT4cPuU7IVsYTYjxB7I2V8qgUZPlHnNctJj5IgbJgV78flsRzpjZ/8iUYZrS49oCt7uxlU3AZv/N5Q==", + "dependencies": { + "cli-color": "^2.0.3", + "deepmerge": "^4.2.2", + "esbuild": "^0.19.2", + "estree-walker": "^2", + "intl-messageformat": "^9.13.0", + "sade": "^1.8.1", + "tiny-glob": "^0.2.9" + }, + "bin": { + "svelte-i18n": "dist/cli.js" + }, + "engines": { + "node": ">= 16" + }, + "peerDependencies": { + "svelte": "^3 || ^4" + } + }, + "node_modules/svelte-i18n/node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==" + }, + "node_modules/timers-ext": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/timers-ext/-/timers-ext-0.1.7.tgz", + "integrity": "sha512-b85NUNzTSdodShTIbky6ZF02e8STtVVfD+fu4aXXShEELpozH+bCpJLYMPZbsABN2wDH7fJpqIoXxJpzbf0NqQ==", + "dependencies": { + "es5-ext": "~0.10.46", + "next-tick": "1" + } + }, + "node_modules/tiny-glob": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/tiny-glob/-/tiny-glob-0.2.9.tgz", + "integrity": "sha512-g/55ssRPUjShh+xkfx9UPDXqhckHEsHr4Vd9zX55oSdGZc/MD0m3sferOkwWtp98bv+kcVfEHtRJgBVJzelrzg==", + "dependencies": { + "globalyzer": "0.1.0", + "globrex": "^0.1.2" + } + }, + "node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/type": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", + "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==" + } + }, + "dependencies": { + "@ampproject/remapping": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", + "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", + "peer": true, + "requires": { + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, + "@esbuild/android-arm": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.5.tgz", + "integrity": "sha512-bhvbzWFF3CwMs5tbjf3ObfGqbl/17ict2/uwOSfr3wmxDE6VdS2GqY/FuzIPe0q0bdhj65zQsvqfArI9MY6+AA==", + "optional": true + }, + "@esbuild/android-arm64": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.5.tgz", + "integrity": "sha512-5d1OkoJxnYQfmC+Zd8NBFjkhyCNYwM4n9ODrycTFY6Jk1IGiZ+tjVJDDSwDt77nK+tfpGP4T50iMtVi4dEGzhQ==", + "optional": true + }, + "@esbuild/android-x64": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.5.tgz", + "integrity": "sha512-9t+28jHGL7uBdkBjL90QFxe7DVA+KGqWlHCF8ChTKyaKO//VLuoBricQCgwhOjA1/qOczsw843Fy4cbs4H3DVA==", + "optional": true + }, + "@esbuild/darwin-arm64": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.5.tgz", + "integrity": "sha512-mvXGcKqqIqyKoxq26qEDPHJuBYUA5KizJncKOAf9eJQez+L9O+KfvNFu6nl7SCZ/gFb2QPaRqqmG0doSWlgkqw==", + "optional": true + }, + "@esbuild/darwin-x64": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.5.tgz", + "integrity": "sha512-Ly8cn6fGLNet19s0X4unjcniX24I0RqjPv+kurpXabZYSXGM4Pwpmf85WHJN3lAgB8GSth7s5A0r856S+4DyiA==", + "optional": true + }, + "@esbuild/freebsd-arm64": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.5.tgz", + "integrity": "sha512-GGDNnPWTmWE+DMchq1W8Sd0mUkL+APvJg3b11klSGUDvRXh70JqLAO56tubmq1s2cgpVCSKYywEiKBfju8JztQ==", + "optional": true + }, + "@esbuild/freebsd-x64": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.5.tgz", + "integrity": "sha512-1CCwDHnSSoA0HNwdfoNY0jLfJpd7ygaLAp5EHFos3VWJCRX9DMwWODf96s9TSse39Br7oOTLryRVmBoFwXbuuQ==", + "optional": true + }, + "@esbuild/linux-arm": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.5.tgz", + "integrity": "sha512-lrWXLY/vJBzCPC51QN0HM71uWgIEpGSjSZZADQhq7DKhPcI6NH1IdzjfHkDQws2oNpJKpR13kv7/pFHBbDQDwQ==", + "optional": true + }, + "@esbuild/linux-arm64": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.5.tgz", + "integrity": "sha512-o3vYippBmSrjjQUCEEiTZ2l+4yC0pVJD/Dl57WfPwwlvFkrxoSO7rmBZFii6kQB3Wrn/6GwJUPLU5t52eq2meA==", + "optional": true + }, + "@esbuild/linux-ia32": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.5.tgz", + "integrity": "sha512-MkjHXS03AXAkNp1KKkhSKPOCYztRtK+KXDNkBa6P78F8Bw0ynknCSClO/ztGszILZtyO/lVKpa7MolbBZ6oJtQ==", + "optional": true + }, + "@esbuild/linux-loong64": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.5.tgz", + "integrity": "sha512-42GwZMm5oYOD/JHqHska3Jg0r+XFb/fdZRX+WjADm3nLWLcIsN27YKtqxzQmGNJgu0AyXg4HtcSK9HuOk3v1Dw==", + "optional": true + }, + "@esbuild/linux-mips64el": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.5.tgz", + "integrity": "sha512-kcjndCSMitUuPJobWCnwQ9lLjiLZUR3QLQmlgaBfMX23UEa7ZOrtufnRds+6WZtIS9HdTXqND4yH8NLoVVIkcg==", + "optional": true + }, + "@esbuild/linux-ppc64": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.5.tgz", + "integrity": "sha512-yJAxJfHVm0ZbsiljbtFFP1BQKLc8kUF6+17tjQ78QjqjAQDnhULWiTA6u0FCDmYT1oOKS9PzZ2z0aBI+Mcyj7Q==", + "optional": true + }, + "@esbuild/linux-riscv64": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.5.tgz", + "integrity": "sha512-5u8cIR/t3gaD6ad3wNt1MNRstAZO+aNyBxu2We8X31bA8XUNyamTVQwLDA1SLoPCUehNCymhBhK3Qim1433Zag==", + "optional": true + }, + "@esbuild/linux-s390x": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.5.tgz", + "integrity": "sha512-Z6JrMyEw/EmZBD/OFEFpb+gao9xJ59ATsoTNlj39jVBbXqoZm4Xntu6wVmGPB/OATi1uk/DB+yeDPv2E8PqZGw==", + "optional": true + }, + "@esbuild/linux-x64": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.5.tgz", + "integrity": "sha512-psagl+2RlK1z8zWZOmVdImisMtrUxvwereIdyJTmtmHahJTKb64pAcqoPlx6CewPdvGvUKe2Jw+0Z/0qhSbG1A==", + "optional": true + }, + "@esbuild/netbsd-x64": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.5.tgz", + "integrity": "sha512-kL2l+xScnAy/E/3119OggX8SrWyBEcqAh8aOY1gr4gPvw76la2GlD4Ymf832UCVbmuWeTf2adkZDK+h0Z/fB4g==", + "optional": true + }, + "@esbuild/openbsd-x64": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.5.tgz", + "integrity": "sha512-sPOfhtzFufQfTBgRnE1DIJjzsXukKSvZxloZbkJDG383q0awVAq600pc1nfqBcl0ice/WN9p4qLc39WhBShRTA==", + "optional": true + }, + "@esbuild/sunos-x64": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.5.tgz", + "integrity": "sha512-dGZkBXaafuKLpDSjKcB0ax0FL36YXCvJNnztjKV+6CO82tTYVDSH2lifitJ29jxRMoUhgkg9a+VA/B03WK5lcg==", + "optional": true + }, + "@esbuild/win32-arm64": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.5.tgz", + "integrity": "sha512-dWVjD9y03ilhdRQ6Xig1NWNgfLtf2o/STKTS+eZuF90fI2BhbwD6WlaiCGKptlqXlURVB5AUOxUj09LuwKGDTg==", + "optional": true + }, + "@esbuild/win32-ia32": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.5.tgz", + "integrity": "sha512-4liggWIA4oDgUxqpZwrDhmEfAH4d0iljanDOK7AnVU89T6CzHon/ony8C5LeOdfgx60x5cnQJFZwEydVlYx4iw==", + "optional": true + }, + "@esbuild/win32-x64": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.5.tgz", + "integrity": "sha512-czTrygUsB/jlM8qEW5MD8bgYU2Xg14lo6kBDXW6HdxKjh8M5PzETGiSHaz9MtbXBYDloHNUAUW2tMiKW4KM9Mw==", + "optional": true + }, + "@formatjs/ecma402-abstract": { + "version": "1.11.4", + "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.11.4.tgz", + "integrity": "sha512-EBikYFp2JCdIfGEb5G9dyCkTGDmC57KSHhRQOC3aYxoPWVZvfWCDjZwkGYHN7Lis/fmuWl906bnNTJifDQ3sXw==", + "requires": { + "@formatjs/intl-localematcher": "0.2.25", + "tslib": "^2.1.0" + } + }, + "@formatjs/fast-memoize": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-1.2.1.tgz", + "integrity": "sha512-Rg0e76nomkz3vF9IPlKeV+Qynok0r7YZjL6syLz4/urSg0IbjPZCB/iYUMNsYA643gh4mgrX3T7KEIFIxJBQeg==", + "requires": { + "tslib": "^2.1.0" + } + }, + "@formatjs/icu-messageformat-parser": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.1.0.tgz", + "integrity": "sha512-Qxv/lmCN6hKpBSss2uQ8IROVnta2r9jd3ymUEIjm2UyIkUCHVcbUVRGL/KS/wv7876edvsPe+hjHVJ4z8YuVaw==", + "requires": { + "@formatjs/ecma402-abstract": "1.11.4", + "@formatjs/icu-skeleton-parser": "1.3.6", + "tslib": "^2.1.0" + } + }, + "@formatjs/icu-skeleton-parser": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.3.6.tgz", + "integrity": "sha512-I96mOxvml/YLrwU2Txnd4klA7V8fRhb6JG/4hm3VMNmeJo1F03IpV2L3wWt7EweqNLES59SZ4d6hVOPCSf80Bg==", + "requires": { + "@formatjs/ecma402-abstract": "1.11.4", + "tslib": "^2.1.0" + } + }, + "@formatjs/intl-localematcher": { + "version": "0.2.25", + "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.2.25.tgz", + "integrity": "sha512-YmLcX70BxoSopLFdLr1Ds99NdlTI2oWoLbaUW2M406lxOIPzE1KQhRz2fPUkq34xVZQaihCoU29h0KK7An3bhA==", + "requires": { + "tslib": "^2.1.0" + } + }, + "@gradio/atoms": { + "version": "0.2.0-beta.4", + "resolved": "https://registry.npmjs.org/@gradio/atoms/-/atoms-0.2.0-beta.4.tgz", + "integrity": "sha512-xZfP9oPmb7iiuGl7KB4vVELSVk9f3w5Y9KRIxkAaMb+oeRpmb5uDtKQPAxntpm0W9rKAZmYG+DIWhInlu1eeKA==", + "requires": { + "@gradio/icons": "^0.2.0-beta.1", + "@gradio/utils": "^0.2.0-beta.4" + } + }, + "@gradio/column": { + "version": "0.1.0-beta.2", + "resolved": "https://registry.npmjs.org/@gradio/column/-/column-0.1.0-beta.2.tgz", + "integrity": "sha512-vL0GECdNL4wAaO/o0JcF3fm2xyMrx5DJWXUiPq/sUwqZwwB95srPGKBVTmVja3HproVXCBEnTzPQmRlrwWK67w==" + }, + "@gradio/icons": { + "version": "0.2.0-beta.1", + "resolved": "https://registry.npmjs.org/@gradio/icons/-/icons-0.2.0-beta.1.tgz", + "integrity": "sha512-6nwP1NIi0u4YQoSoaqC/rY0wuCvJHsnK+8aHDOE37070JpzBGuxB/VUlEgO7trNz5zI/EJy2htIRYsqz1vKmXA==" + }, + "@gradio/statustracker": { + "version": "0.3.0-beta.6", + "resolved": "https://registry.npmjs.org/@gradio/statustracker/-/statustracker-0.3.0-beta.6.tgz", + "integrity": "sha512-AIhaMCnr2uibHdqRrs4K8ZUvZK0q5e430TcvoduLOkaoOrkfnqetrHaHdOLNBz+H4kJlXJRsmt7ZZYV4wwMXRQ==", + "requires": { + "@gradio/atoms": "^0.2.0-beta.4", + "@gradio/column": "^0.1.0-beta.2", + "@gradio/icons": "^0.2.0-beta.1", + "@gradio/utils": "^0.2.0-beta.4" + } + }, + "@gradio/theme": { + "version": "0.2.0-beta.2", + "resolved": "https://registry.npmjs.org/@gradio/theme/-/theme-0.2.0-beta.2.tgz", + "integrity": "sha512-yKrA8eE02URtXUC9w98lBW8tqZk5oGumbBH7bFKOAhsrv1sbVZKir18P4a2/EL4XJ6Um36MwhPB3D5ipMniV5g==" + }, + "@gradio/utils": { + "version": "0.2.0-beta.4", + "resolved": "https://registry.npmjs.org/@gradio/utils/-/utils-0.2.0-beta.4.tgz", + "integrity": "sha512-jaOY3IQs1MnWRagXBICHXl5ZDKFqgF4XMfgsZNjTQxTG6THFOCsrUc14X1BNmXWkh9zVXJJTZcXifekj8O6LZQ==", + "requires": { + "@gradio/theme": "^0.2.0-beta.2", + "svelte-i18n": "^3.6.0" + } + }, + "@jridgewell/gen-mapping": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", + "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "peer": true, + "requires": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, + "@jridgewell/resolve-uri": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", + "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", + "peer": true + }, + "@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "peer": true + }, + "@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "peer": true + }, + "@jridgewell/trace-mapping": { + "version": "0.3.20", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz", + "integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==", + "peer": true, + "requires": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "@types/estree": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.3.tgz", + "integrity": "sha512-CS2rOaoQ/eAgAfcTfq6amKG7bsN+EMcgGY4FAFQdvSj2y1ixvOZTUA9mOtCai7E1SYu283XNw7urKK30nP3wkQ==", + "peer": true + }, + "@zerodevx/svelte-json-view": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@zerodevx/svelte-json-view/-/svelte-json-view-1.0.7.tgz", + "integrity": "sha512-yW0MV+9BCKOwzt3h86y3xDqYdI5st+Rxk+L5pa0Utq7nlPD+VvxyhL7R1gJoLxQvWwjyAvY/fyUCFTdwDyI14w==", + "requires": {} + }, + "acorn": { + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", + "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==", + "peer": true + }, + "aria-query": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", + "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", + "peer": true, + "requires": { + "dequal": "^2.0.3" + } + }, + "axobject-query": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.2.1.tgz", + "integrity": "sha512-jsyHu61e6N4Vbz/v18DHwWYKK0bSWLqn47eeDSKPB7m8tqMHF9YJ+mhIk2lVteyZrY8tnSj/jHOv4YiTCuCJgg==", + "peer": true, + "requires": { + "dequal": "^2.0.3" + } + }, + "cli-color": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/cli-color/-/cli-color-2.0.3.tgz", + "integrity": "sha512-OkoZnxyC4ERN3zLzZaY9Emb7f/MhBOIpePv0Ycok0fJYT+Ouo00UBEIwsVsr0yoow++n5YWlSUgST9GKhNHiRQ==", + "requires": { + "d": "^1.0.1", + "es5-ext": "^0.10.61", + "es6-iterator": "^2.0.3", + "memoizee": "^0.4.15", + "timers-ext": "^0.1.7" + } + }, + "code-red": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/code-red/-/code-red-1.0.4.tgz", + "integrity": "sha512-7qJWqItLA8/VPVlKJlFXU+NBlo/qyfs39aJcuMT/2ere32ZqvF5OSxgdM5xOfJJ7O429gg2HM47y8v9P+9wrNw==", + "peer": true, + "requires": { + "@jridgewell/sourcemap-codec": "^1.4.15", + "@types/estree": "^1.0.1", + "acorn": "^8.10.0", + "estree-walker": "^3.0.3", + "periscopic": "^3.1.0" + } + }, + "css-tree": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz", + "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==", + "peer": true, + "requires": { + "mdn-data": "2.0.30", + "source-map-js": "^1.0.1" + } + }, + "d": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", + "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", + "requires": { + "es5-ext": "^0.10.50", + "type": "^1.0.1" + } + }, + "deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==" + }, + "dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "peer": true + }, + "es5-ext": { + "version": "0.10.62", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.62.tgz", + "integrity": "sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA==", + "requires": { + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.3", + "next-tick": "^1.1.0" + } + }, + "es6-iterator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", + "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==", + "requires": { + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" + } + }, + "es6-symbol": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", + "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", + "requires": { + "d": "^1.0.1", + "ext": "^1.1.2" + } + }, + "es6-weak-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.3.tgz", + "integrity": "sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA==", + "requires": { + "d": "1", + "es5-ext": "^0.10.46", + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.1" + } + }, + "esbuild": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.5.tgz", + "integrity": "sha512-bUxalY7b1g8vNhQKdB24QDmHeY4V4tw/s6Ak5z+jJX9laP5MoQseTOMemAr0gxssjNcH0MCViG8ONI2kksvfFQ==", + "requires": { + "@esbuild/android-arm": "0.19.5", + "@esbuild/android-arm64": "0.19.5", + "@esbuild/android-x64": "0.19.5", + "@esbuild/darwin-arm64": "0.19.5", + "@esbuild/darwin-x64": "0.19.5", + "@esbuild/freebsd-arm64": "0.19.5", + "@esbuild/freebsd-x64": "0.19.5", + "@esbuild/linux-arm": "0.19.5", + "@esbuild/linux-arm64": "0.19.5", + "@esbuild/linux-ia32": "0.19.5", + "@esbuild/linux-loong64": "0.19.5", + "@esbuild/linux-mips64el": "0.19.5", + "@esbuild/linux-ppc64": "0.19.5", + "@esbuild/linux-riscv64": "0.19.5", + "@esbuild/linux-s390x": "0.19.5", + "@esbuild/linux-x64": "0.19.5", + "@esbuild/netbsd-x64": "0.19.5", + "@esbuild/openbsd-x64": "0.19.5", + "@esbuild/sunos-x64": "0.19.5", + "@esbuild/win32-arm64": "0.19.5", + "@esbuild/win32-ia32": "0.19.5", + "@esbuild/win32-x64": "0.19.5" + } + }, + "estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "peer": true, + "requires": { + "@types/estree": "^1.0.0" + } + }, + "event-emitter": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", + "integrity": "sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==", + "requires": { + "d": "1", + "es5-ext": "~0.10.14" + } + }, + "ext": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz", + "integrity": "sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==", + "requires": { + "type": "^2.7.2" + }, + "dependencies": { + "type": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/type/-/type-2.7.2.tgz", + "integrity": "sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==" + } + } + }, + "globalyzer": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/globalyzer/-/globalyzer-0.1.0.tgz", + "integrity": "sha512-40oNTM9UfG6aBmuKxk/giHn5nQ8RVz/SS4Ir6zgzOv9/qC3kKZ9v4etGTcJbEl/NyVQH7FGU7d+X1egr57Md2Q==" + }, + "globrex": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/globrex/-/globrex-0.1.2.tgz", + "integrity": "sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==" + }, + "intl-messageformat": { + "version": "9.13.0", + "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-9.13.0.tgz", + "integrity": "sha512-7sGC7QnSQGa5LZP7bXLDhVDtQOeKGeBFGHF2Y8LVBwYZoQZCgWeKoPGTa5GMG8g/TzDgeXuYJQis7Ggiw2xTOw==", + "requires": { + "@formatjs/ecma402-abstract": "1.11.4", + "@formatjs/fast-memoize": "1.2.1", + "@formatjs/icu-messageformat-parser": "2.1.0", + "tslib": "^2.1.0" + } + }, + "is-promise": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", + "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==" + }, + "is-reference": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.2.tgz", + "integrity": "sha512-v3rht/LgVcsdZa3O2Nqs+NMowLOxeOm7Ay9+/ARQ2F+qEoANRcqrjAZKGN0v8ymUetZGgkp26LTnGT7H0Qo9Pg==", + "peer": true, + "requires": { + "@types/estree": "*" + } + }, + "locate-character": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-character/-/locate-character-3.0.0.tgz", + "integrity": "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==", + "peer": true + }, + "lru-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/lru-queue/-/lru-queue-0.1.0.tgz", + "integrity": "sha512-BpdYkt9EvGl8OfWHDQPISVpcl5xZthb+XPsbELj5AQXxIC8IriDZIQYjBJPEm5rS420sjZ0TLEzRcq5KdBhYrQ==", + "requires": { + "es5-ext": "~0.10.2" + } + }, + "magic-string": { + "version": "0.30.5", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.5.tgz", + "integrity": "sha512-7xlpfBaQaP/T6Vh8MO/EqXSW5En6INHEvEXQiuff7Gku0PWjU3uf6w/j9o7O+SpB5fOAkrI5HeoNgwjEO0pFsA==", + "peer": true, + "requires": { + "@jridgewell/sourcemap-codec": "^1.4.15" + } + }, + "mdn-data": { + "version": "2.0.30", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", + "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==", + "peer": true + }, + "memoizee": { + "version": "0.4.15", + "resolved": "https://registry.npmjs.org/memoizee/-/memoizee-0.4.15.tgz", + "integrity": "sha512-UBWmJpLZd5STPm7PMUlOw/TSy972M+z8gcyQ5veOnSDRREz/0bmpyTfKt3/51DhEBqCZQn1udM/5flcSPYhkdQ==", + "requires": { + "d": "^1.0.1", + "es5-ext": "^0.10.53", + "es6-weak-map": "^2.0.3", + "event-emitter": "^0.3.5", + "is-promise": "^2.2.2", + "lru-queue": "^0.1.0", + "next-tick": "^1.1.0", + "timers-ext": "^0.1.7" + } + }, + "mri": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", + "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==" + }, + "next-tick": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", + "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==" + }, + "periscopic": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/periscopic/-/periscopic-3.1.0.tgz", + "integrity": "sha512-vKiQ8RRtkl9P+r/+oefh25C3fhybptkHKCZSPlcXiJux2tJF55GnEj3BVn4A5gKfq9NWWXXrxkHBwVPUfH0opw==", + "peer": true, + "requires": { + "@types/estree": "^1.0.0", + "estree-walker": "^3.0.0", + "is-reference": "^3.0.0" + } + }, + "sade": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz", + "integrity": "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==", + "requires": { + "mri": "^1.1.0" + } + }, + "source-map-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "peer": true + }, + "svelte": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/svelte/-/svelte-4.2.2.tgz", + "integrity": "sha512-My2tytF2e2NnHSpn2M7/3VdXT4JdTglYVUuSuK/mXL2XtulPYbeBfl8Dm1QiaKRn0zoULRnL+EtfZHHP0k4H3A==", + "peer": true, + "requires": { + "@ampproject/remapping": "^2.2.1", + "@jridgewell/sourcemap-codec": "^1.4.15", + "@jridgewell/trace-mapping": "^0.3.18", + "acorn": "^8.9.0", + "aria-query": "^5.3.0", + "axobject-query": "^3.2.1", + "code-red": "^1.0.3", + "css-tree": "^2.3.1", + "estree-walker": "^3.0.3", + "is-reference": "^3.0.1", + "locate-character": "^3.0.0", + "magic-string": "^0.30.4", + "periscopic": "^3.1.0" + } + }, + "svelte-i18n": { + "version": "3.7.4", + "resolved": "https://registry.npmjs.org/svelte-i18n/-/svelte-i18n-3.7.4.tgz", + "integrity": "sha512-yGRCNo+eBT4cPuU7IVsYTYjxB7I2V8qgUZPlHnNctJj5IgbJgV78flsRzpjZ/8iUYZrS49oCt7uxlU3AZv/N5Q==", + "requires": { + "cli-color": "^2.0.3", + "deepmerge": "^4.2.2", + "esbuild": "^0.19.2", + "estree-walker": "^2", + "intl-messageformat": "^9.13.0", + "sade": "^1.8.1", + "tiny-glob": "^0.2.9" + }, + "dependencies": { + "estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==" + } + } + }, + "timers-ext": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/timers-ext/-/timers-ext-0.1.7.tgz", + "integrity": "sha512-b85NUNzTSdodShTIbky6ZF02e8STtVVfD+fu4aXXShEELpozH+bCpJLYMPZbsABN2wDH7fJpqIoXxJpzbf0NqQ==", + "requires": { + "es5-ext": "~0.10.46", + "next-tick": "1" + } + }, + "tiny-glob": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/tiny-glob/-/tiny-glob-0.2.9.tgz", + "integrity": "sha512-g/55ssRPUjShh+xkfx9UPDXqhckHEsHr4Vd9zX55oSdGZc/MD0m3sferOkwWtp98bv+kcVfEHtRJgBVJzelrzg==", + "requires": { + "globalyzer": "0.1.0", + "globrex": "^0.1.2" + } + }, + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "type": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", + "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==" + } + } } diff --git a/test/conftest.py b/test/conftest.py index c1d7daf53d5d..e7f0358a495e 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -50,8 +50,7 @@ def _connect(demo: gr.Blocks, serialize=True): # because we should set a timeout # the tests that call .cancel() can get stuck # waiting for the thread to join - if demo.enable_queue: - demo._queue.close() + demo._queue.close() demo.is_running = False demo.server.should_exit = True demo.server.thread.join(timeout=1) diff --git a/test/test_blocks.py b/test/test_blocks.py index 265d5da50a42..25b28cd2c796 100644 --- a/test/test_blocks.py +++ b/test/test_blocks.py @@ -1,7 +1,6 @@ import asyncio import copy import io -import json import os import pathlib import random @@ -15,10 +14,10 @@ from string import capwords from unittest.mock import patch +import gradio_client as grc import numpy as np import pytest import uvicorn -import websockets from fastapi.testclient import TestClient from gradio_client import media_data from PIL import Image @@ -316,19 +315,9 @@ async def test_run_without_launching(self): server.run_in_thread() try: - async with websockets.connect(f"ws://localhost:{port}/queue/join") as ws: - completed = False - while not completed: - msg = json.loads(await ws.recv()) - if msg["msg"] == "send_data": - await ws.send(json.dumps({"data": ["Victor"], "fn_index": 0})) - if msg["msg"] == "send_hash": - await ws.send( - json.dumps({"fn_index": 0, "session_hash": "shdce"}) - ) - if msg["msg"] == "process_completed": - completed = True - assert msg["output"]["data"][0] == "Victor" + client = grc.Client(f"http://localhost:{port}") + result = client.predict("Victor", api_name="/predict") + assert result == "Victor" finally: server.close() @@ -349,23 +338,6 @@ def test_exit_called_at_launch(self): demo.launch(prevent_thread_lock=True) assert len(demo.get_config_file()["dependencies"]) == 1 - def test_raise_error_if_event_queued_but_queue_not_enabled(self): - with gr.Blocks() as demo: - with gr.Row(): - with gr.Column(): - input_ = gr.Textbox() - btn = gr.Button("Greet") - with gr.Column(): - output = gr.Textbox() - btn.click( - lambda x: f"Hello, {x}", inputs=input_, outputs=output, queue=True - ) - - with pytest.raises(ValueError, match="The queue is enabled for event lambda"): - demo.launch(prevent_thread_lock=True) - - demo.close() - def test_concurrency_count_zero_gpu(self, monkeypatch): monkeypatch.setenv("SPACES_ZERO_GPU", "true") demo = gr.Blocks() @@ -945,24 +917,6 @@ def trim(words, lens): msg = "In order to use batching, the queue must be enabled." - with pytest.raises(ValueError, match=msg): - demo = gr.Interface( - trim, ["textbox", "number"], ["textbox"], batch=True, max_batch_size=16 - ) - demo.launch(prevent_thread_lock=True) - - with pytest.raises(ValueError, match=msg): - with gr.Blocks() as demo: - with gr.Row(): - word = gr.Textbox(label="word") - leng = gr.Number(label="leng") - output = gr.Textbox(label="Output") - with gr.Row(): - run = gr.Button() - - run.click(trim, [word, leng], output, batch=True, max_batch_size=16) - demo.launch(prevent_thread_lock=True) - with pytest.raises(ValueError, match=msg): with gr.Blocks() as demo: with gr.Row(): @@ -1288,17 +1242,13 @@ def iteration(a): yield a msg = "Queue needs to be enabled!" - with pytest.raises(ValueError, match=msg): - gr.Interface(iteration, inputs=gr.Number(), outputs=gr.Number()).launch( - prevent_thread_lock=True - ) with pytest.raises(ValueError, match=msg): with gr.Blocks() as demo: button = gr.Button(value="Predict") click = button.click(None, None, None) cancel = gr.Button(value="Cancel") - cancel.click(None, None, None, cancels=[click]) + cancel.click(None, None, None, cancels=[click], queue=False) demo.launch(prevent_thread_lock=True) with pytest.raises(ValueError, match=msg): @@ -1328,63 +1278,6 @@ def test_raise_exception_if_parameters_invalid(self): num = gr.Number() num.change(lambda s: s + 1, inputs=[num], outputs=[num], every=-0.1) - @pytest.mark.asyncio - async def test_every_does_not_block_queue(self): - with gr.Blocks() as demo: - num = gr.Number(value=0) - name = gr.Textbox() - greeting = gr.Textbox() - button = gr.Button(value="Greet") - name.change(lambda n: n + random.random(), num, num, every=0.5) - button.click(lambda s: f"Hello, {s}!", name, greeting) - app, _, _ = demo.queue(max_size=1).launch(prevent_thread_lock=True) - client = TestClient(app) - - async with websockets.connect( - f"{demo.local_url.replace('http', 'ws')}queue/join" - ) as ws: - completed = False - while not completed: - msg = json.loads(await ws.recv()) - if msg["msg"] == "send_data": - await ws.send(json.dumps({"data": [0], "fn_index": 0})) - if msg["msg"] == "send_hash": - await ws.send(json.dumps({"fn_index": 0, "session_hash": "shdce"})) - status = client.get("/queue/status") - # If the continuous event got pushed to the queue, the size would be nonzero - # asserting false will terminate the test - if status.json()["queue_size"] != 0: - raise AssertionError() - else: - break - - @pytest.mark.asyncio - async def test_generating_event_cancelled_if_ws_closed(self, connect, capsys): - def generation(): - for i in range(10): - time.sleep(0.1) - print(f"At step {i}") - yield i - return "Hello!" - - with gr.Blocks() as demo: - greeting = gr.Textbox() - button = gr.Button(value="Greet") - button.click(generation, None, greeting) - - with connect(demo) as client: - job = client.submit(0, fn_index=0) - for i, _ in enumerate(job): - if i == 2: - job.cancel() - - await asyncio.sleep(1) - # If the generation function did not get cancelled - # it would have finished running and `At step 9` would - # have been printed - captured = capsys.readouterr() - assert "At step 9" not in captured.out - class TestGetAPIInfo: def test_many_endpoints(self): @@ -1585,22 +1478,6 @@ def moo(a, b, ed: SelectData, c=42, pr=pr): assert new_event_data.value == "foo" -def test_queue_enabled_for_fn(): - with gr.Blocks() as demo: - input = gr.Textbox() - output = gr.Textbox() - number = gr.Number() - button = gr.Button() - button.click(lambda x: f"Hello, {x}!", input, output) - button.click(lambda: 42, None, number, queue=True) - - assert not demo.queue_enabled_for_fn(0) - assert demo.queue_enabled_for_fn(1) - demo.queue() - assert demo.queue_enabled_for_fn(0) - assert demo.queue_enabled_for_fn(1) - - @pytest.mark.asyncio async def test_queue_when_using_auth(): sleep_time = 1 @@ -1616,56 +1493,17 @@ async def say_hello(name): button.click(say_hello, _input, _output) demo.queue() app, _, _ = demo.launch(auth=("abc", "123"), prevent_thread_lock=True) - client = TestClient(app) - resp = client.post( - f"{demo.local_url}login", - data={"username": "abc", "password": "123"}, - follow_redirects=False, - ) - assert resp.status_code == 200 - token = resp.cookies.get(f"access-token-{demo.app.cookie_id}") - assert token - - with pytest.raises(Exception) as e: - async with websockets.connect( - f"{demo.local_url.replace('http', 'ws')}queue/join", - ) as ws: - await ws.recv() - assert e.type == websockets.InvalidStatusCode - - async def run_ws(i): - async with websockets.connect( - f"{demo.local_url.replace('http', 'ws')}queue/join", - extra_headers={"Cookie": f"access-token-{demo.app.cookie_id}={token}"}, - ) as ws: - while True: - try: - msg = json.loads(await ws.recv()) - except websockets.ConnectionClosedOK: - break - if msg["msg"] == "send_hash": - await ws.send( - json.dumps({"fn_index": 0, "session_hash": "enwpitpex2q"}) - ) - if msg["msg"] == "send_data": - await ws.send( - json.dumps( - { - "data": [str(i)], - "fn_index": 0, - "session_hash": "enwpitpex2q", - } - ) - ) - msg = json.loads(await ws.recv()) - assert msg["msg"] == "process_starts" - if msg["msg"] == "process_completed": - assert msg["success"] - assert msg["output"]["data"] == [f"Hello {i}!"] - break - - await asyncio.gather(*[run_ws(i) for i in range(3)]) + with pytest.raises(ValueError): + grc.Client(f"http://localhost:{demo.server_port}") + + client = grc.Client(f"http://localhost:{demo.server_port}", auth=("abc", "123")) + jobs = [] + for i in range(3): + jobs.append(client.submit(f"World {i}", fn_index=0)) + + for i, job in enumerate(jobs): + assert job.result() == f"Hello World {i}!" def test_temp_file_sets_get_extended(): diff --git a/test/test_external.py b/test/test_external.py index 6304f0c616da..c55f4d74fc07 100644 --- a/test/test_external.py +++ b/test/test_external.py @@ -186,7 +186,7 @@ def test_english_to_spanish(self): def test_english_to_spanish_v4(self): with pytest.warns(UserWarning): - io = gr.load("spaces/gradio-tests/english_to_spanish-v4", title="hi") + io = gr.load("spaces/gradio-tests/english_to_spanishv4-sse", title="hi") assert isinstance(io.input_components[0], gr.Textbox) assert isinstance(io.output_components[0], gr.Textbox) @@ -217,7 +217,7 @@ def test_raise_incompatbile_version_error(self): gr.load("spaces/gradio-tests/titanic-survival") def test_numerical_to_label_space(self): - io = gr.load("spaces/gradio-tests/titanic-survival-v4") + io = gr.load("spaces/gradio-tests/titanic-survivalv4-sse") try: assert io.theme.name == "soft" assert io("male", 77, 10)["label"] == "Perishes" @@ -294,7 +294,7 @@ def test_text_to_image_model(self): def test_private_space(self): hf_token = "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" # Intentionally revealing this key for testing purposes io = gr.load( - "spaces/gradio-tests/not-actually-private-space-v4", hf_token=hf_token + "spaces/gradio-tests/not-actually-private-spacev4-sse", hf_token=hf_token ) try: output = io("abc") @@ -307,7 +307,8 @@ def test_private_space(self): def test_private_space_audio(self): hf_token = "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" # Intentionally revealing this key for testing purposes io = gr.load( - "spaces/gradio-tests/not-actually-private-space-audio-v4", hf_token=hf_token + "spaces/gradio-tests/not-actually-private-space-audiov4-sse", + hf_token=hf_token, ) try: output = io(media_data.BASE64_AUDIO["path"]) @@ -319,23 +320,24 @@ def test_multiple_spaces_one_private(self): hf_token = "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" # Intentionally revealing this key for testing purposes with gr.Blocks(): gr.load( - "spaces/gradio-tests/not-actually-private-space-v4", hf_token=hf_token + "spaces/gradio-tests/not-actually-private-spacev4-sse", + hf_token=hf_token, ) gr.load( - "spaces/gradio/test-loading-examples-v4", + "spaces/gradio/test-loading-examplesv4-sse", ) assert Context.hf_token == hf_token def test_loading_files_via_proxy_works(self): hf_token = "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" # Intentionally revealing this key for testing purposes io = gr.load( - "spaces/gradio-tests/test-loading-examples-private-v4", hf_token=hf_token + "spaces/gradio-tests/test-loading-examples-privatev4-sse", hf_token=hf_token ) assert io.theme.name == "default" app, _, _ = io.launch(prevent_thread_lock=True) test_client = TestClient(app) r = test_client.get( - "/proxy=https://gradio-tests-test-loading-examples-private-v4.hf.space/file=Bunny.obj" + "/proxy=https://gradio-tests-test-loading-examples-privatev4-sse.hf.space/file=Bunny.obj" ) assert r.status_code == 200 @@ -360,25 +362,25 @@ def test_interface_load_cache_examples(self, tmp_path): ) def test_root_url(self): - demo = gr.load("spaces/gradio/test-loading-examples-v4") + demo = gr.load("spaces/gradio/test-loading-examplesv4-sse") assert all( c["props"]["root_url"] - == "https://gradio-test-loading-examples-v4.hf.space/" + == "https://gradio-test-loading-examplesv4-sse.hf.space/" for c in demo.get_config_file()["components"] ) def test_root_url_deserialization(self): - demo = gr.load("spaces/gradio/simple_gallery-v4") + demo = gr.load("spaces/gradio/simple_galleryv4-sse") gallery = demo("test") assert all("caption" in d for d in gallery) def test_interface_with_examples(self): # This demo has the "fake_event" correctly removed - demo = gr.load("spaces/gradio-tests/test-calculator-1-v4") + demo = gr.load("spaces/gradio-tests/test-calculator-1v4-sse") assert demo(2, "add", 3) == 5 # This demo still has the "fake_event". both should work - demo = gr.load("spaces/gradio-tests/test-calculator-2-v4") + demo = gr.load("spaces/gradio-tests/test-calculator-2v4-sse") assert demo(2, "add", 4) == 6 @@ -450,13 +452,13 @@ def check_dataset(config, readme_examples): @pytest.mark.xfail def test_load_blocks_with_default_values(): - io = gr.load("spaces/gradio-tests/min-dalle-v4") + io = gr.load("spaces/gradio-tests/min-dallev4-sse") assert isinstance(io.get_config_file()["components"][0]["props"]["value"], list) - io = gr.load("spaces/gradio-tests/min-dalle-later-v4") + io = gr.load("spaces/gradio-tests/min-dalle-laterv4-sse") assert isinstance(io.get_config_file()["components"][0]["props"]["value"], list) - io = gr.load("spaces/gradio-tests/dataframe_load-v4") + io = gr.load("spaces/gradio-tests/dataframe_loadv4-sse") assert io.get_config_file()["components"][0]["props"]["value"] == { "headers": ["a", "b"], "data": [[1, 4], [2, 5], [3, 6]], @@ -483,14 +485,14 @@ def test_can_load_tabular_model_with_different_widget_data(hypothetical_readme): def test_raise_value_error_when_api_name_invalid(): - demo = gr.load(name="spaces/gradio/hello_world-v4") + demo = gr.load(name="spaces/gradio/hello_worldv4-sse") with pytest.raises(InvalidApiNameError): demo("freddy", api_name="route does not exist") def test_use_api_name_in_call_method(): # Interface - demo = gr.load(name="spaces/gradio/hello_world-v4") + demo = gr.load(name="spaces/gradio/hello_worldv4-sse") assert demo("freddy", api_name="predict") == "Hello freddy!" # Blocks demo with multiple functions diff --git a/test/test_helpers.py b/test/test_helpers.py index a7677afcee35..508cc5ddc407 100644 --- a/test/test_helpers.py +++ b/test/test_helpers.py @@ -8,8 +8,9 @@ from pathlib import Path from unittest.mock import patch +import httpx import pytest -import websockets +import requests from gradio_client import media_data, utils from pydub import AudioSegment from starlette.testclient import TestClient @@ -660,22 +661,35 @@ def greet(s, prog=gr.Progress()): button.click(greet, name, greeting) demo.queue(max_size=1).launch(prevent_thread_lock=True) - async with websockets.connect( - f"{demo.local_url.replace('http', 'ws')}queue/join" - ) as ws: - completed = False - progress_updates = [] - while not completed: - msg = json.loads(await ws.recv()) - if msg["msg"] == "send_data": - await ws.send(json.dumps({"data": [0], "fn_index": 0})) - if msg["msg"] == "send_hash": - await ws.send(json.dumps({"fn_index": 0, "session_hash": "shdce"})) - if msg["msg"] == "progress": - progress_updates.append(msg["progress_data"]) - if msg["msg"] == "process_completed": - completed = True - break + progress_updates = [] + async with httpx.AsyncClient() as client: + async with client.stream( + "GET", + f"http://localhost:{demo.server_port}/queue/join", + params={"fn_index": 0, "session_hash": "shdce"}, + ) as response: + async for line in response.aiter_text(): + if line.startswith("data:"): + msg = json.loads(line[5:]) + if msg["msg"] == "send_data": + event_id = msg["event_id"] + req = requests.post( + f"http://localhost:{demo.server_port}/queue/data", + json={ + "event_id": event_id, + "data": [0], + "fn_index": 0, + }, + ) + if not req.ok: + raise ValueError( + f"Could not send payload to endpoint: {req.text}" + ) + if msg["msg"] == "progress": + progress_updates.append(msg["progress_data"]) + if msg["msg"] == "process_completed": + break + assert progress_updates == [ [ { @@ -713,24 +727,35 @@ def greet(s, prog=gr.Progress(track_tqdm=True)): button.click(greet, name, greeting) demo.queue(max_size=1).launch(prevent_thread_lock=True) - async with websockets.connect( - f"{demo.local_url.replace('http', 'ws')}queue/join" - ) as ws: - completed = False - progress_updates = [] - while not completed: - msg = json.loads(await ws.recv()) - if msg["msg"] == "send_data": - await ws.send(json.dumps({"data": [0], "fn_index": 0})) - if msg["msg"] == "send_hash": - await ws.send(json.dumps({"fn_index": 0, "session_hash": "shdce"})) - if ( - msg["msg"] == "progress" and msg["progress_data"] - ): # Ignore empty lists which sometimes appear on Windows - progress_updates.append(msg["progress_data"]) - if msg["msg"] == "process_completed": - completed = True - break + progress_updates = [] + async with httpx.AsyncClient() as client: + async with client.stream( + "GET", + f"http://localhost:{demo.server_port}/queue/join", + params={"fn_index": 0, "session_hash": "shdce"}, + ) as response: + async for line in response.aiter_text(): + if line.startswith("data:"): + msg = json.loads(line[5:]) + if msg["msg"] == "send_data": + event_id = msg["event_id"] + req = requests.post( + f"http://localhost:{demo.server_port}/queue/data", + json={ + "event_id": event_id, + "data": [0], + "fn_index": 0, + }, + ) + if not req.ok: + raise ValueError( + f"Could not send payload to endpoint: {req.text}" + ) + if msg["msg"] == "progress": + progress_updates.append(msg["progress_data"]) + if msg["msg"] == "process_completed": + break + assert progress_updates == [ [ { @@ -787,24 +812,35 @@ def greet(s, _=gr.Progress(track_tqdm=True)): demo = gr.Interface(greet, "text", "text") demo.queue().launch(prevent_thread_lock=True) - async with websockets.connect( - f"{demo.local_url.replace('http', 'ws')}queue/join" - ) as ws: - completed = False - progress_updates = [] - while not completed: - msg = json.loads(await ws.recv()) - if msg["msg"] == "send_data": - await ws.send(json.dumps({"data": ["abc"], "fn_index": 0})) - if msg["msg"] == "send_hash": - await ws.send(json.dumps({"fn_index": 0, "session_hash": "shdce"})) - if ( - msg["msg"] == "progress" and msg["progress_data"] - ): # Ignore empty lists which sometimes appear on Windows - progress_updates.append(msg["progress_data"]) - if msg["msg"] == "process_completed": - completed = True - break + progress_updates = [] + async with httpx.AsyncClient() as client: + async with client.stream( + "GET", + f"http://localhost:{demo.server_port}/queue/join", + params={"fn_index": 0, "session_hash": "shdce"}, + ) as response: + async for line in response.aiter_text(): + if line.startswith("data:"): + msg = json.loads(line[5:]) + if msg["msg"] == "send_data": + event_id = msg["event_id"] + req = requests.post( + f"http://localhost:{demo.server_port}/queue/data", + json={ + "event_id": event_id, + "data": ["abc"], + "fn_index": 0, + }, + ) + if not req.ok: + raise ValueError( + f"Could not send payload to endpoint: {req.text}" + ) + if msg["msg"] == "progress": + progress_updates.append(msg["progress_data"]) + if msg["msg"] == "process_completed": + break + assert progress_updates == [ [ { @@ -848,24 +884,35 @@ def greet(s): demo = gr.Interface(greet, "text", "text") demo.queue().launch(prevent_thread_lock=True) - async with websockets.connect( - f"{demo.local_url.replace('http', 'ws')}queue/join" - ) as ws: - completed = False - log_messages = [] - while not completed: - msg = json.loads(await ws.recv()) - if msg["msg"] == "send_data": - await ws.send(json.dumps({"data": ["abc"], "fn_index": 0})) - if msg["msg"] == "send_hash": - await ws.send(json.dumps({"fn_index": 0, "session_hash": "shdce"})) - if ( - msg["msg"] == "log" - ): # Ignore empty lists which sometimes appear on Windows - log_messages.append([msg["log"], msg["level"]]) - if msg["msg"] == "process_completed": - completed = True - break + log_messages = [] + async with httpx.AsyncClient() as client: + async with client.stream( + "GET", + f"http://localhost:{demo.server_port}/queue/join", + params={"fn_index": 0, "session_hash": "shdce"}, + ) as response: + async for line in response.aiter_text(): + if line.startswith("data:"): + msg = json.loads(line[5:]) + if msg["msg"] == "send_data": + event_id = msg["event_id"] + req = requests.post( + f"http://localhost:{demo.server_port}/queue/data", + json={ + "event_id": event_id, + "data": ["abc"], + "fn_index": 0, + }, + ) + if not req.ok: + raise ValueError( + f"Could not send payload to endpoint: {req.text}" + ) + if msg["msg"] == "log": + log_messages.append([msg["log"], msg["level"]]) + if msg["msg"] == "process_completed": + break + assert log_messages == [ ["Letter a", "info"], ["Letter b", "info"], @@ -892,21 +939,36 @@ def greet_sync(name): async def session_interaction(name, delay=0): await asyncio.sleep(delay) - async with websockets.connect( - f"{demo.local_url.replace('http', 'ws')}queue/join" - ) as ws: - log_messages = [] - while True: - msg = json.loads(await ws.recv()) - if msg["msg"] == "send_data": - await ws.send(json.dumps({"data": [name], "fn_index": 0})) - if msg["msg"] == "send_hash": - await ws.send(json.dumps({"fn_index": 0, "session_hash": name})) - if msg["msg"] == "log": - log_messages.append(msg["log"]) - if msg["msg"] == "process_completed": - break - return log_messages + + log_messages = [] + async with httpx.AsyncClient() as client: + async with client.stream( + "GET", + f"http://localhost:{demo.server_port}/queue/join", + params={"fn_index": 0, "session_hash": name}, + ) as response: + async for line in response.aiter_text(): + if line.startswith("data:"): + msg = json.loads(line[5:]) + if msg["msg"] == "send_data": + event_id = msg["event_id"] + req = requests.post( + f"http://localhost:{demo.server_port}/queue/data", + json={ + "event_id": event_id, + "data": [name], + "fn_index": 0, + }, + ) + if not req.ok: + raise ValueError( + f"Could not send payload to endpoint: {req.text}" + ) + if msg["msg"] == "log": + log_messages.append(msg["log"]) + if msg["msg"] == "process_completed": + break + return log_messages alice_logs, bob_logs = await asyncio.gather( session_interaction("Alice"), diff --git a/test/test_mix.py b/test/test_mix.py index 9c9f2a1645b3..fb87d26ea13a 100644 --- a/test/test_mix.py +++ b/test/test_mix.py @@ -16,8 +16,8 @@ def test_in_interface(self): assert series("Hello") == "Hello World!" def test_with_external(self): - io1 = gr.load("spaces/gradio-tests/image-identity-new-v4") - io2 = gr.load("spaces/gradio-tests/image-classifier-new-v4") + io1 = gr.load("spaces/gradio-tests/image-identity-newv4-sse") + io2 = gr.load("spaces/gradio-tests/image-classifier-newv4-sse") series = mix.Series(io1, io2) try: assert series("gradio/test_data/lion.jpg")["label"] == "lion" @@ -45,8 +45,8 @@ def test_multiple_return_in_interface(self): ] def test_with_external(self): - io1 = gr.load("spaces/gradio-tests/english_to_spanish-v4") - io2 = gr.load("spaces/gradio-tests/english2german-v4") + io1 = gr.load("spaces/gradio-tests/english_to_spanishv4-sse") + io2 = gr.load("spaces/gradio-tests/english2germanv4-sse") parallel = mix.Parallel(io1, io2) try: hello_es, hello_de = parallel("Hello") diff --git a/test/test_networking.py b/test/test_networking.py index e9c18a1414b0..949cbd3cbb41 100644 --- a/test/test_networking.py +++ b/test/test_networking.py @@ -69,7 +69,6 @@ def test_start_server(self, host): networking.INITIAL_PORT_VALUE, networking.INITIAL_PORT_VALUE + networking.TRY_NUM_PORTS, ) - io.enable_queue = False _, _, local_path, _, server = networking.start_server(io, server_port=port) url = urllib.parse.urlparse(local_path) assert url.scheme == "http" diff --git a/test/test_queueing.py b/test/test_queueing.py index 9c102d959b00..2db8a3029a65 100644 --- a/test/test_queueing.py +++ b/test/test_queueing.py @@ -1,427 +1,128 @@ -import asyncio -import os -from collections import deque -from unittest.mock import MagicMock +import time -import pytest +import gradio_client as grc +from fastapi.testclient import TestClient + +import gradio as gr -from gradio.queueing import Event, Queue -os.environ["GRADIO_ANALYTICS_ENABLED"] = "False" - - -class AsyncMock(MagicMock): - async def __call__(self, *args, **kwargs): - return super(AsyncMock, self).__call__(*args, **kwargs) - - -@pytest.fixture() -def queue() -> Queue: - queue_object = Queue( - live_updates=True, - concurrency_count=1, - update_intervals=1, - max_size=None, - blocks_dependencies=[], - ) - yield queue_object - queue_object.close() - - -@pytest.fixture() -def mock_event() -> Event: - websocket = AsyncMock() - event = Event(websocket=websocket, session_hash="test", fn_index=0) - yield event - - -class TestQueueMethods: - def test_start(self, queue: Queue): - queue.start() - assert queue.stopped is False - assert queue.get_active_worker_count() == 0 - - def test_stop_resume(self, queue: Queue): - queue.start() - queue.close() - assert queue.stopped - queue.resume() - assert queue.stopped is False - - @pytest.mark.asyncio - async def test_receive(self, queue: Queue, mock_event: Event): - mock_event.websocket.receive_json.return_value = {"data": ["test"], "fn": 0} - await queue.get_message(mock_event) - assert mock_event.websocket.receive_json.called - - @pytest.mark.asyncio - async def test_receive_timeout(self, queue: Queue, mock_event: Event): - async def take_too_long(): - await asyncio.sleep(1) - - mock_event.websocket.receive_json = take_too_long - data, is_awake = await queue.get_message(mock_event, timeout=0.5) - assert data is None - assert not is_awake - - @pytest.mark.asyncio - async def test_send(self, queue: Queue, mock_event: Event): - await queue.send_message(mock_event, {}) - assert mock_event.websocket.send_json.called - - @pytest.mark.asyncio - async def test_add_to_queue(self, queue: Queue, mock_event: Event): - queue.push(mock_event) - assert len(queue.event_queue) == 1 - - @pytest.mark.asyncio - async def test_add_to_queue_with_max_size(self, queue: Queue, mock_event: Event): - queue.max_size = 1 - queue.push(mock_event) - assert len(queue.event_queue) == 1 - queue.push(mock_event) - assert len(queue.event_queue) == 1 - - @pytest.mark.asyncio - async def test_clean_event(self, queue: Queue, mock_event: Event): - queue.push(mock_event) - await queue.clean_event(mock_event) - assert len(queue.event_queue) == 0 - - @pytest.mark.asyncio - async def test_gather_event_data(self, queue: Queue, mock_event: Event): - queue.send_message = AsyncMock() - queue.get_message = AsyncMock() - queue.send_message.return_value = True - queue.get_message.return_value = {"data": ["test"], "fn": 0}, True - - assert await queue.gather_event_data(mock_event) - assert queue.send_message.called - assert mock_event.data == {"data": ["test"], "fn": 0} - - queue.send_message.called = False - assert await queue.gather_event_data(mock_event) - assert not (queue.send_message.called) - - @pytest.mark.asyncio - async def test_gather_event_data_timeout(self, queue: Queue, mock_event: Event): - async def take_too_long(): - await asyncio.sleep(1) - - queue.send_message = AsyncMock() - queue.send_message.return_value = True - - mock_event.websocket.receive_json = take_too_long - is_awake = await queue.gather_event_data(mock_event, receive_timeout=0.5) - assert not is_awake - - # Have to use awful [1][0][1] syntax cause of python 3.7 - assert queue.send_message.call_args_list[1][0][1] == { - "msg": "process_completed", - "output": {"error": "Time out uploading data to server"}, - "success": False, - } - - -class TestQueueEstimation: - def test_get_update_estimation(self, queue: Queue): - queue.update_estimation(5) - estimation = queue.get_estimation() - assert estimation.avg_event_process_time == 5 - - queue.update_estimation(15) - estimation = queue.get_estimation() - assert estimation.avg_event_process_time == 10 # (5 + 15) / 2 - - queue.update_estimation(100) - estimation = queue.get_estimation() - assert estimation.avg_event_process_time == 40 # (5 + 15 + 100) / 3 - - @pytest.mark.asyncio - async def test_send_estimation(self, queue: Queue, mock_event: Event): - queue.send_message = AsyncMock() - queue.send_message.return_value = True - estimation = queue.get_estimation() - estimation = await queue.send_estimation(mock_event, estimation, 1) - assert queue.send_message.called - assert estimation.rank == 1 - - queue.update_estimation(5) - estimation = await queue.send_estimation(mock_event, estimation, 2) - assert estimation.rank == 2 - assert estimation.rank_eta == 15 - - @pytest.mark.asyncio - async def queue_sets_concurrency_count(self): - queue_object = Queue( - live_updates=True, - concurrency_count=5, - update_intervals=1, - max_size=None, - ) - assert len(queue_object.active_jobs) == 5 - queue_object.close() - - -class TestQueueProcessEvents: - @pytest.mark.asyncio - async def test_process_event(self, queue: Queue, mock_event: Event): - queue.gather_event_data = AsyncMock() - queue.gather_event_data.return_value = True - queue.send_message = AsyncMock() - queue.send_message.return_value = True - queue.call_prediction = AsyncMock() - queue.call_prediction.return_value = {"is_generating": False} - mock_event.disconnect = AsyncMock() - queue.clean_event = AsyncMock() - queue.reset_iterators = AsyncMock() - - queue.active_jobs = [[mock_event]] - await queue.process_events([mock_event], batch=False) - - queue.call_prediction.assert_called_once() - mock_event.disconnect.assert_called_once() - queue.clean_event.assert_called_once() - queue.reset_iterators.assert_called_with( - mock_event.session_hash, - mock_event.fn_index, - ) - - @pytest.mark.asyncio - async def test_process_event_handles_error_when_gathering_data( - self, queue: Queue, mock_event: Event - ): - mock_event.websocket.send_json = AsyncMock() - mock_event.websocket.send_json.side_effect = ValueError("Can't connect") - queue.call_prediction = AsyncMock() - mock_event.disconnect = AsyncMock() - queue.clean_event = AsyncMock() - queue.reset_iterators = AsyncMock() - mock_event.data = None - - queue.active_jobs = [[mock_event]] - await queue.process_events([mock_event], batch=False) - - assert not queue.call_prediction.called - assert queue.clean_event.call_count >= 1 - - @pytest.mark.asyncio - async def test_process_event_handles_error_sending_process_start_msg( - self, queue: Queue, mock_event: Event - ): - mock_event.websocket.send_json = AsyncMock() - mock_event.websocket.receive_json.return_value = {"data": ["test"], "fn": 0} - - mock_event.websocket.send_json.side_effect = ["2", ValueError("Can't connect")] - queue.call_prediction = AsyncMock() - mock_event.disconnect = AsyncMock() - queue.clean_event = AsyncMock() - queue.reset_iterators = AsyncMock() - mock_event.data = None - - queue.active_jobs = [[mock_event]] - await queue.process_events([mock_event], batch=False) - - assert not queue.call_prediction.called - assert queue.clean_event.call_count >= 1 - - @pytest.mark.asyncio - async def test_process_event_handles_exception_in_call_prediction_request( - self, queue: Queue, mock_event: Event - ): - mock_event.disconnect = AsyncMock() - queue.gather_event_data = AsyncMock(return_value=True) - queue.clean_event = AsyncMock() - queue.send_message = AsyncMock(return_value=True) - queue.call_prediction = AsyncMock(side_effect=ValueError("foo")) - queue.reset_iterators = AsyncMock() - - queue.active_jobs = [[mock_event]] - await queue.process_events([mock_event], batch=False) - - queue.call_prediction.assert_called_once() - mock_event.disconnect.assert_called_once() - assert queue.clean_event.call_count >= 1 - - @pytest.mark.asyncio - async def test_process_event_handles_exception_in_is_generating_request( - self, queue: Queue, mock_event: Event - ): - # We need to return a good response with is_generating=True first, - # setting up the function to expect further iterative responses. - # Then we provide a 500 response. - side_effects = [ - {"is_generating": True}, - Exception("Foo"), - ] - mock_event.disconnect = AsyncMock() - queue.gather_event_data = AsyncMock(return_value=True) - queue.clean_event = AsyncMock() - queue.send_message = AsyncMock(return_value=True) - queue.call_prediction = AsyncMock(side_effect=side_effects) - queue.reset_iterators = AsyncMock() - - queue.active_jobs = [[mock_event]] - await queue.process_events([mock_event], batch=False) - queue.send_message.assert_called_with( - mock_event, - { - "msg": "process_completed", - "output": {"error": "Foo"}, - "success": False, - }, - ) - - assert queue.call_prediction.call_count == 2 - mock_event.disconnect.assert_called_once() - assert queue.clean_event.call_count >= 1 - - @pytest.mark.asyncio - async def test_process_event_handles_error_sending_process_completed_msg( - self, queue: Queue, mock_event: Event - ): - mock_event.websocket.receive_json.return_value = {"data": ["test"], "fn": 0} - mock_event.websocket.send_json = AsyncMock() - mock_event.websocket.send_json.side_effect = [ - "2", - "3", - ValueError("Can't connect"), - ] - queue.call_prediction = AsyncMock(return_value={"is_generating": False}) - mock_event.disconnect = AsyncMock() - queue.clean_event = AsyncMock() - queue.reset_iterators = AsyncMock() - mock_event.data = None - - queue.active_jobs = [[mock_event]] - await queue.process_events([mock_event], batch=False) - - queue.call_prediction.assert_called_once() - mock_event.disconnect.assert_called_once() - assert queue.clean_event.call_count >= 1 - - @pytest.mark.asyncio - async def test_process_event_handles_exception_during_disconnect( - self, queue: Queue, mock_event: Event - ): - mock_event.websocket.receive_json.return_value = {"data": ["test"], "fn": 0} - mock_event.websocket.send_json = AsyncMock() - queue.call_prediction = AsyncMock(return_value={"is_generating": False}) - queue.reset_iterators = AsyncMock() - # No exception should be raised during `process_event` - mock_event.disconnect = AsyncMock(side_effect=ValueError("...")) - queue.clean_event = AsyncMock() - mock_event.data = None - queue.active_jobs = [[mock_event]] - await queue.process_events([mock_event], batch=False) - queue.reset_iterators.assert_called_with( - mock_event.session_hash, - mock_event.fn_index, - ) - - -class TestQueueBatch: - @pytest.mark.asyncio - async def test_process_event(self, queue: Queue, mock_event: Event): - queue.gather_event_data = AsyncMock() - queue.gather_event_data.return_value = True - queue.send_message = AsyncMock() - queue.send_message.return_value = True - queue.call_prediction = AsyncMock() - queue.call_prediction.return_value = { - "is_generating": False, - "data": [[1, 2]], - } - mock_event.disconnect = AsyncMock() - queue.clean_event = AsyncMock() - queue.reset_iterators = AsyncMock() - - websocket = MagicMock() - mock_event2 = Event(websocket=websocket, session_hash="test", fn_index=0) - mock_event2.disconnect = AsyncMock() - queue.active_jobs = [[mock_event, mock_event2]] - - await queue.process_events([mock_event, mock_event2], batch=True) - - queue.call_prediction.assert_called_once() # called once for both events - mock_event.disconnect.assert_called_once() - - mock_event2.disconnect.assert_called_once() - assert queue.clean_event.call_count == 2 - - -class TestGetEventsInBatch: - def test_empty_event_queue(self, queue: Queue): - queue.event_queue = deque() - events, _ = queue.get_events_in_batch() - assert events is None - - def test_single_type_of_event(self, queue: Queue): - queue.blocks_dependencies = [{"batch": True, "max_batch_size": 3}] - queue.event_queue = deque() - queue.event_queue.extend( - [ - Event(websocket=MagicMock(), session_hash="test", fn_index=0), - Event(websocket=MagicMock(), session_hash="test", fn_index=0), - Event(websocket=MagicMock(), session_hash="test", fn_index=0), - Event(websocket=MagicMock(), session_hash="test", fn_index=0), - ] - ) - events, batch = queue.get_events_in_batch() - assert batch - assert [e.fn_index for e in events] == [0, 0, 0] - - events, batch = queue.get_events_in_batch() - assert batch - assert [e.fn_index for e in events] == [0] - - def test_multiple_batch_events(self, queue: Queue): - queue.blocks_dependencies = [ - {"batch": True, "max_batch_size": 3}, - {"batch": True, "max_batch_size": 2}, - ] - queue.event_queue = deque() - queue.event_queue.extend( - [ - Event(websocket=MagicMock(), session_hash="test", fn_index=0), - Event(websocket=MagicMock(), session_hash="test", fn_index=1), - Event(websocket=MagicMock(), session_hash="test", fn_index=0), - Event(websocket=MagicMock(), session_hash="test", fn_index=1), - Event(websocket=MagicMock(), session_hash="test", fn_index=0), - Event(websocket=MagicMock(), session_hash="test", fn_index=0), - ] - ) - events, batch = queue.get_events_in_batch() - assert batch - assert [e.fn_index for e in events] == [0, 0, 0] - - events, batch = queue.get_events_in_batch() - assert batch - assert [e.fn_index for e in events] == [1, 1] - - events, batch = queue.get_events_in_batch() - assert batch - assert [e.fn_index for e in events] == [0] - - def test_both_types_of_event(self, queue: Queue): - queue.blocks_dependencies = [ - {"batch": True, "max_batch_size": 3}, - {"batch": False}, - ] - queue.event_queue = deque() - queue.event_queue.extend( - [ - Event(websocket=MagicMock(), session_hash="test", fn_index=0), - Event(websocket=MagicMock(), session_hash="test", fn_index=1), - Event(websocket=MagicMock(), session_hash="test", fn_index=0), - Event(websocket=MagicMock(), session_hash="test", fn_index=1), - Event(websocket=MagicMock(), session_hash="test", fn_index=1), - ] - ) - events, batch = queue.get_events_in_batch() - assert batch - assert [e.fn_index for e in events] == [0, 0] - - events, batch = queue.get_events_in_batch() - assert not (batch) - assert [e.fn_index for e in events] == [1] +class TestQueueing: + def test_single_request(self): + with gr.Blocks() as demo: + name = gr.Textbox() + output = gr.Textbox() + + def greet(x): + return f"Hello, {x}!" + + name.submit(greet, name, output) + + demo.launch(prevent_thread_lock=True) + + client = grc.Client(f"http://localhost:{demo.server_port}") + job = client.submit("x", fn_index=0) + + assert job.result() == "Hello, x!" + + def test_multiple_requests(self): + with gr.Blocks() as demo: + name = gr.Textbox() + output = gr.Textbox() + + def greet(x): + time.sleep(2) + return f"Hello, {x}!" + + name.submit(greet, name, output) + + app, _, _ = demo.queue(concurrency_count=2).launch(prevent_thread_lock=True) + test_client = TestClient(app) + + client = grc.Client(f"http://localhost:{demo.server_port}") + client.submit("a", fn_index=0) + job2 = client.submit("b", fn_index=0) + client.submit("c", fn_index=0) + job4 = client.submit("d", fn_index=0) + + sizes = [] + while job4.status().code.value != "FINISHED": + queue_status = test_client.get("/queue/status").json() + sizes.append(queue_status["queue_size"]) + time.sleep(0.05) + + assert max(sizes) in [ + 2, + 3, + 4, + ] # Can be 2 - 4, depending on if the workers have picked up jobs before the queue status is checked + assert min(sizes) == 0 + assert sizes[-1] == 0 + + assert job2.result() == "Hello, b!" + assert job4.result() == "Hello, d!" + + def test_all_status_messages(self): + with gr.Blocks() as demo: + name = gr.Textbox() + output = gr.Textbox() + + def greet(x): + time.sleep(2) + return f"Hello, {x}!" + + name.submit(greet, name, output) + + app, _, _ = demo.queue(concurrency_count=2).launch(prevent_thread_lock=True) + test_client = TestClient(app) + + client = grc.Client(f"http://localhost:{demo.server_port}") + client.submit("a", fn_index=0) + job2 = client.submit("b", fn_index=0) + client.submit("c", fn_index=0) + job4 = client.submit("d", fn_index=0) + + sizes = [] + while job4.status().code.value != "FINISHED": + queue_status = test_client.get("/queue/status").json() + queue_size = queue_status["queue_size"] + if len(sizes) == 0 or queue_size != sizes[-1]: + sizes.append(queue_size) + time.sleep(0.01) + + time.sleep(0.1) + queue_status = test_client.get("/queue/status").json() + queue_size = queue_status["queue_size"] + if queue_size != sizes[-1]: + sizes.append(queue_size) + + assert max(sizes) in [ + 2, + 3, + 4, + ] # Can be 2 - 4, depending on if the workers have picked up jobs before the queue status is checked + print(sizes) + assert min(sizes) == 0 + assert sizes[-1] == 0 + + assert job2.result() == "Hello, b!" + assert job4.result() == "Hello, d!" + + def test_every_does_not_block_queue(self): + with gr.Blocks() as demo: + num = gr.Number(value=0) + num2 = gr.Number(value=0) + num.submit(lambda n: 2 * n, num, num, every=0.5) + num2.submit(lambda n: 3 * n, num, num) + + app, _, _ = demo.queue(max_size=1).launch(prevent_thread_lock=True) + test_client = TestClient(app) + + client = grc.Client(f"http://localhost:{demo.server_port}") + job = client.submit(1, fn_index=1) + + for _ in range(5): + status = test_client.get("/queue/status").json() + assert status["queue_size"] == 0 + time.sleep(0.5) + + assert job.result() == 3 diff --git a/test/test_routes.py b/test/test_routes.py index 337c2b58aee9..8ed123f06d4f 100644 --- a/test/test_routes.py +++ b/test/test_routes.py @@ -1,15 +1,14 @@ """Contains tests for networking.py and app.py""" import functools -import json import os import tempfile from contextlib import closing +import gradio_client as grc import numpy as np import pandas as pd import pytest import starlette.routing -import websockets from fastapi import FastAPI from fastapi.testclient import TestClient from gradio_client import media_data @@ -360,27 +359,6 @@ def test_do_not_expose_existence_of_files_outside_working_directory( response = test_client.get(r"/file=../fake-file-that-does-not-exist.js") assert response.status_code == 403 # not a 404 - def test_mount_gradio_app_raises_error_if_event_queued_but_queue_disabled(self): - with gr.Blocks() as demo: - with gr.Row(): - with gr.Column(): - input_ = gr.Textbox() - btn = gr.Button("Greet") - with gr.Column(): - output = gr.Textbox() - btn.click( - lambda x: f"Hello, {x}", - inputs=input_, - outputs=output, - queue=True, - api_name="greet", - ) - - with pytest.raises(ValueError, match="The queue is enabled for event greet"): - demo.launch(prevent_thread_lock=True) - - demo.close() - def test_proxy_route_is_restricted_to_load_urls(self): gr.context.Context.hf_token = "abcdef" app = routes.App() @@ -455,17 +433,10 @@ async def test_queue_join_routes_sets_app_if_none_set(self): io = Interface(lambda x: x, "text", "text").queue() io.launch(prevent_thread_lock=True) io._queue.server_path = None - async with websockets.connect( - f"{io.local_url.replace('http', 'ws')}queue/join" - ) as ws: - completed = False - while not completed: - msg = json.loads(await ws.recv()) - if msg["msg"] == "send_data": - await ws.send(json.dumps({"data": ["foo"], "fn_index": 0})) - if msg["msg"] == "send_hash": - await ws.send(json.dumps({"fn_index": 0, "session_hash": "shdce"})) - completed = msg["msg"] == "process_completed" + + client = grc.Client(io.local_url) + client.predict("test") + assert io._queue.server_app == io.server_app