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
32.42k stars 2.42k forks source link

Custom Components in Gradio #1432

Closed abidlabs closed 1 year ago

abidlabs commented 2 years ago

Converted to a tracking + discussion issue for custom components.

Context

to follow

Parts

I envisage many kinds of custom components:

These are the core usecases I think we need to cover.

More detail to follow

Original

We've heard requests from many different places (Discord, #1410) on folks who want to create plugins / custom components in Gradio. We should think about how we want to support this (do we want plugins that are not part of the main library?) and write up a Guide showing how to contribute these kinds of pulgins / components.

cc @pngwn

omerXfaruq commented 2 years ago

Custom component guides seems straightforward to me, but not sure about the plugin part. Could we provide a design that would allow users to create plugins with any use-case, guess not? Wouldn't it be more meaningful for users to add components when they need a specific use-case instead of creating plugins?

Though users being able to interact with components like in #1410 would be very cool, though have no idea about how to support it. Leaving it to our frontend masters, @dawoodkhan82, @aliabid94, @pngwn .

jrabary commented 2 years ago

I'm trying to understand the structure of Gradio in order to build a quick proof of concept of what we want to achieve in the issue #1752. I'm wondering why there are two definitions of each components. One under packages and one a wrapper defined in app/components ?

jrabary commented 2 years ago

Some ideas that I'm testing on my side in order to "plugin" a custom component developed outside Gradio repository

In the python side, define the component as below

from gradio.events import Changeable, Submittable
from gradio.components import IOComponent
from typing import Any, Optional

class CustomComp(Changeable, Submittable, IOComponent):
    def __init__(
        self,
        value: str = None,
        *,
        label: Optional[str] = None,
        show_label: bool = True,
        interactive: Optional[bool] = None,
        visible: bool = True,
        elem_id: Optional[str] = None,
        **kwargs,
    ):

    ....

Then use it as usual

def update(name):
    return f"Welcome to Gradio, {name}!"

with gr.Blocks() as demo:
    gr.Markdown("Start typing below and then click **Run** to see the output.")
    with gr.Row():
        inp = gr.Textbox(placeholder="What is your name?")
        out = gr.Textbox()

    with gr.Row():
        custom = CustomComp()

    btn = gr.Button("Run")
    btn.click(fn=update, inputs=inp, outputs=out)

demo.launch()

The backend seems to be OK with that.

Of course the frontend is missing the definition of customcomp. At this point, we need to update the component_map of the Gradio App but without touching the Gradio Repo. Two things, that I see, are missing in order to do that:

What is your opinion about this approach @abidlabs @freddyaboulton @FarukOzderim ?

pngwn commented 2 years ago

@jrabary There aren't two definitions, we have just split out the core component from the 'gradio' component that comes with a lot of app specific concerns. This is so we can publish the core components in the future and they have a sensible, generic API. It is just an implementation detail and doesn't have much impact on this feature.

What is your opinion about this approach?

I don't think it needs to be this verbose for users in the simple case. All we need is a way to register the component somehow, they are relatively decoupled from everything else. Can't we just define a class factory so users can do something like:

custom = CreateCustomComponent(
  name="whatever", 
  location="whatever", 
  value="whatever",
  prop1="whatever", 
  prop2="whatever"
)

This could even accept a class to extend from without needing to actually define a whole new class. Maybe they just want custom version of a certain component, for example (this feels like the most common case).

From this we can treat it as a component, pre and post processors would just return self.value, other methods would need some sensible default that is basically just an identity function with bells and whistles (serisalisation/deserialisation, etc). For more advanced uses cases we can allow users to extend any Class they want but we still need some kind of special class/ function so that we can distinguish the 'custom' component. We would need to give them a special key of some description in the config in order to handle the component mapping + loading.

Something like this might do for more complex use cases:

class Custom(CustomComponent):
 ...

Or if a use wants to inherit some of the behaviour of other classes:

class Custom(CustomComponent, Radio):
 ...

I'm not really sure that we want to expose every internal class for users to extend, this will increase our API surface area pretty significantly and make breaking changes far more likely. We should maintain a seperate class or set of classes for this purpose to act as an abstraction layer between the internal + external APIs. Even if they are the same now it will give us more flexibility and freedom in the future.

Maybe the CustomComponent extension isn't strictly necessary but it does make for a very explicit API.

It also isn't clear to me where this component would load from (do users self host or do we host it in the gradio app?), how we design the API (when we ship this what is now an internal API becomes public, we'll need to go over the current API carefully), how we guide users to create components (they are built with svelte which requires a bunch of tooling to compile and must be compiled in a specific way to work with gradio).

I don't have the bandwidth to look into these issues right now but will make some time as soon as I can.

jrabary commented 2 years ago

We may borrows some ideas from dash to handle this custom components issue. They describe here https://github.com/plotly/dash-component-boilerplate and here https://dash.plotly.com/plugins how to write custom components. https://github.com/plotly/dash-deck is an example of components developed by third party in a separate repo. Here is what I learn by reading their repo (not sure, I got it right):

omerXfaruq commented 2 years ago

@jrabary I think we should also take a look at what we need for Custom Components.

  1. Are we just trying to change the Backend functionalities(this would be easier)
  2. Are we trying to create components with unique frontend designs or functionalities(this would require a development from scratch at backend and frontend.

For supporting 1, we could design or use a very generalistic component which can support a lot of use-cases, and make it extendible or usable in Backend, maybe?

How does this sound @jrabary?

jrabary commented 2 years ago

@FarukOzderim For me it's more the second option. Here is an example to explain what I have in mind. Currently there is a Component in Gradio that display image and eventually has a crop functionally. What if I want to build a Gradio App that show up an inpainting algorithm ? I would like to display an image and erase some part of it with a brush or to draw some noise with a pen. I'm not sure this feature is supported currently by the Image component and the known way to add this feature is to update the component inside the Gradio codebase. I would like to build such component in its own repo and maybe provide a full set of components library that we can plug inside a Gradio app.

Some how the idea can be applied to the Gradio core component as well. Like, we want to separate them into a different group and import only the group we need:

from gradio import core_components # will import component like Block, ....
from gradio import image_components # will import component like Image

from custom_components import my_component # will import a custom component
omerXfaruq commented 2 years ago

OK! Then I think you can follow this guide, I think it would solve your need. Could you also drop feedback about the guide in a new issue, how clear was it, and were you able to easily create a new component? Or was there anything missing?

omerXfaruq commented 2 years ago

Since we have a custom component guide, converted the title to just Plugins. If a need arise in the future, we would just move the custom components into a different file.

jrabary commented 2 years ago

OK! Then I think you can follow this guide, I think it would solve your need. Could you also drop feedback about the guide in a new issue, how clear was it, and were you able to easily create a new component? Or was there anything missing ?

Thanks for the guide. The guide is pretty clear and answers some of my questions. The thing is, I would like to build my components inside its own repo. Not in the Gradio codebase. My understanding is that I need to add the new components inside gradio source directly and edit internal gradio files like components.py or directory.ts. If I want it to be available publicly I will need to ask you to merge it into the Gradio main branch.

Being able to do the same thing but outside the Gradio source code would be great. Just plug the new components by importing python library and javascript files corresponding to the components.

I see two things that are needed to achieve this:

abidlabs commented 2 years ago

Hi @jrabary, just to chime in here: On one hand, we actually do have support for inpainting demos, using the Image component with the tool set to sketch. So:

πšπš›.π™Έπš–πšŠπšπšŽ(πšπš˜πš˜πš•="πšœπš”πšŽπšπšŒπš‘")

Here's an example demo: https://huggingface.co/spaces/akhaliq/lama With code: https://huggingface.co/spaces/akhaliq/lama/blob/main/app.py#L35

But to your larger point about supporting custom components outside of the library, we fully agree that this is something we need to support. It's a fair bit of work, but it is definitely something on our roadmap! (You can follow this issue for updates)

space-nuko commented 1 year ago

Just for the sake of updates, I was wondering if there has been any progress on this feature? There have been a lot of recent developments where the lack of custom components makes some workflows more inconvenient:

I could maybe help if there's anything actionable at this stage. But that's okay if things are still too early to implement yet.

And thanks to the gradio team for all your continuing hard work so far, without you all the current revolution in generative tech could not have happened

pngwn commented 1 year ago

@space-nuko That's great to hear. This hasn't been a priority up to this point as we worked through theming (which will be with us shortly). But we are actively discussing this at the minute. As soon as we have mapped out this feature, we'll add as much detail as we can to an issue (either this one or a new one) and start to get some feedback from the community to help shape this feature.

It's a pretty large feature with lots of moving parts but we'll be starting on it soon and are keen to get as much feedback as possible from the community, as well as contributions where that makes sense!

abidlabs commented 1 year ago

Let's close this issue and use https://github.com/gradio-app/gradio/issues/5564 to track any issues related to custom components