_space_api.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324
  1. # Copyright 2019-present, the HuggingFace Inc. team.
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License");
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain a copy of the License at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. # See the License for the specific language governing permissions and
  13. # limitations under the License.
  14. from dataclasses import dataclass
  15. from datetime import datetime
  16. from enum import Enum
  17. from typing import Literal
  18. from huggingface_hub.utils import parse_datetime
  19. class SpaceStage(str, Enum):
  20. """
  21. Enumeration of possible stage of a Space on the Hub.
  22. Value can be compared to a string:
  23. ```py
  24. assert SpaceStage.BUILDING == "BUILDING"
  25. ```
  26. Taken from https://github.com/huggingface/moon-landing/blob/main/server/repo_types/SpaceInfo.ts#L61 (private url).
  27. """
  28. # Copied from moon-landing > server > repo_types > SpaceInfo.ts (private repo)
  29. NO_APP_FILE = "NO_APP_FILE"
  30. CONFIG_ERROR = "CONFIG_ERROR"
  31. BUILDING = "BUILDING"
  32. BUILD_ERROR = "BUILD_ERROR"
  33. RUNNING = "RUNNING"
  34. RUNNING_BUILDING = "RUNNING_BUILDING"
  35. RUNTIME_ERROR = "RUNTIME_ERROR"
  36. DELETING = "DELETING"
  37. STOPPED = "STOPPED"
  38. PAUSED = "PAUSED"
  39. APP_STARTING = "APP_STARTING"
  40. RUNNING_APP_STARTING = "RUNNING_APP_STARTING"
  41. class SpaceHardware(str, Enum):
  42. """
  43. Enumeration of hardwares available to run your Space on the Hub.
  44. Value can be compared to a string:
  45. ```py
  46. assert SpaceHardware.CPU_BASIC == "cpu-basic"
  47. ```
  48. Taken from https://github.com/huggingface-internal/moon-landing/blob/main/server/repo_types/SpaceHardwareFlavor.ts (private url).
  49. """
  50. # CPU
  51. CPU_BASIC = "cpu-basic"
  52. CPU_UPGRADE = "cpu-upgrade"
  53. CPU_PERFORMANCE = "cpu-performance"
  54. CPU_XL = "cpu-xl"
  55. SPRX8 = "sprx8"
  56. # ZeroGPU
  57. ZERO_A10G = "zero-a10g"
  58. # GPU
  59. T4_SMALL = "t4-small"
  60. T4_MEDIUM = "t4-medium"
  61. L4X1 = "l4x1"
  62. L4X4 = "l4x4"
  63. L40SX1 = "l40sx1"
  64. L40SX4 = "l40sx4"
  65. L40SX8 = "l40sx8"
  66. A10G_SMALL = "a10g-small"
  67. A10G_LARGE = "a10g-large"
  68. A10G_LARGEX2 = "a10g-largex2"
  69. A10G_LARGEX4 = "a10g-largex4"
  70. A100_LARGE = "a100-large"
  71. A100X4 = "a100x4"
  72. A100X8 = "a100x8"
  73. H200 = "h200"
  74. H200X2 = "h200x2"
  75. H200X4 = "h200x4"
  76. H200X8 = "h200x8"
  77. # Neuron
  78. INF2X6 = "inf2x6"
  79. class SpaceStorage(str, Enum):
  80. """
  81. Enumeration of persistent storage available for your Space on the Hub.
  82. Value can be compared to a string:
  83. ```py
  84. assert SpaceStorage.SMALL == "small"
  85. ```
  86. Taken from https://github.com/huggingface/moon-landing/blob/main/server/repo_types/SpaceHardwareFlavor.ts#L24 (private url).
  87. """
  88. SMALL = "small"
  89. MEDIUM = "medium"
  90. LARGE = "large"
  91. @dataclass
  92. class Volume:
  93. """
  94. Describes a volume to mount in a Space or Job container.
  95. Args:
  96. type (`str`):
  97. Type of volume: `"bucket"`, `"model"`, `"dataset"`, or `"space"`.
  98. source (`str`):
  99. Source identifier, e.g. `"username/my-bucket"` or `"username/my-model"`.
  100. mount_path (`str`):
  101. Mount path inside the container, e.g. `"/data"`. Must start with `/`.
  102. revision (`str` or `None`):
  103. Git revision (only for repos, defaults to `"main"`).
  104. read_only (`bool` or `None`):
  105. Read-only mount. Forced `True` for repos, defaults to `False` for buckets.
  106. path (`str` or `None`):
  107. Subfolder prefix inside the bucket/repo to mount, e.g. `"path/to/dir"`.
  108. """
  109. type: Literal["bucket", "model", "dataset", "space"]
  110. source: str
  111. mount_path: str
  112. revision: str | None = None
  113. read_only: bool | None = None
  114. path: str | None = None
  115. def __init__(self, **kwargs) -> None:
  116. self.type = kwargs.get("type", "model")
  117. self.source = kwargs["source"]
  118. mount_path = kwargs.get("mountPath")
  119. self.mount_path = mount_path if mount_path is not None else kwargs["mount_path"]
  120. self.revision = kwargs.get("revision")
  121. read_only = kwargs.get("readOnly")
  122. self.read_only = read_only if read_only is not None else kwargs.get("read_only")
  123. self.path = kwargs.get("path")
  124. def to_dict(self) -> dict:
  125. """Serialize to the JSON payload expected by the Hub API."""
  126. data: dict = {
  127. "type": self.type,
  128. "source": self.source,
  129. "mountPath": self.mount_path,
  130. }
  131. if self.revision is not None:
  132. data["revision"] = self.revision
  133. if self.read_only is not None:
  134. data["readOnly"] = self.read_only
  135. if self.path is not None:
  136. data["path"] = self.path
  137. return data
  138. def to_hf_handle(self) -> str:
  139. """Return the volume as an HF handle in the format expected by the CLI."""
  140. path = f"/{self.path}" if self.path else ""
  141. revision = f"@{self.revision}" if self.revision else ""
  142. ro = {True: ":ro", False: ":rw", None: ""}.get(self.read_only, "")
  143. return f"hf://{self.type}s/{self.source}{revision}{path}:{self.mount_path}{ro}"
  144. @dataclass
  145. class SpaceHotReloading:
  146. status: Literal["created", "canceled"]
  147. replica_statuses: list[tuple[str, str]] # See _hot_reloading_types.ApiCreateReloadResponse.res.status
  148. raw: dict
  149. def __init__(self, data: dict) -> None:
  150. self.status = data["status"]
  151. self.replica_statuses = data["replicaStatuses"]
  152. self.raw = data
  153. @dataclass
  154. class SpaceRuntime:
  155. """
  156. Contains information about the current runtime of a Space.
  157. Args:
  158. stage (`str`):
  159. Current stage of the space. Example: RUNNING.
  160. hardware (`str` or `None`):
  161. Current hardware of the space. Example: "cpu-basic". Can be `None` if Space
  162. is `BUILDING` for the first time.
  163. requested_hardware (`str` or `None`):
  164. Requested hardware. Can be different from `hardware` especially if the request
  165. has just been made. Example: "t4-medium". Can be `None` if no hardware has
  166. been requested yet.
  167. sleep_time (`int` or `None`):
  168. Number of seconds the Space will be kept alive after the last request. By default (if value is `None`), the
  169. Space will never go to sleep if it's running on an upgraded hardware, while it will go to sleep after 48
  170. hours on a free 'cpu-basic' hardware. For more details, see https://huggingface.co/docs/hub/spaces-gpus#sleep-time.
  171. volumes (`list[Volume]` or `None`):
  172. List of volumes mounted in the Space. Each volume is a [`Volume`] object describing its type, source,
  173. mount path, and optional settings. `None` if no volumes are attached.
  174. raw (`dict`):
  175. Raw response from the server. Contains more information about the Space
  176. runtime like number of replicas, number of cpu, memory size,...
  177. """
  178. stage: SpaceStage
  179. hardware: SpaceHardware | None
  180. requested_hardware: SpaceHardware | None
  181. sleep_time: int | None
  182. storage: SpaceStorage | None
  183. hot_reloading: SpaceHotReloading | None
  184. volumes: list[Volume] | None
  185. raw: dict
  186. def __init__(self, data: dict) -> None:
  187. self.stage = data["stage"]
  188. self.hardware = data.get("hardware", {}).get("current")
  189. self.requested_hardware = data.get("hardware", {}).get("requested")
  190. self.sleep_time = data.get("gcTimeout")
  191. self.storage = data.get("storage")
  192. self.hot_reloading = SpaceHotReloading(raw_hr) if (raw_hr := data.get("hotReloading")) is not None else None
  193. raw_volumes = data.get("volumes")
  194. self.volumes = [Volume(**v) for v in raw_volumes] if raw_volumes is not None else None
  195. self.raw = data
  196. @dataclass
  197. class SpaceVariable:
  198. """
  199. Contains information about the current variables of a Space.
  200. Args:
  201. key (`str`):
  202. Variable key. Example: `"MODEL_REPO_ID"`
  203. value (`str`):
  204. Variable value. Example: `"the_model_repo_id"`.
  205. description (`str` or None):
  206. Description of the variable. Example: `"Model Repo ID of the implemented model"`.
  207. updatedAt (`datetime` or None):
  208. datetime of the last update of the variable (if the variable has been updated at least once).
  209. """
  210. key: str
  211. value: str
  212. description: str | None
  213. updated_at: datetime | None
  214. def __init__(self, key: str, values: dict) -> None:
  215. self.key = key
  216. self.value = values["value"]
  217. self.description = values.get("description")
  218. updated_at = values.get("updatedAt")
  219. self.updated_at = parse_datetime(updated_at) if updated_at is not None else None
  220. @dataclass
  221. class SpaceSearchResult:
  222. """A single result from the Spaces semantic search API.
  223. Returned by [`HfApi.search_spaces`].
  224. Attributes:
  225. id (`str`):
  226. ID of the Space (e.g. `"username/repo-name"`).
  227. author (`str`):
  228. Author of the Space.
  229. title (`str`):
  230. Display title of the Space.
  231. emoji (`str` or `None`):
  232. Emoji icon of the Space.
  233. sdk (`str` or `None`):
  234. SDK used by the Space (e.g. `"gradio"`, `"docker"`, `"static"`).
  235. likes (`int`):
  236. Number of likes.
  237. private (`bool`):
  238. Whether the Space is private.
  239. tags (`list[str]` or `None`):
  240. List of tags.
  241. runtime ([`SpaceRuntime`] or `None`):
  242. Runtime information (stage, hardware, etc.).
  243. ai_short_description (`str` or `None`):
  244. AI-generated short description.
  245. ai_category (`str` or `None`):
  246. AI-generated category (e.g. `"Image Generation"`).
  247. semantic_relevancy_score (`float` or `None`):
  248. Semantic relevancy score (0-1) relative to the search query.
  249. trending_score (`int` or `None`):
  250. Trending score.
  251. """
  252. id: str
  253. author: str
  254. title: str
  255. emoji: str | None
  256. sdk: str | None
  257. likes: int
  258. private: bool
  259. tags: list[str] | None
  260. runtime: SpaceRuntime | None
  261. ai_short_description: str | None
  262. ai_category: str | None
  263. semantic_relevancy_score: float | None
  264. trending_score: int | None
  265. def __init__(self, data: dict) -> None:
  266. runtime = data.get("runtime")
  267. self.id = data["id"]
  268. self.author = data.get("author", "")
  269. self.title = data.get("title", "")
  270. self.emoji = data.get("emoji")
  271. self.sdk = data.get("sdk")
  272. self.likes = data.get("likes", 0)
  273. self.private = data.get("private", False)
  274. self.tags = data.get("tags")
  275. self.runtime = SpaceRuntime(runtime) if runtime else None
  276. self.ai_short_description = data.get("ai_short_description")
  277. self.ai_category = data.get("ai_category")
  278. self.semantic_relevancy_score = data.get("semanticRelevancyScore")
  279. self.trending_score = data.get("trendingScore")