Merge branch 'master' into tutorials
12
.github/workflows/ui.yml
vendored
@ -12,7 +12,7 @@ on:
|
||||
|
||||
defaults:
|
||||
run:
|
||||
working-directory: "./ui"
|
||||
working-directory: ./ui
|
||||
|
||||
jobs:
|
||||
check:
|
||||
@ -25,10 +25,12 @@ jobs:
|
||||
with:
|
||||
node-version: 16
|
||||
|
||||
- name: "install dependencies"
|
||||
- name: install dependencies
|
||||
run: pnpm i --frozen-lockfile
|
||||
- name: "formatting check"
|
||||
- name: formatting check
|
||||
run: pnpm format:check
|
||||
- name: "typecheck"
|
||||
- name: typecheck
|
||||
run: pnpm ts:check
|
||||
continue-on-error: true
|
||||
continue-on-error: true
|
||||
- name: build
|
||||
run: pnpm build
|
6
.gitignore
vendored
@ -13,6 +13,7 @@ build/
|
||||
# JS build
|
||||
gradio/templates/frontend/static
|
||||
gradio/templates/frontend/build
|
||||
gradio/templates/frontend/assets
|
||||
gradio/templates/frontend/global.css
|
||||
|
||||
# Secrets
|
||||
@ -40,4 +41,7 @@ demo/files/*.mp4
|
||||
.DS_Store
|
||||
*.bak
|
||||
workspace.code-workspace
|
||||
*.h5
|
||||
*.h5
|
||||
|
||||
# log files
|
||||
.pnpm-debug.log
|
@ -1,5 +1,5 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" style="min-height: 100%; margin: 0; padding: 0;">
|
||||
<html lang="en" style="height: 100%; margin: 0; padding: 0;">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
@ -14,11 +14,11 @@
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:image" content="{{ config['thumbnail'] or '' }}" />
|
||||
<meta property="og:title" content="{{ config['title'] or '' }}" />
|
||||
<meta property="og:description" content="{{ config['description'] or '' }}" />
|
||||
<meta property="og:description" content="{{ config['simple_description'] or '' }}" />
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<meta name="twitter:creator" content="@teamGradio">
|
||||
<meta name="twitter:title" content="{{ config['title'] or '' }}">
|
||||
<meta name="twitter:description" content="{{ config['description'] or '' }}">
|
||||
<meta name="twitter:description" content="{{ config['simple_description'] or '' }}">
|
||||
<meta name="twitter:image" content="{{ config['thumbnail'] or '' }}">
|
||||
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-156449732-1"></script>
|
||||
<script>
|
||||
|
2
frontend/public/static/img/undo-solid.svg
Normal file
@ -0,0 +1,2 @@
|
||||
<svg aria-hidden="true" width="5.9403949mm"
|
||||
height="5.9403949mm" focusable="false" data-prefix="fas" data-icon="undo" class="svg-inline--fa fa-undo fa-w-16" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M212.333 224.333H12c-6.627 0-12-5.373-12-12V12C0 5.373 5.373 0 12 0h48c6.627 0 12 5.373 12 12v78.112C117.773 39.279 184.26 7.47 258.175 8.007c136.906.994 246.448 111.623 246.157 248.532C504.041 393.258 393.12 504 256.333 504c-64.089 0-122.496-24.313-166.51-64.215-5.099-4.622-5.334-12.554-.467-17.42l33.967-33.967c4.474-4.474 11.662-4.717 16.401-.525C170.76 415.336 211.58 432 256.333 432c97.268 0 176-78.716 176-176 0-97.267-78.716-176-176-176-58.496 0-110.28 28.476-142.274 72.333h98.274c6.627 0 12 5.373 12 12v48c0 6.627-5.373 12-12 12z"></path></svg>
|
After Width: | Height: | Size: 811 B |
@ -1,17 +1,23 @@
|
||||
<script>
|
||||
import Interface from "./Interface.svelte";
|
||||
|
||||
export let title;
|
||||
export let description;
|
||||
export let theme;
|
||||
export let dark;
|
||||
export let input_components;
|
||||
export let output_components;
|
||||
export let examples;
|
||||
export let fn;
|
||||
export let root;
|
||||
export let allow_flagging;
|
||||
export let allow_interpretation;
|
||||
export let title,
|
||||
description,
|
||||
article,
|
||||
theme,
|
||||
dark,
|
||||
input_components,
|
||||
output_components,
|
||||
examples,
|
||||
fn,
|
||||
root,
|
||||
space,
|
||||
allow_flagging,
|
||||
allow_interpretation,
|
||||
live,
|
||||
queue;
|
||||
|
||||
$: embedded = space !== undefined;
|
||||
</script>
|
||||
|
||||
<div
|
||||
@ -20,16 +26,18 @@
|
||||
: 'h-auto'}"
|
||||
{theme}
|
||||
class:dark
|
||||
class:min-h-full={!embedded}
|
||||
>
|
||||
<div
|
||||
class="gradio-page container mx-auto flex flex-col box-border flex-grow text-gray-700 dark:text-gray-50"
|
||||
class:embedded
|
||||
>
|
||||
<div class="content pt-4 px-4 mb-4">
|
||||
{#if title}
|
||||
<h1 class="title text-center p-4 text-4xl">{title}</h1>
|
||||
{/if}
|
||||
{#if description}
|
||||
<p class="description pb-4">{description}</p>
|
||||
<p class="description pb-4">{@html description}</p>
|
||||
{/if}
|
||||
<Interface
|
||||
{input_components}
|
||||
@ -40,8 +48,54 @@
|
||||
{root}
|
||||
{allow_flagging}
|
||||
{allow_interpretation}
|
||||
{live}
|
||||
{queue}
|
||||
/>
|
||||
{#if article}
|
||||
<p class="article prose pt-8 pb-4 max-w-none">
|
||||
{@html article}
|
||||
</p>
|
||||
{/if}
|
||||
</div>
|
||||
{#if embedded}
|
||||
<div class="footer bg-gray-100 p-4 rounded-b">
|
||||
<a
|
||||
href={"https://huggingface.co/spaces/" + space}
|
||||
class="font-semibold"
|
||||
>
|
||||
{space.includes("/")
|
||||
? space[space.indexOf("/") + 1].toUpperCase() +
|
||||
space.substring(space.indexOf("/") + 2)
|
||||
: space}
|
||||
</a>
|
||||
built with
|
||||
<a href="https://gradio.app" class="font-semibold">Gradio</a>, hosted on
|
||||
<a href="https://huggingface.co/spaces" class="font-semibold"
|
||||
>Hugging Face Spaces</a
|
||||
>.
|
||||
</div>
|
||||
{:else}
|
||||
<div
|
||||
class="footer flex-shrink-0 inline-flex gap-2.5 items-center text-gray-400 justify-center py-2"
|
||||
>
|
||||
<a href="api" target="_blank" rel="noreferrer">
|
||||
view the api
|
||||
<img
|
||||
class="h-5 inline-block"
|
||||
src="./static/img/api-logo.svg"
|
||||
alt="api"
|
||||
/>
|
||||
</a>
|
||||
•
|
||||
<a href="https://gradio.app" target="_blank" rel="noreferrer">
|
||||
built with <img
|
||||
class="h-6 inline-block"
|
||||
src="./static/img/logo.svg"
|
||||
alt="logo"
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -49,4 +103,14 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
.gradio-page.embedded {
|
||||
@apply rounded border-2 border-gray-100 shadow-lg;
|
||||
}
|
||||
.gradio-page:not(.embedded) {
|
||||
@apply h-full;
|
||||
.content {
|
||||
@apply flex-grow flex-shrink-0 pt-4 px-4;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -6,21 +6,26 @@
|
||||
import { deepCopy } from "./components/utils/helpers.js";
|
||||
import ExampleSet from "./ExampleSet.svelte";
|
||||
|
||||
export let input_components;
|
||||
export let output_components;
|
||||
export let theme;
|
||||
export let fn;
|
||||
export let examples;
|
||||
export let root;
|
||||
export let allow_flagging;
|
||||
export let allow_interpretation;
|
||||
export let avg_durations;
|
||||
export let input_components,
|
||||
output_components,
|
||||
theme,
|
||||
fn,
|
||||
examples,
|
||||
root,
|
||||
allow_flagging,
|
||||
allow_interpretation,
|
||||
avg_durations,
|
||||
live,
|
||||
queue;
|
||||
|
||||
let examples_dir = root + "file/";
|
||||
let interpret_mode = false;
|
||||
let submission_count = 0;
|
||||
let state = "START";
|
||||
let last_duration = null;
|
||||
let has_changed = false;
|
||||
let queue_index = null;
|
||||
let initial_queue_index = null;
|
||||
|
||||
const default_inputs = input_components.map((component) =>
|
||||
"default" in component ? component.default : null
|
||||
@ -36,9 +41,14 @@
|
||||
let avg_duration = Array.isArray(avg_durations)
|
||||
? this.props.avg_durations[0]
|
||||
: null;
|
||||
let expected_duration = null;
|
||||
|
||||
const setValues = (index, value) => {
|
||||
has_changed = true;
|
||||
input_values[index] = value;
|
||||
if (live && state !== "PENDING") {
|
||||
submit();
|
||||
}
|
||||
};
|
||||
const setExampleId = async (example_id) => {
|
||||
input_components.forEach(async (input_component, i) => {
|
||||
@ -68,11 +78,21 @@
|
||||
if (state === "PENDING") {
|
||||
return;
|
||||
}
|
||||
for (let [i, input_component] of input_components.entries()) {
|
||||
if (
|
||||
input_values[i] === null &&
|
||||
input_component.name !== "state" &&
|
||||
input_component.optional !== true
|
||||
) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
state = "PENDING";
|
||||
submission_count += 1;
|
||||
has_changed = false;
|
||||
let submission_count_at_click = submission_count;
|
||||
startTimer();
|
||||
fn("predict", { data: input_values })
|
||||
fn("predict", { data: input_values }, queue, queueCallback)
|
||||
.then((output) => {
|
||||
if (
|
||||
state !== "PENDING" ||
|
||||
@ -82,13 +102,30 @@
|
||||
}
|
||||
stopTimer();
|
||||
output_values = output["data"];
|
||||
for (let [i, value] of output_values.entries()) {
|
||||
if (output_components[i].name === "state") {
|
||||
for (let [j, input_component] of input_components.entries()) {
|
||||
if (input_component.name === "state") {
|
||||
input_values[j] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if ("durations" in output) {
|
||||
last_duration = output["durations"][0];
|
||||
}
|
||||
if ("avg_duration" in output) {
|
||||
if ("avg_durations" in output) {
|
||||
avg_duration = output["avg_durations"][0];
|
||||
if (queue && initial_queue_index) {
|
||||
expected_duration = avg_duration * (initial_queue_index + 1);
|
||||
} else {
|
||||
expected_duration = avg_duration;
|
||||
}
|
||||
}
|
||||
state = "COMPLETE";
|
||||
if (live && has_changed) {
|
||||
submit();
|
||||
}
|
||||
})
|
||||
.catch((e) => {
|
||||
if (
|
||||
@ -122,37 +159,52 @@
|
||||
if (interpret_mode) {
|
||||
interpret_mode = false;
|
||||
} else {
|
||||
fn("interpret", {
|
||||
data: input_values,
|
||||
}).then((output) => {
|
||||
fn(
|
||||
"interpret",
|
||||
{
|
||||
data: input_values,
|
||||
},
|
||||
queue,
|
||||
queueCallback
|
||||
).then((output) => {
|
||||
interpret_mode = true;
|
||||
interpretation_values = output.interpretation_scores;
|
||||
});
|
||||
}
|
||||
};
|
||||
const queueCallback = (index, is_initial) => {
|
||||
if (is_initial) {
|
||||
initial_queue_index = index;
|
||||
}
|
||||
queue_index = index;
|
||||
};
|
||||
</script>
|
||||
|
||||
<div class="gradio-interface" {theme}>
|
||||
<div class="panels flex flex-wrap justify-center gap-4">
|
||||
<div class="panels flex flex-wrap justify-center gap-4 flex-col sm:flex-row">
|
||||
<div class="panel flex-1">
|
||||
<div
|
||||
class="component-set p-2 rounded flex flex-col flex-1 gap-2"
|
||||
style="min-height: 36px"
|
||||
>
|
||||
{#each input_components as input_component, i}
|
||||
<div class="component" key={i}>
|
||||
<div class="panel-header mb-1.5">{input_component.label}</div>
|
||||
<svelte:component
|
||||
this={input_component_map[input_component.name][
|
||||
interpret_mode ? "interpretation" : "component"
|
||||
]}
|
||||
{...input_component}
|
||||
{theme}
|
||||
value={input_values[i]}
|
||||
interpretation={interpret_mode ? interpretation_values[i] : null}
|
||||
setValue={setValues.bind(this, i)}
|
||||
/>
|
||||
</div>
|
||||
{#if input_component.name !== "state"}
|
||||
<div class="component" key={i}>
|
||||
<div class="panel-header mb-1.5">{input_component.label}</div>
|
||||
<svelte:component
|
||||
this={input_component_map[input_component.name][
|
||||
interpret_mode ? "interpretation" : "component"
|
||||
]}
|
||||
{...input_component}
|
||||
{theme}
|
||||
value={input_values[i]}
|
||||
interpretation={interpret_mode
|
||||
? interpretation_values[i]
|
||||
: null}
|
||||
setValue={setValues.bind(this, i)}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
{/each}
|
||||
</div>
|
||||
<div class="panel-buttons flex gap-4 my-4">
|
||||
@ -177,13 +229,26 @@
|
||||
class:opacity-50={state === "PENDING"}
|
||||
>
|
||||
{#if state !== "START"}
|
||||
<div class="state absolute right-2 flex items-center gap-2 text-xs">
|
||||
<div class="state absolute right-2 flex items-center gap-0.5 text-xs">
|
||||
{#if state === "PENDING"}
|
||||
<div class="timer font-mono">{timer_diff.toFixed(1)}s</div>
|
||||
<div class="timer font-mono text-right" style="max">
|
||||
{timer_diff.toFixed(1)}s
|
||||
{#if expected_duration !== null}
|
||||
<span>
|
||||
(ETA: {expected_duration.toFixed(
|
||||
1
|
||||
)}s<!--
|
||||
-->{#if queue_index}
|
||||
, {queue_index} ahead<!--
|
||||
-->{/if})<!--
|
||||
--></span
|
||||
>
|
||||
{/if}
|
||||
</div>
|
||||
<img
|
||||
src="./static/img/logo.svg"
|
||||
alt="Pending"
|
||||
class="pending h-5 ml-2 inline-block"
|
||||
class="pending h-5 ml-1 inline-block"
|
||||
/>
|
||||
{:else if state === "ERROR"}
|
||||
<img
|
||||
|
@ -31,7 +31,11 @@
|
||||
on:undo={() => sketch.undo()}
|
||||
on:clear={() => sketch.clear()}
|
||||
/>
|
||||
<Sketch bind:this={sketch} on:change={({ detail }) => setValue(detail)} />
|
||||
<Sketch
|
||||
{value}
|
||||
bind:this={sketch}
|
||||
on:change={({ detail }) => setValue(detail)}
|
||||
/>
|
||||
{:else if value === null}
|
||||
{#if source === "upload"}
|
||||
<Upload
|
||||
|
@ -6,7 +6,7 @@
|
||||
type="number"
|
||||
class="input-number w-full rounded box-border p-2 focus:outline-none appearance-none"
|
||||
{value}
|
||||
on:change={(e) => setValue(parseFloat(e.target.value))}
|
||||
on:input={(e) => setValue(parseFloat(e.target.value))}
|
||||
{theme}
|
||||
/>
|
||||
|
||||
|
@ -8,7 +8,7 @@
|
||||
class="input-text w-full rounded box-border p-2 focus:outline-none appearance-none"
|
||||
{value}
|
||||
{placeholder}
|
||||
on:change={(e) => setValue(e.target.value)}
|
||||
on:input={(e) => setValue(e.target.value)}
|
||||
{theme}
|
||||
/>
|
||||
{:else}
|
||||
|
@ -6,15 +6,15 @@
|
||||
|
||||
<div class="z-50 top-0 right-0 flex justify-end m-1 flex gap-1 absolute">
|
||||
<button
|
||||
class="bg-opacity-30 hover:bg-opacity-100 transition p-1 bg-yellow-500 dark:bg-red-600 rounded shadow"
|
||||
class="bg-opacity-30 hover:bg-opacity-100 transition p-1.5 bg-yellow-500 dark:bg-red-600 rounded shadow w-8 h-8"
|
||||
on:click={() => dispatch("undo")}
|
||||
>
|
||||
undo
|
||||
<img alt="undo sketch" src="/static/img/undo-solid.svg" />
|
||||
</button>
|
||||
<button
|
||||
class="clear bg-opacity-30 hover:bg-opacity-100 transition p-1 bg-gray-50 dark:bg-gray-500 rounded shadow"
|
||||
class="clear bg-opacity-30 hover:bg-opacity-100 transition p-1 bg-gray-50 dark:bg-gray-500 rounded shadow w-8 h-8"
|
||||
on:click={() => dispatch("clear")}
|
||||
>
|
||||
clear
|
||||
<img alt="clear sketch" src="static/img/clear.svg" />
|
||||
</button>
|
||||
</div>
|
||||
|
@ -5,7 +5,11 @@
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
let brush_radius = 30;
|
||||
export let value;
|
||||
|
||||
let mounted;
|
||||
|
||||
let brush_radius = 50;
|
||||
let brush_color = "#444";
|
||||
let catenary_color = "#aaa";
|
||||
let background_color = "#FFF";
|
||||
@ -13,6 +17,8 @@
|
||||
let canvas_width = 400;
|
||||
let canvas_height = 400;
|
||||
|
||||
$: mounted && !value && clear();
|
||||
|
||||
function mid_point(p1, p2) {
|
||||
return {
|
||||
x: p1.x + (p2.x - p1.x) / 2,
|
||||
@ -67,7 +73,7 @@
|
||||
);
|
||||
canvas_observer.observe(canvas_container);
|
||||
loop();
|
||||
|
||||
mounted = true;
|
||||
window.setTimeout(() => {
|
||||
const initX = window.innerWidth / 2;
|
||||
const initY = window.innerHeight / 2;
|
||||
@ -84,6 +90,7 @@
|
||||
});
|
||||
|
||||
onDestroy(() => {
|
||||
mounted = false;
|
||||
canvas_observer.unobserve(canvas_container);
|
||||
});
|
||||
|
||||
@ -270,6 +277,8 @@
|
||||
values_changed = true;
|
||||
ctx.drawing.clearRect(0, 0, canvas.drawing.width, canvas.drawing.height);
|
||||
ctx.temp.clearRect(0, 0, canvas.temp.width, canvas.temp.height);
|
||||
ctx.drawing.fillStyle = "#FFFFFF";
|
||||
ctx.drawing.fillRect(0, 0, canvas.drawing.width, canvas.drawing.height);
|
||||
}
|
||||
|
||||
let loop = ({ once = false } = {}) => {
|
||||
|
@ -34,6 +34,15 @@ window.launchGradio = (config, element_query) => {
|
||||
}
|
||||
}
|
||||
|
||||
window.launchGradioFromSpaces = async (space, target) => {
|
||||
const space_url = `https://huggingface.co/gradioiframe/${space}/+/`;
|
||||
let config = await fetch(space_url + "config");
|
||||
config = await config.json();
|
||||
config.root = space_url;
|
||||
config.space = space;
|
||||
launchGradio(config, target);
|
||||
}
|
||||
|
||||
async function get_config() {
|
||||
if ('BUILD_MODE' === "dev") {
|
||||
let config = await fetch("BACKEND_URL" + "config");
|
||||
|
@ -2,7 +2,7 @@ aiohttp
|
||||
analytics-python
|
||||
fastapi
|
||||
ffmpy
|
||||
markdown2
|
||||
markdown-it-py[linkify,plugins]
|
||||
matplotlib
|
||||
numpy
|
||||
pandas
|
||||
|
@ -2,6 +2,7 @@ import base64
|
||||
import json
|
||||
import re
|
||||
import tempfile
|
||||
from pydantic import MissingError
|
||||
|
||||
import requests
|
||||
|
||||
@ -24,13 +25,27 @@ def get_huggingface_interface(model_name, api_key, alias):
|
||||
p = response.json().get("pipeline_tag")
|
||||
|
||||
def encode_to_base64(r: requests.Response) -> str:
|
||||
# Handles the different ways HF API returns the prediction
|
||||
base64_repr = base64.b64encode(r.content).decode("utf-8")
|
||||
data_prefix = ";base64,"
|
||||
# Case 1: base64 representation already includes data prefix
|
||||
if data_prefix in base64_repr:
|
||||
return base64_repr
|
||||
else:
|
||||
content_type = r.headers.get("content-type")
|
||||
return "data:{};base64,".format(content_type) + base64_repr
|
||||
# Case 2: the data prefix is a key in the response
|
||||
if content_type == "application/json":
|
||||
try:
|
||||
content_type = r.json()[0]["content-type"]
|
||||
base64_repr = r.json()[0]["blob"]
|
||||
except KeyError:
|
||||
raise ValueError("Cannot determine content type returned"
|
||||
"by external API.")
|
||||
# Case 3: the data prefix is included in the response headers
|
||||
else:
|
||||
pass
|
||||
new_base64 = "data:{};base64,".format(content_type) + base64_repr
|
||||
return new_base64
|
||||
|
||||
pipelines = {
|
||||
"audio-classification": {
|
||||
@ -44,6 +59,15 @@ def get_huggingface_interface(model_name, api_key, alias):
|
||||
i["label"].split(", ")[0]: i["score"] for i in r.json()
|
||||
},
|
||||
},
|
||||
"audio-to-audio": {
|
||||
# example model: https://hf.co/speechbrain/mtl-mimic-voicebank
|
||||
"inputs": inputs.Audio(label="Input", source="upload", type="filepath"),
|
||||
"outputs": outputs.Audio(label="Output"),
|
||||
"preprocess": lambda i: base64.b64decode(
|
||||
i["data"].split(",")[1]
|
||||
), # convert the base64 representation to binary
|
||||
"postprocess": encode_to_base64,
|
||||
},
|
||||
"automatic-speech-recognition": {
|
||||
# example model: https://hf.co/jonatasgrosman/wav2vec2-large-xlsr-53-english
|
||||
"inputs": inputs.Audio(label="Input", source="upload", type="filepath"),
|
||||
@ -183,7 +207,7 @@ def get_huggingface_interface(model_name, api_key, alias):
|
||||
}
|
||||
|
||||
if p is None or not (p in pipelines):
|
||||
raise ValueError("Unsupported pipeline type: {}".format(type(p)))
|
||||
raise ValueError("Unsupported pipeline type: {}".format(p))
|
||||
|
||||
pipeline = pipelines[p]
|
||||
|
||||
@ -273,7 +297,10 @@ def get_spaces_interface(model_name, api_key, alias):
|
||||
result = re.search(
|
||||
"window.gradio_config = (.*?);</script>", r.text
|
||||
) # some basic regex to extract the config
|
||||
config = json.loads(result.group(1))
|
||||
try:
|
||||
config = json.loads(result.group(1))
|
||||
except AttributeError:
|
||||
raise ValueError("Could not load the Space: {}".format(model_name))
|
||||
interface_info = interface_params_from_config(config)
|
||||
|
||||
# The function should call the API with preprocessed data
|
||||
|
@ -9,6 +9,7 @@ import copy
|
||||
import getpass
|
||||
import os
|
||||
import random
|
||||
import re
|
||||
import sys
|
||||
import time
|
||||
import warnings
|
||||
@ -17,7 +18,8 @@ import webbrowser
|
||||
from logging import warning
|
||||
from typing import TYPE_CHECKING, Any, Callable, List, Optional, Tuple
|
||||
|
||||
import markdown2
|
||||
from markdown_it import MarkdownIt
|
||||
from mdit_py_plugins.footnote import footnote_plugin
|
||||
|
||||
from gradio import (encryptor, interpretation, networking, # type: ignore
|
||||
queueing, strings, utils)
|
||||
@ -241,14 +243,29 @@ class Interface:
|
||||
|
||||
self.session = None
|
||||
self.title = title
|
||||
|
||||
CLEANER = re.compile('<.*?>')
|
||||
def clean_html(raw_html):
|
||||
cleantext = re.sub(CLEANER, '', raw_html)
|
||||
return cleantext
|
||||
md = MarkdownIt("js-default", {
|
||||
"linkify": True,
|
||||
"typographer": True,
|
||||
"html": True,
|
||||
}).use(footnote_plugin)
|
||||
|
||||
simple_description = None
|
||||
if description is not None:
|
||||
description = md.render(description)
|
||||
simple_description = clean_html(description)
|
||||
self.simple_description = simple_description
|
||||
self.description = description
|
||||
if article is not None:
|
||||
article = utils.readme_to_html(article)
|
||||
article = markdown2.markdown(
|
||||
article, extras=["fenced-code-blocks"])
|
||||
article = md.render(article)
|
||||
self.article = article
|
||||
|
||||
self.thumbnail = thumbnail
|
||||
|
||||
theme = theme if theme is not None else os.getenv("GRADIO_THEME", "default")
|
||||
DEPRECATED_THEME_MAP = {
|
||||
"darkdefault": "default",
|
||||
|
@ -1,5 +1,5 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" style="min-height: 100%; margin: 0; padding: 0;">
|
||||
<html lang="en" style="height: 100%; margin: 0; padding: 0;">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
@ -14,11 +14,11 @@
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:image" content="{{ config['thumbnail'] or '' }}" />
|
||||
<meta property="og:title" content="{{ config['title'] or '' }}" />
|
||||
<meta property="og:description" content="{{ config['description'] or '' }}" />
|
||||
<meta property="og:description" content="{{ config['simple_description'] or '' }}" />
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<meta name="twitter:creator" content="@teamGradio">
|
||||
<meta name="twitter:title" content="{{ config['title'] or '' }}">
|
||||
<meta name="twitter:description" content="{{ config['description'] or '' }}">
|
||||
<meta name="twitter:description" content="{{ config['simple_description'] or '' }}">
|
||||
<meta name="twitter:image" content="{{ config['thumbnail'] or '' }}">
|
||||
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-156449732-1"></script>
|
||||
<script>
|
||||
|
@ -196,6 +196,7 @@ def get_config_file(interface: Interface) -> Dict[str, Any]:
|
||||
"show_output": interface.show_output,
|
||||
"title": interface.title,
|
||||
"description": interface.description,
|
||||
"simple_description": interface.simple_description,
|
||||
"article": interface.article,
|
||||
"theme": interface.theme,
|
||||
"css": interface.css,
|
||||
|
2
setup.py
@ -19,7 +19,7 @@ setup(
|
||||
"aiohttp",
|
||||
"fastapi",
|
||||
"ffmpy",
|
||||
"markdown2",
|
||||
"markdown-it-py[linkify,plugins]",
|
||||
"matplotlib",
|
||||
"numpy",
|
||||
"pandas",
|
||||
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 32 KiB |
Before Width: | Height: | Size: 300 KiB After Width: | Height: | Size: 304 KiB |
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 32 KiB |
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 49 KiB |
@ -16,6 +16,18 @@ os.environ["GRADIO_ANALYTICS_ENABLED"] = "False"
|
||||
|
||||
|
||||
class TestHuggingFaceModelAPI(unittest.TestCase):
|
||||
def test_audio_to_audio(self):
|
||||
model_type = "audio-to-audio"
|
||||
interface_info = gr.external.get_huggingface_interface(
|
||||
"speechbrain/mtl-mimic-voicebank",
|
||||
api_key=None,
|
||||
alias=model_type,
|
||||
)
|
||||
self.assertEqual(interface_info["fn"].__name__, model_type)
|
||||
self.assertIsInstance(interface_info["inputs"], gr.inputs.Audio)
|
||||
self.assertIsInstance(interface_info["outputs"], gr.outputs.Audio)
|
||||
|
||||
|
||||
def test_question_answering(self):
|
||||
model_type = "question-answering"
|
||||
interface_info = gr.external.get_huggingface_interface(
|
||||
|
3
ui/.gitignore
vendored
@ -1,5 +1,4 @@
|
||||
node_modules
|
||||
public/build/
|
||||
|
||||
.DS_Store
|
||||
.pnpm-debug.log
|
||||
|
||||
|
55
ui/README.md
@ -2,7 +2,16 @@
|
||||
|
||||
This folder contains all of the Gradio UI and component source code.
|
||||
|
||||
## set up
|
||||
- [set up](#setup)
|
||||
- [running the application](#running-the-application)
|
||||
- [local development](#local-development)
|
||||
- [building for production](#building-for-production)
|
||||
- [quality checks](#quality-checks)
|
||||
- [ci checks](#ci-checks)
|
||||
|
||||
> Note: The below assumes you are in the `ui` directory unless alternative instructions are given.
|
||||
|
||||
## setup
|
||||
|
||||
This folder is managed as 'monorepo' a multi-package repository which make dependency management very simple. In order to do this we use `pnpm` as our package manager.
|
||||
|
||||
@ -15,7 +24,6 @@ You will also need `node` which you probably already have
|
||||
Install all dependencies from the `ui` folder:
|
||||
|
||||
```bash
|
||||
cd ui
|
||||
pnpm i
|
||||
```
|
||||
|
||||
@ -25,7 +33,7 @@ This will install the dependencies for all packages within the `ui` folder and l
|
||||
|
||||
To develop locally, open two browser tabs from the root of the repository.
|
||||
|
||||
Run the python test server:
|
||||
Run the python test server, from the root directory:
|
||||
|
||||
```bash
|
||||
cd demo/kitchen_sink
|
||||
@ -37,7 +45,7 @@ This will start a development server on port `7863` that the web app is expectin
|
||||
Run the web app:
|
||||
|
||||
```bash
|
||||
cd ui
|
||||
cd ui #move back into ui if you haven't already
|
||||
pnpm dev
|
||||
```
|
||||
|
||||
@ -46,8 +54,45 @@ pnpm dev
|
||||
From the `ui` folder run the build.
|
||||
|
||||
```bash
|
||||
cd ui
|
||||
pnpm build
|
||||
```
|
||||
|
||||
This will create the necessary files in `ui/app/public` and also in `gradio/templates/frontend`.
|
||||
|
||||
## quality checks
|
||||
|
||||
The repos currently has two quality checks that can be run locally and are run in CI.
|
||||
|
||||
### formatting
|
||||
|
||||
Formatting is handled by [`prettier`](https://prettier.io/) to ensure consistent formatting and prevent style-focused conversations. Formatting failures will fails CI and should be reoslve before merging.
|
||||
|
||||
To check formatting:
|
||||
|
||||
```bash
|
||||
pnpm format:check
|
||||
```
|
||||
|
||||
If you have formatting failures then you can run the following command to fix them:
|
||||
|
||||
```bash
|
||||
pnpm format:write
|
||||
```
|
||||
|
||||
### type checking
|
||||
|
||||
We use [TypeScript](https://www.typescriptlang.org/) to provide static types to javascript code. These checks are also run in CI.
|
||||
|
||||
to typecheck the code:
|
||||
|
||||
```bash
|
||||
pnpm ts:check
|
||||
```
|
||||
|
||||
## ci checks
|
||||
|
||||
Currently the following checks are run in CI:
|
||||
|
||||
- Format check (`pnpm format:check`)
|
||||
- Type check (`pnpm ts:check`)
|
||||
- Build as a smoke test (`pnpm build`)
|
||||
|
@ -15,14 +15,14 @@
|
||||
<meta property="og:title" content="{{ config['title'] or '' }}" />
|
||||
<meta
|
||||
property="og:description"
|
||||
content="{{ config['description'] or '' }}"
|
||||
content="{{ config['simple_description'] or '' }}"
|
||||
/>
|
||||
<meta name="twitter:card" content="summary_large_image" />
|
||||
<meta name="twitter:creator" content="@teamGradio" />
|
||||
<meta name="twitter:title" content="{{ config['title'] or '' }}" />
|
||||
<meta
|
||||
name="twitter:description"
|
||||
content="{{ config['description'] or '' }}"
|
||||
content="{{ config['simple_description'] or '' }}"
|
||||
/>
|
||||
<meta name="twitter:image" content="{{ config['thumbnail'] or '' }}" />
|
||||
<script
|
||||
|
1
ui/packages/app/public/static/img/undo-solid.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="undo" class="svg-inline--fa fa-undo fa-w-16" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M212.333 224.333H12c-6.627 0-12-5.373-12-12V12C0 5.373 5.373 0 12 0h48c6.627 0 12 5.373 12 12v78.112C117.773 39.279 184.26 7.47 258.175 8.007c136.906.994 246.448 111.623 246.157 248.532C504.041 393.258 393.12 504 256.333 504c-64.089 0-122.496-24.313-166.51-64.215-5.099-4.622-5.334-12.554-.467-17.42l33.967-33.967c4.474-4.474 11.662-4.717 16.401-.525C170.76 415.336 211.58 432 256.333 432c97.268 0 176-78.716 176-176 0-97.267-78.716-176-176-176-58.496 0-110.28 28.476-142.274 72.333h98.274c6.627 0 12 5.373 12 12v48c0 6.627-5.373 12-12 12z"></path></svg>
|
After Width: | Height: | Size: 767 B |
@ -1,6 +1,6 @@
|
||||
<script>
|
||||
import Interface from "./Interface.svelte";
|
||||
// import "./global.css";
|
||||
import "./global.css";
|
||||
|
||||
export let title;
|
||||
export let description;
|
||||
|
@ -31,7 +31,11 @@
|
||||
on:undo={() => sketch.undo()}
|
||||
on:clear={() => sketch.clear()}
|
||||
/>
|
||||
<Sketch bind:this={sketch} on:change={({ detail }) => setValue(detail)} />
|
||||
<Sketch
|
||||
{value}
|
||||
bind:this={sketch}
|
||||
on:change={({ detail }) => setValue(detail)}
|
||||
/>
|
||||
{:else if value === null}
|
||||
{#if source === "upload"}
|
||||
<Upload
|
||||
|
@ -6,15 +6,15 @@
|
||||
|
||||
<div class="z-50 top-0 right-0 flex justify-end m-1 flex gap-1 absolute">
|
||||
<button
|
||||
class="bg-opacity-30 hover:bg-opacity-100 transition p-1 bg-yellow-500 dark:bg-red-600 rounded shadow"
|
||||
class="bg-opacity-30 hover:bg-opacity-100 transition p-1.5 bg-yellow-500 dark:bg-red-600 rounded shadow w-8 h-8"
|
||||
on:click={() => dispatch("undo")}
|
||||
>
|
||||
undo
|
||||
<img alt="undo sketch" src="/static/img/undo-solid.svg" />
|
||||
</button>
|
||||
<button
|
||||
class="clear bg-opacity-30 hover:bg-opacity-100 transition p-1 bg-gray-50 dark:bg-gray-500 rounded shadow"
|
||||
class="clear bg-opacity-30 hover:bg-opacity-100 transition p-1 bg-gray-50 dark:bg-gray-500 rounded shadow w-8 h-8"
|
||||
on:click={() => dispatch("clear")}
|
||||
>
|
||||
clear
|
||||
<img alt="clear sketch" src="static/img/clear.svg" />
|
||||
</button>
|
||||
</div>
|
||||
|
@ -5,7 +5,11 @@
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
let brush_radius = 30;
|
||||
export let value;
|
||||
|
||||
let mounted;
|
||||
|
||||
let brush_radius = 50;
|
||||
let brush_color = "#444";
|
||||
let catenary_color = "#aaa";
|
||||
let background_color = "#FFF";
|
||||
@ -13,6 +17,8 @@
|
||||
let canvas_width = 400;
|
||||
let canvas_height = 400;
|
||||
|
||||
$: mounted && !value && clear();
|
||||
|
||||
function mid_point(p1, p2) {
|
||||
return {
|
||||
x: p1.x + (p2.x - p1.x) / 2,
|
||||
@ -67,7 +73,7 @@
|
||||
);
|
||||
canvas_observer.observe(canvas_container);
|
||||
loop();
|
||||
|
||||
mounted = true;
|
||||
window.setTimeout(() => {
|
||||
const initX = window.innerWidth / 2;
|
||||
const initY = window.innerHeight / 2;
|
||||
@ -84,6 +90,7 @@
|
||||
});
|
||||
|
||||
onDestroy(() => {
|
||||
mounted = false;
|
||||
canvas_observer.unobserve(canvas_container);
|
||||
});
|
||||
|
||||
@ -270,6 +277,8 @@
|
||||
values_changed = true;
|
||||
ctx.drawing.clearRect(0, 0, canvas.drawing.width, canvas.drawing.height);
|
||||
ctx.temp.clearRect(0, 0, canvas.temp.width, canvas.temp.height);
|
||||
ctx.drawing.fillStyle = "#FFFFFF";
|
||||
ctx.drawing.fillRect(0, 0, canvas.drawing.width, canvas.drawing.height);
|
||||
}
|
||||
|
||||
let loop = ({ once = false } = {}) => {
|
||||
|
@ -11,6 +11,9 @@ export default defineConfig(({ mode }) => {
|
||||
const production = mode === "production";
|
||||
|
||||
return {
|
||||
build: {
|
||||
outDir: "../../../gradio/templates/frontend"
|
||||
},
|
||||
define: {
|
||||
BUILD_MODE: production ? JSON.stringify("prod") : JSON.stringify("dev"),
|
||||
BACKEND_URL: production
|
||||
|