gradio-app / gradio

Build and share delightful machine learning apps, all in Python. 🌟 Star to support our work!
http://www.gradio.app
Apache License 2.0
33.88k stars 2.57k forks source link

Adding a section for #GradioGists on the website #3257

Open abidlabs opened 1 year ago

abidlabs commented 1 year ago

We have been releasing occasional code snippets with the hastag #GradioGist on Twitter. This information could be useful to developers browsing the Gradio docs. We could add a section underneath the Guides called #GradioGists, where they would appear to developers who are searching the Guides

In addition to making them searchable (like the Guides), we may also want to structure them in a Q&A format, so that they index better on search engines where developers might ask these questions.

freddyaboulton commented 1 year ago

Came up during https://github.com/gradio-app/gradio/issues/3271

Let's add a gist for how to have examples with updating samples

import gradio as gr
import random

samples = [["What is your name?"], ["Where are you from?"]]

def get_new_examples():
    if random.random() > 0.5:
        return [["What is your favorite food?"], ["How old are you?"]]
    else:
        return [["Where do you live?"]]

def update_examples():
    global samples
    samples = get_new_examples()
    return gr.Dataset.update(samples=samples)

def print_samples():
    global samples
    return {"samples": samples}

def load_example(example_id):
    global samples
    return samples[example_id][0]

with gr.Blocks() as demo:
    with gr.Row():
        with gr.Column():
            question = gr.Textbox(label="question")
            examples = gr.Dataset(samples=samples, components=[question], type="index")
            generate = gr.Button(value="Generate examples")
        with gr.Column():
            global_state = gr.JSON(value={"samples": samples}, label="global variable")

        examples.click(load_example, inputs=[examples], outputs=[question])        
        generate.click(update_examples, inputs=None, outputs=[examples])
        generate.click(print_samples, inputs=None, outputs=[global_state])

demo.launch()
freddyaboulton commented 1 year ago

Also, so we don't forget, here are the other three gists we have created so far

https://twitter.com/search?q=%23gradiogist&src=typed_query

  1. Revealing a component based on a dropdown value
  2. How to hide the clear button in an interface
  3. Displaying live logs from the backend in the gradio ui
  4. Updating examples via button click
gitgithan commented 1 year ago

Edit: Fixed problem below changing lambda x:x to lambda x:x[0] to extract the filepath out of the filepath in a list.

Is there an example for images? I am getting stuck on passing image from dataset click to another image component like how Interface examples parameter allows it.

Image has various types (filepath,numpy,PIL) and i suspect the types in my pipeline are not matching and I don't know how the conversion is happening behind the scenes, which part I have to do myself, which parts I can let gradio do by specifying the type, and which parts I can let gradio infer without even specify type parameter.

My Dataset has samples being a list of list of filepaths. I try dataset.click(lambda x:x,inputs=dataset,outputs=image_component) but get ValueError: Cannot process this value as an Image. I tried setting image_component to both filepaths (trying to make it work to receive dataset clicks) and PIL types (original working type for prediction before i tried to integrate dataset) but both don't work.

When image_component is pil type, my debugger inline debug values shows y =['filepath.jpg'] in section below

    def postprocess(
        self, y: np.ndarray | _Image.Image | str | Path | None
    ) -> str | None:
        """
        Parameters:
            y: image as a numpy array, PIL Image, string/Path filepath, or string URL
        Returns:
            base64 url data
        """
        if y is None:
            return None
        if isinstance(y, np.ndarray):
            return processing_utils.encode_array_to_base64(y)
        elif isinstance(y, _Image.Image):
            return processing_utils.encode_pil_to_base64(y)
        elif isinstance(y, (str, Path)):
            return processing_utils.encode_url_or_file_to_base64(y)
        else:
            raise ValueError("Cannot process this value as an Image")

The convenience of gradio also what makes it hard to understand what types of data to provide in each parameter to make a data flow chain work. For example, Dataset docs component parameter shows List[IOComponent] | List[str] but I can't search what is an IOComponent.

Currently the docs show examples, but don't explain enough the Blocks API building process, though some parts like event listener types exist. The examples still require beginners significant effort to parse through if they don't have a mental model of how things work.

Personally I had difficulty finding answers to

  1. Can I have multiple event listeners on 1 component
  2. Can I pass nested objects to functions and out of functions?
    • eg. 1 idx, 3 list_of_image_components which are 2 parameters with 2nd parameter being list, instead of 4 separate parameters which is what gradio seems to require currently. list_of_image_components was created using list comprehension because manually writing each is impractical. Just to suit the eventlistener api of inputs= and outputs=, my function takes in def mirror_to_preview(*idx_and_list_of_images) and does the hard work parsing instead of the more natural def mirror_to_preview(idx, list_of_images).
  3. What are the keywords possible in the Style method gradio.XXX.style(···)

I'm hoping for 2 types of pages in the docs so users can reason for themselves for all components and event listeners.

  1. Thinking process on building a dataflow and where to place them to keep code organized

    • What type of data you have
    • What component to use as input, output, and their properties
    • What event listener to chain components, and how to specify their input output
  2. Ways to shoot yourself when you use wrong types and error messages that follow (so users can look at errors and infer the cause, then read other docs to fix)

    • Infinite loops when event listener input output forms closed loop
      field1.change(fn=func, inputs=field1, outputs=field2)
      field2.change(fn=func, inputs=field2, outputs=field3)
      field3.change(fn=func, inputs=field3, outputs=field1)
    • Wrong structures in components (eg. Dataset samples being list instead of list of list) causing obscure errors like Dataset has no attribute headers
hfwittmann commented 11 months ago

The problem with this is : you get session conflicts.

An example of this can be seen here: image

Description of the image: I have just pressed "Generate examples" on the right hand side. That works.

However on the left hand side, when pressing "What's your favorite food" you now also get "Where do live?" copied into the text box. That - I think - is clearly undesirable bahaviour for most use cases.

Hopefully there is a fix for this.

hfwittmann commented 11 months ago

I believe the problem is solved if instead of using global samples, one puts the sample into Gradio State:

https://www.gradio.app/guides/state-in-blocks

hfwittmann commented 11 months ago

This works for me:


import random

import gradio as gr

def get_new_examples():
    x = random.random()
    if x > 0.5:
        return [["What is your favorite food?"], ["How old are you?"], [f"{x}"]]
    else:
        return [["Where do you live?"], [f"{x}"]]

def update_examples(customer_examples_var):
    samples = get_new_examples()
    customer_examples_var = gr.State([samples])

    try:
        samples = customer_examples_var.constructor_args["value"][0]
        print("abcd")
    except:
        samples = customer_examples_var[0]
        print("efgh")

    return {"samples": samples}, customer_examples_var, gr.Dataset(samples=samples)

def load_example(example_id, customer_examples_var):
    try:
        samples = customer_examples_var.constructor_args["value"][0]
        print(1234)
    except:
        samples = customer_examples_var[0]
        print(5678)

    return samples[example_id][0], customer_examples_var

with gr.Blocks() as demo:
    samples = [["What is your name?"], ["Where are you from?"]]
    customer_examples_var = gr.State([samples])

    with gr.Row():
        with gr.Column():
            question = gr.Textbox(label="question")
            examples = gr.Dataset(samples=samples, components=[question], type="index")
            generate = gr.Button(value="Generate examples")
        with gr.Column():
            global_state = gr.JSON(
                value={"samples": customer_examples_var}, label="global variable"
            )

        examples.click(
            load_example,
            inputs=[examples, customer_examples_var],
            outputs=[question, customer_examples_var],
        )
        generate.click(
            update_examples,
            inputs=[customer_examples_var],
            outputs=[global_state, customer_examples_var, examples],
        )

demo.launch()
hfwittmann commented 11 months ago

This is a silghtly simplified version:

import random

import gradio as gr

def _extract_samples(customer_examples_var):
    samples = customer_examples_var[0]

    return samples

def get_new_examples():
    x = random.random()
    if x > 0.5:
        return [["What is your favorite food?"], ["How old are you?"], [f"{x}"]]
    else:
        return [["Where do you live?"], [f"{x}"]]

def update_examples(customer_examples_var):
    samples = get_new_examples()
    customer_examples_var[0] = samples

    return {"samples": samples}, customer_examples_var, gr.Dataset(samples=samples)

def load_example(example_id, customer_examples_var):
    samples = _extract_samples(customer_examples_var)
    return samples[example_id][0], customer_examples_var

with gr.Blocks() as demo:
    samples = get_new_examples()
    customer_examples_var = gr.State([samples])

    with gr.Row():
        with gr.Column():
            question = gr.Textbox(label="question")
            examples = gr.Dataset(samples=samples, components=[question], type="index")
            generate = gr.Button(value="Generate examples")
        with gr.Column():
            global_state = gr.JSON(value={"samples": samples}, label="global variable")

        examples.click(
            load_example,
            inputs=[examples, customer_examples_var],
            outputs=[question, customer_examples_var],
        )
        generate.click(
            update_examples,
            inputs=[customer_examples_var],
            outputs=[global_state, customer_examples_var, examples],
        )

demo.launch()