Gradio 发布 gradio.Server,支持任意自定义前端搭配 Gradio 后端
Any Custom Frontend with Gradio's Backend
Gradio 发布 gradio.Server,扩展 FastAPI,在原路由基础上叠加 Gradio 的 API 引擎,提供排队、SSE 流式、并发控制和 gradio_client 兼容。
Gradio 新 Server 模式把排队、并发与 ZeroGPU 能力开放给任意前端框架,读者可据此评估自建前端的后端选型。
几周前,我们写过用 gr.HTML 一次性构建完整 Web 应用:完全在 Gradio 内部,使用自定义 HTML、CSS 和 JavaScript 构建丰富、可交互的前端。这解锁了很多可能性。但如果这还不够呢?
如果你想完全使用自己的前端框架来构建,比如 React、Svelte,甚至纯 HTML/JS,同时仍然享受 Gradio 的队列系统、API 基础设施、MCP 支持以及 Spaces 上的 ZeroGPU 呢?
这正是 gradio.Server 要解决的问题。它改变了 Gradio 和 HF Mirror Spaces 的可能性。
我们想构建什么
Text Behind Image:一个编辑器,你上传一张照片,使用 ML 模型移除背景,然后将风格化文字放置在前景主体和背景之间。文字看起来像是位于图像中人物或物体的后面。
这需要:
- 一个拖放画布,支持分层渲染(背景 → 文字 → 前景)
- 一个丰富的控制面板,带有用于字体大小、粗细、字距、颜色、不透明度、描边、阴影、3D 挤出、透视变换等的滑块
- 一个后端 ML 端点,运行背景移除模型并返回透明 PNG
- 在客户端导出为 PNG
无法用 Gradio 组件来表达这个 UI。它是一个完整的 Web 应用。但我们仍然想要 Gradio 的后端能力:队列、并发管理、ZeroGPU 支持,以及托管在 HF Spaces 上而无需担心基础设施问题。
gradio.Server 登场
gradio.Server 扩展了 FastAPI。它为你提供 FastAPI 的全部能力(自定义路由、中间件、文件上传、任意响应类型),同时在其上添加 Gradio 的 API 引擎:队列、SSE 流式传输、并发控制,以及 gradio_client 兼容性。
这是 Text Behind Image 的整个后端:
import os
import torch
from PIL import Image
from torchvision import transforms
from transformers import AutoModelForImageSegmentation
from gradio import Server
from gradio.data_classes import FileData
from fastapi.responses import HTMLResponse
import spaces
torch.set_float32_matmul_precision("high")
birefnet = AutoModelForImageSegmentation.from_pretrained(
"ZhengPeng7/BiRefNet", trust_remote_code=True
)
birefnet.to("cuda")
birefnet.float()
transform_image = transforms.Compose([
transforms.Resize((1024, 1024)),
transforms.ToTensor(),
transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]),
])
app = Server()
@spaces.GPU
def segment(image: Image.Image) -> Image.Image:
"""Run BiRefNet segmentation to produce a transparency mask."""
image_size = image.size
input_images = transform_image(image).unsqueeze(0).to("cuda")
with torch.no_grad():
preds = birefnet(input_images)[-1].sigmoid().cpu()
pred = preds[0].squeeze()
mask = transforms.ToPILImage()(pred).resize(image_size)
image.putalpha(mask)
return image
@app.api(name="remove_background")
def remove_background(image_path: FileData) -> FileData:
"""Remove background from an image. Returns transparent PNG."""
im = Image.open(image_path["path"]).convert("RGB")
result = segment(im)
out_path = image_path["path"].rsplit(".", 1)[0] + ".png"
result.save(out_path)
return FileData(path=out_path)
@app.get("/", response_class=HTMLResponse)
async def homepage():
html_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "index.html")
with open(html_path, "r", encoding="utf-8") as f:
return f.read()
app.launch(show_error=True)
就这样。约 50 行 Python。模型在启动时加载,@spaces.GPU 处理 ZeroGPU 分配,gradio.Server 管理队列和并发。让我们分解一下发生了什么。
为什么用 @app.api() 而不是普通的 FastAPI 路由?
如果这是一个普通的 FastAPI 应用,你会为背景移除定义一个 @app.post() 路由。这能工作,直到两个用户同时访问它。没有并发管理,两个请求会争抢 GPU,应用会崩溃或返回垃圾数据。
@app.api() 解决了这个问题。它用 Gradio 的队列引擎包装你的函数:请求被串行化,并发受到控制,并且在 ZeroGPU Spaces 上,GPU 分配通过 @spaces.GPU 自动处理。额外好处是,任何 @app.api() 端点也可以通过 gradio_client 调用,因此其他应用或脚本可以以编程方式使用你的 Space:
from gradio_client import Client, handle_file
client = Client("ysharma/text-behind-image")
result = client.predict(
image_path=handle_file("photo.jpg"),
api_name="/remove_background"
)
同时,@app.get("/") 是一个标准的 FastAPI 路由,用于提供 HTML 页面。两者自然共存,因为 Server 就是一个 FastAPI 应用。
前端:纯 HTML/CSS/JS
这个示例中的 index.html 是一个自包含的约 1300 行 Web 应用。没有 React,没有构建步骤,没有打包器。只是 vanilla HTML,带有:
- 一个三层画布:背景图像 → 文字层 → 前景(透明 PNG),使用 CSS
z-index堆叠 - 使用指针事件进行拖放文字定位
- 一个控制面板,带有 20 多个参数:字体系列(25+ 种字体)、大小、粗细、间距、颜色、不透明度、背景填充、描边、阴影、3D 挤出深度和角度、旋转、倾斜,以及完整的 CSS 透视变换
- 使用
<canvas>合成进行客户端 PNG 导出
前端使用 Gradio JS Client 与后端通信:
import { Client, handle_file } from "https://cdn.jsdelivr.net/npm/@gradio/client/dist/index.min.js";
const client = await Client.connect(window.location.origin);
const result = await client.predict("/remove_background", {
image_path: handle_file(file),
});
foregroundLayer.src = result.data[0].url; // transparent PNG
这是关键部分:通过使用 Gradio JS 客户端而不是原始的 fetch() 调用,前端会经过 Gradio 的队列。这意味着并发得到管理,GPU 请求不会冲突,你甚至可以向用户显示队列位置或进度。其他所有事情,文本渲染、图层合成、导出,都在浏览器中进行。
这解锁了什么
在 gradio.Server 之前,以下是不可能做到的:
| 之前 | 之后 |
|---|---|
| 自定义 UI 意味着完全离开 Gradio | 自定义 UI 搭配 Gradio 的后端引擎 |
| 无法从 Gradio 应用提供静态 HTML | @app.get("/") 可以服务任何内容 |
gradio_client 只能与 Gradio 组件应用配合使用 |
@app.api() 端点与客户端兼容 |
| 在 Gradio 的基础设施和设计自由之间做选择 | 两者兼得 |
有了 gradio.Server,Gradio 兼作后端框架,需要时使用它的 UI 系统,不需要时带上你自己的前端。
如果你想要 Gradio 的 UI,可以使用 gr.Blocks、gr.Interface、gr.ChatInterface。如果你想要自己的 UI,使用 gradio.Server 并带上你喜欢的任何前端。无论哪种方式,你都能获得 Spaces 托管、API 队列、gradio_client 访问、完整的 HF 生态等等。
试一试
这个应用已在 Spaces 上线:ysharma/text-behind-image
上传任何有清晰主体的照片,然后开始在其后方叠加文字。试试 3D 挤出、透视倾斜和描边效果,它们组合起来效果很好。
下一步
这篇文章涵盖了核心思想:gradio.Server 让你可以将任何前端与 Gradio 的后端搭配。还有更多值得探索的内容,包括使用 @app.mcp.tool() 进行 MCP 工具注册、用于实时更新的 SSE 流式传输、批处理,以及构建具有共享状态的多页面应用的模式。
我们将在后续文章中深入探讨这些内容。敬请关注。
推荐阅读
- 文档:gradio.Server
- 指南:Server mode
来源:Hugging Face Blog · hf-mirror.com