Merge branch 'main' into 5.0-dev

This commit is contained in:
Abubakar Abid 2024-07-18 23:18:09 -07:00
commit 2c846af1fd
14 changed files with 149 additions and 22 deletions

View File

@ -0,0 +1,6 @@
---
"@gradio/markdown": patch
"gradio": patch
---
fix:Latex Rendering Fix

View File

@ -0,0 +1,7 @@
---
"@gradio/audio": patch
"@gradio/image": patch
"gradio": patch
---
fix:Add `.input()` events to `gr.Audio` and `gr.Image`

View File

@ -1 +1 @@
{"cells": [{"cell_type": "markdown", "id": "302934307671667531413257853548643485645", "metadata": {}, "source": ["# Gradio Demo: audio_component_events"]}, {"cell_type": "code", "execution_count": null, "id": "272996653310673477252411125948039410165", "metadata": {}, "outputs": [], "source": ["!pip install -q gradio "]}, {"cell_type": "code", "execution_count": null, "id": "288918539441861185822528903084949547379", "metadata": {}, "outputs": [], "source": ["import gradio as gr\n", "\n", "with gr.Blocks() as demo:\n", " with gr.Row():\n", " with gr.Column():\n", " input_video = gr.Audio(type=\"filepath\", label=\"Input Audio\", sources=[\"upload\", \"microphone\"])\n", " with gr.Column():\n", " output_video = gr.Audio(label=\"Output Audio\", sources=[\"upload\", \"microphone\"])\n", "\n", " with gr.Row():\n", " with gr.Column():\n", " input_num_change = gr.Number(label=\"# Input Change Events\", value=0)\n", " input_num_load = gr.Number(label=\"# Input Upload Events\", value=0)\n", " input_num_play = gr.Number(label=\"# Input Play Events\", value=0)\n", " input_num_pause = gr.Number(label=\"# Input Pause Events\", value=0)\n", "\n", " with gr.Column():\n", " input_record = gr.Number(label=\"# Input Start Recording Events\", value=0)\n", " input_pause = gr.Number(label=\"# Input Pause Recording Events\", value=0)\n", " input_stop = gr.Number(label=\"# Input Stop Recording Events\", value=0)\n", "\n", "\n", " with gr.Column():\n", " output_num_play = gr.Number(label=\"# Output Play Events\", value=0)\n", " output_num_pause = gr.Number(label=\"# Output Pause Events\", value=0)\n", " output_num_stop = gr.Number(label=\"# Output Stop Events\", value=0)\n", "\n", " input_video.upload(lambda s, n: (s, n + 1), [input_video, input_num_load], [output_video, input_num_load])\n", " input_video.change(lambda n: n + 1, input_num_change, input_num_change)\n", " input_video.play(lambda n: n + 1, input_num_play, input_num_play)\n", " input_video.pause(lambda n: n + 1, input_num_pause, input_num_pause)\n", " input_video.change(lambda n: n + 1, input_num_change, input_num_change)\n", "\n", " input_video.start_recording(lambda n: n + 1, input_record, input_record)\n", " input_video.pause_recording(lambda n: n + 1, input_pause, input_pause)\n", " input_video.stop_recording(lambda n: n + 1, input_stop, input_stop)\n", "\n", " output_video.play(lambda n: n + 1, output_num_play, output_num_play)\n", " output_video.pause(lambda n: n + 1, output_num_pause, output_num_pause)\n", " output_video.stop(lambda n: n + 1, output_num_stop, output_num_stop)\n", "\n", "\n", "\n", "if __name__ == \"__main__\":\n", " demo.launch()"]}], "metadata": {}, "nbformat": 4, "nbformat_minor": 5}
{"cells": [{"cell_type": "markdown", "id": "302934307671667531413257853548643485645", "metadata": {}, "source": ["# Gradio Demo: audio_component_events"]}, {"cell_type": "code", "execution_count": null, "id": "272996653310673477252411125948039410165", "metadata": {}, "outputs": [], "source": ["!pip install -q gradio "]}, {"cell_type": "code", "execution_count": null, "id": "288918539441861185822528903084949547379", "metadata": {}, "outputs": [], "source": ["import gradio as gr\n", "\n", "with gr.Blocks() as demo:\n", " with gr.Row():\n", " with gr.Column():\n", " input_audio = gr.Audio(type=\"filepath\", label=\"Input Audio\", sources=[\"upload\", \"microphone\"])\n", " with gr.Column():\n", " output_audio = gr.Audio(label=\"Output Audio\", sources=[\"upload\", \"microphone\"])\n", "\n", " with gr.Row():\n", " with gr.Column():\n", " input_num_change = gr.Number(label=\"# Input Change Events\", value=0)\n", " input_num_input = gr.Number(label=\"# Input Input Events\", value=0)\n", " input_num_load = gr.Number(label=\"# Input Upload Events\", value=0)\n", " input_num_play = gr.Number(label=\"# Input Play Events\", value=0)\n", " input_num_pause = gr.Number(label=\"# Input Pause Events\", value=0)\n", "\n", " with gr.Column():\n", " input_record = gr.Number(label=\"# Input Start Recording Events\", value=0)\n", " input_pause = gr.Number(label=\"# Input Pause Recording Events\", value=0)\n", " input_stop = gr.Number(label=\"# Input Stop Recording Events\", value=0)\n", "\n", "\n", " with gr.Column():\n", " output_num_play = gr.Number(label=\"# Output Play Events\", value=0)\n", " output_num_pause = gr.Number(label=\"# Output Pause Events\", value=0)\n", " output_num_stop = gr.Number(label=\"# Output Stop Events\", value=0)\n", "\n", " input_audio.upload(lambda s, n: (s, n + 1), [input_audio, input_num_load], [output_audio, input_num_load])\n", " input_audio.change(lambda n: n + 1, input_num_change, input_num_change)\n", " input_audio.play(lambda n: n + 1, input_num_play, input_num_play)\n", " input_audio.pause(lambda n: n + 1, input_num_pause, input_num_pause)\n", " input_audio.change(lambda n: n + 1, input_num_change, input_num_change)\n", " input_audio.input(lambda n: n + 1, input_num_input, input_num_input)\n", "\n", " input_audio.start_recording(lambda n: n + 1, input_record, input_record)\n", " input_audio.pause_recording(lambda n: n + 1, input_pause, input_pause)\n", " input_audio.stop_recording(lambda n: n + 1, input_stop, input_stop)\n", "\n", " output_audio.play(lambda n: n + 1, output_num_play, output_num_play)\n", " output_audio.pause(lambda n: n + 1, output_num_pause, output_num_pause)\n", " output_audio.stop(lambda n: n + 1, output_num_stop, output_num_stop)\n", "\n", "\n", "\n", "if __name__ == \"__main__\":\n", " demo.launch()"]}], "metadata": {}, "nbformat": 4, "nbformat_minor": 5}

View File

@ -3,13 +3,14 @@ import gradio as gr
with gr.Blocks() as demo:
with gr.Row():
with gr.Column():
input_video = gr.Audio(type="filepath", label="Input Audio", sources=["upload", "microphone"])
input_audio = gr.Audio(type="filepath", label="Input Audio", sources=["upload", "microphone"])
with gr.Column():
output_video = gr.Audio(label="Output Audio", sources=["upload", "microphone"])
output_audio = gr.Audio(label="Output Audio", sources=["upload", "microphone"])
with gr.Row():
with gr.Column():
input_num_change = gr.Number(label="# Input Change Events", value=0)
input_num_input = gr.Number(label="# Input Input Events", value=0)
input_num_load = gr.Number(label="# Input Upload Events", value=0)
input_num_play = gr.Number(label="# Input Play Events", value=0)
input_num_pause = gr.Number(label="# Input Pause Events", value=0)
@ -25,19 +26,20 @@ with gr.Blocks() as demo:
output_num_pause = gr.Number(label="# Output Pause Events", value=0)
output_num_stop = gr.Number(label="# Output Stop Events", value=0)
input_video.upload(lambda s, n: (s, n + 1), [input_video, input_num_load], [output_video, input_num_load])
input_video.change(lambda n: n + 1, input_num_change, input_num_change)
input_video.play(lambda n: n + 1, input_num_play, input_num_play)
input_video.pause(lambda n: n + 1, input_num_pause, input_num_pause)
input_video.change(lambda n: n + 1, input_num_change, input_num_change)
input_audio.upload(lambda s, n: (s, n + 1), [input_audio, input_num_load], [output_audio, input_num_load])
input_audio.change(lambda n: n + 1, input_num_change, input_num_change)
input_audio.play(lambda n: n + 1, input_num_play, input_num_play)
input_audio.pause(lambda n: n + 1, input_num_pause, input_num_pause)
input_audio.change(lambda n: n + 1, input_num_change, input_num_change)
input_audio.input(lambda n: n + 1, input_num_input, input_num_input)
input_video.start_recording(lambda n: n + 1, input_record, input_record)
input_video.pause_recording(lambda n: n + 1, input_pause, input_pause)
input_video.stop_recording(lambda n: n + 1, input_stop, input_stop)
input_audio.start_recording(lambda n: n + 1, input_record, input_record)
input_audio.pause_recording(lambda n: n + 1, input_pause, input_pause)
input_audio.stop_recording(lambda n: n + 1, input_stop, input_stop)
output_video.play(lambda n: n + 1, output_num_play, output_num_play)
output_video.pause(lambda n: n + 1, output_num_pause, output_num_pause)
output_video.stop(lambda n: n + 1, output_num_stop, output_num_stop)
output_audio.play(lambda n: n + 1, output_num_play, output_num_play)
output_audio.pause(lambda n: n + 1, output_num_pause, output_num_pause)
output_audio.stop(lambda n: n + 1, output_num_stop, output_num_stop)

View File

@ -1 +1 @@
{"cells": [{"cell_type": "markdown", "id": "302934307671667531413257853548643485645", "metadata": {}, "source": ["# Gradio Demo: image_component_events"]}, {"cell_type": "code", "execution_count": null, "id": "272996653310673477252411125948039410165", "metadata": {}, "outputs": [], "source": ["!pip install -q gradio "]}, {"cell_type": "code", "execution_count": null, "id": "288918539441861185822528903084949547379", "metadata": {}, "outputs": [], "source": ["import gradio as gr\n", "\n", "def test_select_is_defined(n, evt: gr.SelectData):\n", " assert isinstance(evt.index, list)\n", " assert isinstance(evt.index[0], int)\n", " return n + 1\n", "\n", "\n", "with gr.Blocks() as demo:\n", " with gr.Row():\n", " with gr.Column():\n", " input_img = gr.Image(type=\"filepath\", label=\"Input Image\", sources=[\"upload\", \"clipboard\"])\n", " with gr.Column():\n", " output_img = gr.Image(type=\"filepath\", label=\"Output Image\", sources=[\"upload\", \"clipboard\"])\n", " with gr.Column():\n", " num_change = gr.Number(label=\"# Change Events\", value=0)\n", " num_load = gr.Number(label=\"# Upload Events\", value=0)\n", " num_change_o = gr.Number(label=\"# Change Events Output\", value=0)\n", " num_clear = gr.Number(label=\"# Clear Events\", value=0)\n", " num_select = gr.Number(label=\"# Select Events\", value=0)\n", " input_img.upload(lambda s, n: (s, n + 1), [input_img, num_load], [output_img, num_load])\n", " input_img.change(lambda n: n + 1, num_change, num_change)\n", " input_img.clear(lambda n: n + 1, num_clear, num_clear)\n", " output_img.change(lambda n: n + 1, num_change_o, num_change_o)\n", " output_img.select(test_select_is_defined, num_select, num_select)\n", "\n", "if __name__ == \"__main__\":\n", " demo.launch()"]}], "metadata": {}, "nbformat": 4, "nbformat_minor": 5}
{"cells": [{"cell_type": "markdown", "id": "302934307671667531413257853548643485645", "metadata": {}, "source": ["# Gradio Demo: image_component_events"]}, {"cell_type": "code", "execution_count": null, "id": "272996653310673477252411125948039410165", "metadata": {}, "outputs": [], "source": ["!pip install -q gradio "]}, {"cell_type": "code", "execution_count": null, "id": "288918539441861185822528903084949547379", "metadata": {}, "outputs": [], "source": ["import gradio as gr\n", "\n", "def test_select_is_defined(n, evt: gr.SelectData):\n", " assert isinstance(evt.index, list)\n", " assert isinstance(evt.index[0], int)\n", " return n + 1\n", "\n", "\n", "with gr.Blocks() as demo:\n", " with gr.Row():\n", " with gr.Column():\n", " input_img = gr.Image(type=\"filepath\", label=\"Input Image\", sources=[\"upload\", \"clipboard\"])\n", " with gr.Column():\n", " output_img = gr.Image(type=\"filepath\", label=\"Output Image\", sources=[\"upload\", \"clipboard\"])\n", " with gr.Column():\n", " num_change = gr.Number(label=\"# Change Events\", value=0)\n", " num_input = gr.Number(label=\"# Input Events\", value=0)\n", " num_load = gr.Number(label=\"# Upload Events\", value=0)\n", " num_change_o = gr.Number(label=\"# Change Events Output\", value=0)\n", " num_clear = gr.Number(label=\"# Clear Events\", value=0)\n", " num_select = gr.Number(label=\"# Select Events\", value=0)\n", "\n", " input_img.upload(lambda s, n: (s, n + 1), [input_img, num_load], [output_img, num_load])\n", " input_img.input(lambda n: n + 1, num_input, num_input)\n", " input_img.change(lambda n: n + 1, num_change, num_change)\n", " input_img.clear(lambda n: n + 1, num_clear, num_clear)\n", " output_img.change(lambda n: n + 1, num_change_o, num_change_o)\n", " output_img.select(test_select_is_defined, num_select, num_select)\n", "\n", "if __name__ == \"__main__\":\n", " demo.launch()"]}], "metadata": {}, "nbformat": 4, "nbformat_minor": 5}

View File

@ -14,11 +14,14 @@ with gr.Blocks() as demo:
output_img = gr.Image(type="filepath", label="Output Image", sources=["upload", "clipboard"])
with gr.Column():
num_change = gr.Number(label="# Change Events", value=0)
num_input = gr.Number(label="# Input Events", value=0)
num_load = gr.Number(label="# Upload Events", value=0)
num_change_o = gr.Number(label="# Change Events Output", value=0)
num_clear = gr.Number(label="# Clear Events", value=0)
num_select = gr.Number(label="# Select Events", value=0)
input_img.upload(lambda s, n: (s, n + 1), [input_img, num_load], [output_img, num_load])
input_img.input(lambda n: n + 1, num_input, num_input)
input_img.change(lambda n: n + 1, num_change, num_change)
input_img.clear(lambda n: n + 1, num_clear, num_clear)
output_img.change(lambda n: n + 1, num_change_o, num_change_o)

View File

@ -70,6 +70,7 @@ class Audio(
Events.pause_recording,
Events.stop_recording,
Events.upload,
Events.input,
]
data_model = FileData

View File

@ -38,6 +38,7 @@ class Image(StreamingInput, Component):
Events.stream,
Events.select,
Events.upload,
Events.input,
]
data_model = FileData

View File

@ -1,7 +1,7 @@
import { test, expect, drag_and_drop_file } from "@gradio/tootils";
import { chromium } from "playwright";
test("Audio click-to-upload uploads audio successfuly. File downloading works and file has correct name.", async ({
test("Audio events are dispatched correctly. File downloading works and file has correct name.", async ({
page
}) => {
await page
@ -11,10 +11,12 @@ test("Audio click-to-upload uploads audio successfuly. File downloading works an
await uploader.setInputFiles(["../../test/test_files/audio_sample.wav"]);
await expect(page.getByLabel("# Input Change Events")).toHaveValue("1");
await expect(page.getByLabel("# Input Input Events")).toHaveValue("1");
await expect(page.getByLabel("# Input Upload Events")).toHaveValue("1");
await page.getByLabel("Clear").click();
await expect(page.getByLabel("# Input Change Events")).toHaveValue("2");
await expect(page.getByLabel("# Input Input Events")).toHaveValue("2");
await page
.getByRole("button", { name: "Drop Audio Here - or - Click to Upload" })
.click();
@ -22,6 +24,7 @@ test("Audio click-to-upload uploads audio successfuly. File downloading works an
await uploader.setInputFiles(["../../test/test_files/audio_sample.wav"]);
await expect(page.getByLabel("# Input Change Events")).toHaveValue("3");
await expect(page.getByLabel("# Input Input Events")).toHaveValue("3");
await expect(page.getByLabel("# Input Upload Events")).toHaveValue("2");
const downloadPromise = page.waitForEvent("download");

View File

@ -1,7 +1,7 @@
import { test, expect, drag_and_drop_file } from "@gradio/tootils";
import fs from "fs";
test("Image click-to-upload uploads image successfuly. Clear button dispatches event correctly. Downloading the file works and has the correct name.", async ({
test("Image events are dispatched correctly. Downloading the file works and has the correct name.", async ({
page
}) => {
await page.getByRole("button", { name: "Drop Image Here" }).click();
@ -9,6 +9,7 @@ test("Image click-to-upload uploads image successfuly. Clear button dispatches e
const change_counter = await page.getByLabel("# Change Events", {
exact: true
});
const input_counter = await page.getByLabel("# Input Events");
const clear_counter = await page.getByLabel("# Clear Events");
const upload_counter = await page.getByLabel("# Upload Events");
const change_output_counter = await page.getByLabel("# Change Events Output");
@ -16,6 +17,7 @@ test("Image click-to-upload uploads image successfuly. Clear button dispatches e
await uploader.setInputFiles("./test/files/cheetah1.jpg");
await expect(change_counter).toHaveValue("1");
await expect(input_counter).toHaveValue("1");
await expect(upload_counter).toHaveValue("1");
await expect(change_output_counter).toHaveValue("1");
@ -28,10 +30,12 @@ test("Image click-to-upload uploads image successfuly. Clear button dispatches e
await page.getByLabel("Remove Image").click();
await expect(clear_counter).toHaveValue("1");
await expect(change_counter).toHaveValue("2");
await expect(input_counter).toHaveValue("2");
await expect(upload_counter).toHaveValue("1");
await uploader.setInputFiles("./test/files/gradio-logo.svg");
await expect(change_counter).toHaveValue("3");
await expect(input_counter).toHaveValue("3");
await expect(upload_counter).toHaveValue("2");
await expect(change_output_counter).toHaveValue("2");

View File

@ -5,6 +5,7 @@
import type { FileData } from "@gradio/client";
import type { LoadingStatus } from "@gradio/statustracker";
import { afterUpdate } from "svelte";
import StaticAudio from "./static/StaticAudio.svelte";
import InteractiveAudio from "./interactive/InteractiveAudio.svelte";
@ -12,6 +13,7 @@
import { Block, UploadText } from "@gradio/atoms";
import type { WaveformOptions } from "./shared/types";
export let value_is_output = false;
export let elem_id = "";
export let elem_classes: string[] = [];
export let visible = true;
@ -38,6 +40,7 @@
export let pending: boolean;
export let streaming: boolean;
export let gradio: Gradio<{
input: never;
change: typeof value;
stream: typeof value;
error: string;
@ -78,6 +81,9 @@
if (JSON.stringify(value) !== JSON.stringify(old_value)) {
old_value = value;
gradio.dispatch("change");
if (!value_is_output) {
gradio.dispatch("input");
}
}
}
@ -134,6 +140,10 @@
loading_status.message = detail;
gradio.dispatch(level as "error" | "warning", detail);
}
afterUpdate(() => {
value_is_output = false;
});
</script>
{#if !interactive}

View File

@ -12,6 +12,7 @@
import type { Gradio, SelectData } from "@gradio/utils";
import StaticImage from "./shared/ImagePreview.svelte";
import ImageUploader from "./shared/ImageUploader.svelte";
import { afterUpdate } from "svelte";
import { Block, Empty, UploadText } from "@gradio/atoms";
import { Image } from "@gradio/icons";
@ -21,6 +22,7 @@
type sources = "upload" | "webcam" | "clipboard" | null;
export let value_is_output = false;
export let elem_id = "";
export let elem_classes: string[] = [];
export let visible = true;
@ -51,6 +53,7 @@
export let mirror_webcam: boolean;
export let gradio: Gradio<{
input: never;
change: never;
error: string;
edit: never;
@ -67,8 +70,14 @@
if (JSON.stringify(value) !== JSON.stringify(old_value)) {
old_value = value;
gradio.dispatch("change");
if (!value_is_output) {
gradio.dispatch("input");
}
}
}
afterUpdate(() => {
value_is_output = false;
});
let dragging: boolean;
let active_source: sources = null;

View File

@ -24,7 +24,8 @@
const marked = create_marked({
header_links,
line_breaks
line_breaks,
latex_delimiters
});
const is_external_url = (link: string | null): boolean => {
@ -44,14 +45,41 @@
}
});
function escapeRegExp(string: string): string {
return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}
function process_message(value: string): string {
let parsedValue = value;
if (render_markdown) {
value = marked.parse(value) as string;
const latexBlocks: string[] = [];
latex_delimiters.forEach((delimiter, index) => {
const leftDelimiter = escapeRegExp(delimiter.left);
const rightDelimiter = escapeRegExp(delimiter.right);
const regex = new RegExp(
`${leftDelimiter}([\\s\\S]+?)${rightDelimiter}`,
"g"
);
parsedValue = parsedValue.replace(regex, (match, p1) => {
latexBlocks.push(match);
return `%%%LATEX_BLOCK_${latexBlocks.length - 1}%%%`;
});
});
parsedValue = marked.parse(parsedValue) as string;
parsedValue = parsedValue.replace(
/%%%LATEX_BLOCK_(\d+)%%%/g,
(match, p1) => latexBlocks[parseInt(p1, 10)]
);
}
if (sanitize_html) {
value = DOMPurify.sanitize(value);
parsedValue = DOMPurify.sanitize(parsedValue);
}
return value;
return parsedValue;
}
$: if (message && message.trim()) {

View File

@ -66,6 +66,53 @@ function escape(html: string, encode?: boolean): string {
return html;
}
interface LatexTokenizer {
name: string;
level: string;
start: (src: string) => number | undefined;
tokenizer: (src: string, tokens: any) => any;
renderer: (token: any) => string;
}
function createLatexTokenizer(
delimiters: { left: string; right: string; display: boolean }[]
): LatexTokenizer {
const delimiterPatterns = delimiters.map((delimiter) => ({
start: new RegExp(delimiter.left.replace(/[-\/\\^$*+?.()|[\]{}]/g, "\\$&")),
end: new RegExp(delimiter.right.replace(/[-\/\\^$*+?.()|[\]{}]/g, "\\$&"))
}));
return {
name: "latex",
level: "block",
start(src: string) {
for (const pattern of delimiterPatterns) {
const match = src.match(pattern.start);
if (match) {
return match.index;
}
}
return -1;
},
tokenizer(src: string, tokens: any) {
for (const pattern of delimiterPatterns) {
const match = new RegExp(
`${pattern.start.source}([\\s\\S]+?)${pattern.end.source}`
).exec(src);
if (match) {
return {
type: "latex",
raw: match[0],
text: match[1].trim()
};
}
}
},
renderer(token: any) {
return `<div class="latex-block">${token.text}</div>`;
}
};
}
const renderer: Partial<Omit<Renderer, "constructor" | "options">> = {
code(
@ -102,10 +149,12 @@ const slugger = new GithubSlugger();
export function create_marked({
header_links,
line_breaks
line_breaks,
latex_delimiters
}: {
header_links: boolean;
line_breaks: boolean;
latex_delimiters: { left: string; right: string; display: boolean }[];
}): typeof marked {
const marked = new Marked();
@ -148,6 +197,10 @@ export function create_marked({
]
});
}
const latexTokenizer = createLatexTokenizer(latex_delimiters);
marked.use({
extensions: [latexTokenizer]
});
return marked;
}