From 7a04ebe7fd1ba871e3fe38a20a67ac47e6c0c540 Mon Sep 17 00:00:00 2001 From: Abubakar Abid Date: Wed, 19 Apr 2023 11:38:11 -0700 Subject: [PATCH] Fix some documentation-related issues in Guides (#3903) * client doc fixes * simplify demo * added guides * chatbot guide * notebooks * format --- client/python/gradio_client/client.py | 10 ++++-- demo/chatbot_simple/run.ipynb | 2 +- demo/chatbot_simple/run.py | 15 +++----- demo/chatbot_streaming/run.ipynb | 1 + demo/chatbot_streaming/run.py | 28 +++++++++++++++ gradio/blocks.py | 2 +- gradio/components.py | 27 +++++++------- gradio/flagging.py | 6 ++-- gradio/helpers.py | 2 +- gradio/interface.py | 2 +- gradio/layouts.py | 6 ++-- gradio/mix.py | 4 +-- guides/01_getting-started/02_key-features.md | 2 +- ..._getting-started-with-the-python-client.md | 11 +++--- .../07_other-tutorials/creating-a-chatbot.md | 35 +++++++++++++------ 15 files changed, 100 insertions(+), 53 deletions(-) create mode 100644 demo/chatbot_streaming/run.ipynb create mode 100644 demo/chatbot_streaming/run.py diff --git a/client/python/gradio_client/client.py b/client/python/gradio_client/client.py index 986bbb9de7..bb8ba98f63 100644 --- a/client/python/gradio_client/client.py +++ b/client/python/gradio_client/client.py @@ -35,7 +35,7 @@ from gradio_client.utils import Communicator, JobStatus, Status, StatusUpdate set_documentation_group("py-client") -@document("predict", "submit", "view_api") +@document("predict", "submit", "view_api", "duplicate") class Client: """ The main Client class for the Python client. This class is used to connect to a remote Gradio app and call its API endpoints. @@ -51,7 +51,6 @@ class Client: job = client.submit("hello", api_name="/predict") # runs the prediction in a background thread job.result() >> 49 # returns the result of the remote API call (blocking call) - """ def __init__( @@ -160,6 +159,13 @@ class Client: sleep_timeout: The number of minutes after which the duplicate Space will be puased if no requests are made to it (to minimize billing charges). Defaults to 5 minutes. max_workers: The maximum number of thread workers that can be used to make requests to the remote Gradio app simultaneously. verbose: Whether the client should print statements to the console. + Example: + import os + from gradio_client import Client + HF_TOKEN = os.environ.get("HF_TOKEN") + client = Client.duplicate("abidlabs/whisper", hf_token=HF_TOKEN) + client.predict("audio_sample.wav") + >> "This is a test of the whisper speech recognition model." """ try: original_info = huggingface_hub.get_space_runtime(from_id, token=hf_token) diff --git a/demo/chatbot_simple/run.ipynb b/demo/chatbot_simple/run.ipynb index 78ec25d29a..837c646027 100644 --- a/demo/chatbot_simple/run.ipynb +++ b/demo/chatbot_simple/run.ipynb @@ -1 +1 @@ -{"cells": [{"cell_type": "markdown", "id": 302934307671667531413257853548643485645, "metadata": {}, "source": ["# Gradio Demo: chatbot_simple"]}, {"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.Button(\"Clear\")\n", "\n", " def user(user_message, history):\n", " return \"\", history + [[user_message, None]]\n", "\n", " def bot(history):\n", " bot_message = random.choice([\"Yes\", \"No\"])\n", " history[-1][1] = bot_message\n", " time.sleep(1)\n", " return history\n", "\n", " msg.submit(user, [msg, chatbot], [msg, chatbot], queue=False).then(\n", " bot, chatbot, chatbot\n", " )\n", " clear.click(lambda: None, None, chatbot, queue=False)\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_simple"]}, {"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.Button(\"Clear\")\n", "\n", " def respond(message, chat_history):\n", " bot_message = random.choice([\"How are you?\", \"I love you\", \"I'm very hungry\"])\n", " chat_history.append((message, bot_message))\n", " time.sleep(1)\n", " return \"\", chat_history\n", "\n", " msg.submit(respond, [msg, chatbot], [msg, chatbot])\n", " clear.click(lambda: None, None, chatbot, queue=False)\n", "\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_simple/run.py b/demo/chatbot_simple/run.py index 2057b311e0..c71f1e4f28 100644 --- a/demo/chatbot_simple/run.py +++ b/demo/chatbot_simple/run.py @@ -7,18 +7,13 @@ with gr.Blocks() as demo: msg = gr.Textbox() clear = gr.Button("Clear") - def user(user_message, history): - return "", history + [[user_message, None]] - - def bot(history): - bot_message = random.choice(["Yes", "No"]) - history[-1][1] = bot_message + def respond(message, chat_history): + bot_message = random.choice(["How are you?", "I love you", "I'm very hungry"]) + chat_history.append((message, bot_message)) time.sleep(1) - return history + return "", chat_history - msg.submit(user, [msg, chatbot], [msg, chatbot], queue=False).then( - bot, chatbot, chatbot - ) + msg.submit(respond, [msg, chatbot], [msg, chatbot]) clear.click(lambda: None, None, chatbot, queue=False) if __name__ == "__main__": diff --git a/demo/chatbot_streaming/run.ipynb b/demo/chatbot_streaming/run.ipynb new file mode 100644 index 0000000000..72a22daacc --- /dev/null +++ b/demo/chatbot_streaming/run.ipynb @@ -0,0 +1 @@ +{"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.Button(\"Clear\")\n", "\n", " def user(user_message, history):\n", " return \"\", 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", " msg.submit(user, [msg, chatbot], [msg, chatbot], queue=False).then(\n", " bot, chatbot, chatbot\n", " )\n", " clear.click(lambda: None, None, chatbot, 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 new file mode 100644 index 0000000000..3c55971512 --- /dev/null +++ b/demo/chatbot_streaming/run.py @@ -0,0 +1,28 @@ +import gradio as gr +import random +import time + +with gr.Blocks() as demo: + chatbot = gr.Chatbot() + msg = gr.Textbox() + clear = gr.Button("Clear") + + def user(user_message, history): + return "", 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 + + msg.submit(user, [msg, chatbot], [msg, chatbot], queue=False).then( + bot, chatbot, chatbot + ) + clear.click(lambda: None, None, chatbot, queue=False) + +demo.queue() +if __name__ == "__main__": + demo.launch() diff --git a/gradio/blocks.py b/gradio/blocks.py index 1e7ad6a3f9..6bbac85fb5 100644 --- a/gradio/blocks.py +++ b/gradio/blocks.py @@ -603,7 +603,7 @@ class Blocks(BlockContext): demo.launch() Demos: blocks_hello, blocks_flipper, blocks_speech_text_sentiment, generate_english_german, sound_alert - Guides: blocks_and_event_listeners, controlling_layout, state_in_blocks, custom_CSS_and_JS, custom_interpretations_with_blocks, using_blocks_like_functions + Guides: blocks-and-event-listeners, controlling-layout, state-in-blocks, custom-CSS-and-JS, custom-interpretations-with-blocks, using-blocks-like-functions """ def __init__( diff --git a/gradio/components.py b/gradio/components.py index e302e8ec55..6e8c88a05a 100644 --- a/gradio/components.py +++ b/gradio/components.py @@ -385,7 +385,7 @@ class Textbox( Examples-format: a {str} representing the textbox input. Demos: hello_world, diff_texts, sentence_builder - Guides: creating_a_chatbot, real_time_speech_recognition + Guides: creating-a-chatbot, real-time-speech-recognition """ def __init__( @@ -780,7 +780,7 @@ class Slider( Examples-format: A {float} or {int} representing the slider's value. Demos: sentence_builder, slider_release, generate_tone, titanic_survival, interface_random_slider, blocks_random_slider - Guides: create_your_own_friends_with_a_gan + Guides: create-your-own-friends-with-a-gan """ def __init__( @@ -1621,7 +1621,7 @@ class Image( Postprocessing: expects a {numpy.array}, {PIL.Image} or {str} or {pathlib.Path} filepath to an image and displays the image. Examples-format: a {str} filepath to a local file that contains the image. Demos: image_mod, image_mod_default_image - Guides: Gradio_and_ONNX_on_Hugging_Face, image_classification_in_pytorch, image_classification_in_tensorflow, image_classification_with_vision_transformers, building_a_pictionary_app, create_your_own_friends_with_a_gan + Guides: image-classification-in-pytorch, image-classification-in-tensorflow, image-classification-with-vision-transformers, building-a-pictionary_app, create-your-own-friends-with-a-gan """ def __init__( @@ -2300,7 +2300,7 @@ class Audio( Postprocessing: expects a {Tuple(int, numpy.array)} corresponding to (sample rate in Hz, audio data as a float or int numpy array) or as a {str} filepath or URL to an audio file, which gets displayed Examples-format: a {str} filepath to a local file that contains audio. Demos: main_note, generate_tone, reverse_audio - Guides: real_time_speech_recognition + Guides: real-time-speech-recognition """ def __init__( @@ -3224,7 +3224,7 @@ class State(IOComponent, SimpleSerializable): Preprocessing: No preprocessing is performed Postprocessing: No postprocessing is performed Demos: blocks_simple_squares - Guides: creating_a_chatbot, real_time_speech_recognition + Guides: real-time-speech-recognition """ allow_string_shortcut = False @@ -3618,7 +3618,7 @@ class Label(Changeable, Selectable, IOComponent, JSONSerializable): Postprocessing: expects a {Dict[str, float]} of classes and confidences, or {str} with just the class or an {int}/{float} for regression outputs, or a {str} path to a .json file containing a json dictionary in the structure produced by Label.postprocess(). Demos: main_note, titanic_survival - Guides: Gradio_and_ONNX_on_Hugging_Face, image_classification_in_pytorch, image_classification_in_tensorflow, image_classification_with_vision_transformers, building_a_pictionary_app + Guides: image-classification-in-pytorch, image-classification-in-tensorflow, image-classification-with-vision-transformers, building-a-pictionary-app """ CONFIDENCES_KEY = "confidences" @@ -3763,7 +3763,7 @@ class HighlightedText(Changeable, Selectable, IOComponent, JSONSerializable): Postprocessing: expects a {List[Tuple[str, float | str]]]} consisting of spans of text and their associated labels, or a {Dict} with two keys: (1) "text" whose value is the complete text, and "entities", which is a list of dictionaries, each of which have the keys: "entity" (consisting of the entity label), "start" (the character index where the label starts), and "end" (the character index where the label ends). Entities should not overlap. Demos: diff_texts, text_analysis - Guides: named_entity_recognition + Guides: named-entity-recognition """ def __init__( @@ -4218,7 +4218,7 @@ class HTML(Changeable, IOComponent, StringSerializable): Postprocessing: expects a valid HTML {str}. Demos: text_analysis - Guides: key_features + Guides: key-features """ def __init__( @@ -4463,6 +4463,7 @@ class Chatbot(Changeable, Selectable, IOComponent, JSONSerializable): Postprocessing: expects function to return a {List[List[str | None | Tuple]]}, a list of lists. The inner list should have 2 elements: the user message and the response message. Messages should be strings, tuples, or Nones. If the message is a string, it can include Markdown. If it is a tuple, it should consist of (string filepath to image/video/audio, [optional string alt text]). Messages that are `None` are not displayed. Demos: chatbot_simple, chatbot_multimodal + Guides: creating-a-chatbot """ def __init__( @@ -4652,7 +4653,7 @@ class Model3D(Changeable, Editable, Clearable, IOComponent, FileSerializable): Postprocessing: expects function to return a {str} path to a file of type (.obj, glb, or .gltf) Demos: model3D - Guides: how_to_use_3D_model_component + Guides: how-to-use-3D-model-component """ def __init__( @@ -4780,7 +4781,7 @@ class Plot(Changeable, Clearable, IOComponent, JSONSerializable): Postprocessing: expects either a {matplotlib.figure.Figure}, a {plotly.graph_objects._figure.Figure}, or a {dict} corresponding to a bokeh plot (json_item format) Demos: altair_plot, outbreak_forecast, blocks_kinematics, stock_forecast, map_airbnb - Guides: plot_component_for_maps + Guides: plot-component-for-maps """ def __init__( @@ -4905,7 +4906,7 @@ class ScatterPlot(Plot): Postprocessing: expects a pandas dataframe with the data to plot. Demos: native_plots - Guides: creating_a_dashboard_from_bigquery_data + Guides: creating-a-dashboard-from-bigquery-data """ def __init__( @@ -5905,7 +5906,7 @@ class Markdown(IOComponent, Changeable, StringSerializable): Postprocessing: expects a valid {str} that can be rendered as Markdown. Demos: blocks_hello, blocks_kinematics - Guides: key_features + Guides: key-features """ def __init__( @@ -6199,7 +6200,7 @@ class Interpretation(Component, SimpleSerializable): Preprocessing: this component does *not* accept input. Postprocessing: expects a {dict} with keys "original" and "interpretation". - Guides: custom_interpretations_with_blocks + Guides: custom-interpretations-with-blocks """ def __init__( diff --git a/gradio/flagging.py b/gradio/flagging.py index b45ba157bf..41d9d44a71 100644 --- a/gradio/flagging.py +++ b/gradio/flagging.py @@ -171,7 +171,7 @@ class CSVLogger(FlaggingCallback): return {'cat': 0.3, 'dog': 0.7} demo = gr.Interface(fn=image_classifier, inputs="image", outputs="label", flagging_callback=CSVLogger()) - Guides: using_flagging + Guides: using-flagging """ def __init__(self): @@ -246,7 +246,7 @@ class HuggingFaceDatasetSaver(FlaggingCallback): return {'cat': 0.3, 'dog': 0.7} demo = gr.Interface(fn=image_classifier, inputs="image", outputs="label", allow_flagging="manual", flagging_callback=hf_writer) - Guides: using_flagging + Guides: using-flagging """ def __init__( @@ -382,7 +382,7 @@ class HuggingFaceDatasetJSONSaver(FlaggingCallback): return {'cat': 0.3, 'dog': 0.7} demo = gr.Interface(fn=image_classifier, inputs="image", outputs="label", allow_flagging="manual", flagging_callback=hf_writer) - Guides: using_flagging + Guides: using-flagging """ def __init__( diff --git a/gradio/helpers.py b/gradio/helpers.py index 1c03c2bb8f..91fbe9c697 100644 --- a/gradio/helpers.py +++ b/gradio/helpers.py @@ -81,7 +81,7 @@ class Examples: components. Optionally handles example caching for fast inference. Demos: blocks_inputs, fake_gan - Guides: more_on_examples_and_flagging, using_hugging_face_integrations, image_classification_in_pytorch, image_classification_in_tensorflow, image_classification_with_vision_transformers, create_your_own_friends_with_a_gan + Guides: more-on-examples-and-flagging, using-hugging-face-integrations, image-classification-in-pytorch, image-classification-in-tensorflow, image-classification-with-vision-transformers, create-your-own-friends-with-a-gan """ def __init__( diff --git a/gradio/interface.py b/gradio/interface.py index 0ff5883a6c..a2a1c70f06 100644 --- a/gradio/interface.py +++ b/gradio/interface.py @@ -56,7 +56,7 @@ class Interface(Blocks): demo = gr.Interface(fn=image_classifier, inputs="image", outputs="label") demo.launch() Demos: hello_world, hello_world_3, gpt_j - Guides: quickstart, key_features, sharing_your_app, interface_state, reactive_interfaces, advanced_interface_features, setting_up_a_gradio_demo_for_maximum_performance + Guides: quickstart, key-features, sharing-your-app, interface-state, reactive-interfaces, advanced-interface-features, setting-up-a-gradio-demo-for-maximum-performance """ # stores references to all currently existing Interface instances diff --git a/gradio/layouts.py b/gradio/layouts.py index c24af477e7..eb5d10011f 100644 --- a/gradio/layouts.py +++ b/gradio/layouts.py @@ -21,7 +21,7 @@ class Row(BlockContext): gr.Image("lion.jpg") gr.Image("tiger.jpg") demo.launch() - Guides: controlling_layout + Guides: controlling-layout """ def __init__( @@ -89,7 +89,7 @@ class Column(BlockContext): with gradio.Column(scale=4): btn1 = gr.Button("Button 1") btn2 = gr.Button("Button 2") - Guides: controlling_layout + Guides: controlling-layout """ def __init__( @@ -187,7 +187,7 @@ class Tab(BlockContext, Selectable): with gradio.Tab("Tiger"): gr.Image("tiger.jpg") gr.Button("New Tiger") - Guides: controlling_layout + Guides: controlling-layout """ def __init__( diff --git a/gradio/mix.py b/gradio/mix.py index 16b34a70b7..caf2c68b83 100644 --- a/gradio/mix.py +++ b/gradio/mix.py @@ -18,7 +18,7 @@ class Parallel(gradio.Interface): The Interfaces to put in Parallel must share the same input components (but can have different output components). Demos: interface_parallel, interface_parallel_load - Guides: advanced_interface_features + Guides: advanced-interface-features """ def __init__(self, *interfaces: gradio.Interface, **options): @@ -72,7 +72,7 @@ class Series(gradio.Interface): and so the input and output components must agree between the interfaces). Demos: interface_series, interface_series_load - Guides: advanced_interface_features + Guides: advanced-interface-features """ def __init__(self, *interfaces: gradio.Interface, **options): diff --git a/guides/01_getting-started/02_key-features.md b/guides/01_getting-started/02_key-features.md index b1b09b56b0..b8b98c13fe 100644 --- a/guides/01_getting-started/02_key-features.md +++ b/guides/01_getting-started/02_key-features.md @@ -186,7 +186,7 @@ demo2.launch() ## Iterative Outputs -In some cases, you may want to show a sequence of outputs rather than a single output. For example, you might have an image generation model and you want to show the image that is generated at each step, leading up to the final image. +In some cases, you may want to stream a sequence of outputs rather than show a single output at once. For example, you might have an image generation model and you want to show the image that is generated at each step, leading up to the final image. Or you might have a chatbot which streams its response one word at a time instead of returning it all at once. In such cases, you can supply a **generator** function into Gradio instead of a regular function. Creating generators in Python is very simple: instead of a single `return` value, a function should `yield` a series of values instead. Usually the `yield` statement is put in some kind of loop. Here's an example of an generator that simply counts up to a given number: diff --git a/guides/06_client-libraries/01_getting-started-with-the-python-client.md b/guides/06_client-libraries/01_getting-started-with-the-python-client.md index 710076a3fa..3fc6b0bbcb 100644 --- a/guides/06_client-libraries/01_getting-started-with-the-python-client.md +++ b/guides/06_client-libraries/01_getting-started-with-the-python-client.md @@ -56,17 +56,20 @@ client = Client("abidlabs/my-private-space", hf_token="...") ``` -## Duplicating a Space for private use** +## Duplicating a Space for private use While you can use any public Space as an API, you may get rate limited by Hugging Face if you make too many requests. For unlimited usage of a Space, simply duplicate the Space to create a private Space, -and then use it to make as many requests as you'd like! +and then use it to make as many requests as you'd like! -The `gradio_client` includes a class method: `Client.duplicate()` to make this process simple: +The `gradio_client` includes a class method: `Client.duplicate()` to make this process simple (you'll need to pass in your [Hugging Face token](https://huggingface.co/settings/tokens) or be logged in using the Hugging Face CLI): ```python +import os from gradio_client import Client -client = Client.duplicate("abidlabs/whisper") +HF_TOKEN = os.environ.get("HF_TOKEN") + +client = Client.duplicate("abidlabs/whisper", hf_token=HF_TOKEN) client.predict("audio_sample.wav") >> "This is a test of the whisper speech recognition model." diff --git a/guides/07_other-tutorials/creating-a-chatbot.md b/guides/07_other-tutorials/creating-a-chatbot.md index b93e110310..d61935d5ed 100644 --- a/guides/07_other-tutorials/creating-a-chatbot.md +++ b/guides/07_other-tutorials/creating-a-chatbot.md @@ -6,38 +6,52 @@ Tags: NLP, TEXT, CHAT Chatbots are widely used in natural language processing (NLP) research and industry. Because chatbots are designed to be used directly by customers and end users, it is important to validate that chatbots are behaving as expected when confronted with a wide variety of input prompts. -Using `gradio`, you can easily build a demo of your chatbot model and share that with a testing team, or test it yourself using an intuitive chatbot GUI. +Using `gradio`, you can easily build a demo of your chatbot model and share that with your users, or try it yourself using an intuitive chatbot GUI. -This tutorial will show how to make two kinds of chatbot UIs with Gradio: a simple one to display text and a more sophisticated one that can handle media files as well. The simple chatbot interface that we create will look something like this: +This tutorial will show how to make several kinds of chatbot UIs with Gradio: first a simple one to display text, second one to stream text responses, and finally a chatbot that can handle media files as well. The chatbot interface that we create will look something like this: -$demo_chatbot_simple +$demo_chatbot_streaming **Prerequisite**: We'll be using the `gradio.Blocks` class to build our Chatbot demo. You can [read the Guide to Blocks first](https://gradio.app/quickstart/#blocks-more-flexibility-and-control) if you are not already familiar with it. ## A Simple Chatbot Demo -Let's start with recreating the simple demo above. As you may have noticed, our bot simply randomly responds "yes" or "no" to any input. Here's the code to create this with Gradio: +Let's start with recreating the simple demo above. As you may have noticed, our bot simply randomly responds "How are you?", "I love you", or "I'm very hungry" to any input. Here's the code to create this with Gradio: $code_chatbot_simple There are three Gradio components here: + * A `Chatbot`, whose value stores the entire history of the conversation, as a list of response pairs between the user and bot. * A `Textbox` where the user can type their message, and then hit enter/submit to trigger the chatbot response * A `Clear` button to clear the entire Chatbot history -Note that when a user submits their message, we chain two event events with `.then`: +We have a single function, `respond()`, which takes in the entire history of the chatbot, appends a random message, waits 1 second, and then returns the updated chat history. The `respond()` function also clears the textbox when it returns. + +Of course, in practice, you would replace `respond()` with your own more complex function, which might call a pretrained model or an API, to generate a response. + +Finally, when the `Clear` button is clicked, we assign `None` to the value of the `Chatbot` to clear its entire history. Try out this chatbot here: + +$demo_chatbot_simple + + +## Add Streaming to your Chatbot + +There are several ways we can improve the user experience of the chatbot above. First, we can stream responses so the user doesn't have to wait as long for a message to be generated. Second, we can have the user message appear immediately in the chat history, while the chatbot's response is being generated. Here's the code to achieve that: + +$code_chatbot_streaming + + +You'll notice that when a user submits their message, we now *chain* two event events with `.then()`: 1. The first method `user()` updates the chatbot with the user message and clears the input field. Because we want this to happen instantly, we set `queue=False`, which would skip any queue if it had been enabled. The chatbot's history is appended with `(user_message, None)`, the `None` signifying that the bot has not responded. -2. The second method, `bot()` waits for the bot to respond, and then updates the chatbot with the bot's response. Instead of creating a new message, we just replace the previous `None` message with the bot's response. We add a `time.sleep` to simulate the bot's processing time. - -The reason we split these events is so that the user can see their message appear in the chatbot immediately before the bot responds, which can take some time to process. - -Note we pass the entire history of the chatbot to these functions and back to the component. To clear the chatbot, we pass it `None`. +2. The second method, `bot()` updates the chatbot history with the bot's response. Instead of creating a new message, we just replace the previously-created `None` message with the bot's response. Finally, we construct the message character by character and `yield` the intermediate outputs as they are being constructed. Gradio automatically turns any function with the `yield` keyword [into a streaming output interface](/key-features/#iterative-outputs). Of course, in practice, you would replace `bot()` with your own more complex function, which might call a pretrained model or an API, to generate a response. +Finally, we enable queuing by running `demo.queue()`, which is required for streaming intermediate outputs. You can try the improved chatbot by scrolling to the demo at the top of this page. ## Adding Markdown, Images, Audio, or Videos @@ -60,7 +74,6 @@ def add_file(history, file): Putting this together, we can create a *multimodal* chatbot with a textbox for a user to submit text and an file upload button to submit images / audio / video files. The rest of the code looks pretty much the same as before: - $code_chatbot_multimodal $demo_chatbot_multimodal