Skip to content

Commit

Permalink
Add show_api parameter to events, and fix gr.load(). Also makes s…
Browse files Browse the repository at this point in the history
…ome minor improvements to the "view API" page when running on Spaces (#6846)

* show api

* chnages

* add changeset

* changes

* changes to interface

* set show_api=False

* changes

* development

* examples

* changes

* add changeset

* changes

* changes

* format

* add changeset

* add changeset

* changes

* revert

* fixes

* is vaild

* push

* fixes'

* fixing

* fixes

* format

* fixed

* demo

* test

* format

* add changeset

* fix tests

* fix

* format

* cleanup

* remove

* fixes

* add test

* format

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>
  • Loading branch information
abidlabs and gradio-pr-bot committed Dec 22, 2023
1 parent 15c97c6 commit 48d6534
Show file tree
Hide file tree
Showing 23 changed files with 273 additions and 275 deletions.
7 changes: 7 additions & 0 deletions .changeset/shaky-peaches-try.md
@@ -0,0 +1,7 @@
---
"@gradio/app": minor
"gradio": minor
"gradio_client": minor
---

fix:Add `show_api` parameter to events, and fix `gr.load()`. Also makes some minor improvements to the "view API" page when running on Spaces
16 changes: 12 additions & 4 deletions client/python/gradio_client/client.py
Expand Up @@ -683,7 +683,12 @@ def _infer_fn_index(self, api_name: str | None, fn_index: int | None) -> int:
raise ValueError(f"Invalid function index: {fn_index}.")
else:
valid_endpoints = [
e for e in self.endpoints if e.is_valid and e.api_name is not None
e
for e in self.endpoints
if e.is_valid
and e.api_name is not None
and e.backend_fn is not None
and e.show_api
]
if len(valid_endpoints) == 1:
inferred_fn_index = valid_endpoints[0].fn_index
Expand Down Expand Up @@ -919,9 +924,10 @@ def __init__(
hf_token=self.client.hf_token,
root_url=self.root_url,
)
# Only a real API endpoint if backend_fn is True (so not just a frontend function), serializers are valid,
# and api_name is not False (meaning that the developer has explicitly disabled the API endpoint)
self.is_valid = self.dependency["backend_fn"] and self.api_name is not False
# Disallow hitting endpoints that the Gradio app has disabled
self.is_valid = self.api_name is not False
self.backend_fn = dependency.get("backend_fn")
self.show_api = dependency.get("show_api")

def _get_component_type(self, component_id: int):
component = next(
Expand Down Expand Up @@ -1218,6 +1224,8 @@ def __init__(self, client: Client, fn_index: int, dependency: dict, *args):
self.is_valid = self.dependency["backend_fn"] and self.api_name is not False
except SerializationSetupError:
self.is_valid = False
self.backend_fn = dependency.get("backend_fn")
self.show_api = True

def __repr__(self):
return f"Endpoint src: {self.client.src}, api_name: {self.api_name}, fn_index: {self.fn_index}"
Expand Down
6 changes: 4 additions & 2 deletions client/python/gradio_client/utils.py
Expand Up @@ -856,9 +856,11 @@ def _json_schema_to_python_type(schema: Any, defs) -> str:
elif type_ == "null":
return "None"
elif type_ == "const":
return f"Litetal[{schema['const']}]"
return f"Literal[{schema['const']}]"
elif type_ == "enum":
return f"Literal[{', '.join([str(v) for v in schema['enum']])}]"
return (
"Literal[" + ", ".join(["'" + str(v) + "'" for v in schema["enum"]]) + "]"
)
elif type_ == "integer":
return "int"
elif type_ == "string":
Expand Down
7 changes: 2 additions & 5 deletions client/python/test/test_client.py
Expand Up @@ -872,7 +872,7 @@ def test_private_space(self):
"unnamed_endpoints": {},
}

def test_fetch_fixed_version_space(self, calculator_demo):
def test_api_info_of_local_demo(self, calculator_demo):
with connect(calculator_demo) as client:
api_info = client.view_api(return_format="dict")
assert isinstance(api_info, dict)
Expand All @@ -893,7 +893,7 @@ def test_fetch_fixed_version_space(self, calculator_demo):
"type": "string",
},
"python_type": {
"type": "Literal[add, subtract, multiply, divide]",
"type": "Literal['add', 'subtract', 'multiply', 'divide']",
"description": "",
},
"component": "Radio",
Expand All @@ -916,9 +916,6 @@ def test_fetch_fixed_version_space(self, calculator_demo):
}
],
}
assert (
"/load_example" in api_info["named_endpoints"]
) # The exact api configuration includes Block IDs and thus is not deterministic
assert api_info["unnamed_endpoints"] == {}

def test_unnamed_endpoints_use_fn_index(self, count_generator_demo):
Expand Down
1 change: 1 addition & 0 deletions demo/load_space/run.ipynb
@@ -0,0 +1 @@
{"cells": [{"cell_type": "markdown", "id": "302934307671667531413257853548643485645", "metadata": {}, "source": ["# Gradio Demo: load_space"]}, {"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", "demo = gr.load(\"gradio/test-gr-load\", src=\"spaces\")\n", "\n", "if __name__ == \"__main__\":\n", " demo.launch()"]}], "metadata": {}, "nbformat": 4, "nbformat_minor": 5}
6 changes: 6 additions & 0 deletions demo/load_space/run.py
@@ -0,0 +1,6 @@
import gradio as gr

demo = gr.load("gradio/test-gr-load", src="spaces")

if __name__ == "__main__":
demo.launch()
64 changes: 41 additions & 23 deletions gradio/blocks.py
Expand Up @@ -7,6 +7,7 @@
import os
import random
import secrets
import string
import sys
import tempfile
import threading
Expand Down Expand Up @@ -825,6 +826,7 @@ def set_event_trigger(
trigger_mode: Literal["once", "multiple", "always_last"] | None = "once",
concurrency_limit: int | None | Literal["default"] = "default",
concurrency_id: str | None = None,
show_api: bool = True,
) -> tuple[dict[str, Any], int]:
"""
Adds an event to the component's dependencies.
Expand All @@ -851,6 +853,7 @@ def set_event_trigger(
trigger_mode: If "once" (default for all events except `.change()`) would not allow any submissions while an event is pending. If set to "multiple", unlimited submissions are allowed while pending, and "always_last" (default for `.change()` event) would allow a second submission after the pending event is complete.
concurrency_limit: If set, this this is the maximum number of this event that can be running simultaneously. Can be set to None to mean no concurrency_limit (any number of this event can be running simultaneously). Set to "default" to use the default concurrency limit (defined by the `default_concurrency_limit` parameter in `queue()`, which itself is 1 by default).
concurrency_id: If set, this is the id of the concurrency group. Events with the same concurrency_id will be limited by the lowest set concurrency_limit.
show_api: whether to show this event in the "view API" page of the Gradio app, or in the ".view_api()" method of the Gradio clients. Unlike setting api_name to False, setting show_api to False will still allow downstream apps to use this event. If fn is None, show_api will automatically be set to False.
Returns: dependency information, dependency index
"""
# Support for singular parameter
Expand Down Expand Up @@ -928,12 +931,36 @@ def set_event_trigger(
tracks_progress=progress_index is not None,
)
)
if api_name is not None and api_name is not False:
api_name_ = utils.append_unique_suffix(

# If api_name is None or empty string, use the function name
if api_name is None or isinstance(api_name, str) and api_name.strip() == "":
if fn is not None:
if not hasattr(fn, "__name__"):
if hasattr(fn, "__class__") and hasattr(fn.__class__, "__name__"):
name = fn.__class__.__name__
else:
name = "unnamed"
else:
name = fn.__name__
api_name = "".join(
[s for s in name if s not in set(string.punctuation) - {"-", "_"}]
)
elif js is not None:
api_name = "js_fn"
show_api = False
else:
api_name = "unnamed"
show_api = False

if api_name is not False:
api_name = utils.append_unique_suffix(
api_name, [dep["api_name"] for dep in self.dependencies]
)
if api_name != api_name_:
api_name = api_name_
else:
show_api = False

# The `show_api` parameter is False if: (1) the user explicitly sets it (2) the user sets `api_name` to False
# or (3) the user sets `fn` to None (there's no backend function)

if collects_event_data is None:
collects_event_data = event_data_index is not None
Expand Down Expand Up @@ -962,6 +989,7 @@ def set_event_trigger(
"trigger_after": trigger_after,
"trigger_only_on_success": trigger_only_on_success,
"trigger_mode": trigger_mode,
"show_api": show_api,
}
self.dependencies.append(dependency)
return dependency, len(self.dependencies) - 1
Expand Down Expand Up @@ -2252,9 +2280,15 @@ def get_api_info(self):
"""
config = self.config
api_info = {"named_endpoints": {}, "unnamed_endpoints": {}}
mode = config.get("mode", None)

for d, dependency in enumerate(config["dependencies"]):
for dependency in config["dependencies"]:
if (
not dependency["backend_fn"]
or not dependency["show_api"]
or dependency["api_name"] is False
):
continue

dependency_info = {"parameters": [], "returns": []}
skip_endpoint = False

Expand Down Expand Up @@ -2316,25 +2350,9 @@ def get_api_info(self):
}
)

if not dependency["backend_fn"]:
skip_endpoint = True

if skip_endpoint:
continue
if (
dependency["api_name"] is not None
and dependency["api_name"] is not False
):
if not skip_endpoint:
api_info["named_endpoints"][
f"/{dependency['api_name']}"
] = dependency_info
elif (
dependency["api_name"] is False
or mode == "interface"
or mode == "tabbed_interface"
):
pass # Skip unnamed endpoints in interface mode
else:
api_info["unnamed_endpoints"][str(d)] = dependency_info

return api_info
28 changes: 14 additions & 14 deletions gradio/chat_interface.py
Expand Up @@ -293,21 +293,21 @@ def _setup_events(self) -> None:
self._clear_and_save_textbox,
[self.textbox],
[self.textbox, self.saved_input],
api_name=False,
show_api=False,
queue=False,
)
.then(
self._display_input,
[self.saved_input, self.chatbot_state],
[self.chatbot, self.chatbot_state],
api_name=False,
show_api=False,
queue=False,
)
.then(
submit_fn,
[self.saved_input, self.chatbot_state] + self.additional_inputs,
[self.chatbot, self.chatbot_state],
api_name=False,
show_api=False,
concurrency_limit=cast(
Union[int, Literal["default"], None], self.concurrency_limit
),
Expand All @@ -321,21 +321,21 @@ def _setup_events(self) -> None:
self._delete_prev_fn,
[self.chatbot_state],
[self.chatbot, self.saved_input, self.chatbot_state],
api_name=False,
show_api=False,
queue=False,
)
.then(
self._display_input,
[self.saved_input, self.chatbot_state],
[self.chatbot, self.chatbot_state],
api_name=False,
show_api=False,
queue=False,
)
.then(
submit_fn,
[self.saved_input, self.chatbot_state] + self.additional_inputs,
[self.chatbot, self.chatbot_state],
api_name=False,
show_api=False,
concurrency_limit=cast(
Union[int, Literal["default"], None], self.concurrency_limit
),
Expand All @@ -348,13 +348,13 @@ def _setup_events(self) -> None:
self._delete_prev_fn,
[self.chatbot_state],
[self.chatbot, self.saved_input, self.chatbot_state],
api_name=False,
show_api=False,
queue=False,
).then(
lambda x: x,
[self.saved_input],
[self.textbox],
api_name=False,
show_api=False,
queue=False,
)

Expand All @@ -364,7 +364,7 @@ def _setup_events(self) -> None:
None,
[self.chatbot, self.chatbot_state, self.saved_input],
queue=False,
api_name=False,
show_api=False,
)

def _setup_stop_events(
Expand All @@ -380,14 +380,14 @@ def _setup_stop_events(
),
None,
[self.submit_btn, self.stop_btn],
api_name=False,
show_api=False,
queue=False,
)
event_to_cancel.then(
lambda: (Button(visible=True), Button(visible=False)),
None,
[self.submit_btn, self.stop_btn],
api_name=False,
show_api=False,
queue=False,
)
else:
Expand All @@ -396,22 +396,22 @@ def _setup_stop_events(
lambda: Button(visible=True),
None,
[self.stop_btn],
api_name=False,
show_api=False,
queue=False,
)
event_to_cancel.then(
lambda: Button(visible=False),
None,
[self.stop_btn],
api_name=False,
show_api=False,
queue=False,
)
self.stop_btn.click(
None,
None,
None,
cancels=event_to_cancel,
api_name=False,
show_api=False,
)

def _setup_api(self) -> None:
Expand Down
4 changes: 3 additions & 1 deletion gradio/component_meta.py
Expand Up @@ -33,7 +33,8 @@ def {{ event }}(self,
trigger_mode: Literal["once", "multiple", "always_last"] | None = None,
js: str | None = None,
concurrency_limit: int | None | Literal["default"] = "default",
concurrency_id: str | None = None) -> Dependency:
concurrency_id: str | None = None,
show_api: bool = True) -> Dependency:
"""
Parameters:
fn: the function to call when this event is triggered. Often a machine learning model's prediction function. Each parameter of the function corresponds to one input component, and the function should return a single value or a tuple of values, with each element in the tuple corresponding to one output component.
Expand All @@ -53,6 +54,7 @@ def {{ event }}(self,
js: Optional frontend js method to run before running 'fn'. Input arguments for js method are values of 'inputs' and 'outputs', return should be a list of values for output components.
concurrency_limit: If set, this is the maximum number of this event that can be running simultaneously. Can be set to None to mean no concurrency_limit (any number of this event can be running simultaneously). Set to "default" to use the default concurrency limit (defined by the `default_concurrency_limit` parameter in `Blocks.queue()`, which itself is 1 by default).
concurrency_id: If set, this is the id of the concurrency group. Events with the same concurrency_id will be limited by the lowest set concurrency_limit.
show_api: whether to show this event in the "view API" page of the Gradio app, or in the ".view_api()" method of the Gradio clients. Unlike setting api_name to False, setting show_api to False will still allow downstream apps to use this event. If fn is None, show_api will automatically be set to False.
"""
...
{% endfor %}
Expand Down
16 changes: 14 additions & 2 deletions gradio/components/clear_button.py
Expand Up @@ -42,6 +42,8 @@ def __init__(
render: bool = True,
scale: int | None = None,
min_width: int | None = None,
api_name: str | None | Literal["False"] = None,
show_api: bool = False,
):
super().__init__(
value,
Expand All @@ -58,6 +60,8 @@ def __init__(
scale=scale,
min_width=min_width,
)
self.api_name = api_name
self.show_api = show_api
self.add(components)

def add(self, components: None | Component | list[Component]) -> ClearButton:
Expand Down Expand Up @@ -86,13 +90,21 @@ def add(self, components: None | Component | list[Component]) -> ClearButton:
none = none.model_dump()
none_values.append(none)
clear_values = json.dumps(none_values)
self.click(None, [], components, js=f"() => {clear_values}")
self.click(
None,
[],
components,
js=f"() => {clear_values}",
api_name=self.api_name,
show_api=self.show_api,
)
if state_components:
self.click(
lambda: resolve_singleton(initial_states),
None,
state_components,
api_name="reset_state",
api_name=self.api_name,
show_api=self.show_api,
)
return self

Expand Down

0 comments on commit 48d6534

Please sign in to comment.