huggingface-hub 0.35.0rc0__py3-none-any.whl → 0.35.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of huggingface-hub might be problematic. Click here for more details.

Files changed (50) hide show
  1. huggingface_hub/__init__.py +19 -1
  2. huggingface_hub/_jobs_api.py +168 -12
  3. huggingface_hub/_local_folder.py +1 -1
  4. huggingface_hub/_oauth.py +5 -9
  5. huggingface_hub/_tensorboard_logger.py +9 -10
  6. huggingface_hub/_upload_large_folder.py +108 -1
  7. huggingface_hub/cli/auth.py +4 -1
  8. huggingface_hub/cli/cache.py +7 -9
  9. huggingface_hub/cli/hf.py +2 -5
  10. huggingface_hub/cli/jobs.py +591 -13
  11. huggingface_hub/cli/repo.py +10 -4
  12. huggingface_hub/commands/delete_cache.py +2 -2
  13. huggingface_hub/commands/scan_cache.py +1 -1
  14. huggingface_hub/dataclasses.py +3 -0
  15. huggingface_hub/file_download.py +12 -10
  16. huggingface_hub/hf_api.py +549 -95
  17. huggingface_hub/hf_file_system.py +4 -10
  18. huggingface_hub/hub_mixin.py +5 -3
  19. huggingface_hub/inference/_client.py +98 -181
  20. huggingface_hub/inference/_common.py +72 -70
  21. huggingface_hub/inference/_generated/_async_client.py +116 -201
  22. huggingface_hub/inference/_generated/types/chat_completion.py +2 -0
  23. huggingface_hub/inference/_mcp/_cli_hacks.py +3 -3
  24. huggingface_hub/inference/_mcp/cli.py +1 -1
  25. huggingface_hub/inference/_mcp/constants.py +1 -1
  26. huggingface_hub/inference/_mcp/mcp_client.py +28 -11
  27. huggingface_hub/inference/_mcp/types.py +3 -0
  28. huggingface_hub/inference/_mcp/utils.py +7 -3
  29. huggingface_hub/inference/_providers/__init__.py +13 -0
  30. huggingface_hub/inference/_providers/_common.py +29 -4
  31. huggingface_hub/inference/_providers/black_forest_labs.py +1 -1
  32. huggingface_hub/inference/_providers/fal_ai.py +33 -2
  33. huggingface_hub/inference/_providers/hf_inference.py +15 -7
  34. huggingface_hub/inference/_providers/publicai.py +6 -0
  35. huggingface_hub/inference/_providers/replicate.py +1 -1
  36. huggingface_hub/inference/_providers/scaleway.py +28 -0
  37. huggingface_hub/lfs.py +2 -4
  38. huggingface_hub/repocard.py +2 -1
  39. huggingface_hub/utils/_dotenv.py +24 -20
  40. huggingface_hub/utils/_git_credential.py +1 -1
  41. huggingface_hub/utils/_http.py +3 -5
  42. huggingface_hub/utils/_runtime.py +1 -0
  43. huggingface_hub/utils/_typing.py +24 -4
  44. huggingface_hub/utils/_xet_progress_reporting.py +31 -10
  45. {huggingface_hub-0.35.0rc0.dist-info → huggingface_hub-0.35.1.dist-info}/METADATA +7 -4
  46. {huggingface_hub-0.35.0rc0.dist-info → huggingface_hub-0.35.1.dist-info}/RECORD +50 -48
  47. {huggingface_hub-0.35.0rc0.dist-info → huggingface_hub-0.35.1.dist-info}/LICENSE +0 -0
  48. {huggingface_hub-0.35.0rc0.dist-info → huggingface_hub-0.35.1.dist-info}/WHEEL +0 -0
  49. {huggingface_hub-0.35.0rc0.dist-info → huggingface_hub-0.35.1.dist-info}/entry_points.txt +0 -0
  50. {huggingface_hub-0.35.0rc0.dist-info → huggingface_hub-0.35.1.dist-info}/top_level.txt +0 -0
huggingface_hub/hf_api.py CHANGED
@@ -67,7 +67,7 @@ from ._commit_api import (
67
67
  _warn_on_overwriting_operations,
68
68
  )
69
69
  from ._inference_endpoints import InferenceEndpoint, InferenceEndpointType
70
- from ._jobs_api import JobInfo
70
+ from ._jobs_api import JobInfo, ScheduledJobInfo, _create_job_spec
71
71
  from ._space_api import SpaceHardware, SpaceRuntime, SpaceStorage, SpaceVariable
72
72
  from ._upload_large_folder import upload_large_folder_internal
73
73
  from .community import (
@@ -132,8 +132,12 @@ from .utils import (
132
132
  validate_hf_hub_args,
133
133
  )
134
134
  from .utils import tqdm as hf_tqdm
135
- from .utils._auth import _get_token_from_environment, _get_token_from_file, _get_token_from_google_colab
136
- from .utils._deprecation import _deprecate_method
135
+ from .utils._auth import (
136
+ _get_token_from_environment,
137
+ _get_token_from_file,
138
+ _get_token_from_google_colab,
139
+ )
140
+ from .utils._deprecation import _deprecate_arguments, _deprecate_method
137
141
  from .utils._runtime import is_xet_available
138
142
  from .utils._typing import CallableT
139
143
  from .utils.endpoint_helpers import _is_emission_within_threshold
@@ -1777,18 +1781,20 @@ class HfApi:
1777
1781
  try:
1778
1782
  hf_raise_for_status(r)
1779
1783
  except HTTPError as e:
1780
- error_message = "Invalid user token."
1781
- # Check which token is the effective one and generate the error message accordingly
1782
- if effective_token == _get_token_from_google_colab():
1783
- error_message += " The token from Google Colab vault is invalid. Please update it from the UI."
1784
- elif effective_token == _get_token_from_environment():
1785
- error_message += (
1786
- " The token from HF_TOKEN environment variable is invalid. "
1787
- "Note that HF_TOKEN takes precedence over `hf auth login`."
1788
- )
1789
- elif effective_token == _get_token_from_file():
1790
- error_message += " The token stored is invalid. Please run `hf auth login` to update it."
1791
- raise HTTPError(error_message, request=e.request, response=e.response) from e
1784
+ if e.response.status_code == 401:
1785
+ error_message = "Invalid user token."
1786
+ # Check which token is the effective one and generate the error message accordingly
1787
+ if effective_token == _get_token_from_google_colab():
1788
+ error_message += " The token from Google Colab vault is invalid. Please update it from the UI."
1789
+ elif effective_token == _get_token_from_environment():
1790
+ error_message += (
1791
+ " The token from HF_TOKEN environment variable is invalid. "
1792
+ "Note that HF_TOKEN takes precedence over `hf auth login`."
1793
+ )
1794
+ elif effective_token == _get_token_from_file():
1795
+ error_message += " The token stored is invalid. Please run `hf auth login` to update it."
1796
+ raise HTTPError(error_message, request=e.request, response=e.response) from e
1797
+ raise
1792
1798
  return r.json()
1793
1799
 
1794
1800
  @_deprecate_method(
@@ -1849,6 +1855,9 @@ class HfApi:
1849
1855
  hf_raise_for_status(r)
1850
1856
  return r.json()
1851
1857
 
1858
+ @_deprecate_arguments(
1859
+ version="1.0", deprecated_args=["language", "library", "task", "tags"], custom_message="Use `filter` instead."
1860
+ )
1852
1861
  @validate_hf_hub_args
1853
1862
  def list_models(
1854
1863
  self,
@@ -1856,15 +1865,12 @@ class HfApi:
1856
1865
  # Search-query parameter
1857
1866
  filter: Union[str, Iterable[str], None] = None,
1858
1867
  author: Optional[str] = None,
1868
+ apps: Optional[Union[str, List[str]]] = None,
1859
1869
  gated: Optional[bool] = None,
1860
1870
  inference: Optional[Literal["warm"]] = None,
1861
1871
  inference_provider: Optional[Union[Literal["all"], "PROVIDER_T", List["PROVIDER_T"]]] = None,
1862
- library: Optional[Union[str, List[str]]] = None,
1863
- language: Optional[Union[str, List[str]]] = None,
1864
1872
  model_name: Optional[str] = None,
1865
- task: Optional[Union[str, List[str]]] = None,
1866
1873
  trained_dataset: Optional[Union[str, List[str]]] = None,
1867
- tags: Optional[Union[str, List[str]]] = None,
1868
1874
  search: Optional[str] = None,
1869
1875
  pipeline_tag: Optional[str] = None,
1870
1876
  emissions_thresholds: Optional[Tuple[float, float]] = None,
@@ -1878,6 +1884,11 @@ class HfApi:
1878
1884
  cardData: bool = False,
1879
1885
  fetch_config: bool = False,
1880
1886
  token: Union[bool, str, None] = None,
1887
+ # Deprecated arguments - use `filter` instead
1888
+ language: Optional[Union[str, List[str]]] = None,
1889
+ library: Optional[Union[str, List[str]]] = None,
1890
+ tags: Optional[Union[str, List[str]]] = None,
1891
+ task: Optional[Union[str, List[str]]] = None,
1881
1892
  ) -> Iterable[ModelInfo]:
1882
1893
  """
1883
1894
  List models hosted on the Huggingface Hub, given some filters.
@@ -1885,9 +1896,13 @@ class HfApi:
1885
1896
  Args:
1886
1897
  filter (`str` or `Iterable[str]`, *optional*):
1887
1898
  A string or list of string to filter models on the Hub.
1899
+ Models can be filtered by library, language, task, tags, and more.
1888
1900
  author (`str`, *optional*):
1889
1901
  A string which identify the author (user or organization) of the
1890
1902
  returned models.
1903
+ apps (`str` or `List`, *optional*):
1904
+ A string or list of strings to filter models on the Hub that
1905
+ support the specified apps. Example values include `"ollama"` or `["ollama", "vllm"]`.
1891
1906
  gated (`bool`, *optional*):
1892
1907
  A boolean to filter models on the Hub that are gated or not. By default, all models are returned.
1893
1908
  If `gated=True` is passed, only gated models are returned.
@@ -1898,23 +1913,19 @@ class HfApi:
1898
1913
  A string to filter models on the Hub that are served by a specific provider.
1899
1914
  Pass `"all"` to get all models served by at least one provider.
1900
1915
  library (`str` or `List`, *optional*):
1901
- A string or list of strings of foundational libraries models were
1902
- originally trained from, such as pytorch, tensorflow, or allennlp.
1916
+ Deprecated. Pass a library name in `filter` to filter models by library.
1903
1917
  language (`str` or `List`, *optional*):
1904
- A string or list of strings of languages, both by name and country
1905
- code, such as "en" or "English"
1918
+ Deprecated. Pass a language in `filter` to filter models by language.
1906
1919
  model_name (`str`, *optional*):
1907
1920
  A string that contain complete or partial names for models on the
1908
1921
  Hub, such as "bert" or "bert-base-cased"
1909
1922
  task (`str` or `List`, *optional*):
1910
- A string or list of strings of tasks models were designed for, such
1911
- as: "fill-mask" or "automatic-speech-recognition"
1923
+ Deprecated. Pass a task in `filter` to filter models by task.
1912
1924
  trained_dataset (`str` or `List`, *optional*):
1913
1925
  A string tag or a list of string tags of the trained dataset for a
1914
1926
  model on the Hub.
1915
1927
  tags (`str` or `List`, *optional*):
1916
- A string tag or a list of tags to filter models on the Hub by, such
1917
- as `text-generation` or `spacy`.
1928
+ Deprecated. Pass tags in `filter` to filter models by tags.
1918
1929
  search (`str`, *optional*):
1919
1930
  A string that will be contained in the returned model ids.
1920
1931
  pipeline_tag (`str`, *optional*):
@@ -1985,7 +1996,7 @@ class HfApi:
1985
1996
  if expand and (full or cardData or fetch_config):
1986
1997
  raise ValueError("`expand` cannot be used if `full`, `cardData` or `fetch_config` are passed.")
1987
1998
 
1988
- if emissions_thresholds is not None and cardData is None:
1999
+ if emissions_thresholds is not None and not cardData:
1989
2000
  raise ValueError("`emissions_thresholds` were passed without setting `cardData=True`.")
1990
2001
 
1991
2002
  path = f"{self.endpoint}/api/models"
@@ -2017,6 +2028,10 @@ class HfApi:
2017
2028
  # Handle other query params
2018
2029
  if author:
2019
2030
  params["author"] = author
2031
+ if apps:
2032
+ if isinstance(apps, str):
2033
+ apps = [apps]
2034
+ params["apps"] = apps
2020
2035
  if gated is not None:
2021
2036
  params["gated"] = gated
2022
2037
  if inference is not None:
@@ -2068,6 +2083,7 @@ class HfApi:
2068
2083
  if emissions_thresholds is None or _is_emission_within_threshold(model_info, *emissions_thresholds):
2069
2084
  yield model_info
2070
2085
 
2086
+ @_deprecate_arguments(version="1.0", deprecated_args=["tags"], custom_message="Use `filter` instead.")
2071
2087
  @validate_hf_hub_args
2072
2088
  def list_datasets(
2073
2089
  self,
@@ -2082,7 +2098,6 @@ class HfApi:
2082
2098
  language: Optional[Union[str, List[str]]] = None,
2083
2099
  multilinguality: Optional[Union[str, List[str]]] = None,
2084
2100
  size_categories: Optional[Union[str, List[str]]] = None,
2085
- tags: Optional[Union[str, List[str]]] = None,
2086
2101
  task_categories: Optional[Union[str, List[str]]] = None,
2087
2102
  task_ids: Optional[Union[str, List[str]]] = None,
2088
2103
  search: Optional[str] = None,
@@ -2094,6 +2109,8 @@ class HfApi:
2094
2109
  expand: Optional[List[ExpandDatasetProperty_T]] = None,
2095
2110
  full: Optional[bool] = None,
2096
2111
  token: Union[bool, str, None] = None,
2112
+ # Deprecated arguments - use `filter` instead
2113
+ tags: Optional[Union[str, List[str]]] = None,
2097
2114
  ) -> Iterable[DatasetInfo]:
2098
2115
  """
2099
2116
  List datasets hosted on the Huggingface Hub, given some filters.
@@ -2128,7 +2145,7 @@ class HfApi:
2128
2145
  the Hub by the size of the dataset such as `100K<n<1M` or
2129
2146
  `1M<n<10M`.
2130
2147
  tags (`str` or `List`, *optional*):
2131
- A string tag or a list of tags to filter datasets on the Hub.
2148
+ Deprecated. Pass tags in `filter` to filter datasets by tags.
2132
2149
  task_categories (`str` or `List`, *optional*):
2133
2150
  A string or list of strings that can be used to identify datasets on
2134
2151
  the Hub by the designed task, such as `audio_classification` or
@@ -5315,14 +5332,18 @@ class HfApi:
5315
5332
  1. (Check parameters and setup.)
5316
5333
  2. Create repo if missing.
5317
5334
  3. List local files to upload.
5318
- 4. Start workers. Workers can perform the following tasks:
5335
+ 4. Run validation checks and display warnings if repository limits might be exceeded:
5336
+ - Warns if the total number of files exceeds 100k (recommended limit).
5337
+ - Warns if any folder contains more than 10k files (recommended limit).
5338
+ - Warns about files larger than 20GB (recommended) or 50GB (hard limit).
5339
+ 5. Start workers. Workers can perform the following tasks:
5319
5340
  - Hash a file.
5320
5341
  - Get upload mode (regular or LFS) for a list of files.
5321
5342
  - Pre-upload an LFS file.
5322
5343
  - Commit a bunch of files.
5323
5344
  Once a worker finishes a task, it will move on to the next task based on the priority list (see below) until
5324
5345
  all files are uploaded and committed.
5325
- 5. While workers are up, regularly print a report to sys.stdout.
5346
+ 6. While workers are up, regularly print a report to sys.stdout.
5326
5347
 
5327
5348
  Order of priority:
5328
5349
  1. Commit if more than 5 minutes since last commit attempt (and at least 1 file).
@@ -9994,7 +10015,7 @@ class HfApi:
9994
10015
 
9995
10016
  ```python
9996
10017
  >>> from huggingface_hub import run_job
9997
- >>> run_job("python:3.12", ["python", "-c" ,"print('Hello from HF compute!')"])
10018
+ >>> run_job(image="python:3.12", command=["python", "-c" ,"print('Hello from HF compute!')"])
9998
10019
  ```
9999
10020
 
10000
10021
  Run a GPU Job:
@@ -10003,47 +10024,23 @@ class HfApi:
10003
10024
  >>> from huggingface_hub import run_job
10004
10025
  >>> image = "pytorch/pytorch:2.6.0-cuda12.4-cudnn9-devel"
10005
10026
  >>> command = ["python", "-c", "import torch; print(f"This code ran with the following GPU: {torch.cuda.get_device_name()}")"]
10006
- >>> run_job(image, command, flavor="a10g-small")
10027
+ >>> run_job(image=image, command=command, flavor="a10g-small")
10007
10028
  ```
10008
10029
 
10009
10030
  """
10010
- if flavor is None:
10011
- flavor = SpaceHardware.CPU_BASIC
10012
-
10013
- # prepare payload to send to HF Jobs API
10014
- input_json: Dict[str, Any] = {
10015
- "command": command,
10016
- "arguments": [],
10017
- "environment": env or {},
10018
- "flavor": flavor,
10019
- }
10020
- # secrets are optional
10021
- if secrets:
10022
- input_json["secrets"] = secrets
10023
- # timeout is optional
10024
- if timeout:
10025
- time_units_factors = {"s": 1, "m": 60, "h": 3600, "d": 3600 * 24}
10026
- if isinstance(timeout, str) and timeout[-1] in time_units_factors:
10027
- input_json["timeoutSeconds"] = int(float(timeout[:-1]) * time_units_factors[timeout[-1]])
10028
- else:
10029
- input_json["timeoutSeconds"] = int(timeout)
10030
- # input is either from docker hub or from HF spaces
10031
- for prefix in (
10032
- "https://huggingface.co/spaces/",
10033
- "https://hf.co/spaces/",
10034
- "huggingface.co/spaces/",
10035
- "hf.co/spaces/",
10036
- ):
10037
- if image.startswith(prefix):
10038
- input_json["spaceId"] = image[len(prefix) :]
10039
- break
10040
- else:
10041
- input_json["dockerImage"] = image
10042
10031
  if namespace is None:
10043
10032
  namespace = self.whoami(token=token)["name"]
10033
+ job_spec = _create_job_spec(
10034
+ image=image,
10035
+ command=command,
10036
+ env=env,
10037
+ secrets=secrets,
10038
+ flavor=flavor,
10039
+ timeout=timeout,
10040
+ )
10044
10041
  response = get_session().post(
10045
10042
  f"https://huggingface.co/api/jobs/{namespace}",
10046
- json=input_json,
10043
+ json=job_spec,
10047
10044
  headers=self._build_hf_headers(token=token),
10048
10045
  )
10049
10046
  hf_raise_for_status(response)
@@ -10076,8 +10073,8 @@ class HfApi:
10076
10073
 
10077
10074
  ```python
10078
10075
  >>> from huggingface_hub import fetch_job_logs, run_job
10079
- >>> job = run_job("python:3.12", ["python", "-c" ,"print('Hello from HF compute!')"])
10080
- >>> for log in fetch_job_logs(job.job_id):
10076
+ >>> job = run_job(image="python:3.12", command=["python", "-c" ,"print('Hello from HF compute!')"])
10077
+ >>> for log in fetch_job_logs(job.id):
10081
10078
  ... print(log)
10082
10079
  Hello from HF compute!
10083
10080
  ```
@@ -10200,8 +10197,8 @@ class HfApi:
10200
10197
 
10201
10198
  ```python
10202
10199
  >>> from huggingface_hub import inspect_job, run_job
10203
- >>> job = run_job("python:3.12", ["python", "-c" ,"print('Hello from HF compute!')"])
10204
- >>> inspect_job(job.job_id)
10200
+ >>> job = run_job(image="python:3.12", command=["python", "-c" ,"print('Hello from HF compute!')"])
10201
+ >>> inspect_job(job.id)
10205
10202
  JobInfo(
10206
10203
  id='68780d00bbe36d38803f645f',
10207
10204
  created_at=datetime.datetime(2025, 7, 16, 20, 35, 12, 808000, tzinfo=datetime.timezone.utc),
@@ -10276,10 +10273,10 @@ class HfApi:
10276
10273
 
10277
10274
  Args:
10278
10275
  script (`str`):
10279
- Path or URL of the UV script.
10276
+ Path or URL of the UV script, or a command.
10280
10277
 
10281
10278
  script_args (`List[str]`, *optional*)
10282
- Arguments to pass to the script.
10279
+ Arguments to pass to the script or command.
10283
10280
 
10284
10281
  dependencies (`List[str]`, *optional*)
10285
10282
  Dependencies to use to run the UV script.
@@ -10287,7 +10284,7 @@ class HfApi:
10287
10284
  python (`str`, *optional*)
10288
10285
  Use a specific Python version. Default is 3.12.
10289
10286
 
10290
- image (`str`, *optional*, defaults to "ghcr.io/astral-sh/uv:python3.12-bookworm-slim"):
10287
+ image (`str`, *optional*, defaults to "ghcr.io/astral-sh/uv:python3.12-bookworm"):
10291
10288
  Use a custom Docker image with `uv` installed.
10292
10289
 
10293
10290
  env (`Dict[str, Any]`, *optional*):
@@ -10314,13 +10311,474 @@ class HfApi:
10314
10311
 
10315
10312
  Example:
10316
10313
 
10314
+ Run a script from a URL:
10315
+
10317
10316
  ```python
10318
10317
  >>> from huggingface_hub import run_uv_job
10319
10318
  >>> script = "https://raw.githubusercontent.com/huggingface/trl/refs/heads/main/trl/scripts/sft.py"
10320
- >>> run_uv_job(script, dependencies=["trl"], flavor="a10g-small")
10319
+ >>> script_args = ["--model_name_or_path", "Qwen/Qwen2-0.5B", "--dataset_name", "trl-lib/Capybara", "--push_to_hub"]
10320
+ >>> run_uv_job(script, script_args=script_args, dependencies=["trl"], flavor="a10g-small")
10321
+ ```
10322
+
10323
+ Run a local script:
10324
+
10325
+ ```python
10326
+ >>> from huggingface_hub import run_uv_job
10327
+ >>> script = "my_sft.py"
10328
+ >>> script_args = ["--model_name_or_path", "Qwen/Qwen2-0.5B", "--dataset_name", "trl-lib/Capybara", "--push_to_hub"]
10329
+ >>> run_uv_job(script, script_args=script_args, dependencies=["trl"], flavor="a10g-small")
10330
+ ```
10331
+
10332
+ Run a command:
10333
+
10334
+ ```python
10335
+ >>> from huggingface_hub import run_uv_job
10336
+ >>> script = "lighteval"
10337
+ >>> script_args= ["endpoint", "inference-providers", "model_name=openai/gpt-oss-20b,provider=auto", "lighteval|gsm8k|0|0"]
10338
+ >>> run_uv_job(script, script_args=script_args, dependencies=["lighteval"], flavor="a10g-small")
10339
+ ```
10340
+ """
10341
+ image = image or "ghcr.io/astral-sh/uv:python3.12-bookworm"
10342
+ env = env or {}
10343
+ secrets = secrets or {}
10344
+
10345
+ # Build command
10346
+ command, env, secrets = self._create_uv_command_env_and_secrets(
10347
+ script=script,
10348
+ script_args=script_args,
10349
+ dependencies=dependencies,
10350
+ python=python,
10351
+ env=env,
10352
+ secrets=secrets,
10353
+ namespace=namespace,
10354
+ token=token,
10355
+ _repo=_repo,
10356
+ )
10357
+ # Create RunCommand args
10358
+ return self.run_job(
10359
+ image=image,
10360
+ command=command,
10361
+ env=env,
10362
+ secrets=secrets,
10363
+ flavor=flavor,
10364
+ timeout=timeout,
10365
+ namespace=namespace,
10366
+ token=token,
10367
+ )
10368
+
10369
+ def create_scheduled_job(
10370
+ self,
10371
+ *,
10372
+ image: str,
10373
+ command: List[str],
10374
+ schedule: str,
10375
+ suspend: Optional[bool] = None,
10376
+ concurrency: Optional[bool] = None,
10377
+ env: Optional[Dict[str, Any]] = None,
10378
+ secrets: Optional[Dict[str, Any]] = None,
10379
+ flavor: Optional[SpaceHardware] = None,
10380
+ timeout: Optional[Union[int, float, str]] = None,
10381
+ namespace: Optional[str] = None,
10382
+ token: Union[bool, str, None] = None,
10383
+ ) -> ScheduledJobInfo:
10384
+ """
10385
+ Create scheduled compute Jobs on Hugging Face infrastructure.
10386
+
10387
+ Args:
10388
+ image (`str`):
10389
+ The Docker image to use.
10390
+ Examples: `"ubuntu"`, `"python:3.12"`, `"pytorch/pytorch:2.6.0-cuda12.4-cudnn9-devel"`.
10391
+ Example with an image from a Space: `"hf.co/spaces/lhoestq/duckdb"`.
10392
+
10393
+ command (`List[str]`):
10394
+ The command to run. Example: `["echo", "hello"]`.
10395
+
10396
+ schedule (`str`):
10397
+ One of "@annually", "@yearly", "@monthly", "@weekly", "@daily", "@hourly", or a
10398
+ CRON schedule expression (e.g., '0 9 * * 1' for 9 AM every Monday).
10399
+
10400
+ suspend (`bool`, *optional*):
10401
+ If True, the scheduled Job is suspended (paused). Defaults to False.
10402
+
10403
+ concurrency (`bool`, *optional*):
10404
+ If True, multiple instances of this Job can run concurrently. Defaults to False.
10405
+
10406
+ env (`Dict[str, Any]`, *optional*):
10407
+ Defines the environment variables for the Job.
10408
+
10409
+ secrets (`Dict[str, Any]`, *optional*):
10410
+ Defines the secret environment variables for the Job.
10411
+
10412
+ flavor (`str`, *optional*):
10413
+ Flavor for the hardware, as in Hugging Face Spaces. See [`SpaceHardware`] for possible values.
10414
+ Defaults to `"cpu-basic"`.
10415
+
10416
+ timeout (`Union[int, float, str]`, *optional*):
10417
+ Max duration for the Job: int/float with s (seconds, default), m (minutes), h (hours) or d (days).
10418
+ Example: `300` or `"5m"` for 5 minutes.
10419
+
10420
+ namespace (`str`, *optional*):
10421
+ The namespace where the Job will be created. Defaults to the current user's namespace.
10422
+
10423
+ token `(Union[bool, str, None]`, *optional*):
10424
+ A valid user access token. If not provided, the locally saved token will be used, which is the
10425
+ recommended authentication method. Set to `False` to disable authentication.
10426
+ Refer to: https://huggingface.co/docs/huggingface_hub/quick-start#authentication.
10427
+
10428
+ Example:
10429
+ Create your first scheduled Job:
10430
+
10431
+ ```python
10432
+ >>> from huggingface_hub import create_scheduled_job
10433
+ >>> create_scheduled_job(image="python:3.12", command=["python", "-c" ,"print('Hello from HF compute!')"], schedule="@hourly")
10434
+ ```
10435
+
10436
+ Use a CRON schedule expression:
10437
+
10438
+ ```python
10439
+ >>> from huggingface_hub import create_scheduled_job
10440
+ >>> create_scheduled_job(image="python:3.12", command=["python", "-c" ,"print('this runs every 5min')"], schedule="*/5 * * * *")
10441
+ ```
10442
+
10443
+ Create a scheduled GPU Job:
10444
+
10445
+ ```python
10446
+ >>> from huggingface_hub import create_scheduled_job
10447
+ >>> image = "pytorch/pytorch:2.6.0-cuda12.4-cudnn9-devel"
10448
+ >>> command = ["python", "-c", "import torch; print(f"This code ran with the following GPU: {torch.cuda.get_device_name()}")"]
10449
+ >>> create_scheduled_job(image, command, flavor="a10g-small", schedule="@hourly")
10450
+ ```
10451
+
10452
+ """
10453
+ if namespace is None:
10454
+ namespace = self.whoami(token=token)["name"]
10455
+
10456
+ # prepare payload to send to HF Jobs API
10457
+ job_spec = _create_job_spec(
10458
+ image=image,
10459
+ command=command,
10460
+ env=env,
10461
+ secrets=secrets,
10462
+ flavor=flavor,
10463
+ timeout=timeout,
10464
+ )
10465
+ input_json: Dict[str, Any] = {
10466
+ "jobSpec": job_spec,
10467
+ "schedule": schedule,
10468
+ }
10469
+ if concurrency is not None:
10470
+ input_json["concurrency"] = concurrency
10471
+ if suspend is not None:
10472
+ input_json["suspend"] = suspend
10473
+ response = get_session().post(
10474
+ f"https://huggingface.co/api/scheduled-jobs/{namespace}",
10475
+ json=input_json,
10476
+ headers=self._build_hf_headers(token=token),
10477
+ )
10478
+ hf_raise_for_status(response)
10479
+ scheduled_job_info = response.json()
10480
+ return ScheduledJobInfo(**scheduled_job_info)
10481
+
10482
+ def list_scheduled_jobs(
10483
+ self,
10484
+ *,
10485
+ timeout: Optional[int] = None,
10486
+ namespace: Optional[str] = None,
10487
+ token: Union[bool, str, None] = None,
10488
+ ) -> List[ScheduledJobInfo]:
10489
+ """
10490
+ List scheduled compute Jobs on Hugging Face infrastructure.
10491
+
10492
+ Args:
10493
+ timeout (`float`, *optional*):
10494
+ Whether to set a timeout for the request to the Hub.
10495
+
10496
+ namespace (`str`, *optional*):
10497
+ The namespace from where it lists the jobs. Defaults to the current user's namespace.
10498
+
10499
+ token `(Union[bool, str, None]`, *optional*):
10500
+ A valid user access token. If not provided, the locally saved token will be used, which is the
10501
+ recommended authentication method. Set to `False` to disable authentication.
10502
+ Refer to: https://huggingface.co/docs/huggingface_hub/quick-start#authentication.
10503
+ """
10504
+ if namespace is None:
10505
+ namespace = self.whoami(token=token)["name"]
10506
+ response = get_session().get(
10507
+ f"{self.endpoint}/api/scheduled-jobs/{namespace}",
10508
+ headers=self._build_hf_headers(token=token),
10509
+ timeout=timeout,
10510
+ )
10511
+ hf_raise_for_status(response)
10512
+ return [ScheduledJobInfo(**scheduled_job_info) for scheduled_job_info in response.json()]
10513
+
10514
+ def inspect_scheduled_job(
10515
+ self,
10516
+ *,
10517
+ scheduled_job_id: str,
10518
+ namespace: Optional[str] = None,
10519
+ token: Union[bool, str, None] = None,
10520
+ ) -> ScheduledJobInfo:
10521
+ """
10522
+ Inspect a scheduled compute Job on Hugging Face infrastructure.
10523
+
10524
+ Args:
10525
+ scheduled_job_id (`str`):
10526
+ ID of the scheduled Job.
10527
+
10528
+ namespace (`str`, *optional*):
10529
+ The namespace where the scheduled Job is. Defaults to the current user's namespace.
10530
+
10531
+ token `(Union[bool, str, None]`, *optional*):
10532
+ A valid user access token. If not provided, the locally saved token will be used, which is the
10533
+ recommended authentication method. Set to `False` to disable authentication.
10534
+ Refer to: https://huggingface.co/docs/huggingface_hub/quick-start#authentication.
10535
+
10536
+ Example:
10537
+
10538
+ ```python
10539
+ >>> from huggingface_hub import inspect_job, create_scheduled_job
10540
+ >>> scheduled_job = create_scheduled_job(image="python:3.12", command=["python", "-c" ,"print('Hello from HF compute!')"], schedule="@hourly")
10541
+ >>> inspect_scheduled_job(scheduled_job.id)
10542
+ ```
10543
+ """
10544
+ if namespace is None:
10545
+ namespace = self.whoami(token=token)["name"]
10546
+ response = get_session().get(
10547
+ f"{self.endpoint}/api/scheduled-jobs/{namespace}/{scheduled_job_id}",
10548
+ headers=self._build_hf_headers(token=token),
10549
+ )
10550
+ hf_raise_for_status(response)
10551
+ return ScheduledJobInfo(**response.json())
10552
+
10553
+ def delete_scheduled_job(
10554
+ self,
10555
+ *,
10556
+ scheduled_job_id: str,
10557
+ namespace: Optional[str] = None,
10558
+ token: Union[bool, str, None] = None,
10559
+ ) -> None:
10560
+ """
10561
+ Delete a scheduled compute Job on Hugging Face infrastructure.
10562
+
10563
+ Args:
10564
+ scheduled_job_id (`str`):
10565
+ ID of the scheduled Job.
10566
+
10567
+ namespace (`str`, *optional*):
10568
+ The namespace where the scheduled Job is. Defaults to the current user's namespace.
10569
+
10570
+ token `(Union[bool, str, None]`, *optional*):
10571
+ A valid user access token. If not provided, the locally saved token will be used, which is the
10572
+ recommended authentication method. Set to `False` to disable authentication.
10573
+ Refer to: https://huggingface.co/docs/huggingface_hub/quick-start#authentication.
10574
+ """
10575
+ if namespace is None:
10576
+ namespace = self.whoami(token=token)["name"]
10577
+ response = get_session().delete(
10578
+ f"{self.endpoint}/api/scheduled-jobs/{namespace}/{scheduled_job_id}",
10579
+ headers=self._build_hf_headers(token=token),
10580
+ )
10581
+ hf_raise_for_status(response)
10582
+
10583
+ def suspend_scheduled_job(
10584
+ self,
10585
+ *,
10586
+ scheduled_job_id: str,
10587
+ namespace: Optional[str] = None,
10588
+ token: Union[bool, str, None] = None,
10589
+ ) -> None:
10590
+ """
10591
+ Suspend (pause) a scheduled compute Job on Hugging Face infrastructure.
10592
+
10593
+ Args:
10594
+ scheduled_job_id (`str`):
10595
+ ID of the scheduled Job.
10596
+
10597
+ namespace (`str`, *optional*):
10598
+ The namespace where the scheduled Job is. Defaults to the current user's namespace.
10599
+
10600
+ token `(Union[bool, str, None]`, *optional*):
10601
+ A valid user access token. If not provided, the locally saved token will be used, which is the
10602
+ recommended authentication method. Set to `False` to disable authentication.
10603
+ Refer to: https://huggingface.co/docs/huggingface_hub/quick-start#authentication.
10604
+ """
10605
+ if namespace is None:
10606
+ namespace = self.whoami(token=token)["name"]
10607
+ get_session().post(
10608
+ f"{self.endpoint}/api/scheduled-jobs/{namespace}/{scheduled_job_id}/suspend",
10609
+ headers=self._build_hf_headers(token=token),
10610
+ ).raise_for_status()
10611
+
10612
+ def resume_scheduled_job(
10613
+ self,
10614
+ *,
10615
+ scheduled_job_id: str,
10616
+ namespace: Optional[str] = None,
10617
+ token: Union[bool, str, None] = None,
10618
+ ) -> None:
10619
+ """
10620
+ Resume (unpause) a scheduled compute Job on Hugging Face infrastructure.
10621
+
10622
+ Args:
10623
+ scheduled_job_id (`str`):
10624
+ ID of the scheduled Job.
10625
+
10626
+ namespace (`str`, *optional*):
10627
+ The namespace where the scheduled Job is. Defaults to the current user's namespace.
10628
+
10629
+ token `(Union[bool, str, None]`, *optional*):
10630
+ A valid user access token. If not provided, the locally saved token will be used, which is the
10631
+ recommended authentication method. Set to `False` to disable authentication.
10632
+ Refer to: https://huggingface.co/docs/huggingface_hub/quick-start#authentication.
10633
+ """
10634
+ if namespace is None:
10635
+ namespace = self.whoami(token=token)["name"]
10636
+ get_session().post(
10637
+ f"{self.endpoint}/api/scheduled-jobs/{namespace}/{scheduled_job_id}/resume",
10638
+ headers=self._build_hf_headers(token=token),
10639
+ ).raise_for_status()
10640
+
10641
+ @experimental
10642
+ def create_scheduled_uv_job(
10643
+ self,
10644
+ script: str,
10645
+ *,
10646
+ script_args: Optional[List[str]] = None,
10647
+ schedule: str,
10648
+ suspend: Optional[bool] = None,
10649
+ concurrency: Optional[bool] = None,
10650
+ dependencies: Optional[List[str]] = None,
10651
+ python: Optional[str] = None,
10652
+ image: Optional[str] = None,
10653
+ env: Optional[Dict[str, Any]] = None,
10654
+ secrets: Optional[Dict[str, Any]] = None,
10655
+ flavor: Optional[SpaceHardware] = None,
10656
+ timeout: Optional[Union[int, float, str]] = None,
10657
+ namespace: Optional[str] = None,
10658
+ token: Union[bool, str, None] = None,
10659
+ _repo: Optional[str] = None,
10660
+ ) -> ScheduledJobInfo:
10661
+ """
10662
+ Run a UV script Job on Hugging Face infrastructure.
10663
+
10664
+ Args:
10665
+ script (`str`):
10666
+ Path or URL of the UV script, or a command.
10667
+
10668
+ script_args (`List[str]`, *optional*)
10669
+ Arguments to pass to the script, or a command.
10670
+
10671
+ schedule (`str`):
10672
+ One of "@annually", "@yearly", "@monthly", "@weekly", "@daily", "@hourly", or a
10673
+ CRON schedule expression (e.g., '0 9 * * 1' for 9 AM every Monday).
10674
+
10675
+ suspend (`bool`, *optional*):
10676
+ If True, the scheduled Job is suspended (paused). Defaults to False.
10677
+
10678
+ concurrency (`bool`, *optional*):
10679
+ If True, multiple instances of this Job can run concurrently. Defaults to False.
10680
+
10681
+ dependencies (`List[str]`, *optional*)
10682
+ Dependencies to use to run the UV script.
10683
+
10684
+ python (`str`, *optional*)
10685
+ Use a specific Python version. Default is 3.12.
10686
+
10687
+ image (`str`, *optional*, defaults to "ghcr.io/astral-sh/uv:python3.12-bookworm"):
10688
+ Use a custom Docker image with `uv` installed.
10689
+
10690
+ env (`Dict[str, Any]`, *optional*):
10691
+ Defines the environment variables for the Job.
10692
+
10693
+ secrets (`Dict[str, Any]`, *optional*):
10694
+ Defines the secret environment variables for the Job.
10695
+
10696
+ flavor (`str`, *optional*):
10697
+ Flavor for the hardware, as in Hugging Face Spaces. See [`SpaceHardware`] for possible values.
10698
+ Defaults to `"cpu-basic"`.
10699
+
10700
+ timeout (`Union[int, float, str]`, *optional*):
10701
+ Max duration for the Job: int/float with s (seconds, default), m (minutes), h (hours) or d (days).
10702
+ Example: `300` or `"5m"` for 5 minutes.
10703
+
10704
+ namespace (`str`, *optional*):
10705
+ The namespace where the Job will be created. Defaults to the current user's namespace.
10706
+
10707
+ token `(Union[bool, str, None]`, *optional*):
10708
+ A valid user access token. If not provided, the locally saved token will be used, which is the
10709
+ recommended authentication method. Set to `False` to disable authentication.
10710
+ Refer to: https://huggingface.co/docs/huggingface_hub/quick-start#authentication.
10711
+
10712
+ Example:
10713
+
10714
+ Schedule a script from a URL:
10715
+
10716
+ ```python
10717
+ >>> from huggingface_hub import create_scheduled_uv_job
10718
+ >>> script = "https://raw.githubusercontent.com/huggingface/trl/refs/heads/main/trl/scripts/sft.py"
10719
+ >>> script_args = ["--model_name_or_path", "Qwen/Qwen2-0.5B", "--dataset_name", "trl-lib/Capybara", "--push_to_hub"]
10720
+ >>> create_scheduled_uv_job(script, script_args=script_args, dependencies=["trl"], flavor="a10g-small", schedule="@weekly")
10721
+ ```
10722
+
10723
+ Schedule a local script:
10724
+
10725
+ ```python
10726
+ >>> from huggingface_hub import create_scheduled_uv_job
10727
+ >>> script = "my_sft.py"
10728
+ >>> script_args = ["--model_name_or_path", "Qwen/Qwen2-0.5B", "--dataset_name", "trl-lib/Capybara", "--push_to_hub"]
10729
+ >>> create_scheduled_uv_job(script, script_args=script_args, dependencies=["trl"], flavor="a10g-small", schedule="@weekly")
10730
+ ```
10731
+
10732
+ Schedule a command:
10733
+
10734
+ ```python
10735
+ >>> from huggingface_hub import create_scheduled_uv_job
10736
+ >>> script = "lighteval"
10737
+ >>> script_args= ["endpoint", "inference-providers", "model_name=openai/gpt-oss-20b,provider=auto", "lighteval|gsm8k|0|0"]
10738
+ >>> create_scheduled_uv_job(script, script_args=script_args, dependencies=["lighteval"], flavor="a10g-small", schedule="@weekly")
10321
10739
  ```
10322
10740
  """
10323
- image = image or "ghcr.io/astral-sh/uv:python3.12-bookworm-slim"
10741
+ image = image or "ghcr.io/astral-sh/uv:python3.12-bookworm"
10742
+ # Build command
10743
+ command, env, secrets = self._create_uv_command_env_and_secrets(
10744
+ script=script,
10745
+ script_args=script_args,
10746
+ dependencies=dependencies,
10747
+ python=python,
10748
+ env=env,
10749
+ secrets=secrets,
10750
+ namespace=namespace,
10751
+ token=token,
10752
+ _repo=_repo,
10753
+ )
10754
+ # Create RunCommand args
10755
+ return self.create_scheduled_job(
10756
+ image=image,
10757
+ command=command,
10758
+ schedule=schedule,
10759
+ suspend=suspend,
10760
+ concurrency=concurrency,
10761
+ env=env,
10762
+ secrets=secrets,
10763
+ flavor=flavor,
10764
+ timeout=timeout,
10765
+ namespace=namespace,
10766
+ token=token,
10767
+ )
10768
+
10769
+ def _create_uv_command_env_and_secrets(
10770
+ self,
10771
+ *,
10772
+ script: str,
10773
+ script_args: Optional[List[str]],
10774
+ dependencies: Optional[List[str]],
10775
+ python: Optional[str],
10776
+ env: Optional[Dict[str, Any]],
10777
+ secrets: Optional[Dict[str, Any]],
10778
+ namespace: Optional[str],
10779
+ token: Union[bool, str, None],
10780
+ _repo: Optional[str],
10781
+ ) -> Tuple[List[str], Dict[str, Any], Dict[str, Any]]:
10324
10782
  env = env or {}
10325
10783
  secrets = secrets or {}
10326
10784
 
@@ -10336,8 +10794,9 @@ class HfApi:
10336
10794
  if namespace is None:
10337
10795
  namespace = self.whoami(token=token)["name"]
10338
10796
 
10339
- if script.startswith("http://") or script.startswith("https://"):
10340
- # Direct URL execution - no upload needed
10797
+ is_url = script.startswith("http://") or script.startswith("https://")
10798
+ if is_url or not Path(script).is_file():
10799
+ # Direct URL execution or command - no upload needed
10341
10800
  command = ["uv", "run"] + uv_args + [script] + script_args
10342
10801
  else:
10343
10802
  # Local file - upload to HF
@@ -10348,7 +10807,6 @@ class HfApi:
10348
10807
  repo_id = _repo
10349
10808
  if "/" not in repo_id:
10350
10809
  repo_id = f"{namespace}/{repo_id}"
10351
- repo_id = _repo
10352
10810
  else:
10353
10811
  repo_id = f"{namespace}/hf-cli-jobs-uv-run-scripts"
10354
10812
 
@@ -10365,15 +10823,15 @@ class HfApi:
10365
10823
  with open(script_path, "r") as f:
10366
10824
  script_content = f.read()
10367
10825
 
10368
- self.upload_file(
10826
+ commit_hash = self.upload_file(
10369
10827
  path_or_fileobj=script_content.encode(),
10370
10828
  path_in_repo=filename,
10371
10829
  repo_id=repo_id,
10372
10830
  repo_type="dataset",
10373
- )
10831
+ ).oid
10374
10832
 
10375
- script_url = f"https://huggingface.co/datasets/{repo_id}/resolve/main/{filename}"
10376
- repo_url = f"https://huggingface.co/datasets/{repo_id}"
10833
+ script_url = f"{self.endpoint}/datasets/{repo_id}/resolve/{commit_hash}/{filename}"
10834
+ repo_url = f"{self.endpoint}/datasets/{repo_id}"
10377
10835
 
10378
10836
  logger.debug(f"✓ Script uploaded to: {repo_url}/blob/main/{filename}")
10379
10837
 
@@ -10430,18 +10888,7 @@ class HfApi:
10430
10888
  pre_command = ["python", "-c", '"' + "; ".join(pre_command) + '"']
10431
10889
  command = ["uv", "run"] + uv_args + ["/tmp/script.py"] + script_args
10432
10890
  command = ["bash", "-c", " ".join(pre_command) + " && " + " ".join(command)]
10433
-
10434
- # Create RunCommand args
10435
- return self.run_job(
10436
- image=image,
10437
- command=command,
10438
- env=env,
10439
- secrets=secrets,
10440
- flavor=flavor,
10441
- timeout=timeout,
10442
- namespace=namespace,
10443
- token=token,
10444
- )
10891
+ return command, env, secrets
10445
10892
 
10446
10893
 
10447
10894
  def _parse_revision_from_pr_url(pr_url: str) -> str:
@@ -10607,3 +11054,10 @@ list_jobs = api.list_jobs
10607
11054
  inspect_job = api.inspect_job
10608
11055
  cancel_job = api.cancel_job
10609
11056
  run_uv_job = api.run_uv_job
11057
+ create_scheduled_job = api.create_scheduled_job
11058
+ list_scheduled_jobs = api.list_scheduled_jobs
11059
+ inspect_scheduled_job = api.inspect_scheduled_job
11060
+ delete_scheduled_job = api.delete_scheduled_job
11061
+ suspend_scheduled_job = api.suspend_scheduled_job
11062
+ resume_scheduled_job = api.resume_scheduled_job
11063
+ create_scheduled_uv_job = api.create_scheduled_uv_job