Skip to content

Commit

Permalink
[wip] Implement webhooks API (#2209)
Browse files Browse the repository at this point in the history
* first draft for discussion

* Restyling + reintroduce webhook secret + WatchedItem to dataclass

* Added webhook apis to __init__.py

* Added WEBHOOK_DOMAIN_T as constant

* Updated update_webhook docstring

* lint

* style

* style

* style

* style

* style

* Add Webhook dataclass as return value and update docstrings'

* Update links to Webhook definition in docstrings

* Update docstring style

* Update token typing and description

* Update token typing and description to newest version

* Rename Webhook dataclass to WebhookInfo"

* Add WebhookInfo dataclass to huggingface_hub/__init__.py

* Add import statements in examples

* Update WebhookInfo references and standardize wording

* Add WebhookWatchedItem to huggingface_hub/__init__.py

* Apply suggestions: Args kw-only, all (not necessary) args optional

Co-authored-by: Lucain <lucainp@gmail.com>

* Apply suggestion: Add WebhookWatchedItem as import in example

Co-authored-by: Lucain <lucainp@gmail.com>

* Fix JSON parsing and dataclass serialization issues

* Reorder args for consistency

* Fix typo

* Fix TypeError in update_webhook by ensuring watched is iterable and dataclass is serializable

* Add tests

* Update args order in WebhookInfo dataclass

* Rename .../webhooks_server.md to .../webhooks.md

* Apply suggestions

* Fix commit issue

* Add 'Managing Webhooks' section to docs

* Update _redirects.yml

* Update _toctree.yml

* Update package_reference with WebhookInfo and WatchedWebhookInfo

* Fix typos in hf_api.md

* Update _toctree.yml

* Update _toctree.yml and _redirects.yml

* Update titles in guides/webhooks.md

* Format and rephrase guides/webhooks`

* switch docs sections + fix code quality

* test dicts as well

---------

Co-authored-by: Lucain Pouget <lucainp@gmail.com>
  • Loading branch information
lappemic and Wauplin authored May 24, 2024
1 parent 3900efc commit ce00270
Show file tree
Hide file tree
Showing 8 changed files with 632 additions and 17 deletions.
3 changes: 3 additions & 0 deletions docs/source/en/_redirects.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,6 @@ package_reference/inference_api: package_reference/inference_client

# Alias for hf-transfer description
hf_transfer: package_reference/environment_variables#hfhubenablehftransfer

# Rename webhooks_server to webhooks
guides/webhooks_server: guides/webhooks
4 changes: 2 additions & 2 deletions docs/source/en/_toctree.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@
title: Manage your Space
- local: guides/integrations
title: Integrate a library
- local: guides/webhooks_server
title: Webhooks server
- local: guides/webhooks
title: Webhooks
- title: "Conceptual guides"
sections:
- local: concepts/git_vs_http
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,91 @@
rendered properly in your Markdown viewer.
-->

# Webhooks Server
# Webhooks

Webhooks are a foundation for MLOps-related features. They allow you to listen for new changes on specific repos or to
all repos belonging to particular users/organizations you're interested in following. This guide will explain how to
leverage `huggingface_hub` to create a server listening to webhooks and deploy it to a Space. It assumes you are
familiar with the concept of webhooks on the Huggingface Hub. To learn more about webhooks themselves, you can read
this [guide](https://huggingface.co/docs/hub/webhooks) first.
Webhooks are a foundation for MLOps-related features. They allow you to listen for new changes on specific repos or to all repos belonging to particular users/organizations you're interested in following. This guide will first explain how to manage webhooks programmatically. Then we'll see how to leverage `huggingface_hub` to create a server listening to webhooks and deploy it to a Space.

The base class that we will use in this guide is [`WebhooksServer`]. It is a class for easily configuring a server that
This guide assumes you are familiar with the concept of webhooks on the Huggingface Hub. To learn more about webhooks themselves, you should read this [guide](https://huggingface.co/docs/hub/webhooks) first.

## Managing Webhooks

`huggingface_hub` allows you to manage your webhooks programmatically. You can list your existing webhooks, create new ones, and update, enable, disable or delete them. This section guides you through the procedures using the Hugging Face Hub's API functions.

### Creating a Webhook

To create a new webhook, use [`create_webhook`] and specify the URL where payloads should be sent, what events should be watched, and optionally set a domain and a secret for security.

```python
from huggingface_hub import create_webhook

# Example: Creating a webhook
webhook = create_webhook(
url="https://webhook.site/your-custom-url",
watched=[{"type": "user", "name": "your-username"}, {"type": "org", "name": "your-org-name"}],
domains=["repo", "discussion"],
secret="your-secret"
)
```

### Listing Webhooks

To see all the webhooks you have configured, you can list them with [`list_webhooks`]. This is useful to review their IDs, URLs, and statuses.

```python
from huggingface_hub import list_webhooks

# Example: Listing all webhooks
webhooks = list_webhooks()
for webhook in webhooks:
print(webhook)
```

### Updating a Webhook

If you need to change the configuration of an existing webhook, such as the URL or the events it watches, you can update it using [`update_webhook`].

```python
from huggingface_hub import update_webhook

# Example: Updating a webhook
updated_webhook = update_webhook(
webhook_id="your-webhook-id",
url="https://new.webhook.site/url",
watched=[{"type": "user", "name": "new-username"}],
domains=["repo"]
)
```

### Enabling and Disabling Webhooks

You might want to temporarily disable a webhook without deleting it. This can be done using [`disable_webhook`], and the webhook can be re-enabled later with [`enable_webhook`].

```python
from huggingface_hub import enable_webhook, disable_webhook

# Example: Enabling a webhook
enabled_webhook = enable_webhook("your-webhook-id")
print("Enabled:", enabled_webhook)

# Example: Disabling a webhook
disabled_webhook = disable_webhook("your-webhook-id")
print("Disabled:", disabled_webhook)
```

### Deleting a Webhook

When a webhook is no longer needed, it can be permanently deleted using [`delete_webhook`].

```python
from huggingface_hub import delete_webhook

# Example: Deleting a webhook
delete_webhook("your-webhook-id")
```

## Webhooks Server

The base class that we will use in this guides section is [`WebhooksServer`]. It is a class for easily configuring a server that
can receive webhooks from the Huggingface Hub. The server is based on a [Gradio](https://gradio.app/) app. It has a UI
to display instructions for you or your users and an API to listen to webhooks.

Expand All @@ -30,7 +106,7 @@ notice. Make sure to pin the version of `huggingface_hub` in your requirements.
</Tip>


## Create an endpoint
### Create an endpoint

Implementing a webhook endpoint is as simple as decorating a function. Let's see a first example to explain the main
concepts:
Expand Down Expand Up @@ -87,7 +163,7 @@ once even if you have multiple endpoints.
</Tip>


## Configure a Webhook
### Configure a Webhook

Now that you have a webhook server running, you want to configure a Webhook to start receiving messages.
Go to https://huggingface.co/settings/webhooks, click on "Add a new webhook" and configure your Webhook. Set the target
Expand All @@ -102,7 +178,7 @@ Activity tab of your Webhook to see the events that have been triggered. Now tha
test it and quickly iterate. If you modify your code and restart the server, your public URL might change. Make sure
to update the webhook configuration on the Hub if needed.

## Deploy to a Space
### Deploy to a Space

Now that you have a working webhook server, the goal is to deploy it to a Space. Go to https://huggingface.co/new-space
to create a Space. Give it a name, select the Gradio SDK and click on "Create Space". Upload your code to the Space
Expand All @@ -118,12 +194,12 @@ And this is it! Your Space is now ready to receive webhooks from the Hub. Please
on a free 'cpu-basic' hardware, it will be shut down after 48 hours of inactivity. If you need a permanent Space, you
should consider setting to an [upgraded hardware](https://huggingface.co/docs/hub/spaces-gpus#hardware-specs).

## Advanced usage
### Advanced usage

The guide above explained the quickest way to setup a [`WebhooksServer`]. In this section, we will see how to customize
it further.

### Multiple endpoints
#### Multiple endpoints

You can register multiple endpoints on the same server. For example, you might want to have one endpoint to trigger
a training job and another one to trigger a model evaluation. You can do this by adding multiple `@webhook_endpoint`
Expand Down Expand Up @@ -155,7 +231,7 @@ Webhooks are correctly setup and ready to use:
- POST https://1fadb0f52d8bf825fc.gradio.live/webhooks/trigger_evaluation
```

### Custom server
#### Custom server

To get more flexibility, you can also create a [`WebhooksServer`] object directly. This is useful if you want to
customize the landing page of your server. You can do this by passing a [Gradio UI](https://gradio.app/docs/#blocks)
Expand Down
8 changes: 8 additions & 0 deletions docs/source/en/package_reference/hf_api.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,14 @@ models = hf_api.list_models()

[[autodoc]] huggingface_hub.hf_api.UserLikes

### WebhookInfo

[[autodoc]] huggingface_hub.hf_api.WebhookInfo

### WebhookWatchedItem

[[autodoc]] huggingface_hub.hf_api.WebhookWatchedItem

## CommitOperation

Below are the supported values for [`CommitOperation`]:
Expand Down
18 changes: 18 additions & 0 deletions src/huggingface_hub/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,8 @@
"RepoUrl",
"User",
"UserLikes",
"WebhookInfo",
"WebhookWatchedItem",
"accept_access_request",
"add_collection_item",
"add_space_secret",
Expand All @@ -166,6 +168,7 @@
"create_pull_request",
"create_repo",
"create_tag",
"create_webhook",
"dataset_info",
"delete_branch",
"delete_collection",
Expand All @@ -178,8 +181,11 @@
"delete_space_storage",
"delete_space_variable",
"delete_tag",
"delete_webhook",
"disable_webhook",
"duplicate_space",
"edit_discussion_comment",
"enable_webhook",
"file_exists",
"get_collection",
"get_dataset_tags",
Expand All @@ -193,6 +199,7 @@
"get_space_runtime",
"get_space_variables",
"get_token_permission",
"get_webhook",
"grant_access",
"like",
"list_accepted_access_requests",
Expand All @@ -210,6 +217,7 @@
"list_repo_refs",
"list_repo_tree",
"list_spaces",
"list_webhooks",
"merge_pull_request",
"model_info",
"move_repo",
Expand Down Expand Up @@ -237,6 +245,7 @@
"update_collection_metadata",
"update_inference_endpoint",
"update_repo_visibility",
"update_webhook",
"upload_file",
"upload_folder",
"whoami",
Expand Down Expand Up @@ -630,6 +639,8 @@ def __dir__():
RepoUrl, # noqa: F401
User, # noqa: F401
UserLikes, # noqa: F401
WebhookInfo, # noqa: F401
WebhookWatchedItem, # noqa: F401
accept_access_request, # noqa: F401
add_collection_item, # noqa: F401
add_space_secret, # noqa: F401
Expand All @@ -646,6 +657,7 @@ def __dir__():
create_pull_request, # noqa: F401
create_repo, # noqa: F401
create_tag, # noqa: F401
create_webhook, # noqa: F401
dataset_info, # noqa: F401
delete_branch, # noqa: F401
delete_collection, # noqa: F401
Expand All @@ -658,8 +670,11 @@ def __dir__():
delete_space_storage, # noqa: F401
delete_space_variable, # noqa: F401
delete_tag, # noqa: F401
delete_webhook, # noqa: F401
disable_webhook, # noqa: F401
duplicate_space, # noqa: F401
edit_discussion_comment, # noqa: F401
enable_webhook, # noqa: F401
file_exists, # noqa: F401
get_collection, # noqa: F401
get_dataset_tags, # noqa: F401
Expand All @@ -673,6 +688,7 @@ def __dir__():
get_space_runtime, # noqa: F401
get_space_variables, # noqa: F401
get_token_permission, # noqa: F401
get_webhook, # noqa: F401
grant_access, # noqa: F401
like, # noqa: F401
list_accepted_access_requests, # noqa: F401
Expand All @@ -690,6 +706,7 @@ def __dir__():
list_repo_refs, # noqa: F401
list_repo_tree, # noqa: F401
list_spaces, # noqa: F401
list_webhooks, # noqa: F401
merge_pull_request, # noqa: F401
model_info, # noqa: F401
move_repo, # noqa: F401
Expand Down Expand Up @@ -717,6 +734,7 @@ def __dir__():
update_collection_metadata, # noqa: F401
update_inference_endpoint, # noqa: F401
update_repo_visibility, # noqa: F401
update_webhook, # noqa: F401
upload_file, # noqa: F401
upload_folder, # noqa: F401
whoami, # noqa: F401
Expand Down
3 changes: 3 additions & 0 deletions src/huggingface_hub/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,9 @@ def _as_int(value: Optional[str]) -> Optional[int]:
DiscussionStatusFilter = Literal["all", "open", "closed"]
DISCUSSION_STATUS: Tuple[DiscussionTypeFilter, ...] = typing.get_args(DiscussionStatusFilter)

# Webhook subscription types
WEBHOOK_DOMAIN_T = Literal["repo", "discussions"]

# default cache
default_home = os.path.join(os.path.expanduser("~"), ".cache")
HF_HOME = os.path.expanduser(
Expand Down
Loading

0 comments on commit ce00270

Please sign in to comment.