diff --git a/.config/playwright-setup.js b/.config/playwright-setup.js
index 8ce06d653840..c0832966806a 100644
--- a/.config/playwright-setup.js
+++ b/.config/playwright-setup.js
@@ -132,8 +132,8 @@ ${demos.map((d) => `from demo.${d}.run import demo as ${d}`).join("\n")}
app = FastAPI()
${demos
- .map((d) => `app = gr.mount_gradio_app(app, ${d}, path="/${d}")`)
- .join("\n")}
+ .map((d) => `app = gr.mount_gradio_app(app, ${d}, path="/${d}")`)
+ .join("\n")}
config = uvicorn.Config(app, port=${port}, log_level="info")
server = uvicorn.Server(config=config)
diff --git a/demo/chatbot_multimodal/run.ipynb b/demo/chatbot_multimodal/run.ipynb
index 8130dab1c330..d4ed8aa95814 100644
--- a/demo/chatbot_multimodal/run.ipynb
+++ b/demo/chatbot_multimodal/run.ipynb
@@ -1 +1 @@
-{"cells": [{"cell_type": "markdown", "id": 302934307671667531413257853548643485645, "metadata": {}, "source": ["# Gradio Demo: chatbot_multimodal"]}, {"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", "\n", "def add_text(history, text):\n", " history = history + [(text, None)]\n", " return history, gr.update(value=\"\", interactive=False)\n", "\n", "\n", "def add_file(history, file):\n", " history = history + [((file.name,), None)]\n", " return history\n", "\n", "\n", "def bot(history):\n", " response = \"**That's cool!**\"\n", " history[-1][1] = response\n", " return history\n", "\n", "\n", "with gr.Blocks() as demo:\n", " chatbot = gr.Chatbot([], elem_id=\"chatbot\").style(height=750)\n", "\n", " with gr.Row():\n", " with gr.Column(scale=0.85):\n", " txt = gr.Textbox(\n", " show_label=False,\n", " placeholder=\"Enter text and press enter, or upload an image\",\n", " ).style(container=False)\n", " with gr.Column(scale=0.15, min_width=0):\n", " btn = gr.UploadButton(\"\ud83d\udcc1\", file_types=[\"image\", \"video\", \"audio\"])\n", "\n", " txt_msg = txt.submit(add_text, [chatbot, txt], [chatbot, txt], queue=False).then(\n", " bot, chatbot, chatbot\n", " )\n", " txt_msg.then(lambda: gr.update(interactive=True), None, [txt], queue=False)\n", " file_msg = btn.upload(add_file, [chatbot, btn], [chatbot], queue=False).then(\n", " bot, chatbot, chatbot\n", " )\n", "\n", "if __name__ == \"__main__\":\n", " demo.launch()\n"]}], "metadata": {}, "nbformat": 4, "nbformat_minor": 5}
\ No newline at end of file
+{"cells": [{"cell_type": "markdown", "id": 302934307671667531413257853548643485645, "metadata": {}, "source": ["# Gradio Demo: chatbot_multimodal"]}, {"cell_type": "code", "execution_count": null, "id": 272996653310673477252411125948039410165, "metadata": {}, "outputs": [], "source": ["!pip install -q gradio "]}, {"cell_type": "code", "execution_count": null, "id": 288918539441861185822528903084949547379, "metadata": {}, "outputs": [], "source": ["import gradio as gr\n", "import random\n", "import time\n", "\n", "# Chatbot demo with multimodal input (text, markdown, LaTeX, code blocks, image, audio, & video). Plus shows support for streaming text.\n", "\n", "def add_text(history, text):\n", " history = history + [(text, None)]\n", " return history, gr.update(value=\"\", interactive=False)\n", "\n", "\n", "def add_file(history, file):\n", " history = history + [((file.name,), None)]\n", " return history\n", "\n", "\n", "def bot(history):\n", " response = \"**That's cool!**\"\n", " history[-1][1] = \"\"\n", " for character in response:\n", " history[-1][1] += character\n", " time.sleep(0.05)\n", " yield history\n", "\n", "\n", "with gr.Blocks() as demo:\n", " chatbot = gr.Chatbot([], elem_id=\"chatbot\").style(height=750)\n", "\n", " with gr.Row():\n", " with gr.Column(scale=0.85):\n", " txt = gr.Textbox(\n", " show_label=False,\n", " placeholder=\"Enter text and press enter, or upload an image\",\n", " ).style(container=False)\n", " with gr.Column(scale=0.15, min_width=0):\n", " btn = gr.UploadButton(\"\ud83d\udcc1\", file_types=[\"image\", \"video\", \"audio\"])\n", "\n", " txt_msg = txt.submit(add_text, [chatbot, txt], [chatbot, txt], queue=False).then(\n", " bot, chatbot, chatbot\n", " )\n", " txt_msg.then(lambda: gr.update(interactive=True), None, [txt], queue=False)\n", " file_msg = btn.upload(add_file, [chatbot, btn], [chatbot], queue=False).then(\n", " bot, chatbot, chatbot\n", " )\n", "\n", "demo.queue()\n", "if __name__ == \"__main__\":\n", " demo.launch()\n"]}], "metadata": {}, "nbformat": 4, "nbformat_minor": 5}
\ No newline at end of file
diff --git a/demo/chatbot_multimodal/run.py b/demo/chatbot_multimodal/run.py
index 824c7c0fc761..3c680155570f 100644
--- a/demo/chatbot_multimodal/run.py
+++ b/demo/chatbot_multimodal/run.py
@@ -1,5 +1,8 @@
import gradio as gr
+import random
+import time
+# Chatbot demo with multimodal input (text, markdown, LaTeX, code blocks, image, audio, & video). Plus shows support for streaming text.
def add_text(history, text):
history = history + [(text, None)]
@@ -13,8 +16,11 @@ def add_file(history, file):
def bot(history):
response = "**That's cool!**"
- history[-1][1] = response
- return history
+ history[-1][1] = ""
+ for character in response:
+ history[-1][1] += character
+ time.sleep(0.05)
+ yield history
with gr.Blocks() as demo:
@@ -37,5 +43,6 @@ def bot(history):
bot, chatbot, chatbot
)
+demo.queue()
if __name__ == "__main__":
demo.launch()
diff --git a/demo/chatbot_streaming/run.ipynb b/demo/chatbot_streaming/run.ipynb
deleted file mode 100644
index 292a8177e84d..000000000000
--- a/demo/chatbot_streaming/run.ipynb
+++ /dev/null
@@ -1 +0,0 @@
-{"cells": [{"cell_type": "markdown", "id": 302934307671667531413257853548643485645, "metadata": {}, "source": ["# Gradio Demo: chatbot_streaming"]}, {"cell_type": "code", "execution_count": null, "id": 272996653310673477252411125948039410165, "metadata": {}, "outputs": [], "source": ["!pip install -q gradio "]}, {"cell_type": "code", "execution_count": null, "id": 288918539441861185822528903084949547379, "metadata": {}, "outputs": [], "source": ["import gradio as gr\n", "import random\n", "import time\n", "\n", "with gr.Blocks() as demo:\n", " chatbot = gr.Chatbot()\n", " msg = gr.Textbox()\n", " clear = gr.ClearButton([msg, chatbot])\n", "\n", " def user(user_message, history):\n", " return gr.update(value=\"\", interactive=False), history + [[user_message, None]]\n", "\n", " def bot(history):\n", " bot_message = random.choice([\"How are you?\", \"I love you\", \"I'm very hungry\"])\n", " history[-1][1] = \"\"\n", " for character in bot_message:\n", " history[-1][1] += character\n", " time.sleep(0.05)\n", " yield history\n", "\n", " response = msg.submit(user, [msg, chatbot], [msg, chatbot], queue=False).then(\n", " bot, chatbot, chatbot\n", " )\n", " response.then(lambda: gr.update(interactive=True), None, [msg], queue=False)\n", "\n", "demo.queue()\n", "if __name__ == \"__main__\":\n", " demo.launch()\n"]}], "metadata": {}, "nbformat": 4, "nbformat_minor": 5}
\ No newline at end of file
diff --git a/demo/chatbot_streaming/run.py b/demo/chatbot_streaming/run.py
deleted file mode 100644
index ab6b13502173..000000000000
--- a/demo/chatbot_streaming/run.py
+++ /dev/null
@@ -1,28 +0,0 @@
-import gradio as gr
-import random
-import time
-
-with gr.Blocks() as demo:
- chatbot = gr.Chatbot()
- msg = gr.Textbox()
- clear = gr.ClearButton([msg, chatbot])
-
- def user(user_message, history):
- return gr.update(value="", interactive=False), history + [[user_message, None]]
-
- def bot(history):
- bot_message = random.choice(["How are you?", "I love you", "I'm very hungry"])
- history[-1][1] = ""
- for character in bot_message:
- history[-1][1] += character
- time.sleep(0.05)
- yield history
-
- response = msg.submit(user, [msg, chatbot], [msg, chatbot], queue=False).then(
- bot, chatbot, chatbot
- )
- response.then(lambda: gr.update(interactive=True), None, [msg], queue=False)
-
-demo.queue()
-if __name__ == "__main__":
- demo.launch()
diff --git a/js/app/package.json b/js/app/package.json
index f5107132a85d..b83154985547 100644
--- a/js/app/package.json
+++ b/js/app/package.json
@@ -16,7 +16,7 @@
"preview": "vite preview",
"test:snapshot": "pnpm exec playwright test snapshots/ --config=../../.config/playwright.config.js",
"test:browser": "pnpm exec playwright test test/ --config=../../.config/playwright.config.js",
- "test:browser:debug": "pnpm exec playwright test test/ --debug --config=../../.config/playwright.config.js",
+ "test:browser:dev": "pnpm exec playwright test test/ --ui --config=../../.config/playwright.config.js",
"build:css": "pollen -c pollen.config.cjs -o src/pollen-dev.css"
},
"dependencies": {
@@ -57,4 +57,4 @@
"msw": {
"workerDirectory": "public"
}
-}
+}
\ No newline at end of file
diff --git a/js/app/src/components/Chatbot/Chatbot.test.ts b/js/app/src/components/Chatbot/Chatbot.test.ts
index 337ae292ad77..8bb4b7c80530 100644
--- a/js/app/src/components/Chatbot/Chatbot.test.ts
+++ b/js/app/src/components/Chatbot/Chatbot.test.ts
@@ -1,6 +1,5 @@
import { test, describe, assert, afterEach } from "vitest";
import { cleanup, render, get_text } from "@gradio/tootils";
-import { tick } from "svelte";
import Chatbot from "./Chatbot.svelte";
import type { LoadingStatus } from "../StatusTracker/types";
diff --git a/js/app/test/chatbot_multimodal.spec.ts b/js/app/test/chatbot_multimodal.spec.ts
new file mode 100644
index 000000000000..00bdae4e6495
--- /dev/null
+++ b/js/app/test/chatbot_multimodal.spec.ts
@@ -0,0 +1,108 @@
+import { test, expect } from "@gradio/tootils";
+
+test("text input by a user should be shown in the chatbot as a paragraph", async ({ page }) => {
+ const textbox = await page.getByTestId('textbox');
+ await textbox.fill("Lorem ipsum");
+ await page.keyboard.press('Enter');
+ const user_message = await page.getByTestId('user').first().getByRole('paragraph').textContent();
+ const bot_message = await page.getByTestId('bot').first().getByRole('paragraph').textContent();
+ await expect(user_message).toEqual("Lorem ipsum");
+ await expect(bot_message).toBeTruthy();
+});
+
+test("images uploaded by a user should be shown in the chat", async ({ page }) => {
+ const fileChooserPromise = page.waitForEvent('filechooser');
+ await page.getByRole('button', { name: '📁' }).click();
+ const fileChooser = await fileChooserPromise;
+ await fileChooser.setFiles("./test/files/cheetah1.jpg");
+ await page.keyboard.press('Enter');
+
+ const user_message = await page.getByTestId('user').first().getByRole('img');
+ const bot_message = await page.getByTestId('bot').first().getByRole('paragraph').textContent();
+ const image_data = await user_message.getAttribute("src");
+ await expect(image_data).toContain("cheetah1.jpg");
+ await expect(bot_message).toBeTruthy();
+});
+
+test("audio uploaded by a user should be shown in the chatbot", async ({ page }) => {
+ const fileChooserPromise = page.waitForEvent('filechooser');
+ await page.getByRole('button', { name: '📁' }).click();
+ const fileChooser = await fileChooserPromise;
+ await fileChooser.setFiles("../../test/test_files/audio_sample.wav");
+ await page.keyboard.press('Enter');
+
+ const user_message = await page.getByTestId('user').first().locator('audio');
+ const bot_message = await page.getByTestId('bot').first().getByRole('paragraph').textContent();
+ const audio_data = await user_message.getAttribute("src");
+ await expect(audio_data).toContain("audio_sample.wav");
+ await expect(bot_message).toBeTruthy();
+
+});
+
+test("videos uploaded by a user should be shown in the chatbot", async ({ page }) => {
+ const fileChooserPromise = page.waitForEvent('filechooser');
+ await page.getByRole('button', { name: '📁' }).click();
+ const fileChooser = await fileChooserPromise;
+ await fileChooser.setFiles("../../test/test_files/video_sample.mp4");
+ await page.keyboard.press('Enter');
+
+ const user_message = await page.getByTestId('user').first().locator('video');
+ const bot_message = await page.getByTestId('bot').first().getByRole('paragraph').textContent();
+ const video_data = await user_message.getAttribute("src");
+ await expect(video_data).toContain("video_sample.mp4");
+ await expect(bot_message).toBeTruthy();
+});
+
+
+test("markdown input by a user should be correctly formatted: bold, italics, links", async ({ page }) => {
+ const textbox = await page.getByTestId('textbox');
+ await textbox.fill("This is **bold text**. This is *italic text*. This is a [link](https://gradio.app).");
+ await page.keyboard.press('Enter');
+ const user_message = await page.getByTestId('user').first().getByRole('paragraph').innerHTML();
+ const bot_message = await page.getByTestId('bot').first().getByRole('paragraph').textContent();
+ await expect(user_message).toEqual('This is bold text. This is italic text. This is a link.');
+ await expect(bot_message).toBeTruthy();
+});
+
+test("inline code markdown input by the user should be correctly formatted", async ({ page }) => {
+ const textbox = await page.getByTestId('textbox');
+ await textbox.fill("This is `code`.");
+ await page.keyboard.press('Enter');
+ const user_message = await page.getByTestId('user').first().getByRole('paragraph').innerHTML();
+ const bot_message = await page.getByTestId('bot').first().getByRole('paragraph').textContent();
+ await expect(user_message).toEqual("This is code
.");
+ await expect(bot_message).toBeTruthy();
+});
+
+test("markdown code blocks input by a user should be rendered correctly with the correct language tag", async ({ page }) => {
+ const textbox = await page.getByTestId('textbox');
+ await textbox.fill("```python\nprint('Hello')\nprint('World!')\n```");
+ await page.keyboard.press('Enter');
+ const user_message = await page.getByTestId('user').first().locator('pre').innerHTML();
+ const bot_message = await page.getByTestId('bot').first().getByRole('paragraph').textContent();
+ await expect(user_message).toContain("language-python");
+ await expect(bot_message).toBeTruthy();
+
+});
+
+test("LaTeX input by a user should be rendered correctly", async ({ page }) => {
+ const textbox = await page.getByTestId('textbox');
+ await textbox.fill("This is LaTeX $$x^2$$");
+ await page.keyboard.press('Enter');
+ const user_message = await page.getByTestId('user').first().getByRole('paragraph').innerHTML();
+ const bot_message = await page.getByTestId('bot').first().getByRole('paragraph').textContent();
+ await expect(user_message).toContain("katex-display");
+ await expect(bot_message).toBeTruthy();
+});
+
+
+test("when a new message is sent the chatbot should scroll to the latest message", async ({ page }) => {
+ const textbox = await page.getByTestId('textbox');
+ const line_break = "
"
+ await textbox.fill(line_break.repeat(30));
+ await page.keyboard.press('Enter');
+ const bot_message = await page.getByTestId('bot').first().getByRole('paragraph');
+ await expect(bot_message).toBeVisible();
+ const bot_message_text = bot_message.textContent();
+ await expect(bot_message_text).toBeTruthy();
+});
\ No newline at end of file
diff --git a/package.json b/package.json
index 049cba29b264..f7833e121fe0 100644
--- a/package.json
+++ b/package.json
@@ -22,7 +22,7 @@
"test:browser": "pnpm --filter @gradio/app test:browser",
"test:browser:full": "run-s build test:browser",
"test:browser:verbose": "GRADIO_TEST_VERBOSE=true pnpm test:browser",
- "test:browser:debug": "pnpm --filter @gradio/app test:browser:debug",
+ "test:browser:dev": "pnpm --filter @gradio/app test:browser:dev",
"ci:publish": "pnpm publish --no-git-checks --access public -r",
"ci:version": "changeset version && pnpm i --lockfile-only",
"test:ct": "playwright test -c ./.config/playwright-ct.config.ts"
@@ -92,4 +92,4 @@
".."
]
}
-}
+}
\ No newline at end of file