# 管理您的 Space

在本指南中，我们将了解如何使用 `huggingface_hub` 管理您的 Space 运行时（[secrets](https://huggingface.co/docs/hub/spaces-overview#managing-secrets)、[hardware](https://huggingface.co/docs/hub/spaces-gpus) 和卷）。

## 搜索 Spaces

您可以使用 [search_spaces()](/docs/huggingface_hub/v2.4.0/zh/package_reference/hf_api#huggingface_hub.HfApi.search_spaces) 在 Hub 上通过语义搜索查找 Spaces。对于多词查询，它使用基于嵌入的搜索；对于单词查询，则使用全文搜索。

```py
>>> from huggingface_hub import search_spaces
>>> results = list(search_spaces("generate image"))
>>> results[0]
SpaceSearchResult(id='mrfakename/Z-Image-Turbo', title='Z Image Turbo', sdk='gradio', likes=2867, ...)
```

您可以按 SDK 或标签筛选结果：

```py
>>> results = search_spaces("chatbot", sdk="gradio", filter="mcp-server")
```

或通过 CLI：

```bash
>>> hf spaces search "generate image"
>>> hf spaces search "chatbot" --sdk gradio --limit 5
```

## 一个简单的示例：配置密钥和硬件。

下面是一个端到端的示例，用于在 Hub 上创建并配置一个 Space。

### 在 Hub 上创建一个 Space

```py
>>> from huggingface_hub import HfApi
>>> repo_id = "Wauplin/my-cool-training-space"
>>> api = HfApi()

# For example with a Gradio SDK
>>> api.create_repo(repo_id=repo_id, repo_type="space", space_sdk="gradio")
```

> [!TIP]
> 静态 Spaces 对所有人免费。Gradio 和 Docker Spaces 在计算资源上运行：在免费的 `cpu-basic` 硬件上，它们需要订阅（用户为 PRO，组织为 Team 或 Enterprise），而付费硬件仅需账单（一种支付方式和预付费额度）。如果没有订阅，在 `cpu-basic` 上创建、复制或移动 Gradio 或 Docker Space 会因支付错误（HTTP 402）而失败。有关更多详细信息，请参阅 [定价](https://huggingface.co/pricing)。

### 从模板创建 Space

您可以从 Hub 上提供的官方模板（例如 JupyterLab、Gradio 聊天机器人、Streamlit 应用等）初始化一个新的 Space，而无需从空 Space 开始。使用 [list_space_templates()](/docs/huggingface_hub/v2.4.0/zh/package_reference/hf_api#huggingface_hub.HfApi.list_space_templates) 列出可用模板，然后将模板的 `repo_id`（或其简短的 `name`）作为 `space_template` 传递给 [create_repo()](/docs/huggingface_hub/v2.4.0/zh/package_reference/hf_api#huggingface_hub.HfApi.create_repo)。请注意，`space_sdk` 仍然是必需的：模板用于初始化文件，而 SDK 用于设置卡片元数据。

```py
>>> from huggingface_hub import HfApi
>>> api = HfApi()

# List available templates
>>> for template in api.list_space_templates():
...     print(f"{template.name} ({template.repo_id})")
Streamlit (streamlit/streamlit-template-space)
JupyterLab (SpacesExamples/jupyterlab)
chatbot (gradio-templates/chatbot)
...

# Create a Space from a template, by repo id or by short name
>>> api.create_repo(repo_id=repo_id, repo_type="space", space_template="SpacesExamples/jupyterlab")
>>> api.create_repo(repo_id=repo_id, repo_type="space", space_template="JupyterLab")
```

某些模板建议设置为私有（例如 JupyterLab）。如果您未显式设置可见性，此类 Spaces 将自动创建为私有。

在 CLI 中，也可以通过 `hf spaces templates` 和 `hf repos create --template` 使用相同的功能：

```bash
# List available templates
>>> hf spaces templates
NAME            REPO_ID                                        SDK    PREFERRED_PRIVATE
--------------- ---------------------------------------------- ------ -----------------
Streamlit       streamlit/streamlit-template-space             docker
JupyterLab      SpacesExamples/jupyterlab                      docker ✔
Argilla         argilla/argilla-template-space                 docker
Livebook        livebook-dev/livebook                          docker
...

# Create a Space from a template
>>> hf repos create my-jupyterlab --type space --template jupyterlab
```

### 复制一个 Space

如果您希望基于现有的 Space 进行构建，而不是从头开始，这可能会很有用。如果您希望控制公共 Space 的配置/设置，这也很有用。有关更多详细信息，请参阅 [duplicate_repo()](/docs/huggingface_hub/v2.4.0/zh/package_reference/hf_api#huggingface_hub.HfApi.duplicate_repo)。

```py
>>> api.duplicate_repo("multimodalart/dreambooth-training", repo_type="space")
```

### 使用您首选的方案上传您的代码

以下是一个示例，用于将您机器上的本地文件夹 `src/` 上传到您的 Space：

```py
>>> api.upload_folder(repo_id=repo_id, repo_type="space", folder_path="src/")
```

在此步骤中，您的应用应该已经在 Hub 上运行了！不过，您可能希望使用密钥和升级的硬件对其进行进一步配置。

### 配置密钥和变量

您的 Space 可能需要一些密钥、令牌或变量才能正常运行。有关更多详细信息，请参阅 [文档](https://huggingface.co/docs/hub/spaces-overview#managing-secrets)。例如，一个 HF 令牌，用于在您的 Space 生成图像数据集后将其上传到 Hub。

```py
>>> api.add_space_secret(repo_id=repo_id, key="HF_TOKEN", value="hf_api_***")
>>> api.add_space_variable(repo_id=repo_id, key="MODEL_REPO_ID", value="user/repo")
```

您可以列出已有的密钥和变量。密钥的值是只写的，因此仅返回键、描述和更新时间戳：

```py
>>> api.get_space_secrets(repo_id=repo_id)
{'HF_TOKEN': SpaceSecret(key='HF_TOKEN', description=None, updated_at=datetime.datetime(...))}
>>> api.get_space_variables(repo_id=repo_id)
{'MODEL_REPO_ID': SpaceVariable(key='MODEL_REPO_ID', value='user/repo', description=None, updated_at=...)}
```

Secrets 和变量也可以被删除：
```py
>>> api.delete_space_secret(repo_id=repo_id, key="HF_TOKEN")
>>> api.delete_space_variable(repo_id=repo_id, key="MODEL_REPO_ID")
```

> [!TIP]
> 在您的 Space 内部，密钥可作为环境变量使用（如果使用 Streamlit，则通过 Streamlit Secrets Management 管理）。无需通过 API 获取它们！

> [!WARNING]
> 对您的 Space 配置（密钥或硬件）的任何更改都会触发您的应用重启。

**额外提示：在创建或复制 Space 时设置密钥和变量！**

在创建或复制空间时，可以设置密钥和变量：

```py
>>> api.create_repo(
...     repo_id=repo_id,
...     repo_type="space",
...     space_sdk="gradio",
...     space_secrets=[{"key": "HF_TOKEN", "value": "hf_api_***"}, ...],
...     space_variables=[{"key": "MODEL_REPO_ID", "value": "user/repo"}, ...],
... )
```

```py
>>> api.duplicate_repo(
...     from_id=repo_id,
...     repo_type="space",
...     space_secrets=[{"key": "HF_TOKEN", "value": "hf_api_***"}, ...],
...     space_variables=[{"key": "MODEL_REPO_ID", "value": "user/repo"}, ...],
... )
```

### 配置硬件

默认情况下，您的 Space 运行在免费的 `cpu-basic` 硬件上（Gradio 和 Docker Spaces 需要订阅，详见上文）。您可以升级硬件以在 GPU 上运行。升级 Space 需要支付方式以及预付费额度（或社区资助）。有关更多详细信息，请参阅 [文档](https://huggingface.co/docs/hub/spaces-gpus)。

```py
# Use `SpaceHardware` enum
>>> from huggingface_hub import SpaceHardware
>>> api.request_space_hardware(repo_id=repo_id, hardware=SpaceHardware.T4_MEDIUM)

# Or simply pass a string value
>>> api.request_space_hardware(repo_id=repo_id, hardware="t4-medium")
```

硬件更新不会立即生效，因为您的 Space 需要在我们的服务器上重新加载。您可以随时查看您的 Space 正在运行的硬件，以确认您的请求是否已得到满足。

```py
>>> runtime = api.get_space_runtime(repo_id=repo_id)
>>> runtime.stage
"RUNNING_BUILDING"
>>> runtime.hardware
"cpu-basic"
>>> runtime.requested_hardware
"t4-medium"
```

您现在已经拥有一个完全配置好的 Space。使用完毕后，请务必将您的 Space 降级回 `cpu-basic`。

**额外提示：在创建或复制 Space 时请求硬件！**

硬件升级后，您的 Space 构建完成时会自动分配。

```py
>>> api.create_repo(
...     repo_id=repo_id,
...     repo_type="space",
...     space_sdk="gradio",
...     space_hardware="cpu-upgrade",
...     space_sleep_time="7200", # 2 hours in secs
... )
```
```py
>>> api.duplicate_repo(
...     from_id=repo_id,
...     repo_type="space",
...     space_hardware="cpu-upgrade",
...     space_sleep_time="7200", # 2 hours in secs
... )
```

### 跟踪您的 ZeroGPU 配额

[ZeroGPU](https://huggingface.co/docs/hub/spaces-zerogpu) Spaces 运行在按需分配的共享 GPU 上。每次您使用 ZeroGPU Space（通过 UI 或其 API）时，都会从您的个人配额中扣除一定的 GPU 时间，该配额会定期重置。如果您正在基于 ZeroGPU Spaces 构建应用、智能体或 MCP 服务器，可以使用 [get_zero_gpu_quota()](/docs/huggingface_hub/v2.4.0/zh/package_reference/hf_api#huggingface_hub.HfApi.get_zero_gpu_quota) 来跟踪您的使用情况。数值以 GPU 秒为单位表示。

```py
>>> quota = api.get_zero_gpu_quota()
>>> quota
ZeroGpuQuota(base=2400, remaining=1810, resets_at=datetime.datetime(2026, 9, 30, 9, 12, 3, tzinfo=datetime.timezone.utc), overquota_used=0)
>>> if quota.remaining < 60:
...     print(f"Less than 1 minute of ZeroGPU left. Quota resets at {quota.resets_at}.")
```

`resets_at` 在自上次重置以来未使用过 ZeroGPU 时为 `None`。如果使用细粒度令牌，该令牌必须具有“Billing > Read billing usage and payment method status”权限。相同的信息也可以通过 CLI 命令 `hf spaces zero-gpu quota` 获取。

配额用尽后，PRO、Team 和 Enterprise 用户可以通过消耗预付费额度继续使用 ZeroGPU Spaces。您可以从[账单设置](https://huggingface.co/settings/billing?add-credits=true)中购买额度。有关更多详细信息，请参阅[使用额度扩展配额](https://huggingface.co/docs/hub/spaces-zerogpu#extending-quota-with-credits)。

### 暂停并重启您的 Space

默认情况下，如果您的 Space 运行在升级后的硬件上，它将永远不会停止。但为了避免产生费用，您可能希望在未使用时将其暂停。这可以通过 [pause_space()](/docs/huggingface_hub/v2.4.0/zh/package_reference/hf_api#huggingface_hub.HfApi.pause_space) 实现。已暂停的 Space 将处于非活动状态，直到 Space 的所有者通过 UI 或使用 [restart_space()](/docs/huggingface_hub/v2.4.0/zh/package_reference/hf_api#huggingface_hub.HfApi.restart_space) 通过 API 将其重新启动。有关暂停模式的更多详细信息，请参阅 [此部分](https://huggingface.co/docs/hub/spaces-gpus#pause)

```py
# Pause your Space to avoid getting billed
>>> api.pause_space(repo_id=repo_id)
# (...)
# Restart it when you need it
>>> api.restart_space(repo_id=repo_id)
```

另一种方法是为您的 Space 设置超时时间。如果您的 Space 在超过超时时长后处于非活动状态，它将进入休眠。任何访问您 Space 的用户都会将其重新唤醒。您可以使用 [set_space_sleep_time()](/docs/huggingface_hub/v2.4.0/zh/package_reference/hf_api#huggingface_hub.HfApi.set_space_sleep_time) 来设置超时时间。有关休眠模式的更多详细信息，请参阅 [此部分](https://huggingface.co/docs/hub/spaces-gpus#sleep-time)。

```py
# Put your Space to sleep after 1h of inactivity
>>> api.set_space_sleep_time(repo_id=repo_id, sleep_time=3600)
```

注意：如果您使用的是 'cpu-basic' 硬件，则无法配置自定义休眠时间。您的 Space 将在 48 小时无活动后自动暂停。

**附加功能：在请求硬件时设置睡眠时间**

硬件升级后，您的 Space 构建完成时会自动分配。

```py
>>> api.request_space_hardware(repo_id=repo_id, hardware=SpaceHardware.T4_MEDIUM, sleep_time=3600)
```

**额外提示：在创建或复制 Space 时设置休眠时间！**

```py
>>> api.create_repo(
...     repo_id=repo_id,
...     repo_type="space",
...     space_sdk="gradio",
...     space_hardware="t4-medium",
...     space_sleep_time="3600",
... )
```
```py
>>> api.duplicate_repo(
...     from_id=repo_id,
...     repo_type="space",
...     space_hardware="t4-medium",
...     space_sleep_time="3600",
... )
```

### 通过读取日志来调试失败的 Space

当 Space 构建失败或在运行时崩溃时，您通常在浏览器中查看的日志也可以通过 [fetch_space_logs()](/docs/huggingface_hub/v2.4.0/zh/package_reference/hf_api#huggingface_hub.HfApi.fetch_space_logs) 以编程方式获取。这在无法打开浏览器的脚本或智能体工作流中尤为有用。

```py
# Drain the currently available run logs and return immediately (like `docker logs`)
>>> for line in api.fetch_space_logs(repo_id=repo_id):
...     print(line, end="")

# Read the container build logs instead (useful when the Space is stuck in BUILD_ERROR)
>>> for line in api.fetch_space_logs(repo_id=repo_id, build=True):
...     print(line, end="")

# Stream run logs in real time until the server closes the stream (Ctrl-C to stop)
>>> for line in api.fetch_space_logs(repo_id=repo_id, follow=True):
...     print(line, end="")
```

相同的功能也可以通过命令行界面使用：

```bash
hf spaces logs username/my-space             # drain run logs
hf spaces logs username/my-space --build     # read build logs
hf spaces logs username/my-space -f          # stream in real time
hf spaces logs username/my-space -n 50       # last 50 lines only
```

### 通过 SSH 连接到 Space（开发模式）

[开发模式](https://huggingface.co/docs/hub/spaces-dev-mode)允许你通过 SSH 连接到正在运行的 Space 容器，以便进行实时调试和开发。使用 `hf spaces ssh` 可以直接从终端打开一个会话。如果该 Space 尚未启用开发模式，CLI 会提示你启用它（或者传入 `--auto` 以跳过提示）。

您的 SSH 公钥必须在[您的设置](https://huggingface.co/settings/keys)中注册。

```bash
# SSH into a Space (enables Dev Mode if needed)
hf spaces ssh username/my-space

# Auto-enable Dev Mode without prompting
hf spaces ssh username/my-space --auto

# Print the SSH command without running it
hf spaces ssh username/my-space --dry-run

# Use a specific SSH key
hf spaces ssh username/my-space -i ~/.ssh/id_ed25519
```

您也可以在不使用 SSH 的情况下，通过 `hf spaces dev-mode` 启用开发模式，该命令会打印出用于 SSH、VS Code、Cursor 和 Windsurf 的连接说明：

```bash
hf spaces dev-mode username/my-space
```

启用 Dev Mode 需要 PRO 或 Team & Enterprise 套餐。禁用 Dev Mode 始终被允许，例如，如果在 Dev Mode 开启期间您的订阅已过期：

```bash
hf spaces dev-mode username/my-space --stop
```

### 在您的 Space 中挂载卷

您可以将 Hub 资源（模型、数据集或存储桶）作为卷挂载到 Space 的容器中。这样，您的 Space 就可以直接通过文件系统访问这些资源，而无需在代码中下载它们。在创建或复制 Space 时，可以直接设置卷：

```py
>>> from huggingface_hub import Volume
>>> api.create_repo(
...     repo_id=repo_id,
...     repo_type="space",
...     space_sdk="gradio",
...     space_volumes=[
...         Volume(type="model", source="username/my-model", mount_path="/models", read_only=True),
...         Volume(type="bucket", source="username/my-bucket", mount_path="/data"),
...     ],
... )
```
```py
>>> api.duplicate_repo(
...     from_id=repo_id,
...     repo_type="space",
...     space_volumes=[
...         Volume(type="model", source="username/my-model", mount_path="/models", read_only=True),
...         Volume(type="bucket", source="username/my-bucket", mount_path="/data"),
...     ],
... )
```

您可以通过 Space 运行时检查当前挂载了哪些卷：

```py
>>> runtime = api.get_space_runtime(repo_id=repo_id)
>>> runtime.volumes
[Volume(type='model', source='username/my-model', mount_path='/models', read_only=True), ...]
```

如果需要更新现有 Space 上的卷，请使用 [set_space_volumes()](/docs/huggingface_hub/v2.4.0/zh/package_reference/hf_api#huggingface_hub.HfApi.set_space_volumes)。请注意，此操作会替换所有先前挂载的卷。

```py
>>> api.set_space_volumes(
...     repo_id=repo_id,
...     volumes=[
...         Volume(type="model", source="username/my-model", mount_path="/models", read_only=True),
...         Volume(type="dataset", source="username/my-dataset", mount_path="/data", read_only=True),
...         Volume(type="bucket", source="username/my-bucket", mount_path="/output"),
...     ],
... )
```

要从您的 Space 中移除所有卷：

```py
>>> api.delete_space_volumes(repo_id=repo_id)
```

> [!NOTE]
> 模型、数据集和 Spaces 始终以只读方式挂载。只有存储桶支持读写挂载。

> [!WARNING]
> 设置卷会替换之前挂载的所有卷。若要向现有列表中添加卷，请先从运行时读取当前卷，并将其包含在新列表中。

所有卷操作也可从命令行界面（CLI）中调用：

```bash
# List current volumes
hf spaces volumes ls username/my-space

# Set (replace) volumes
hf spaces volumes set username/my-space \
    -v hf://models/username/my-model:/models \
    -v hf://buckets/username/my-bucket:/data

# Remove all volumes
hf spaces volumes delete username/my-space
```

## 更高级的操作：临时升级你的 Space！

Spaces 支持多种不同的使用场景。有时，您可能希望临时在特定硬件上运行一个 Space，完成某些操作后再将其关闭。在本节中，我们将探讨如何利用 Spaces 按需微调模型。这只是解决该特定问题的一种方法，应将其视为一种建议，并根据您的具体使用场景进行调整。

假设我们有一个用于微调模型的 Space。它是一个 Gradio 应用，接收模型 ID 和数据集 ID 作为输入。工作流程如下：

0. （提示用户输入模型和数据集）
1. 从 Hub 加载模型。
2. 从 Hub 加载数据集。
3. 在数据集上微调模型。
4. 将新模型上传到 Hub。

步骤 3. 需要自定义硬件，但你又不希望你的 Space 一直在付费 GPU 上运行。一种解决方案是动态请求用于训练的硬件，并在训练结束后将其关闭。由于请求硬件会重启你的 Space，你的应用必须以某种方式“记住”当前正在执行的任务。实现这一点有多种方法。在本指南中，我们将展示一种使用 Dataset 作为“任务调度器”的解决方案。

### 应用骨架

以下是您的应用运行时的样子。启动时，检查是否有已调度的任务，如果有，则在正确的硬件上运行它。完成后，将硬件重置为空闲的 `cpu-basic` CPU，并提示用户输入新任务。

> [!WARNING]
> 此类工作流不支持像普通演示那样进行并发访问。特别是，在训练发生时，界面将被禁用。建议将您的仓库设置为私有，以确保您是唯一的使用者。

```py
# Space will need your token to request hardware: set it as a Secret !
HF_TOKEN = os.environ.get("HF_TOKEN")

# Space own repo_id
TRAINING_SPACE_ID = "Wauplin/dreambooth-training"

from huggingface_hub import HfApi, SpaceHardware
api = HfApi(token=HF_TOKEN)

# On Space startup, check if a task is scheduled. If yes, finetune the model. If not,
# display an interface to request a new task.
task = get_task()
if task is None:
    # Start Gradio app
    def gradio_fn(task):
        # On user request, add task and request hardware
        add_task(task)
        api.request_space_hardware(repo_id=TRAINING_SPACE_ID, hardware=SpaceHardware.T4_MEDIUM)

    gr.Interface(fn=gradio_fn, ...).launch()
else:
    runtime = api.get_space_runtime(repo_id=TRAINING_SPACE_ID)
    # Check if Space is loaded with a GPU.
    if runtime.hardware == SpaceHardware.T4_MEDIUM:
        # If yes, finetune base model on dataset !
        train_and_upload(task)

        # Then, mark the task as "DONE"
        mark_as_done(task)

        # DO NOT FORGET: set back CPU hardware
        api.request_space_hardware(repo_id=TRAINING_SPACE_ID, hardware=SpaceHardware.CPU_BASIC)
    else:
        api.request_space_hardware(repo_id=TRAINING_SPACE_ID, hardware=SpaceHardware.T4_MEDIUM)
```

### 任务调度器

任务调度可以通过多种方式实现。下面是一个示例，展示如何使用以 Dataset 形式存储的简单 CSV 文件来完成任务调度。

```py
# Dataset ID in which a `tasks.csv` file contains the tasks to perform.
# Here is a basic example for `tasks.csv` containing inputs (base model and dataset)
# and status (PENDING or DONE).
#     multimodalart/sd-fine-tunable,Wauplin/concept-1,DONE
#     multimodalart/sd-fine-tunable,Wauplin/concept-2,PENDING
TASK_DATASET_ID = "Wauplin/dreambooth-task-scheduler"

def _get_csv_file():
    return hf_hub_download(repo_id=TASK_DATASET_ID, filename="tasks.csv", repo_type="dataset", token=HF_TOKEN)

def get_task():
    with open(_get_csv_file()) as csv_file:
        csv_reader = csv.reader(csv_file, delimiter=',')
        for row in csv_reader:
            if row[2] == "PENDING":
                return row[0], row[1] # model_id, dataset_id

def add_task(task):
    model_id, dataset_id = task
    with open(_get_csv_file()) as csv_file:
        with open(csv_file, "r") as f:
            tasks = f.read()

    api.upload_file(
        repo_id=repo_id,
        repo_type=repo_type,
        path_in_repo="tasks.csv",
        # Quick and dirty way to add a task
        path_or_fileobj=(tasks + f"\n{model_id},{dataset_id},PENDING").encode()
    )

def mark_as_done(task):
    model_id, dataset_id = task
    with open(_get_csv_file()) as csv_file:
        with open(csv_file, "r") as f:
            tasks = f.read()

    api.upload_file(
        repo_id=repo_id,
        repo_type=repo_type,
        path_in_repo="tasks.csv",
        # Quick and dirty way to set the task as DONE
        path_or_fileobj=tasks.replace(
            f"{model_id},{dataset_id},PENDING",
            f"{model_id},{dataset_id},DONE"
        ).encode()
    )
```

