mirror of
https://github.com/gradio-app/gradio.git
synced 2025-01-12 10:34:32 +08:00
345ddd888e
* Add code * add changeset * Undo * add changeset * Undo * Delete old * more undo * More undo * more undo * Fix link * Remove tip * add changeset --------- Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>
227 lines
8.5 KiB
Markdown
227 lines
8.5 KiB
Markdown
# The Backend 🐍
|
|
|
|
This guide will cover everything you need to know to implement your custom component's backend processing.
|
|
|
|
## Which Class to Inherit From
|
|
|
|
All components inherit from one of three classes `Component`, `FormComponent`, or `BlockContext`.
|
|
You need to inherit from one so that your component behaves like all other gradio components.
|
|
When you start from a template with `gradio cc create --template`, you don't need to worry about which one to choose since the template uses the correct one.
|
|
For completeness, and in the event that you need to make your own component from scratch, we explain what each class is for.
|
|
|
|
* `FormComponent`: Use this when you want your component to be grouped together in the same `Form` layout with other `FormComponents`. The `Slider`, `Textbox`, and `Number` components are all `FormComponents`.
|
|
* `BlockContext`: Use this when you want to place other components "inside" your component. This enabled `with MyComponent() as component:` syntax.
|
|
* `Component`: Use this for all other cases.
|
|
|
|
Tip: If your component supports streaming output, inherit from the `StreamingOutput` class.
|
|
|
|
Tip: If you inherit from `BlockContext`, you also need to set the metaclass to be `ComponentMeta`. See example below.
|
|
|
|
```python
|
|
from gradio.blocks import BlockContext
|
|
from gradio.component_meta import ComponentMeta
|
|
|
|
set_documentation_group("layout")
|
|
|
|
|
|
@document()
|
|
class Row(BlockContext, metaclass=ComponentMeta):
|
|
pass
|
|
```
|
|
|
|
## The methods you need to implement
|
|
|
|
When you inherit from any of these classes, the following methods must be implemented.
|
|
Otherwise the Python interpreter will raise an error when you instantiate your component!
|
|
|
|
### `preprocess` and `postprocess`
|
|
|
|
Explained in the [Key Concepts](./key-component-concepts#the-value-and-how-it-is-preprocessed-postprocessed) guide.
|
|
They handle the conversion from the data sent by the frontend to the format expected by the python function.
|
|
|
|
```python
|
|
@abstractmethod
|
|
def preprocess(self, x: Any) -> Any:
|
|
"""
|
|
Convert from the web-friendly (typically JSON) value in the frontend to the format expected by the python function.
|
|
"""
|
|
return x
|
|
|
|
@abstractmethod
|
|
def postprocess(self, y):
|
|
"""
|
|
Convert from the data returned by the python function to the web-friendly (typically JSON) value expected by the frontend.
|
|
"""
|
|
return y
|
|
```
|
|
|
|
### `as_example`
|
|
|
|
Takes in the original Python value and returns the modified value that should be displayed in the examples preview in the app.
|
|
Let's look at the following example from the `Radio` component.
|
|
|
|
```python
|
|
def as_example(self, input_data):
|
|
return next((c[0] for c in self.choices if c[1] == input_data), None)
|
|
```
|
|
|
|
Since `self.choices` is a list of tuples corresponding to (`display_name`, `value`), this converts the value that a user provides to the display value (or if the value is not present in `self.choices`, it is converted to `None`).
|
|
|
|
```python
|
|
@abstractmethod
|
|
def as_example(self, y):
|
|
pass
|
|
```
|
|
|
|
### `api_info`
|
|
|
|
A JSON-schema representation of the value that the `preprocess` expects.
|
|
This powers api usage via the gradio clients.
|
|
You do **not** need to implement this yourself if you components specifies a `data_model`.
|
|
The `data_model` in the following section.
|
|
|
|
```python
|
|
@abstractmethod
|
|
def api_info(self) -> dict[str, list[str]]:
|
|
"""
|
|
A JSON-schema representation of the value that the `preprocess` expects and the `postprocess` returns.
|
|
"""
|
|
pass
|
|
```
|
|
|
|
### `example_inputs`
|
|
|
|
The example inputs for this component displayed in the `View API` page.
|
|
Must be JSON-serializable.
|
|
If your component expects a file, it is best to use a publicly accessible URL.
|
|
|
|
```python
|
|
@abstractmethod
|
|
def example_inputs(self) -> Any:
|
|
"""
|
|
The example inputs for this component for API usage. Must be JSON-serializable.
|
|
"""
|
|
pass
|
|
```
|
|
|
|
### `flag`
|
|
|
|
Write the component's value to a format that can be stored in the `csv` or `json` file used for flagging.
|
|
You do **not** need to implement this yourself if you components specifies a `data_model`.
|
|
The `data_model` in the following section.
|
|
|
|
```python
|
|
@abstractmethod
|
|
def flag(self, x: Any | GradioDataModel, flag_dir: str | Path = "") -> str:
|
|
pass
|
|
```
|
|
|
|
### `read_from_flag`
|
|
Convert from the format stored in the `csv` or `json` file used for flagging to the component's python `value`.
|
|
You do **not** need to implement this yourself if you components specifies a `data_model`.
|
|
The `data_model` in the following section.
|
|
|
|
```python
|
|
@abstractmethod
|
|
def read_from_flag(
|
|
self,
|
|
x: Any,
|
|
flag_dir: str | Path | None = None,
|
|
) -> GradioDataModel | Any:
|
|
"""
|
|
Convert the data from the csv or jsonl file into the component state.
|
|
"""
|
|
return x
|
|
```
|
|
|
|
## The `data_model`
|
|
|
|
The `data_model` is how you define the expected data format your component's value will be stored in the frontend.
|
|
It specifies the data format your `preprocess` method expects and the format the `postprocess` method returns.
|
|
It is not necessary to define a `data_model` for your component but it greatly simplifies the process of creating a custom component.
|
|
If you define a custom component you only need to implement three methods - `preprocess`, `postprocess`, and `example_inputs`!
|
|
|
|
You define a `data_model` by defining a [pydantic model](https://docs.pydantic.dev/latest/concepts/models/#basic-model-usage) that inherits from either `GradioModel` or `GradioRootModel`.
|
|
|
|
This is best explained with an example. Let's look at the core `Video` component, which stores the video data as a JSON object with two keys `video` and `subtitles` which point to separate files.
|
|
|
|
```python
|
|
from gradio.data_classes import FileData, GradioModel
|
|
|
|
class VideoData(GradioModel):
|
|
video: FileData
|
|
subtitles: Optional[FileData] = None
|
|
|
|
class Video(Component):
|
|
data_model = VideoData
|
|
```
|
|
|
|
By adding these four lines of code, your component automatically implements the methods needed for API usage, the flagging methods, and example caching methods!
|
|
It also has the added benefit of self-documenting your code.
|
|
Anyone who reads your component code will know exactly the data it expects.
|
|
|
|
Tip: If your component expects files to be uploaded from the frontend, your must use the `FileData` model! It will be explained in the following section.
|
|
|
|
Tip: Read the pydantic docs [here](https://docs.pydantic.dev/latest/concepts/models/#basic-model-usage).
|
|
|
|
The difference between a `GradioModel` and a `GradioRootModel` is that the `RootModel` will not serialize the data to a dictionary.
|
|
For example, the `Names` model will serialize the data to `{'names': ['freddy', 'pete']}` whereas the `NamesRoot` model will serialize it to `['freddy', 'pete']`.
|
|
|
|
```python
|
|
from typing import List
|
|
|
|
class Names(GradioModel):
|
|
names: List[str]
|
|
|
|
class NamesRoot(GradioRootModel):
|
|
root: List[str]
|
|
```
|
|
|
|
Even if your component does not expect a "complex" JSON data structure it can be beneficial to define a `GradioRootModel` so that you don't have to worry about implementing the API and flagging methods.
|
|
|
|
Tip: Use classes from the Python typing library to type your models. e.g. `List` instead of `list`.
|
|
|
|
## Handling Files
|
|
|
|
If your component expects uploaded files as input, or returns saved files to the frontend, you **MUST** use the `FileData` to type the files in your `data_model`.
|
|
|
|
When you use the `FileData`:
|
|
|
|
* Gradio knows that it should allow serving this file to the frontend. Gradio automatically blocks requests to serve arbitrary files in the computer running the server.
|
|
|
|
* Gradio will automatically place the file in a cache so that duplicate copies of the file don't get saved.
|
|
|
|
* The client libraries will automatically know that they should upload input files prior to sending the request. They will also automatically download files.
|
|
|
|
If you do not use the `FileData`, your component will not work as expected!
|
|
|
|
|
|
## Adding Event Triggers To Your Component
|
|
|
|
The events triggers for your component are defined in the `EVENTS` class attribute.
|
|
This is a list that contains the string names of the events.
|
|
Adding an event to this list will automatically add a method with that same name to your component!
|
|
|
|
You can import the `Events` enum from `gradio.events` to access commonly used events in the core gradio components.
|
|
|
|
For example, the following code will define `text_submit`, `file_upload` and `change` methods in the `MyComponent` class.
|
|
|
|
```python
|
|
from gradio.events import Events
|
|
from gradio.components import FormComponent
|
|
|
|
class MyComponent(FormComponent):
|
|
|
|
EVENTS = [
|
|
"text_submit",
|
|
"file_upload",
|
|
Events.change
|
|
]
|
|
```
|
|
|
|
|
|
Tip: Don't forget to also handle these events in the JavaScript code!
|
|
|
|
## Conclusion
|
|
|