Adding API docs to all interfaces (#309)
* api view with basic html * added base docs for raw inputs/outputs * reading correct url from frontend * styling * fill in the blank request snippet * post a request from docs * post button and random generator * styling * added view the api to interface * style changes * corrected input output docs * prefill with example instead of random * added curl and javascript syntax * removed scrollbars * API doc fixes * added correct docs to all pre/post processing methods * updated to new doc style * live demo with sample inputs * fixing golden screenshots * correct timeseries preprocess doc * correct timeseries preprocess doc * correct timeseries preprocess doc * fixed overwrite issue * remove static from git * fix merge * fix tests * fix tests * fix tests Co-authored-by: Ali Abid <you@example.comgit>
@ -18,6 +18,7 @@ jobs:
|
||||
. venv/bin/activate
|
||||
pip install --upgrade pip
|
||||
pip install -r gradio.egg-info/requires.txt
|
||||
pip install shap IPython
|
||||
pip install selenium==4.0.0a6.post2 coverage
|
||||
- run:
|
||||
command: |
|
||||
|
2
.gitignore
vendored
@ -32,7 +32,7 @@ demo/flagged
|
||||
test.txt
|
||||
build/
|
||||
gradio/launches.json
|
||||
gradio/frontend/static
|
||||
gradio/templates/frontend/static
|
||||
workspace.code-workspace
|
||||
*.db
|
||||
demo/files/*.avi
|
||||
|
@ -1,2 +1,2 @@
|
||||
recursive-include gradio/frontend *
|
||||
recursive-include gradio/templates *
|
||||
include gradio/version.txt
|
@ -29,7 +29,7 @@ module.exports = {
|
||||
webpackConfig.entry = "./src/index";
|
||||
webpackConfig.output = {
|
||||
publicPath: "",
|
||||
path: path.resolve(__dirname, "../gradio/frontend"),
|
||||
path: path.resolve(__dirname, "../gradio/templates/frontend"),
|
||||
filename: "static/bundle.js",
|
||||
chunkFilename: "static/js/[name].chunk.js"
|
||||
};
|
||||
|
@ -39,15 +39,19 @@ export class GradioPage extends React.Component {
|
||||
false
|
||||
)}
|
||||
</div>
|
||||
<a href="/api/" target="_blank" class="footer" rel="noreferrer">
|
||||
<span>view the api </span><img class="logo" src="https://i.ibb.co/6DVLqmf/noun-tools-2220412.png" alt="logo"/>
|
||||
<span> |</span>
|
||||
<a
|
||||
href="https://gradio.app"
|
||||
target="_blank"
|
||||
className="footer"
|
||||
rel="noreferrer"
|
||||
>
|
||||
<span>built with</span>
|
||||
<span> built with</span>
|
||||
<img className="logo" src={logo} alt="logo" />
|
||||
</a>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@ -335,6 +339,7 @@ export class GradioInterface extends React.Component {
|
||||
<button className="panel_button submit" onClick={this.submit}>
|
||||
Submit
|
||||
</button>
|
||||
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -19,6 +19,9 @@
|
||||
.footer {
|
||||
@apply flex-shrink-0 flex gap-1 items-center text-gray-400 dark:text-gray-50 justify-center py-2;
|
||||
}
|
||||
.api {
|
||||
@apply flex-shrink-0 flex gap-1 items-center text-gray-400 dark:text-gray-50 justify-end py-2;
|
||||
}
|
||||
.logo {
|
||||
@apply h-6;
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
Metadata-Version: 2.1
|
||||
Metadata-Version: 1.0
|
||||
Name: gradio
|
||||
Version: 2.3.9
|
||||
Summary: Python library for easily interacting with trained machine learning models
|
||||
@ -6,9 +6,6 @@ Home-page: https://github.com/gradio-app/gradio-UI
|
||||
Author: Abubakar Abid
|
||||
Author-email: a12d@stanford.edu
|
||||
License: Apache License 2.0
|
||||
Description: UNKNOWN
|
||||
Keywords: machine learning,visualization,reproducibility
|
||||
Platform: UNKNOWN
|
||||
License-File: LICENSE
|
||||
|
||||
UNKNOWN
|
||||
|
||||
|
@ -1,17 +1,15 @@
|
||||
numpy
|
||||
pydub
|
||||
matplotlib
|
||||
pandas
|
||||
pillow
|
||||
ffmpy
|
||||
markdown2
|
||||
pycryptodome
|
||||
requests
|
||||
paramiko
|
||||
analytics-python
|
||||
Flask>=1.1.1
|
||||
Flask-Cors>=3.0.8
|
||||
flask-cachebuster
|
||||
Flask-Login
|
||||
IPython
|
||||
shap
|
||||
Flask>=1.1.1
|
||||
analytics-python
|
||||
ffmpy
|
||||
flask-cachebuster
|
||||
markdown2
|
||||
matplotlib
|
||||
numpy
|
||||
pandas
|
||||
paramiko
|
||||
pillow
|
||||
pycryptodome
|
||||
pydub
|
||||
requests
|
||||
|
140
gradio/inputs.py
@ -78,6 +78,13 @@ class InputComponent(Component):
|
||||
'''
|
||||
pass
|
||||
|
||||
def generate_sample(self):
|
||||
"""
|
||||
Returns a sample value of the input that would be accepted by the api. Used for api documentation.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class Textbox(InputComponent):
|
||||
"""
|
||||
Component creates a textbox for user to enter input. Provides a string as an argument to the wrapped function.
|
||||
@ -130,6 +137,10 @@ class Textbox(InputComponent):
|
||||
}
|
||||
|
||||
def preprocess(self, x):
|
||||
"""
|
||||
Parameters:
|
||||
x (str): text input
|
||||
"""
|
||||
if self.type == "str":
|
||||
return x
|
||||
elif self.type == "number":
|
||||
@ -195,10 +206,13 @@ class Textbox(InputComponent):
|
||||
result.append((self.interpretation_separator, 0))
|
||||
return result
|
||||
|
||||
def generate_sample(self):
|
||||
return "Hello World"
|
||||
|
||||
|
||||
class Number(InputComponent):
|
||||
"""
|
||||
Component creates a field for user to enter numeric input. Provides a nuber as an argument to the wrapped function.
|
||||
Component creates a field for user to enter numeric input. Provides a number as an argument to the wrapped function.
|
||||
Input type: float
|
||||
Demos: tax_calculator.py, titanic_survival.py
|
||||
"""
|
||||
@ -228,8 +242,10 @@ class Number(InputComponent):
|
||||
|
||||
def preprocess(self, x):
|
||||
"""
|
||||
Parameters:
|
||||
x (number): numeric input
|
||||
Returns:
|
||||
(float): Number representing function input
|
||||
(float): number representing function input
|
||||
"""
|
||||
return float(x)
|
||||
|
||||
@ -275,6 +291,9 @@ class Number(InputComponent):
|
||||
interpretation.insert(int(len(interpretation) / 2), [x, None])
|
||||
return interpretation
|
||||
|
||||
def generate_sample(self):
|
||||
return 1
|
||||
|
||||
|
||||
class Slider(InputComponent):
|
||||
"""
|
||||
@ -319,6 +338,15 @@ class Slider(InputComponent):
|
||||
"slider": {},
|
||||
}
|
||||
|
||||
def preprocess(self, x):
|
||||
"""
|
||||
Parameters:
|
||||
x (number): numeric input
|
||||
Returns:
|
||||
(number): numeric input
|
||||
"""
|
||||
return x
|
||||
|
||||
def preprocess_example(self, x):
|
||||
"""
|
||||
Returns:
|
||||
@ -345,6 +373,9 @@ class Slider(InputComponent):
|
||||
"""
|
||||
return scores
|
||||
|
||||
def generate_sample(self):
|
||||
return self.maximum
|
||||
|
||||
|
||||
class Checkbox(InputComponent):
|
||||
"""
|
||||
@ -376,6 +407,15 @@ class Checkbox(InputComponent):
|
||||
"checkbox": {},
|
||||
}
|
||||
|
||||
def preprocess(self, x):
|
||||
"""
|
||||
Parameters:
|
||||
x (bool): boolean input
|
||||
Returns:
|
||||
(bool): boolean input
|
||||
"""
|
||||
return x
|
||||
|
||||
def preprocess_example(self, x):
|
||||
"""
|
||||
Returns:
|
||||
@ -402,6 +442,8 @@ class Checkbox(InputComponent):
|
||||
else:
|
||||
return None, scores[0]
|
||||
|
||||
def generate_sample(self):
|
||||
return True
|
||||
|
||||
class CheckboxGroup(InputComponent):
|
||||
"""
|
||||
@ -433,6 +475,12 @@ class CheckboxGroup(InputComponent):
|
||||
}
|
||||
|
||||
def preprocess(self, x):
|
||||
"""
|
||||
Parameters:
|
||||
x (List[str]): list of selected choices
|
||||
Returns:
|
||||
(Union[List[str], List[int]]): list of selected choices as strings or indices within choice list
|
||||
"""
|
||||
if self.type == "value":
|
||||
return x
|
||||
elif self.type == "index":
|
||||
@ -481,6 +529,8 @@ class CheckboxGroup(InputComponent):
|
||||
def restore_flagged(self, data):
|
||||
return json.loads(data)
|
||||
|
||||
def generate_sample(self):
|
||||
return self.choices
|
||||
|
||||
class Radio(InputComponent):
|
||||
"""
|
||||
@ -512,6 +562,12 @@ class Radio(InputComponent):
|
||||
}
|
||||
|
||||
def preprocess(self, x):
|
||||
"""
|
||||
Parameters:
|
||||
x (str): selected choice
|
||||
Returns:
|
||||
(Union[str, int]): selected choice as string or index within choice list
|
||||
"""
|
||||
if self.type == "value":
|
||||
return x
|
||||
elif self.type == "index":
|
||||
@ -539,6 +595,9 @@ class Radio(InputComponent):
|
||||
scores.insert(self.choices.index(x), None)
|
||||
return scores
|
||||
|
||||
def generate_sample(self):
|
||||
return self.choices[0]
|
||||
|
||||
|
||||
class Dropdown(InputComponent):
|
||||
"""
|
||||
@ -570,6 +629,12 @@ class Dropdown(InputComponent):
|
||||
}
|
||||
|
||||
def preprocess(self, x):
|
||||
"""
|
||||
Parameters:
|
||||
x (str): selected choice
|
||||
Returns:
|
||||
(Union[str, int]): selected choice as string or index within choice list
|
||||
"""
|
||||
if self.type == "value":
|
||||
return x
|
||||
elif self.type == "index":
|
||||
@ -597,6 +662,10 @@ class Dropdown(InputComponent):
|
||||
scores.insert(self.choices.index(x), None)
|
||||
return scores
|
||||
|
||||
def generate_sample(self):
|
||||
return self.choices[0]
|
||||
|
||||
|
||||
class Image(InputComponent):
|
||||
"""
|
||||
Component creates an image upload box with editing capabilities.
|
||||
@ -647,6 +716,12 @@ class Image(InputComponent):
|
||||
}
|
||||
|
||||
def preprocess(self, x):
|
||||
"""
|
||||
Parameters:
|
||||
x (str): base64 url data
|
||||
Returns:
|
||||
(Union[numpy.array, PIL.Image, file-object]): image in requested format
|
||||
"""
|
||||
if x is None:
|
||||
return x
|
||||
im = processing_utils.decode_base64_to_image(x)
|
||||
@ -791,6 +866,9 @@ class Image(InputComponent):
|
||||
"""
|
||||
return self.save_flagged_file(dir, label, data, encryption_key)
|
||||
|
||||
def generate_sample(self):
|
||||
return test_data.BASE64_IMAGE
|
||||
|
||||
|
||||
class Video(InputComponent):
|
||||
"""
|
||||
@ -823,9 +901,15 @@ class Video(InputComponent):
|
||||
}
|
||||
|
||||
def preprocess(self, x):
|
||||
"""
|
||||
Parameters:
|
||||
x (Dict[name: str, data: str]): JSON object with filename as 'name' property and base64 data as 'data' property
|
||||
Returns:
|
||||
(str): file path to video
|
||||
"""
|
||||
if x is None:
|
||||
return x
|
||||
file_name, file_data, is_example = x["name"], x["data"], x["is_example"]
|
||||
file_name, file_data, is_example = x["name"], x["data"], x.get("is_example", False)
|
||||
if is_example:
|
||||
file = processing_utils.create_tmp_copy_of_file(file_name)
|
||||
else:
|
||||
@ -857,6 +941,9 @@ class Video(InputComponent):
|
||||
"""
|
||||
return self.save_flagged_file(dir, label, data, encryption_key)
|
||||
|
||||
def generate_sample(self):
|
||||
return test_data.BASE64_VIDEO
|
||||
|
||||
|
||||
class Audio(InputComponent):
|
||||
"""
|
||||
@ -877,8 +964,7 @@ class Audio(InputComponent):
|
||||
requires_permissions = source == "microphone"
|
||||
self.type = type
|
||||
self.optional = optional
|
||||
self.test_input = {"name": "sample.wav",
|
||||
"data": test_data.BASE64_AUDIO, "is_example": False}
|
||||
self.test_input = test_data.BASE64_AUDIO
|
||||
self.interpret_by_tokens = True
|
||||
super().__init__(label, requires_permissions)
|
||||
|
||||
@ -898,6 +984,12 @@ class Audio(InputComponent):
|
||||
}
|
||||
|
||||
def preprocess(self, x):
|
||||
"""
|
||||
Parameters:
|
||||
x (Dict[name: str, data: str]): JSON object with filename as 'name' property and base64 data as 'data' property
|
||||
Returns:
|
||||
(Union[Tuple[int, numpy.array], file-object, numpy.array]): audio in requested format
|
||||
"""
|
||||
if x is None:
|
||||
return x
|
||||
file_name, file_data, is_example = x["name"], x["data"], x.get("is_example", False)
|
||||
@ -1023,12 +1115,15 @@ class Audio(InputComponent):
|
||||
"""
|
||||
return self.save_flagged_file(dir, label, data, encryption_key)
|
||||
|
||||
def generate_sample(self):
|
||||
return test_data.BASE64_AUDIO
|
||||
|
||||
|
||||
class File(InputComponent):
|
||||
"""
|
||||
Component accepts generic file uploads.
|
||||
Input type: Union[file-object, bytes, List[Union[file-object, bytes]]]
|
||||
Demos: zip_to_json.py, zip_two_files.py
|
||||
Demos: zisp_to_json.py, zip_two_files.py
|
||||
"""
|
||||
|
||||
def __init__(self, file_count="single", type="file", label=None, keep_filename=True, optional=False):
|
||||
@ -1061,11 +1156,17 @@ class File(InputComponent):
|
||||
}
|
||||
|
||||
def preprocess(self, x):
|
||||
"""
|
||||
Parameters:
|
||||
x (List[Dict[name: str, data: str]]): List of JSON objects with filename as 'name' property and base64 data as 'data' property
|
||||
Returns:
|
||||
(Union[file-object, bytes, List[Union[file-object, bytes]]]): File objects in requested format
|
||||
"""
|
||||
if x is None:
|
||||
return None
|
||||
|
||||
def process_single_file(f):
|
||||
file_name, data, is_example = f["name"], f["data"], f["is_example"]
|
||||
file_name, data, is_example = f["name"], f["data"], f.get("is_example", False)
|
||||
if self.type == "file":
|
||||
if is_example:
|
||||
return processing_utils.create_tmp_copy_of_file(file_name)
|
||||
@ -1093,6 +1194,9 @@ class File(InputComponent):
|
||||
"""
|
||||
return self.save_flagged_file(dir, label, data["data"], encryption_key)
|
||||
|
||||
def generate_sample(self):
|
||||
return test_data.BASE64_FILE
|
||||
|
||||
|
||||
class Dataframe(InputComponent):
|
||||
"""
|
||||
@ -1151,6 +1255,12 @@ class Dataframe(InputComponent):
|
||||
}
|
||||
|
||||
def preprocess(self, x):
|
||||
"""
|
||||
Parameters:
|
||||
x (List[List[Union[str, number, bool]]]): 2D array of str, numeric, or bool data
|
||||
Returns:
|
||||
(Union[pandas.DataFrame, numpy.array, List[Union[str, float]], List[List[Union[str, float]]]]): Dataframe in requested format
|
||||
"""
|
||||
if self.type == "pandas":
|
||||
if self.headers:
|
||||
return pd.DataFrame(x, columns=self.headers)
|
||||
@ -1175,6 +1285,9 @@ class Dataframe(InputComponent):
|
||||
def restore_flagged(self, data):
|
||||
return json.loads(data)
|
||||
|
||||
def generate_sample(self):
|
||||
return [[1, 2, 3], [4, 5, 6]]
|
||||
|
||||
|
||||
class Timeseries(InputComponent):
|
||||
"""
|
||||
@ -1213,10 +1326,16 @@ class Timeseries(InputComponent):
|
||||
}
|
||||
|
||||
def preprocess(self, x):
|
||||
"""
|
||||
Parameters:
|
||||
x (Dict[data: List[List[Union[str, number, bool]]], headers: List[str], range: List[number]]): Dict with keys 'data': 2D array of str, numeric, or bool data, 'headers': list of strings for header names, 'range': optional two element list designating start of end of subrange.
|
||||
Returns:
|
||||
(pandas.DataFrame): Dataframe of timeseries data
|
||||
"""
|
||||
if x is None:
|
||||
return x
|
||||
dataframe = pd.DataFrame(data=x["data"], columns=x["headers"])
|
||||
if x["range"] is not None:
|
||||
if x.get("range") is not None:
|
||||
dataframe = dataframe.loc[dataframe[self.x or 0] >= x["range"][0]]
|
||||
dataframe = dataframe.loc[dataframe[self.x or 0] <= x["range"][1]]
|
||||
return dataframe
|
||||
@ -1230,6 +1349,11 @@ class Timeseries(InputComponent):
|
||||
def restore_flagged(self, data):
|
||||
return json.loads(data)
|
||||
|
||||
def generate_sample(self):
|
||||
return {"data": [[1] + [2] * len(self.y)] * 4, "headers": [self.x] + self.y}
|
||||
|
||||
|
||||
|
||||
|
||||
def get_input_instance(iface):
|
||||
if isinstance(iface, str):
|
||||
|
@ -25,9 +25,9 @@ from gradio import encryptor
|
||||
from gradio import queue
|
||||
from functools import wraps
|
||||
import io
|
||||
import inspect
|
||||
import traceback
|
||||
|
||||
|
||||
INITIAL_PORT_VALUE = int(os.getenv(
|
||||
'GRADIO_SERVER_PORT', "7860")) # The http server will try to open on port 7860. If not available, 7861, 7862, etc.
|
||||
TRY_NUM_PORTS = int(os.getenv(
|
||||
@ -37,8 +37,8 @@ LOCALHOST_NAME = os.getenv(
|
||||
GRADIO_API_SERVER = "https://api.gradio.app/v1/tunnel-request"
|
||||
GRADIO_FEATURE_ANALYTICS_URL = "https://api.gradio.app/gradio-feature-analytics/"
|
||||
|
||||
STATIC_TEMPLATE_LIB = pkg_resources.resource_filename("gradio", "frontend/")
|
||||
STATIC_PATH_LIB = pkg_resources.resource_filename("gradio", "frontend/static")
|
||||
STATIC_TEMPLATE_LIB = pkg_resources.resource_filename("gradio", "templates/")
|
||||
STATIC_PATH_LIB = pkg_resources.resource_filename("gradio", "templates/frontend/static")
|
||||
VERSION_FILE = pkg_resources.resource_filename("gradio", "version.txt")
|
||||
with open(VERSION_FILE) as version_file:
|
||||
GRADIO_STATIC_ROOT = "https://gradio.s3-us-west-2.amazonaws.com/" + \
|
||||
@ -129,7 +129,7 @@ def get_first_available_port(initial, final):
|
||||
@login_check
|
||||
def main():
|
||||
session["state"] = None
|
||||
return render_template("index.html", config=app.interface.config)
|
||||
return render_template("frontend/index.html", config=app.interface.config)
|
||||
|
||||
|
||||
@app.route("/static/<path:path>", methods=["GET"])
|
||||
@ -145,7 +145,7 @@ def static_resource(path):
|
||||
def login():
|
||||
if request.method == "GET":
|
||||
config = get_config()
|
||||
return render_template("index.html", config=config)
|
||||
return render_template("frontend/index.html", config=config)
|
||||
elif request.method == "POST":
|
||||
username = request.form.get("username")
|
||||
password = request.form.get("password")
|
||||
@ -207,6 +207,49 @@ def predict():
|
||||
return jsonify(output)
|
||||
|
||||
|
||||
def get_types(cls_set, component):
|
||||
docset = []
|
||||
types = []
|
||||
if component == "input":
|
||||
for cls in cls_set:
|
||||
doc = inspect.getdoc(cls.preprocess)
|
||||
doc_lines = doc.split("\n")
|
||||
docset.append(doc_lines[1].split(":")[-1])
|
||||
types.append(doc_lines[1].split(")")[0].split("(")[-1])
|
||||
else:
|
||||
for cls in cls_set:
|
||||
doc = inspect.getdoc(cls.postprocess)
|
||||
doc_lines = doc.split("\n")
|
||||
docset.append(doc_lines[-1].split(":")[-1])
|
||||
types.append(doc_lines[-1].split(")")[0].split("(")[-1])
|
||||
return docset, types
|
||||
|
||||
|
||||
@app.route("/api/", methods=["GET"])
|
||||
def api_docs():
|
||||
inputs = [type(inp) for inp in app.interface.input_components]
|
||||
outputs = [type(out) for out in app.interface.output_components]
|
||||
input_types_doc, input_types = get_types(inputs, "input")
|
||||
output_types_doc, output_types = get_types(outputs, "output")
|
||||
input_names = [type(inp).__name__ for inp in app.interface.input_components]
|
||||
output_names = [type(out).__name__ for out in app.interface.output_components]
|
||||
sample_inputs = [inp.generate_sample() for inp in app.interface.input_components]
|
||||
docs = {
|
||||
"inputs": input_names,
|
||||
"outputs": output_names,
|
||||
"len_inputs": len(inputs),
|
||||
"len_outputs": len(outputs),
|
||||
"inputs_lower": [name.lower() for name in input_names],
|
||||
"outputs_lower": [name.lower() for name in output_names],
|
||||
"input_types": input_types,
|
||||
"output_types": output_types,
|
||||
"input_types_doc": input_types_doc,
|
||||
"output_types_doc": output_types_doc,
|
||||
"sample_inputs": sample_inputs
|
||||
}
|
||||
return render_template("api_docs.html", **docs)
|
||||
|
||||
|
||||
def log_feature_analytics(feature):
|
||||
if app.interface.analytics_enabled:
|
||||
try:
|
||||
|
@ -68,6 +68,12 @@ class Textbox(OutputComponent):
|
||||
}
|
||||
|
||||
def postprocess(self, y):
|
||||
"""
|
||||
Parameters:
|
||||
y (str): text output
|
||||
Returns:
|
||||
(Union[str, number]): output value
|
||||
"""
|
||||
if self.type == "str" or self.type == "auto":
|
||||
return str(y)
|
||||
elif self.type == "number":
|
||||
@ -98,6 +104,12 @@ class Label(OutputComponent):
|
||||
super().__init__(label)
|
||||
|
||||
def postprocess(self, y):
|
||||
"""
|
||||
Parameters:
|
||||
y (Dict[str, float]): dictionary mapping label to confidence value
|
||||
Returns:
|
||||
(Dict[label: str, confidences: List[Dict[label: str, confidence: number]]]): Object with key 'label' representing primary label, and key 'confidences' representing a list of label-confidence pairs
|
||||
"""
|
||||
if self.type == "label" or (self.type == "auto" and (isinstance(y, str) or isinstance(y, Number))):
|
||||
return {"label": str(y)}
|
||||
elif self.type == "confidences" or (self.type == "auto" and isinstance(y, dict)):
|
||||
@ -190,6 +202,12 @@ class Image(OutputComponent):
|
||||
}
|
||||
|
||||
def postprocess(self, y):
|
||||
"""
|
||||
Parameters:
|
||||
y (Union[numpy.array, PIL.Image, str, matplotlib.pyplot, Tuple[Union[numpy.array, PIL.Image, str], List[Tuple[str, float, float, float, float]]]]): image in specified format
|
||||
Returns:
|
||||
(str): base64 url data
|
||||
"""
|
||||
if self.type == "auto":
|
||||
if isinstance(y, np.ndarray):
|
||||
dtype = "numpy"
|
||||
@ -252,6 +270,12 @@ class Video(OutputComponent):
|
||||
}
|
||||
|
||||
def postprocess(self, y):
|
||||
"""
|
||||
Parameters:
|
||||
y (str): path to video
|
||||
Returns:
|
||||
(str): base64 url data
|
||||
"""
|
||||
returned_format = y.split(".")[-1].lower()
|
||||
if self.type is not None and returned_format != self.type:
|
||||
output_file_name = y[0: y.rindex(
|
||||
@ -292,6 +316,12 @@ class KeyValues(OutputComponent):
|
||||
super().__init__(label)
|
||||
|
||||
def postprocess(self, y):
|
||||
"""
|
||||
Parameters:
|
||||
y (Union[Dict, List[Tuple[str, Union[str, int, float]]]]): dictionary or tuple list representing key value pairs
|
||||
Returns:
|
||||
(List[Tuple[str, Union[str, number]]]): list of key value pairs
|
||||
"""
|
||||
if isinstance(y, dict):
|
||||
return list(y.items())
|
||||
elif isinstance(y, list):
|
||||
@ -343,6 +373,13 @@ class HighlightedText(OutputComponent):
|
||||
}
|
||||
|
||||
def postprocess(self, y):
|
||||
"""
|
||||
Parameters:
|
||||
y (Union[Dict, List[Tuple[str, Union[str, int, float]]]]): dictionary or tuple list representing key value pairs
|
||||
Returns:
|
||||
(List[Tuple[str, Union[str, number]]]): list of key value pairs
|
||||
|
||||
"""
|
||||
return y
|
||||
|
||||
def save_flagged(self, dir, label, data, encryption_key):
|
||||
@ -380,6 +417,12 @@ class Audio(OutputComponent):
|
||||
}
|
||||
|
||||
def postprocess(self, y):
|
||||
"""
|
||||
Parameters:
|
||||
y (Union[Tuple[int, numpy.array], str]): audio data in requested format
|
||||
Returns:
|
||||
(str): base64 url data
|
||||
"""
|
||||
if self.type in ["numpy", "file", "auto"]:
|
||||
if self.type == "numpy" or (self.type == "auto" and isinstance(y, tuple)):
|
||||
sample_rate, data = y
|
||||
@ -416,6 +459,12 @@ class JSON(OutputComponent):
|
||||
super().__init__(label)
|
||||
|
||||
def postprocess(self, y):
|
||||
"""
|
||||
Parameters:
|
||||
y (Union[Dict, List, str]): JSON output
|
||||
Returns:
|
||||
(Union[Dict, List]): JSON output
|
||||
"""
|
||||
if isinstance(y, str):
|
||||
return json.dumps(y)
|
||||
else:
|
||||
@ -448,6 +497,15 @@ class HTML(OutputComponent):
|
||||
'''
|
||||
super().__init__(label)
|
||||
|
||||
def postprocess(self, x):
|
||||
"""
|
||||
Parameters:
|
||||
y (str): HTML output
|
||||
Returns:
|
||||
(str): HTML output
|
||||
"""
|
||||
return x
|
||||
|
||||
@classmethod
|
||||
def get_shortcut_implementations(cls):
|
||||
return {
|
||||
@ -476,6 +534,12 @@ class File(OutputComponent):
|
||||
}
|
||||
|
||||
def postprocess(self, y):
|
||||
"""
|
||||
Parameters:
|
||||
y (str): file path
|
||||
Returns:
|
||||
(Dict[name: str, size: number, data: str]): JSON object with key 'name' for filename, 'data' for base64 url, and 'size' for filesize in bytes
|
||||
"""
|
||||
return {
|
||||
"name": os.path.basename(y),
|
||||
"size": os.path.getsize(y),
|
||||
@ -532,6 +596,12 @@ class Dataframe(OutputComponent):
|
||||
}
|
||||
|
||||
def postprocess(self, y):
|
||||
"""
|
||||
Parameters:
|
||||
y (Union[pandas.DataFrame, numpy.array, List[Union[str, float]], List[List[Union[str, float]]]]): dataframe in given format
|
||||
Returns:
|
||||
(Dict[headers: List[str], data: List[List[Union[str, number]]]]): JSON object with key 'headers' for list of header names, 'data' for 2D array of string or numeric data
|
||||
"""
|
||||
if self.type == "auto":
|
||||
if isinstance(y, pd.core.frame.DataFrame):
|
||||
dtype = "pandas"
|
||||
@ -589,6 +659,12 @@ class Carousel(OutputComponent):
|
||||
}
|
||||
|
||||
def postprocess(self, y):
|
||||
"""
|
||||
Parameters:
|
||||
y (List[List[Any]]): carousel output
|
||||
Returns:
|
||||
(List[List[Any]]): 2D array, where each sublist represents one set of outputs or 'slide' in the carousel
|
||||
"""
|
||||
if isinstance(y, list):
|
||||
if len(y) != 0 and not isinstance(y[0], list):
|
||||
y = [[z] for z in y]
|
||||
@ -667,6 +743,12 @@ class Timeseries(OutputComponent):
|
||||
}
|
||||
|
||||
def postprocess(self, y):
|
||||
"""
|
||||
Parameters:
|
||||
y (pandas.DataFrame): timeseries data
|
||||
Returns:
|
||||
(Dict[headers: List[str], data: List[List[Union[str, number]]]]): JSON object with key 'headers' for list of header names, 'data' for 2D array of string or numeric data
|
||||
"""
|
||||
return {
|
||||
"headers": y.columns.values.tolist(),
|
||||
"data": y.values.tolist()
|
||||
|
486
gradio/templates/api_docs.html
Normal file
@ -1,6 +1,6 @@
|
||||
{
|
||||
"files": {
|
||||
"main.css": "/static/css/main.ac4c682c.css",
|
||||
"main.css": "/static/css/main.6169d032.css",
|
||||
"main.js": "/static/bundle.js",
|
||||
"index.html": "/index.html",
|
||||
"static/media/arrow-left.e497f657.svg": "/static/media/arrow-left.e497f657.svg",
|
||||
@ -10,7 +10,7 @@
|
||||
},
|
||||
"entrypoints": [
|
||||
"static/bundle.css",
|
||||
"static/css/main.ac4c682c.css",
|
||||
"static/css/main.6169d032.css",
|
||||
"static/bundle.js"
|
||||
]
|
||||
}
|
@ -8,4 +8,4 @@
|
||||
window.config = {{ config|tojson }};
|
||||
} catch (e) {
|
||||
window.config = {};
|
||||
}</script><script src="https://cdnjs.cloudflare.com/ajax/libs/iframe-resizer/4.3.1/iframeResizer.contentWindow.min.js"></script><title>Gradio</title><link href="static/bundle.css" rel="stylesheet"><link href="static/css/main.ac4c682c.css" rel="stylesheet"></head><body style="height:100%"><div id="root" style="height:100%"></div><script src="static/bundle.js"></script></body></html>
|
||||
}</script><script src="https://cdnjs.cloudflare.com/ajax/libs/iframe-resizer/4.3.1/iframeResizer.contentWindow.min.js"></script><title>Gradio</title><link href="static/bundle.css" rel="stylesheet"><link href="static/css/main.6169d032.css" rel="stylesheet"></head><body style="height:100%"><div id="root" style="height:100%"></div><script src="static/bundle.js"></script></body></html>
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 23 KiB |
Before Width: | Height: | Size: 294 KiB After Width: | Height: | Size: 295 KiB |
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 23 KiB |
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 41 KiB |
@ -89,14 +89,14 @@ class TestImage(unittest.TestCase):
|
||||
|
||||
class TestAudio(unittest.TestCase):
|
||||
def test_as_component(self):
|
||||
x_wav = {"name": "sample.wav", "data": gr.test_data.BASE64_AUDIO, "is_example": False}
|
||||
x_wav = gr.test_data.BASE64_AUDIO
|
||||
audio_input = gr.inputs.Audio()
|
||||
output = audio_input.preprocess(x_wav)
|
||||
self.assertEqual(output[0], 8000)
|
||||
self.assertEqual(output[1].shape, (8046,))
|
||||
|
||||
def test_in_interface(self):
|
||||
x_wav = {"name": "sample.wav", "data": gr.test_data.BASE64_AUDIO, "is_example": False}
|
||||
x_wav = gr.test_data.BASE64_AUDIO
|
||||
def max_amplitude_from_wav_file(wav_file):
|
||||
audio_segment = AudioSegment.from_file(wav_file.name)
|
||||
data = np.array(audio_segment.get_array_of_samples())
|
||||
@ -110,11 +110,7 @@ class TestAudio(unittest.TestCase):
|
||||
|
||||
class TestFile(unittest.TestCase):
|
||||
def test_in_interface(self):
|
||||
x_file = {
|
||||
"name": "audio.wav",
|
||||
"data": gr.test_data.BASE64_AUDIO,
|
||||
"is_example": False
|
||||
}
|
||||
x_file = gr.test_data.BASE64_AUDIO
|
||||
def get_size_of_file(file_obj):
|
||||
return os.path.getsize(file_obj.name)
|
||||
|
||||
|
@ -108,7 +108,7 @@ class TestHighlightedText(unittest.TestCase):
|
||||
|
||||
class TestAudio(unittest.TestCase):
|
||||
def test_as_component(self):
|
||||
y_audio = gr.processing_utils.decode_base64_to_file(gr.test_data.BASE64_AUDIO)
|
||||
y_audio = gr.processing_utils.decode_base64_to_file(gr.test_data.BASE64_AUDIO["data"])
|
||||
audio_output = gr.outputs.Audio(type="file")
|
||||
self.assertTrue(audio_output.postprocess(y_audio.name).startswith("data:audio/wav;base64,UklGRuI/AABXQVZFZm10IBAAA"))
|
||||
|
||||
|
@ -18,7 +18,7 @@ fi
|
||||
read -p "frontend updates? " -r
|
||||
if [[ $REPLY =~ ^[Yy]$ ]]
|
||||
then
|
||||
aws s3 cp gradio/frontend s3://gradio/$new_version/ --recursive
|
||||
aws s3 cp gradio/templates/frontend s3://gradio/$new_version/ --recursive
|
||||
echo $new_version > gradio/version.txt
|
||||
fi
|
||||
|
||||
|