65
65
_warn_on_overwriting_operations ,
66
66
)
67
67
from ._inference_endpoints import InferenceEndpoint , InferenceEndpointType
68
- from ._jobs_api import JobInfo , ScheduledJobInfo , _create_job_spec
68
+ from ._jobs_api import JobInfo , JobSpec , ScheduledJobInfo , _create_job_spec
69
69
from ._space_api import SpaceHardware , SpaceRuntime , SpaceStorage , SpaceVariable
70
70
from ._upload_large_folder import upload_large_folder_internal
71
71
from .community import (
@@ -503,11 +503,15 @@ class WebhookWatchedItem:
503
503
class WebhookInfo :
504
504
"""Data structure containing information about a webhook.
505
505
506
+ One of `url` or `job` is specified, but not both.
507
+
506
508
Attributes:
507
509
id (`str`):
508
510
ID of the webhook.
509
- url (`str`):
511
+ url (`str`, *optional* ):
510
512
URL of the webhook.
513
+ job (`JobSpec`, *optional*):
514
+ Specifications of the Job to trigger.
511
515
watched (`List[WebhookWatchedItem]`):
512
516
List of items watched by the webhook, see [`WebhookWatchedItem`].
513
517
domains (`List[WEBHOOK_DOMAIN_T]`):
@@ -519,7 +523,8 @@ class WebhookInfo:
519
523
"""
520
524
521
525
id : str
522
- url : str
526
+ url : Optional [str ]
527
+ job : Optional [JobSpec ]
523
528
watched : List [WebhookWatchedItem ]
524
529
domains : List [constants .WEBHOOK_DOMAIN_T ]
525
530
secret : Optional [str ]
@@ -9035,6 +9040,7 @@ def get_webhook(self, webhook_id: str, *, token: Union[bool, str, None] = None)
9035
9040
>>> print(webhook)
9036
9041
WebhookInfo(
9037
9042
id="654bbbc16f2ec14d77f109cc",
9043
+ job=None,
9038
9044
watched=[WebhookWatchedItem(type="user", name="julien-c"), WebhookWatchedItem(type="org", name="HuggingFaceH4")],
9039
9045
url="https://webhook.site/a2176e82-5720-43ee-9e06-f91cb4c91548",
9040
9046
secret="my-secret",
@@ -9054,7 +9060,8 @@ def get_webhook(self, webhook_id: str, *, token: Union[bool, str, None] = None)
9054
9060
9055
9061
webhook = WebhookInfo (
9056
9062
id = webhook_data ["id" ],
9057
- url = webhook_data ["url" ],
9063
+ url = webhook_data .get ("url" ),
9064
+ job = JobSpec (** webhook_data ["job" ]) if webhook_data .get ("job" ) else None ,
9058
9065
watched = watched_items ,
9059
9066
domains = webhook_data ["domains" ],
9060
9067
secret = webhook_data .get ("secret" ),
@@ -9104,7 +9111,8 @@ def list_webhooks(self, *, token: Union[bool, str, None] = None) -> List[Webhook
9104
9111
return [
9105
9112
WebhookInfo (
9106
9113
id = webhook ["id" ],
9107
- url = webhook ["url" ],
9114
+ url = webhook .get ("url" ),
9115
+ job = JobSpec (** webhook ["job" ]) if webhook .get ("job" ) else None ,
9108
9116
watched = [WebhookWatchedItem (type = item ["type" ], name = item ["name" ]) for item in webhook ["watched" ]],
9109
9117
domains = webhook ["domains" ],
9110
9118
secret = webhook .get ("secret" ),
@@ -9117,17 +9125,24 @@ def list_webhooks(self, *, token: Union[bool, str, None] = None) -> List[Webhook
9117
9125
def create_webhook (
9118
9126
self ,
9119
9127
* ,
9120
- url : str ,
9128
+ url : Optional [str ] = None ,
9129
+ job_id : Optional [str ] = None ,
9121
9130
watched : List [Union [Dict , WebhookWatchedItem ]],
9122
9131
domains : Optional [List [constants .WEBHOOK_DOMAIN_T ]] = None ,
9123
9132
secret : Optional [str ] = None ,
9124
9133
token : Union [bool , str , None ] = None ,
9125
9134
) -> WebhookInfo :
9126
9135
"""Create a new webhook.
9127
9136
9137
+ The webhook can either send a payload to a URL, or trigger a Job to run on Hugging Face infrastructure.
9138
+ This function should be called with one of `url` or `job_id`, but not both.
9139
+
9128
9140
Args:
9129
9141
url (`str`):
9130
9142
URL to send the payload to.
9143
+ job_id (`str`):
9144
+ ID of the source Job to trigger with the webhook payload in the environment variable WEBHOOK_PAYLOAD.
9145
+ Additional environment variables are available for convenience: WEBHOOK_REPO_ID, WEBHOOK_REPO_TYPE and WEBHOOK_SECRET.
9131
9146
watched (`List[WebhookWatchedItem]`):
9132
9147
List of [`WebhookWatchedItem`] to be watched by the webhook. It can be users, orgs, models, datasets or spaces.
9133
9148
Watched items can also be provided as plain dictionaries.
@@ -9145,6 +9160,8 @@ def create_webhook(
9145
9160
Info about the newly created webhook.
9146
9161
9147
9162
Example:
9163
+
9164
+ Create a webhook that sends a payload to a URL
9148
9165
```python
9149
9166
>>> from huggingface_hub import create_webhook
9150
9167
>>> payload = create_webhook(
@@ -9157,6 +9174,43 @@ def create_webhook(
9157
9174
WebhookInfo(
9158
9175
id="654bbbc16f2ec14d77f109cc",
9159
9176
url="https://webhook.site/a2176e82-5720-43ee-9e06-f91cb4c91548",
9177
+ job=None,
9178
+ watched=[WebhookWatchedItem(type="user", name="julien-c"), WebhookWatchedItem(type="org", name="HuggingFaceH4")],
9179
+ domains=["repo", "discussion"],
9180
+ secret="my-secret",
9181
+ disabled=False,
9182
+ )
9183
+ ```
9184
+
9185
+ Run a Job and then create a webhook that triggers this Job
9186
+ ```python
9187
+ >>> from huggingface_hub import create_webhook, run_job
9188
+ >>> job = run_job(
9189
+ ... image="ubuntu",
9190
+ ... command=["bash", "-c", r"echo An event occured in $WEBHOOK_REPO_ID: $WEBHOOK_PAYLOAD"],
9191
+ ... )
9192
+ >>> payload = create_webhook(
9193
+ ... watched=[{"type": "user", "name": "julien-c"}, {"type": "org", "name": "HuggingFaceH4"}],
9194
+ ... job_id=job.id,
9195
+ ... domains=["repo", "discussion"],
9196
+ ... secret="my-secret",
9197
+ ... )
9198
+ >>> print(payload)
9199
+ WebhookInfo(
9200
+ id="654bbbc16f2ec14d77f109cc",
9201
+ url=None,
9202
+ job=JobSpec(
9203
+ docker_image='ubuntu',
9204
+ space_id=None,
9205
+ command=['bash', '-c', 'echo An event occured in $WEBHOOK_REPO_ID: $WEBHOOK_PAYLOAD'],
9206
+ arguments=[],
9207
+ environment={},
9208
+ secrets=[],
9209
+ flavor='cpu-basic',
9210
+ timeout=None,
9211
+ tags=None,
9212
+ arch=None
9213
+ ),
9160
9214
watched=[WebhookWatchedItem(type="user", name="julien-c"), WebhookWatchedItem(type="org", name="HuggingFaceH4")],
9161
9215
domains=["repo", "discussion"],
9162
9216
secret="my-secret",
@@ -9166,9 +9220,19 @@ def create_webhook(
9166
9220
"""
9167
9221
watched_dicts = [asdict (item ) if isinstance (item , WebhookWatchedItem ) else item for item in watched ]
9168
9222
9223
+ post_webhooks_json = {"watched" : watched_dicts , "domains" : domains , "secret" : secret }
9224
+ if url is not None and job_id is not None :
9225
+ raise ValueError ("Set `url` or `job_id` but not both." )
9226
+ elif url is not None :
9227
+ post_webhooks_json ["url" ] = url
9228
+ elif job_id is not None :
9229
+ post_webhooks_json ["jobSourceId" ] = job_id
9230
+ else :
9231
+ raise ValueError ("Missing argument for webhook: `url` or `job_id`." )
9232
+
9169
9233
response = get_session ().post (
9170
9234
f"{ constants .ENDPOINT } /api/settings/webhooks" ,
9171
- json = { "watched" : watched_dicts , "url" : url , "domains" : domains , "secret" : secret } ,
9235
+ json = post_webhooks_json ,
9172
9236
headers = self ._build_hf_headers (token = token ),
9173
9237
)
9174
9238
hf_raise_for_status (response )
@@ -9177,7 +9241,8 @@ def create_webhook(
9177
9241
9178
9242
webhook = WebhookInfo (
9179
9243
id = webhook_data ["id" ],
9180
- url = webhook_data ["url" ],
9244
+ url = webhook_data .get ("url" ),
9245
+ job = JobSpec (** webhook_data ["job" ]) if webhook_data .get ("job" ) else None ,
9181
9246
watched = watched_items ,
9182
9247
domains = webhook_data ["domains" ],
9183
9248
secret = webhook_data .get ("secret" ),
@@ -9233,6 +9298,7 @@ def update_webhook(
9233
9298
>>> print(updated_payload)
9234
9299
WebhookInfo(
9235
9300
id="654bbbc16f2ec14d77f109cc",
9301
+ job=None,
9236
9302
url="https://new.webhook.site/a2176e82-5720-43ee-9e06-f91cb4c91548",
9237
9303
watched=[WebhookWatchedItem(type="user", name="julien-c"), WebhookWatchedItem(type="org", name="HuggingFaceH4")],
9238
9304
domains=["repo"],
@@ -9256,7 +9322,8 @@ def update_webhook(
9256
9322
9257
9323
webhook = WebhookInfo (
9258
9324
id = webhook_data ["id" ],
9259
- url = webhook_data ["url" ],
9325
+ url = webhook_data .get ("url" ),
9326
+ job = JobSpec (** webhook_data ["job" ]) if webhook_data .get ("job" ) else None ,
9260
9327
watched = watched_items ,
9261
9328
domains = webhook_data ["domains" ],
9262
9329
secret = webhook_data .get ("secret" ),
@@ -9288,6 +9355,7 @@ def enable_webhook(self, webhook_id: str, *, token: Union[bool, str, None] = Non
9288
9355
>>> enabled_webhook
9289
9356
WebhookInfo(
9290
9357
id="654bbbc16f2ec14d77f109cc",
9358
+ job=None,
9291
9359
url="https://webhook.site/a2176e82-5720-43ee-9e06-f91cb4c91548",
9292
9360
watched=[WebhookWatchedItem(type="user", name="julien-c"), WebhookWatchedItem(type="org", name="HuggingFaceH4")],
9293
9361
domains=["repo", "discussion"],
@@ -9307,7 +9375,8 @@ def enable_webhook(self, webhook_id: str, *, token: Union[bool, str, None] = Non
9307
9375
9308
9376
webhook = WebhookInfo (
9309
9377
id = webhook_data ["id" ],
9310
- url = webhook_data ["url" ],
9378
+ url = webhook_data .get ("url" ),
9379
+ job = JobSpec (** webhook_data ["job" ]) if webhook_data .get ("job" ) else None ,
9311
9380
watched = watched_items ,
9312
9381
domains = webhook_data ["domains" ],
9313
9382
secret = webhook_data .get ("secret" ),
@@ -9340,6 +9409,7 @@ def disable_webhook(self, webhook_id: str, *, token: Union[bool, str, None] = No
9340
9409
WebhookInfo(
9341
9410
id="654bbbc16f2ec14d77f109cc",
9342
9411
url="https://webhook.site/a2176e82-5720-43ee-9e06-f91cb4c91548",
9412
+ jon=None,
9343
9413
watched=[WebhookWatchedItem(type="user", name="julien-c"), WebhookWatchedItem(type="org", name="HuggingFaceH4")],
9344
9414
domains=["repo", "discussion"],
9345
9415
secret="my-secret",
@@ -9358,7 +9428,8 @@ def disable_webhook(self, webhook_id: str, *, token: Union[bool, str, None] = No
9358
9428
9359
9429
webhook = WebhookInfo (
9360
9430
id = webhook_data ["id" ],
9361
- url = webhook_data ["url" ],
9431
+ url = webhook_data .get ("url" ),
9432
+ job = JobSpec (** webhook_data ["job" ]) if webhook_data .get ("job" ) else None ,
9362
9433
watched = watched_items ,
9363
9434
domains = webhook_data ["domains" ],
9364
9435
secret = webhook_data .get ("secret" ),
0 commit comments