mirror of
https://github.com/gradio-app/gradio.git
synced 2025-03-07 11:46:51 +08:00
Duplicate spaces integration into gradio (#4458)
* changes * changes * changes * changes * changes * changes * changes * changes * changes * changes * changes * changes * changes * changes * changes * changes * changes * changes * changes * changes * changes * changes * changes * changes * changes * changes * changes * changes
This commit is contained in:
parent
4d1ad7c820
commit
0909c35102
@ -3,6 +3,7 @@
|
||||
## New Features:
|
||||
|
||||
- Allow the web component `space`, `src`, and `host` attributes to be updated dynamically by [@pngwn](https://github.com/pngwn) in [PR 4461](https://github.com/gradio-app/gradio/pull/4461)
|
||||
- Spaces Duplication built into Gradio, by [@aliabid94](https://github.com/aliabid94) in [PR 4458](https://github.com/gradio-app/gradio/pull/4458)
|
||||
|
||||
## Bug Fixes:
|
||||
|
||||
|
2
client/js/src/globals.d.ts
vendored
2
client/js/src/globals.d.ts
vendored
@ -23,7 +23,7 @@ export interface Config {
|
||||
theme: string;
|
||||
title: string;
|
||||
version: string;
|
||||
is_space: boolean;
|
||||
space_id: string | null;
|
||||
is_colab: boolean;
|
||||
show_api: boolean;
|
||||
stylesheets: string[];
|
||||
|
@ -13,7 +13,7 @@ export interface Config {
|
||||
theme: string;
|
||||
title: string;
|
||||
version: string;
|
||||
is_space: boolean;
|
||||
space_id: string | null;
|
||||
is_colab: boolean;
|
||||
show_api: boolean;
|
||||
stylesheets: string[];
|
||||
|
@ -1 +1 @@
|
||||
{"cells": [{"cell_type": "markdown", "id": 302934307671667531413257853548643485645, "metadata": {}, "source": ["# Gradio Demo: blocks_xray"]}, {"cell_type": "code", "execution_count": null, "id": 272996653310673477252411125948039410165, "metadata": {}, "outputs": [], "source": ["!pip install -q gradio "]}, {"cell_type": "code", "execution_count": null, "id": 288918539441861185822528903084949547379, "metadata": {}, "outputs": [], "source": ["import gradio as gr\n", "import time\n", "\n", "disease_values = [0.25, 0.5, 0.75]\n", "\n", "def xray_model(diseases, img):\n", " return [{disease: disease_values[idx] for idx,disease in enumerate(diseases)}]\n", "\n", "\n", "def ct_model(diseases, img):\n", " return [{disease: 0.1 for disease in diseases}]\n", "\n", "with gr.Blocks() as demo:\n", " gr.Markdown(\n", " \"\"\"\n", "# Detect Disease From Scan\n", "With this model you can lorem ipsum\n", "- ipsum 1\n", "- ipsum 2\n", "\"\"\"\n", " )\n", " disease = gr.CheckboxGroup(\n", " info=\"Select the diseases you want to scan for.\",\n", " choices=[\"Covid\", \"Malaria\", \"Lung Cancer\"], label=\"Disease to Scan For\"\n", " )\n", " slider = gr.Slider(0, 100)\n", "\n", " with gr.Tab(\"X-ray\") as x_tab:\n", " with gr.Row():\n", " xray_scan = gr.Image()\n", " xray_results = gr.JSON()\n", " xray_run = gr.Button(\"Run\")\n", " xray_run.click(\n", " xray_model,\n", " inputs=[disease, xray_scan],\n", " outputs=xray_results,\n", " api_name=\"xray_model\"\n", " )\n", "\n", " with gr.Tab(\"CT Scan\"):\n", " with gr.Row():\n", " ct_scan = gr.Image()\n", " ct_results = gr.JSON()\n", " ct_run = gr.Button(\"Run\")\n", " ct_run.click(\n", " ct_model,\n", " inputs=[disease, ct_scan],\n", " outputs=ct_results,\n", " api_name=\"ct_model\"\n", " )\n", "\n", " upload_btn = gr.Button(\"Upload Results\", variant=\"primary\")\n", " upload_btn.click(\n", " lambda ct, xr: None,\n", " inputs=[ct_results, xray_results],\n", " outputs=[],\n", " )\n", "\n", "if __name__ == \"__main__\":\n", " demo.launch()\n"]}], "metadata": {}, "nbformat": 4, "nbformat_minor": 5}
|
||||
{"cells": [{"cell_type": "markdown", "id": 302934307671667531413257853548643485645, "metadata": {}, "source": ["# Gradio Demo: blocks_xray"]}, {"cell_type": "code", "execution_count": null, "id": 272996653310673477252411125948039410165, "metadata": {}, "outputs": [], "source": ["!pip install -q gradio "]}, {"cell_type": "code", "execution_count": null, "id": 288918539441861185822528903084949547379, "metadata": {}, "outputs": [], "source": ["import gradio as gr\n", "import time\n", "\n", "disease_values = [0.25, 0.5, 0.75]\n", "\n", "def xray_model(diseases, img):\n", " return [{disease: disease_values[idx] for idx,disease in enumerate(diseases)}]\n", "\n", "\n", "def ct_model(diseases, img):\n", " return [{disease: 0.1 for disease in diseases}]\n", "\n", "with gr.Blocks() as demo:\n", " gr.Markdown(\n", " \"\"\"\n", "# Detect Disease From Scan\n", "With this model you can lorem ipsum\n", "- ipsum 1\n", "- ipsum 2\n", "\"\"\"\n", " )\n", " gr.DuplicateButton()\n", " disease = gr.CheckboxGroup(\n", " info=\"Select the diseases you want to scan for.\",\n", " choices=[\"Covid\", \"Malaria\", \"Lung Cancer\"], label=\"Disease to Scan For\"\n", " )\n", " slider = gr.Slider(0, 100)\n", "\n", " with gr.Tab(\"X-ray\") as x_tab:\n", " with gr.Row():\n", " xray_scan = gr.Image()\n", " xray_results = gr.JSON()\n", " xray_run = gr.Button(\"Run\")\n", " xray_run.click(\n", " xray_model,\n", " inputs=[disease, xray_scan],\n", " outputs=xray_results,\n", " api_name=\"xray_model\"\n", " )\n", "\n", " with gr.Tab(\"CT Scan\"):\n", " with gr.Row():\n", " ct_scan = gr.Image()\n", " ct_results = gr.JSON()\n", " ct_run = gr.Button(\"Run\")\n", " ct_run.click(\n", " ct_model,\n", " inputs=[disease, ct_scan],\n", " outputs=ct_results,\n", " api_name=\"ct_model\"\n", " )\n", "\n", " upload_btn = gr.Button(\"Upload Results\", variant=\"primary\")\n", " upload_btn.click(\n", " lambda ct, xr: None,\n", " inputs=[ct_results, xray_results],\n", " outputs=[],\n", " )\n", "\n", "if __name__ == \"__main__\":\n", " demo.launch()\n"]}], "metadata": {}, "nbformat": 4, "nbformat_minor": 5}
|
@ -19,6 +19,7 @@ With this model you can lorem ipsum
|
||||
- ipsum 2
|
||||
"""
|
||||
)
|
||||
gr.DuplicateButton()
|
||||
disease = gr.CheckboxGroup(
|
||||
info="Select the diseases you want to scan for.",
|
||||
choices=["Covid", "Malaria", "Lung Cancer"], label="Disease to Scan For"
|
||||
|
2
globals.d.ts
vendored
2
globals.d.ts
vendored
@ -23,7 +23,7 @@ export interface Config {
|
||||
theme: string;
|
||||
title: string;
|
||||
version: string;
|
||||
is_space: boolean;
|
||||
space_id: string | null;
|
||||
is_colab: boolean;
|
||||
show_api: boolean;
|
||||
stylesheets: string[];
|
||||
|
@ -27,6 +27,7 @@ from gradio.components import (
|
||||
Dataframe,
|
||||
Dataset,
|
||||
Dropdown,
|
||||
DuplicateButton,
|
||||
File,
|
||||
Gallery,
|
||||
Highlight,
|
||||
|
@ -399,7 +399,7 @@ class BlockFunction:
|
||||
def spaces_auto_wrap(self):
|
||||
if spaces is None:
|
||||
return
|
||||
if not utils.is_space():
|
||||
if utils.get_space() is None:
|
||||
return
|
||||
self.fn = spaces.gradio_auto_wrap(self.fn)
|
||||
|
||||
@ -718,7 +718,7 @@ class Blocks(BlockContext):
|
||||
self.height = None
|
||||
self.api_open = True
|
||||
|
||||
self.is_space = utils.is_space()
|
||||
self.space_id = utils.get_space()
|
||||
self.favicon_path = None
|
||||
self.auth = None
|
||||
self.dev_mode = True
|
||||
@ -1384,7 +1384,7 @@ Received outputs:
|
||||
"components": [],
|
||||
"css": self.css,
|
||||
"title": self.title or "Gradio",
|
||||
"is_space": self.is_space,
|
||||
"space_id": self.space_id,
|
||||
"enable_queue": getattr(self, "enable_queue", False), # launch attributes
|
||||
"show_error": getattr(self, "show_error", False),
|
||||
"show_api": self.show_api,
|
||||
@ -1735,7 +1735,7 @@ Received outputs:
|
||||
DeprecationWarning,
|
||||
)
|
||||
|
||||
if self.is_space:
|
||||
if self.space_id:
|
||||
self.enable_queue = self.enable_queue is not False
|
||||
else:
|
||||
self.enable_queue = self.enable_queue is True
|
||||
@ -1859,7 +1859,7 @@ Received outputs:
|
||||
)
|
||||
|
||||
if self.share:
|
||||
if self.is_space:
|
||||
if self.space_id:
|
||||
raise RuntimeError("Share is not supported when you are in Spaces")
|
||||
try:
|
||||
if self.share_url is None:
|
||||
@ -1958,7 +1958,7 @@ Received outputs:
|
||||
"show_tips": self.show_tips,
|
||||
"server_name": server_name,
|
||||
"server_port": server_port,
|
||||
"is_spaces": self.is_space,
|
||||
"is_space": self.space_id is not None,
|
||||
"mode": self.mode,
|
||||
}
|
||||
analytics.launched_analytics(self, data)
|
||||
|
@ -23,6 +23,7 @@ from gradio.components.color_picker import ColorPicker
|
||||
from gradio.components.dataframe import Dataframe
|
||||
from gradio.components.dataset import Dataset
|
||||
from gradio.components.dropdown import Dropdown
|
||||
from gradio.components.duplicate_button import DuplicateButton
|
||||
from gradio.components.file import File
|
||||
from gradio.components.gallery import Gallery
|
||||
from gradio.components.highlighted_text import HighlightedText
|
||||
@ -74,6 +75,7 @@ __all__ = [
|
||||
"Dataframe",
|
||||
"DataFrame",
|
||||
"Dataset",
|
||||
"DuplicateButton",
|
||||
"Form",
|
||||
"FormComponent",
|
||||
"Gallery",
|
||||
|
61
gradio/components/duplicate_button.py
Normal file
61
gradio/components/duplicate_button.py
Normal file
@ -0,0 +1,61 @@
|
||||
""" Predefined buttons with bound events that can be included in a gr.Blocks for convenience. """
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Literal
|
||||
|
||||
from gradio_client.documentation import document, set_documentation_group
|
||||
|
||||
from gradio.components import Button
|
||||
from gradio.utils import get_space
|
||||
|
||||
set_documentation_group("component")
|
||||
|
||||
|
||||
@document()
|
||||
class DuplicateButton(Button):
|
||||
"""
|
||||
Button that appears only on Hugging Face Spaces, triggering a Spaces Duplication.
|
||||
Preprocessing: passes the button value as a {str} into the function
|
||||
Postprocessing: expects a {str} to be returned from a function, which is set as the label of the button
|
||||
"""
|
||||
|
||||
is_template = True
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
value: str = "Duplicate Space",
|
||||
variant: Literal["primary", "secondary", "stop"] = "secondary",
|
||||
size: Literal["sm", "lg"] | None = "sm",
|
||||
visible: bool = True,
|
||||
interactive: bool = True,
|
||||
elem_id: str | None = None,
|
||||
elem_classes: list[str] | str | None = None,
|
||||
scale: int | None = 0,
|
||||
min_width: int | None = None,
|
||||
_activate: bool = True,
|
||||
**kwargs,
|
||||
):
|
||||
super().__init__(
|
||||
value,
|
||||
variant=variant,
|
||||
size=size,
|
||||
visible=visible,
|
||||
interactive=interactive,
|
||||
elem_id=elem_id,
|
||||
elem_classes=elem_classes,
|
||||
scale=scale,
|
||||
min_width=min_width,
|
||||
**kwargs,
|
||||
)
|
||||
if _activate:
|
||||
self.activate()
|
||||
|
||||
def activate(self):
|
||||
space_name = get_space()
|
||||
if space_name is not None:
|
||||
self.click(
|
||||
fn=None,
|
||||
_js=f"() => {{ window.open(`https://huggingface.co/spaces/{space_name}?duplicate=true`, '_blank') }}",
|
||||
)
|
@ -19,6 +19,7 @@ from gradio.blocks import Blocks
|
||||
from gradio.components import (
|
||||
Button,
|
||||
ClearButton,
|
||||
DuplicateButton,
|
||||
Interpretation,
|
||||
IOComponent,
|
||||
Markdown,
|
||||
@ -141,6 +142,7 @@ class Interface(Blocks):
|
||||
batch: bool = False,
|
||||
max_batch_size: int = 4,
|
||||
_api_mode: bool = False,
|
||||
allow_duplication: bool = False,
|
||||
**kwargs,
|
||||
):
|
||||
"""
|
||||
@ -167,6 +169,7 @@ class Interface(Blocks):
|
||||
analytics_enabled: Whether to allow basic telemetry. If None, will use GRADIO_ANALYTICS_ENABLED environment variable if defined, or default to True.
|
||||
batch: If True, then the function should process a batch of inputs, meaning that it should accept a list of input values for each parameter. The lists should be of equal length (and be up to length `max_batch_size`). The function is then *required* to return a tuple of lists (even if there is only 1 output component), with each list in the tuple corresponding to one output component.
|
||||
max_batch_size: Maximum number of inputs to batch together if this is called from the queue (only relevant if batch=True)
|
||||
allow_duplication: If True, then will show a 'Duplicate Spaces' button on Hugging Face Spaces.
|
||||
"""
|
||||
super().__init__(
|
||||
analytics_enabled=analytics_enabled,
|
||||
@ -202,7 +205,7 @@ class Interface(Blocks):
|
||||
if not isinstance(outputs, list):
|
||||
outputs = [outputs]
|
||||
|
||||
if self.is_space and cache_examples is None:
|
||||
if self.space_id and cache_examples is None:
|
||||
self.cache_examples = True
|
||||
else:
|
||||
self.cache_examples = cache_examples or False
|
||||
@ -358,6 +361,7 @@ class Interface(Blocks):
|
||||
self.flagging_dir = flagging_dir
|
||||
self.batch = batch
|
||||
self.max_batch_size = max_batch_size
|
||||
self.allow_duplication = allow_duplication
|
||||
|
||||
self.save_to = None # Used for selenium tests
|
||||
self.share = None
|
||||
@ -400,7 +404,13 @@ class Interface(Blocks):
|
||||
with self:
|
||||
self.render_title_description()
|
||||
|
||||
submit_btn, clear_btn, stop_btn, flag_btns = None, None, None, None
|
||||
submit_btn, clear_btn, stop_btn, flag_btns, duplicate_btn = (
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
)
|
||||
interpretation_btn, interpretation_set = None, None
|
||||
input_component_column, interpret_component_column = None, None
|
||||
|
||||
@ -426,6 +436,7 @@ class Interface(Blocks):
|
||||
(
|
||||
submit_btn_out,
|
||||
clear_btn_2_out,
|
||||
duplicate_btn,
|
||||
stop_btn_2_out,
|
||||
flag_btns_out,
|
||||
interpretation_btn,
|
||||
@ -441,6 +452,8 @@ class Interface(Blocks):
|
||||
self.attach_clear_events(
|
||||
clear_btn, input_component_column, interpret_component_column
|
||||
)
|
||||
if duplicate_btn is not None:
|
||||
duplicate_btn.activate()
|
||||
self.attach_interpretation_events(
|
||||
interpretation_btn,
|
||||
interpretation_set,
|
||||
@ -533,10 +546,21 @@ class Interface(Blocks):
|
||||
self,
|
||||
submit_btn_in: Button | None,
|
||||
) -> tuple[
|
||||
Button | None, ClearButton | None, Button | None, list | None, Button | None
|
||||
Button | None,
|
||||
ClearButton | None,
|
||||
DuplicateButton,
|
||||
Button | None,
|
||||
list | None,
|
||||
Button | None,
|
||||
]:
|
||||
submit_btn = submit_btn_in
|
||||
interpretation_btn, clear_btn, flag_btns, stop_btn = None, None, None, None
|
||||
interpretation_btn, clear_btn, duplicate_btn, flag_btns, stop_btn = (
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
)
|
||||
|
||||
with Column(variant="panel"):
|
||||
for component in self.output_components:
|
||||
@ -561,10 +585,21 @@ class Interface(Blocks):
|
||||
elif self.allow_flagging == "auto":
|
||||
assert submit_btn is not None, "Submit button not rendered"
|
||||
flag_btns = [submit_btn]
|
||||
|
||||
if self.interpretation:
|
||||
interpretation_btn = Button("Interpret")
|
||||
|
||||
return submit_btn, clear_btn, stop_btn, flag_btns, interpretation_btn
|
||||
if self.allow_duplication:
|
||||
duplicate_btn = DuplicateButton(scale=1, size="lg", _activate=False)
|
||||
|
||||
return (
|
||||
submit_btn,
|
||||
clear_btn,
|
||||
duplicate_btn,
|
||||
stop_btn,
|
||||
flag_btns,
|
||||
interpretation_btn,
|
||||
)
|
||||
|
||||
def render_article(self):
|
||||
if self.article:
|
||||
|
@ -258,7 +258,7 @@ class App(FastAPI):
|
||||
config = {
|
||||
"auth_required": True,
|
||||
"auth_message": blocks.auth_message,
|
||||
"is_space": app.get_blocks().is_space,
|
||||
"space_id": app.get_blocks().space_id,
|
||||
"root": root_path,
|
||||
}
|
||||
|
||||
|
@ -211,7 +211,7 @@ XRAY_CONFIG = {
|
||||
],
|
||||
"css": None,
|
||||
"title": "Gradio",
|
||||
"is_space": False,
|
||||
"space_id": False,
|
||||
"enable_queue": None,
|
||||
"show_error": True,
|
||||
"show_api": True,
|
||||
@ -525,7 +525,7 @@ XRAY_CONFIG_DIFF_IDS = {
|
||||
],
|
||||
"css": None,
|
||||
"title": "Gradio",
|
||||
"is_space": False,
|
||||
"space_id": False,
|
||||
"enable_queue": None,
|
||||
"show_error": True,
|
||||
"show_api": True,
|
||||
|
@ -107,8 +107,10 @@ def ipython_check() -> bool:
|
||||
return is_ipython
|
||||
|
||||
|
||||
def is_space() -> bool:
|
||||
return os.getenv("SYSTEM") == "spaces"
|
||||
def get_space() -> str | None:
|
||||
if os.getenv("SYSTEM") == "spaces":
|
||||
return os.getenv("SPACE_ID")
|
||||
return None
|
||||
|
||||
|
||||
def readme_to_html(article: str) -> str:
|
||||
|
@ -43,6 +43,7 @@
|
||||
export let app_mode: boolean;
|
||||
export let theme_mode: ThemeMode;
|
||||
export let app: Awaited<ReturnType<typeof client>>;
|
||||
export let space_id: string | null;
|
||||
|
||||
let loading_status = create_loading_status_store();
|
||||
|
||||
@ -237,6 +238,11 @@
|
||||
let _error_id = -1;
|
||||
const MESSAGE_QUOTE_RE = /^'([^]+)'$/;
|
||||
|
||||
const DUPLICATE_MESSAGE =
|
||||
"There is a long queue of requests pending. Duplicate this Space to skip.";
|
||||
const SHOW_DUPLICATE_MESSAGE_ON_ETA = 15;
|
||||
let showed_duplicate_message = false;
|
||||
|
||||
const trigger_api_call = async (
|
||||
dep_index: number,
|
||||
event_data: unknown = null
|
||||
@ -298,6 +304,25 @@
|
||||
progress: status.progress_data,
|
||||
fn_index
|
||||
});
|
||||
if (
|
||||
!showed_duplicate_message &&
|
||||
space_id !== null &&
|
||||
status.position !== undefined &&
|
||||
status.position >= 2 &&
|
||||
status.eta !== undefined &&
|
||||
status.eta > SHOW_DUPLICATE_MESSAGE_ON_ETA
|
||||
) {
|
||||
showed_duplicate_message = true;
|
||||
messages = [
|
||||
{
|
||||
type: "warning",
|
||||
message: DUPLICATE_MESSAGE,
|
||||
id: ++_error_id,
|
||||
fn_index
|
||||
},
|
||||
...messages
|
||||
];
|
||||
}
|
||||
|
||||
if (status.stage === "complete") {
|
||||
dependencies.map(async (dep, i) => {
|
||||
|
@ -23,7 +23,7 @@
|
||||
theme: string;
|
||||
title: string;
|
||||
version: string;
|
||||
is_space: boolean;
|
||||
space_id: string | null;
|
||||
is_colab: boolean;
|
||||
show_api: boolean;
|
||||
stylesheets?: string[];
|
||||
@ -321,7 +321,7 @@
|
||||
<Login
|
||||
auth_message={config.auth_message}
|
||||
root={config.root}
|
||||
is_space={config.is_space}
|
||||
space_id={space}
|
||||
{app_mode}
|
||||
/>
|
||||
{:else if config && Blocks && css_ready}
|
||||
|
@ -6,7 +6,7 @@
|
||||
export let root: string;
|
||||
export let auth_message: string | null;
|
||||
export let app_mode: boolean;
|
||||
export let is_space: boolean;
|
||||
export let space_id: string | null;
|
||||
|
||||
let username = "";
|
||||
let password = "";
|
||||
@ -37,7 +37,7 @@
|
||||
{#if auth_message}
|
||||
<p class="auth">{auth_message}</p>
|
||||
{/if}
|
||||
{#if is_space}
|
||||
{#if space_id}
|
||||
<p class="auth">
|
||||
If you are visiting a HuggingFace Space in Incognito mode, you must
|
||||
enable third party cookies.
|
||||
|
@ -9,9 +9,9 @@
|
||||
<div class="toast-wrap">
|
||||
{#each messages as { type, message, id } (id)}
|
||||
<div animate:flip={{ duration: 300 }} style:width="100%">
|
||||
{#if type === "error"}
|
||||
<Error {message} on:close {id} />
|
||||
{/if}
|
||||
<!-- {#if type === "error"} -->
|
||||
<Error {message} on:close {id} />
|
||||
<!-- {/if} -->
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
|
@ -14,6 +14,7 @@
|
||||
class:hide={!visible}
|
||||
class="{size} {variant} {elem_classes.join(' ')}"
|
||||
style:flex-grow={scale}
|
||||
style:width={scale === 0 ? "fit-content" : null}
|
||||
style:min-width={typeof min_width === "number"
|
||||
? `calc(min(${min_width}px, 100%))`
|
||||
: null}
|
||||
|
Loading…
Reference in New Issue
Block a user