【Gradio】Additional Features 额外功能
Queuing 队列每个 Gradio 应用都内置了一个队列系统,可以扩展到数千个并发用户。您可以使用queue()方法来配置队列,该方法由gr.Interface、gr.Blocks和gr.ChatInterface类支持。例如,您可以通过设置queue()的default_concurrency_limit参数来控制一次处理的请求数量,例如demo = gr.Inte...
Queuing 队列
每个 Gradio 应用都内置了一个队列系统,可以扩展到数千个并发用户。您可以使用 queue()
方法来配置队列,该方法由 gr.Interface
、 gr.Blocks
和 gr.ChatInterface
类支持。
例如,您可以通过设置 queue()
的 default_concurrency_limit
参数来控制一次处理的请求数量,例如
demo = gr.Interface(...).queue(default_concurrency_limit=5)
demo.launch()
这限制了此事件监听器一次处理的请求数量为 5。默认情况下, default_concurrency_limit
实际上被设置为 1
,这意味着当许多用户在使用您的应用程序时,一次只会处理一个用户的请求。这是因为许多机器学习功能消耗大量内存,因此一次只适合一个用户使用演示。但是,您可以轻松地在演示中更改此参数。
有关配置队列参数的更多详细信息,请参阅队列文档。https://www.gradio.app/docs/gradio/interface
您可以通过访问应用程序的 /monitoring
端点来查看队列处理的所有请求的数量和状态的分析。该端点将打印一个秘密 URL 到您的控制台,该 URL 链接到完整的分析仪表板。
Streaming outputs 流输出
在某些情况下,您可能希望流式传输一系列输出,而不是一次显示单个输出。例如,您可能有一个图像生成模型,您希望显示每个步骤生成的图像,直到最终图像。或者,您可能有一个聊天机器人,它一次一个令牌地流式传输其响应,而不是一次性返回所有响应。
在这种情况下,您可以将生成器函数提供给 Gradio,而不是常规函数。在 Python 中创建生成器非常简单:函数应该 yield
一系列值,而不是单个 return
值。通常 yield
语句放在某种循环中。这里有一个简单的生成器示例,它只是计数到给定的数字:
def my_generator(x):
for i in range(x):
yield i
您可以像使用常规函数一样将生成器提供给 Gradio。例如,这里有一个(假的)图像生成模型,它使用 gr.Interface
类在输出图像之前生成几个步骤的噪声:
import gradio as gr # 导入gradio库,用于构建Web应用界面
import numpy as np # 导入numpy库,用于数学计算和操作数组
import time # 导入time库,用于控制时间相关的操作
# 定义一个模拟扩散过程的函数,接收步骤数为参数
def fake_diffusion(steps):
rng = np.random.default_rng() # 创建一个随机数生成器
for i in range(steps): # 对于每一步
time.sleep(1) # 暂停1秒,模拟扩散过程耗时
image = rng.random(size=(600, 600, 3)) # 生成一个600x600x3的随机数组,模拟该步骤的图像
yield image # 使用yield返回图像,允许函数以生成器方式运行,每次调用返回一个结果
image = np.ones((1000,1000,3), np.uint8) # 生成一个全为1的1000x1000x3数组,代表纯色图像
image[:] = [255, 124, 0] # 将图像填充为特定颜色(橙色),代表最终扩散结果
yield image # 返回最终扩散结果图像
# 创建Gradio界面
demo = gr.Interface(
fake_diffusion, # 指定处理函数为fake_diffusion
inputs=gr.Slider(1, 10, 3, step=1), # 定义一个滑块输入组件,范围从1到10,默认值为3,步长为1
outputs="image" # 定义输出为图像类型
)
demo.launch() # 启动界面
这段代码构造了一个模拟图像扩散过程的Web应用。该应用使用了一个名为fake_diffusion
的生成器函数,用户可以通过滑块输入一个步骤数(从1到10),每一步之间有1秒的间隔。对于给定的步数,在每一步中,函数会生成一个600x600的随机彩色图像来模拟扩散过程的中间状态,并通过yield
返回该图像。扩散完成后,函数会生成并返回一个1000x1000的橙色纯色图像作为模拟扩散过程的最终结果。
此应用通过Gradio的Interface
构造函数创建界面,允许用户通过滑块选择扩散过程的步数,并逐步展示扩散过程的生成图像,直至显示最终的橙色纯色图像。这个示例展示了如何将复杂的过程和numpy库结合起来进行数值模拟,并通过Gradio界面使得这一过程可视化和互动化。
请注意,我们在迭代器中添加了一个 time.sleep(1)
,以在步骤之间创建人为暂停,以便您能够观察迭代器的步骤(在真实的图像生成模型中,这可能不是必需的)。
同样,Gradio 可以处理流输入,例如每当用户在文本框中输入一个字母时就会重新运行的图像生成模型。我们的指南中有更详细地介绍了如何构建反应式接口。https://www.gradio.app/guides/reactive-interfaces
Alerts 警报
您可能希望向用户显示警报。为此,请在您的函数中引发一个 gr.Error("custom message")
,以停止您的函数的执行并向用户显示错误消息。
或者,可以通过在函数中将 gr.Warning("custom message")
或 gr.Info("custom message")
作为独立行来发出,这将立即显示模态框,同时继续执行您的函数。 gr.Info()
和 gr.Warning()
之间的唯一区别是警报的颜色。
def start_process(name):
gr.Info("Starting process") # 直接调用函数,输出信息表示进程开始
if name is None: # 检查参数name是否为None
gr.Warning("Name is empty") # 如果name是None,直接调用函数,输出警告表示名称为空
# ...(此处省略了其余的处理逻辑)
if success == False: # 检查变量success的值是否为False
raise gr.Error("Process failed") # 如果success为False,抛出异常,表示进程失败
Tip: 注意gr.Error()
是一个必须要被抛出的异常,而gr.Warning()
和gr.Info()
是直接被调用的函数。这意味着当你希望记录一般信息或者警告时,应当直接调用gr.Info()
和gr.Warning()
函数。但当遇到错误,需要提醒程序的执行应当被终止或需要特别处理时,应当通过raise
语句抛出gr.Error()
异常。这种设计模式有助于区分程序的正常运行流程和异常处理流程,提高代码的可读性和易维护性。
Styling 样式
Gradio 主题是自定义应用程序外观和感觉的最简单方法。您可以从多种主题中选择,或创建自己的主题。要这样做,请将 theme=
kwarg 传递给 Interface
构造函数。例如:
demo = gr.Interface(..., theme=gr.themes.Monochrome())
Gradio 附带一套预建主题,您可以从 gr.themes.*
加载。您可以扩展这些主题或从头开始创建自己的主题 - 有关更多详细信息,请参阅主题指南。
为了增加额外的样式能力,您可以将任何 CSS(以及自定义 JavaScript)传递给您的 Gradio 应用程序。我们的自定义 JS 和 CSS 指南中有更详细的讨论。https://www.gradio.app/guides/custom-CSS-and-JS
Progress bars 进度条
Gradio支持创建自定义进度条,这样您就可以自定义并控制向用户显示的进度更新。为了启用这一功能,只需向您的方法添加一个参数,该参数的默认值为gr.Progress实例。然后,您可以通过直接使用0到1之间的浮点数调用此实例,或使用Progress实例的tqdm()方法来跟踪可迭代对象上的进度,如下所示。
import gradio as gr # 导入gradio库,用于构建web应用界面
import time # 导入time库,用于控制延时
# 定义一个函数,接受一个单词和一个进度条对象作为参数
def slowly_reverse(word, progress=gr.Progress()):
progress(0, desc="Starting") # 初始化进度条为0,并设置描述为"Starting"
time.sleep(1) # 等待1秒,模拟处理延迟
progress(0.05) # 将进度条更新为5%
new_string = "" # 初始化一个空字符串,用于存放反转后的单词
# 使用progress.tqdm来遍历输入的单词,同时更新进度条,并设置描述为"Reversing"
for letter in progress.tqdm(word, desc="Reversing"):
time.sleep(0.25) # 每遍历到一个字母,等待0.25秒,模拟处理过程
new_string = letter + new_string # 将当前字母添加到新字符串的最前面,实现反转
return new_string # 返回反转后的字符串
# 创建Gradio界面
demo = gr.Interface(
slowly_reverse, # 指定处理函数为slowly_reverse
gr.Text(), # 定义输入控件为文本输入框
gr.Text() # 定义输出控件为文本显示框
)
demo.launch() # 启动界面
这段代码构建了一个使用Gradio库的简单web应用,它的功能是将用户输入的单词进行反转,并通过gr.Progress()
对象可视化地展示处理过程的进度。该应用首先将进度条初始化并等待一秒,之后以5%的进度开始,使用progress.tqdm
来遍历用户输入的单词,每处理一个字母,进度条根据单词长度相应更新,并将该字母添加到新字符串的开头,完成单词的反转。反转过程中每个字母的处理将会使进程暂停0.25秒,以模拟耗时操作,整个反转处理过程的进展会通过进度条显示给用户。
如果您使用 tqdm 库,您甚至可以通过将默认参数设置为 gr.Progress(track_tqdm=True) 来自动从函数中已存在的任何 tqdm.tqdm 报告进度更新!
Batch functions 批处理函数
Gradio 支持传递批处理函数。批处理函数只是接收一系列输入并返回一系列预测的函数。
例如,这里有一个批处理函数,它接收两个输入列表(一个单词列表和一个整数列表),并返回一个修剪过的单词列表作为输出:
import time # 导入time模块,用于实现程序延时
# 定义一个函数,用于根据给定的长度裁剪一组单词
def trim_words(words, lens):
trimmed_words = [] # 初始化一个空列表,用于存放裁剪后的单词
time.sleep(5) # 让程序暂停5秒,模拟一个耗时操作
for w, l in zip(words, lens): # 使用zip函数同时遍历单词列表和长度列表
trimmed_words.append(w[:int(l)]) # 根据给定长度裁剪单词,并将裁剪后的单词添加到列表中
return [trimmed_words] # 返回包含裁剪后单词列表的列表
这段代码定义了一个函数trim_words
,它接受两个参数:words
和lens
。words
是一个单词列表,lens
是一个与之对应的,指定每个单词需要被裁剪到的长度的列表。函数首先会让程序暂停5秒,以模拟某种耗时操作。之后,使用zip
函数遍历words
和lens
列表,对每个单词按照对应的长度进行裁剪,并将裁剪后的单词添加到一个新的列表trimmed_words
中。最终,函数返回一个包含trimmed_words
列表的列表。这个操作可能用于文本处理或预处理任务中,比如为了统一输入单词的长度。
使用批处理函数的优势在于,如果您启用了queuing排队,Gradio 服务器可以自动批量处理传入的请求并并行处理它们,这可能会加快您的演示速度。这是 Gradio 代码的样子(注意 batch=True
和 max_batch_size=16
)
使用 gr.Interface
类:
demo = gr.Interface(
fn=trim_words,
inputs=["textbox", "number"],
outputs=["output"],
batch=True,
max_batch_size=16
)
demo.launch()
使用 gr.Blocks
类:
import gradio as gr
with gr.Blocks() as demo:
with gr.Row():
word = gr.Textbox(label="word")
leng = gr.Number(label="leng")
output = gr.Textbox(label="Output")
with gr.Row():
run = gr.Button()
event = run.click(trim_words, [word, leng], output, batch=True, max_batch_size=16)
demo.launch()
这段代码将trim_words
函数与Gradio界面结合,创建了一个Web应用,允许用户输入多个单词和一个裁剪长度,应用会根据这个长度裁剪这些单词。用户可以批量地输入待处理的数据,提高效率,并且设置了单次批处理的最大数量为16,防止一次性处理过多数据,可能导致系统资源不足。该应用适用于需要批量文本处理的场景,如文本预处理、数据清洗等。
import gradio as gr # 导入gradio库,用于构建应用界面
# 定义一个裁剪单词的函数,此函数之前已经被定义
def trim_words(words, lens):
trimmed_words = []
time.sleep(5) # 模拟耗时操作,使程序暂停5秒
for w, l in zip(words, lens): # 遍历单词列表和长度列表
trimmed_words.append(w[:int(l)]) # 将每个单词裁剪到指定长度,并添加到新列表中
return [trimmed_words] # 返回包含裁剪后单词列表的列表
# 使用gradio.Interface创建界面
demo = gr.Interface(
fn=trim_words, # 指定处理函数为trim_words
inputs=[gr.inputs.Textbox(lines=2, placeholder="Type words here..."), gr.inputs.Number(label="Length")],
# 定义两个输入控件:一个文本输入框用于输入单词,一个数字输入框用于输入裁剪长度
outputs=[gr.outputs.Textbox()], # 定义输出控件为文本显示框
batch=True, # 启用批处理模式,允许一次处理多个输入
max_batch_size=16 # 设置最大批处理大小为16
)
demo.launch() # 启动界面
在上面的例子中,16 个请求可以并行处理(总推理时间为 5 秒),而不是每个请求被单独处理(总推理时间为 80 秒)。许多 Hugging Face transformers
和 diffusers
模型与 Gradio 的批处理模式非常自然地协作:这里有一个使用 diffusers 批量生成图像的示例演示 https://github.com/gradio-app/gradio/blob/main/demo/diffusers_with_batching/run.py
Sharing Your App 分享您的应用程序
在本指南中,我们将更深入地探讨与他人共享 Gradio 应用的各个方面。我们将涵盖:
Sharing demos with the share parameter
Hosting on HF Spaces
Embedding hosted spaces
Using the API page
Accessing network requests
Mounting within FastAPI
Authentication
Security and file access
Analytics
使用分享参数共享演示
在 HF 空间上托管
嵌入托管空间
使用 API 页面 访问网络请求
在 FastAPI 中挂载
身份验证
安全与文件访问
分析学
分享演示
通过在 launch()
方法中设置 share=True
,可以轻松地公开分享 Gradio 演示。像这样:
import gradio as gr
def greet(name):
return "Hello " + name + "!"
demo = gr.Interface(fn=greet, inputs="textbox", outputs="textbox")
demo.launch(share=True) # Share your demo with just 1 extra parameter 🚀
这将生成一个公共的、可分享的链接,您可以发送给任何人!当您发送此链接时,对方用户可以在其浏览器中尝试模型。因为处理过程发生在您的设备上(只要您的设备保持开启状态),您无需担心打包任何依赖项
分享链接通常看起来像这样:https://07ff8706ab.gradio.live。尽管链接是通过 Gradio 分享服务器提供的,但这些服务器仅是您本地服务器的代理,并且不存储通过您的应用发送的任何数据。分享链接在 72 小时后过期。(也可以在您自己的云服务器上设置您自己的分享服务器,以克服这一限制。)
✍️ 提示:请记住,分享链接是公开可访问的,这意味着任何人都可以使用您的模型进行预测!因此,请确保不通过您编写的函数暴露任何敏感信息,或允许在您的设备上发生任何关键变化。或者,您可以按照下面的讨论,在您的 Gradio 应用中添加认证。
请注意,默认情况下, share=False
,这意味着您的服务器仅在本地运行。(这是默认设置,除了在 Google Colab 笔记本中,会自动创建共享链接)。作为使用共享链接的替代方法,您可以使用 SSH 端口转发与特定用户共享您的本地服务器。
在 HF Spaces 上托管
如果您想在互联网上拥有一个永久链接到您的 Gradio 演示,请使用 Hugging Face Spaces。Hugging Face Spaces 提供了永久免费托管您的机器学习模型的基础设施!
在您创建了一个免费的 Hugging Face 账户后,您有两种方法将您的 Gradio 应用部署到 Hugging Face Spaces:
从终端:在您的应用目录中运行
gradio deploy
。CLI 将收集一些基本元数据,然后启动您的应用。要更新您的空间,您可以重新运行此命令,或启用 Github Actions 选项以在git push
上自动更新 Spaces。从您的浏览器:将包含您的 Gradio 模型和所有相关文件的文件夹拖放到这里。有关如何在 Hugging Face Spaces 上托管的更多信息,请参阅本指南,或观看嵌入的视频:
嵌入托管空间
一旦您已经在 Hugging Face Spaces(或您自己的服务器)上托管了您的应用程序,您可能会想要在不同的网站上嵌入演示,例如您的博客或您的作品集。嵌入一个交互式演示允许人们尝试您构建的机器学习模型,无需下载或安装任何东西——直接在他们的浏览器中!最棒的部分是,您甚至可以在静态网站上嵌入交互式演示,例如 GitHub 页面。
有两种方法可以嵌入您的 Gradio 演示。您可以在 Hugging Face Space 页面上直接找到两个选项的快速链接,在“嵌入此空间”下拉选项中:
使用 Web 组件嵌入
Web 组件通常比 IFrames 为用户提供更好的体验。Web 组件会懒加载,这意味着它们不会减慢您网站的加载时间,并且它们会根据 Gradio 应用的大小自动调整其高度。
要嵌入 Web 组件:
通过在您的网站中添加下面的脚本来导入 gradio JS 库(将 URL 中的 4.36.1 替换为您正在使用的 Gradio 库版本)。
<script
type="module"
src="https://gradio.s3-us-west-2.amazonaws.com/4.36.1/gradio.js"
></script>
添加
<gradio-app src="https://$your_space_host.hf.space"></gradio-app>
您想要放置应用程序的元素。将 src=
属性设置为您的 Space 的嵌入 URL,您可以在“嵌入此 Space”按钮中找到。例如:
<gradio-app
src="https://abidlabs-pytorch-image-classifier.hf.space"
></gradio-app>
您可以在 Gradio 登录页面上看到 web 组件的样例。
您还可以通过传入 <gradio-app>
标签的属性来自定义 web 组件的外观和行为:
src
:正如我们所见,src
属性链接到您希望嵌入的托管 Gradio 演示的 URLspace
:如果您的 Gradio 演示托管在 Hugging Face Space 上,这是一个可选的简写。接受username/space_name
而不是完整的 URL。例如:gradio/Echocardiogram-Segmentation
。如果提供了此属性,则不需要提供src
。control_page_title
:一个布尔值,指定页面的 html 标题是否应设置为 Gradio 应用的标题(默认为"false"
)initial_height
:加载 Gradio 应用时,web 组件的初始高度(默认为"300px"
)。请注意,最终高度是根据 Gradio 应用的大小设置的。container
:是否显示边框和有关 Space 托管位置的信息(默认为"true"
)info
:是否仅在嵌入式应用程序下方显示有关空间托管位置的信息(默认为"true"
)autoscroll
: 预测完成后是否自动滚动到输出(默认为"false"
)eager
: 是否在页面加载时立即加载 Gradio 应用(默认为"false"
)theme_mode
: 是否使用dark
、light
或默认的system
主题模式(默认为"system"
)render
: 嵌入空间渲染完成后触发的事件。
这里有一个如何使用这些属性来创建一个不懒加载且初始高度为 0px 的 Gradio 应用的例子。
<gradio-app
space="gradio/Echocardiogram-Segmentation"
eager="true"
initial_height="0px"
></gradio-app>
这是另一个如何使用 render
事件的例子。事件监听器用于捕获 render
事件,并将在渲染完成后调用 handleLoadComplete()
函数。
<script>
function handleLoadComplete() {
console.log("Embedded space has finished rendering");
}
const gradioApp = document.querySelector("gradio-app");
gradioApp.addEventListener("render", handleLoadComplete);
</script>
注意:虽然 Gradio 的 CSS 永远不会影响嵌入页面,但嵌入页面可以影响嵌入的 Gradio 应用程序的样式。确保父页面中的任何 CSS 都不会太通用,以至于它也可能适用于嵌入的 Gradio 应用程序并导致样式破坏。元素选择器如 header { ... }
和 footer { ... }
最有可能引起问题。
使用 IFrames 嵌入
要改用 IFrames 嵌入(例如,如果您的网站无法添加 javascript),请添加此元素:
<iframe src="https://$your_space_host.hf.space"></iframe>
再次,您可以在“嵌入此空间”按钮中找到您空间嵌入 URL 的 src=
属性。
注意:如果您使用 IFrames,您可能需要添加一个固定的 height
属性,并设置 style="border:0;"
以去除边框。此外,如果您的应用程序需要诸如访问网络摄像头或麦克风之类的权限,您还需要使用 allow
属性来提供这些权限。
API 页面
您几乎可以使用任何 Gradio 应用作为 API!在像这样的 Gradio 应用的页脚中,您会看到一个“通过 API 使用”的链接。
这是一个页面,列出了可以用来查询 Gradio 应用的端点,通过我们支持的客户端:Python 客户端或 JavaScript 客户端。对于每个端点,Gradio 会自动生成参数及其类型,以及像这样的示例输入。
终端会在您启动 Gradio Interface
时自动创建。如果您正在使用 Gradio Blocks
,您也可以设置 Gradio API 页面,尽管我们建议您明确命名每个事件监听器,例如
btn.click(add, [num1, num2], output, api_name="addition")
这将会添加并记录端点 /api/addition/
到自动生成的 API 页面。否则,您的 API 端点将显示为“未命名”端点。
直接 访问网络请求
当用户对您的应用程序进行预测时,您可能需要底层网络请求,以获取请求头(例如,用于高级认证)、记录客户端 IP 地址、获取查询参数或出于其他原因。Gradio 以类似于 FastAPI 的方式支持这一点:只需添加一个函数参数,其类型提示为 gr.Request
,Gradio 将把网络请求作为该参数传入。以下是一个示例:
import gradio as gr # 导入gradio库,用于构建交互式Web应用界面
# 定义一个函数,将输入的文本原样返回,并打印请求的相关信息
def echo(text, request: gr.Request):
if request: # 检查是否存在请求对象
print("Request headers dictionary:", request.headers) # 打印请求头信息
print("IP address:", request.client.host) # 打印客户端的IP地址
print("Query parameters:", dict(request.query_params)) # 打印查询参数
return text # 返回输入的文本
# 创建gradio界面
io = gr.Interface(
echo, # 指定处理函数为echo
"textbox", # 定义输入控件为文本输入框
"textbox" # 定义输出控件也为文本显示框
).launch() # 启动界面
注意:如果您的函数是直接调用而不是通过 UI 调用的(例如,当示例被缓存,或者当 Gradio 应用通过 API 调用时会发生这种情况),那么 request
将会是 None
。您应该明确处理这种情况,以确保您的应用不会抛出任何错误。这就是为什么我们有明确的检查 if request
。
这段代码使用Gradio库创建了一个简单的Web应用,它包含一个文本输入框和一个文本显示框。用户可以在输入框中输入文本,应用会原样返回这个文本并显示在输出框中。此外,当用户提交文本时,echo
函数还会打印出当前请求的一些信息,包括请求头信息、客户端IP地址以及查询参数。
这种应用可能用于开发阶段作为一个简易的调试工具,帮助开发者了解用户请求的信息,诸如IP地址和请求头的内容等,这对于排查问题和了解用户行为非常有用。
挂载在另一个 FastAPI 应用程序中
在某些情况下,您可能已经有一个现有的 FastAPI 应用程序,并且您想为 Gradio 演示添加一个路径。您可以轻松地通过 gradio.mount_gradio_app()
来做到这一点。
这是一个完整的例子:
from fastapi import FastAPI # 从fastapi模块导入FastAPI类
import gradio as gr # 导入gradio库,用于创建交互式Web应用界面
CUSTOM_PATH = "/gradio" # 定义gradio应用将挂载的路径
app = FastAPI() # 创建一个FastAPI应用实例
@app.get("/") # 使用装饰器定义一个根路径的get请求处理函数
def read_main():
return {"message": "This is your main app"} # 返回一个简单的json消息
# 创建一个gradio界面,包含一个文本输入框和一个文本显示框,输入的文本会被添加问候语后显示
io = gr.Interface(lambda x: "Hello, " + x + "!", "textbox", "textbox")
# 使用gradio的mount_gradio_app函数将Gradio界面挂载到FastAPI应用上,指定路径为CUSTOM_PATH
app = gr.mount_gradio_app(app, io, path=CUSTOM_PATH)
# 提示用户如何运行FastAPI应用,以及如何在浏览器中访问gradio界面
# 运行方式:在终端中使用命令`uvicorn run:app`启动FastAPI应用
# 访问方式:在浏览器中导航到http://localhost:8000/gradio
这段代码整合了FastAPI和Gradio,创建了一个同时提供REST API和交互式Web应用界面的服务。主要应用(FastAPI)具有一个根路径"/"
的GET方法,返回一个简单的消息。此外,使用Gradio创建了一个简单的界面:用户输入文本,系统添加一个问候语后将其返回。通过gr.mount_gradio_app
函数,这个Gradio界面被挂载到了FastAPI应用上,挂载路径为"/gradio"
。这样就能通过一个FastAPI应用同时提供API服务和交云界面服务,便于开发调试和展示。
要运行这个应用,需要在终端中启动FastAPI应用(通常使用uvicorn run:app
命令),然后在浏览器中访问http://localhost:8000/gradio
来与Gradio界面交互。
请注意,这种方法还允许您在自定义路径上运行您的 Gradio 应用程序(如上例中的 http://localhost:8000/gradio
)。
认证
密码保护应用程序
您可能希望在应用程序前放置一个认证页面,以限制可以打开您的应用程序的用户。通过在 launch()
方法中使用 auth=
关键字参数,您可以提供一个带有用户名和密码的元组,或者一个可接受的用户名/密码元组列表;这里有一个为名为“admin”的单一用户提供基于密码的认证的示例:
demo.launch(auth=("admin", "pass1234"))
对于更复杂的身份验证处理,您甚至可以传递一个函数,该函数接受用户名和密码作为参数,并返回 True
以允许访问,否则返回 False
。
这是一个函数的例子,它接受任何用户名和密码相同的登录:
def same_auth(username, password):
return username == password
demo.launch(auth=same_auth)
如果您有多个用户,您可能希望根据登录的用户自定义显示的内容。您可以按照上面的讨论直接访问网络请求,然后读取请求的 .username 属性。这里有一个例子:
import gradio as gr # 导入gradio库,用于创建交互式Web应用界面
# 定义一个函数,用于更新消息,该函数依赖于请求对象gr.Request
# 这里假设请求对象中包含了一个username属性,这是一个示例,实际中gr.Request没有username属性
def update_message(request: gr.Request):
return f"Welcome, {request.username}" # 返回欢迎信息,其中包含了请求中的username
# 使用gr.Blocks创建一个Gradio界面布局的上下文管理器
with gr.Blocks() as demo:
m = gr.Markdown() # 创建一个Markdown组件
demo.load(update_message, None, m) # 使用load方法加载update_message函数,将其输出连接到Markdown组件
# 启动这个Gradio界面,同时设置基本认证,用户名和密码分别为("Abubakar", "Abubakar")和("Ali", "Ali")
demo.launch(auth=[("Abubakar", "Abubakar"), ("Ali", "Ali")])
这段代码创建了一个Gradio应用,使用gr.Blocks
来布局应用界面。在这个应用中,定义了一个update_message
函数,该函数原本意在使用请求对象gr.Request
来动态生成欢迎信息。然而,需要注意的是,在实际使用gr.Request
时,并不直接支持访问username
等属性,这里的request.username
是一个假设性的示例,用于演示如何在函数中引用请求对象。
在gr.Blocks
上下文中,利用demo.load
方法来动态加载update_message
函数的结果到一个Markdown组件m
中。不过,由于Gradio的接口特性限制,demo.load
方法实际在这个场景中可能无法直接如预期工作来处理gr.Request
对象,因此此代码段主要作为一个理论上的示例。
最后,通过demo.launch
方法启动应用,并设置了基本的HTTP认证,只有用户名和密码分别是Abubakar
和Ali
的用户才能访问这个Gradio应用。在实际部署和应用开发过程中,可能还需要做一些调整和测试,以确保所有功能按预期运行。
注意:为了使认证正常工作,您的浏览器必须启用第三方 cookie。对于 Safari 或 Chrome 隐身模式,默认情况下不是这样。
如果用户访问您的 Gradio 应用程序的 /logout
页面,他们将自动注销并删除会话 cookie。这也允许您为您的 Gradio 应用程序添加注销功能。让我们更新前面的示例,以包括一个注销按钮:
import gradio as gr # 导入gradio库,用于创建交互式Web应用界面
# 定义一个函数,用于更新消息,该函数依赖于请求对象gr.Request
# 这里假设请求对象中包含了一个username属性,这是一个示例,实际中gr.Request没有username属性
def update_message(request: gr.Request):
return f"Welcome, {request.username}" # 返回欢迎信息,其中包含了请求中的username
# 使用gr.Blocks创建一个Gradio界面布局的上下文管理器
with gr.Blocks() as demo:
m = gr.Markdown() # 创建一个Markdown组件
logout_button = gr.Button("Logout", link="/logout") # 创建一个注销按钮,点击将重定向到"/logout"链接
demo.load(update_message, None, m) # 使用load方法加载update_message函数,将其输出连接到Markdown组件
# 启动这个Gradio界面,同时设置基本认证,用户名和密码分别为("Pete", "Pete")和("Dawood", "Dawood")
demo.launch(auth=[("Pete", "Pete"), ("Dawood", "Dawood")])
注意:Gradio 内置的身份验证提供了一个简单基础的访问控制层,但不提供针对需要严格访问控制的应用程序(例如多因素身份验证、速率限制或自动锁定策略)的强大安全功能。
这段代码创建了一个Gradio应用,在应用中集成了Gradio的gr.Blocks
功能来布置界面。主要通过定义了一个update_message
函数,并假设gr.Request
对象含有username
属性,用于生成带有用户名称的欢迎信息。实际上gr.Request
对象不包含username
属性,这只是一个示例代码。
在Gradio Blocks布局中,添加了一个Markdown组件m
用于展示欢迎消息,和一个注销按钮logout_button
。注销按钮在点击时会尝试链接重定向到/logout
,但实际上这个按钮并不会触发实际的注销流程,因为Gradio应用本身并不支持直接处理Web路径重定向或注销逻辑。这里的注销按钮主要是作为一个界面元素的展示。
最后,通过demo.launch
调用启动应用并设置了HTTP基本认证。只有用户名和密码分别是Pete
和Dawood
的用户才能登录并使用这个Gradio应用。
需要注意的是,这个示例代码在Gradio中创建按钮模拟注销功能主要用于展示目的,并没有实现真正的用户登出逻辑。实际应用中,处理用户认证和会话管理等功能可能需要结合其他Web框架和技术来实现。
OAuth(通过 Hugging Face 登录)
Gradio 天生支持通过 Hugging Face 的 OAuth 登录。换句话说,您可以轻松地在您的演示中添加一个“通过 Hugging Face 登录”的按钮,这允许您获取用户的 HF 用户名以及他们 HF 个人资料中的其他信息。查看这个 Space 以获取现场演示。
要启用 OAuth,您必须在 README.md 文件中将 hf_oauth: true
设置为 Space 元数据。这将在 Hugging Face 上注册您的 Space 为 OAuth 应用程序。接下来,您可以使用 gr.LoginButton
在 Gradio 应用程序中添加登录按钮。一旦用户使用他们的 HF 账户登录,您可以通过向任何 Gradio 函数添加类型为 gr.OAuthProfile
的参数来检索他们的个人资料。用户个人资料将自动作为参数值注入。如果您想代表用户执行操作(例如,列出用户的私有仓库,创建仓库等),您可以通过添加类型为 gr.OAuthToken
的参数来检索用户令牌。您必须在 Space 元数据中定义您将使用的范围(更多详细信息请参阅文档)。
import gradio as gr # 导入gradio库,用于创建交互式Web应用界面
from huggingface_hub import whoami # 从huggingface_hub导入whoami函数,用于获取用户信息
# 定义一个函数,根据用户提供的OAuthProfile返回欢迎信息
# 如果没有用户信息,则返回“I don't know you.”
def hello(profile: gr.OAuthProfile | None) -> str:
if profile is None:
return "I don't know you."
return f"Hello {profile.name}" # 使用用户的名字返回个性化的欢迎信息
# 定义一个函数,根据用户的OAuthToken来列出所属的组织
# 如果没有提供OAuthToken,则提示用户登录
def list_organizations(oauth_token: gr.OAuthToken | None) -> str:
if oauth_token is None:
return "Please log in to list organizations."
# 调用whoami函数获取用户所属组织的名称,并拼接成字符串返回
org_names = [org["name"] for org in whoami(oauth_token.token)["orgs"]]
return f"You belong to {', '.join(org_names)}."
# 使用gr.Blocks创建一个Gradio界面布局的上下文管理器
with gr.Blocks() as demo:
gr.LoginButton() # 在界面上添加一个登录按钮,允许用户通过OAuth登录
m1 = gr.Markdown() # 创建一个Markdown组件用来显示个性化欢迎信息
m2 = gr.Markdown() # 创建第二个Markdown组件用来显示用户所属的组织信息
# 使用load方法加载hello函数和list_organizations函数,连接它们的输出到对应的Markdown组件
demo.load(hello, inputs=None, outputs=m1)
demo.load(list_organizations, inputs=None, outputs=m2)
demo.launch() # 启动Gradio界面
这段代码展示了如何在Gradio应用中使用gr.LoginButton
来实现OAuth登录功能,并基于用户的登录状态,显示个性化的信息或用户所属组织的列表。hello
函数用来生成一个针对登录用户的欢迎信息,而list_organizations
函数则是利用用户的OAuthToken调用whoami
函数来获取并显示用户所属的组织信息。
通过Gradio的gr.Blocks
功能,代码创建了一个交互式界面,其中包括一个登录按钮和两个Markdown组件。当用户通过登录按钮登录后,界面会动态显示用户的名称和用户所属组织的名称列表。
这个示例展示了如何在Gradio应用中集成OAuth登录功能,并根据用户登录的状态显示不同的信息,适用于需要用户认证的Web应用场景。
当用户点击登录按钮时,他们会被重定向到一个新页面以授权您的空间。
用户可以随时在他们的设置中撤销对其个人资料的访问权限。
如上所述,OAuth 功能只有当应用程序在 Space 中运行时才可用。不过,在部署应用程序之前,您往往需要在本地对其进行测试。要在本地测试 OAuth 功能,你的机器必须登录 Hugging Face。请运行 huggingface-cli 登录,或将 HF_TOKEN 设为环境变量,并在其中加入一个访问令牌。您可以在设置页面 (https://huggingface.co/settings/tokens) 生成一个新的令牌。然后,点击 gr.LoginButton 将登录您的本地 Hugging Face 配置文件,这样您就可以在将应用程序部署到 Space 之前使用您的 Hugging Face 账户进行调试。
OAuth(与外部提供商)
您也可以在 Gradio 应用程序中使用外部 OAuth 提供商(例如 Google OAuth)进行身份验证。为此,首先在 FastAPI 应用程序中挂载您的 Gradio 应用程序(如上所述)。然后,您必须编写一个身份验证函数,该函数从 OAuth 提供商获取用户的用户名并返回它。这个函数应该传递给 auth_dependency
参数在 gr.mount_gradio_app
中。
类似于 FastAPI 依赖函数,由 auth_dependency
指定的函数将在您的 FastAPI 应用中任何与 Gradio 相关的路由之前运行。该函数应接受单一参数:FastAPI Request
并返回一个字符串(代表用户的用户名)或 None
。如果返回一个字符串,用户将能够访问您的 FastAPI 应用中与 Gradio 相关的路由。
首先,让我们展示一个简单的例子来说明 auth_dependency
参数:
from fastapi import FastAPI, Request # 从fastapi库导入FastAPI类和Request类
import gradio as gr # 导入gradio库,用于创建交互式Web应用界面
import uvicorn # 导入uvicorn库,用于运行ASGI应用
app = FastAPI() # 创建一个FastAPI应用实例
# 定义一个函数,用于从请求中获取用户信息
def get_user(request: Request):
return request.headers.get("user") # 从请求头中获取"user"字段的值
# 创建一个Gradio Interface,接受一个文本输入并返回一个带有输入内容的问候语
demo = gr.Interface(lambda s: f"Hello {s}!", "textbox", "textbox")
# 使用gradio的mount_gradio_app函数将Gradio应用挂载到FastAPI应用上
# 挂载点设置为"/demo",并使用get_user函数作为验证用户身份的依赖函数
app = gr.mount_gradio_app(app, demo, path="/demo", auth_dependency=get_user)
# 如果当前脚本作为主程序运行,则使用uvicorn来运行FastAPI应用
if __name__ == '__main__':
uvicorn.run(app)
这段代码结合了FastAPI和Gradio,创建了一个支持交互式Web界面的API应用。在该应用中,定义了一个get_user
函数,该函数旨在从每个HTTP请求的头信息中获取用户信息,即"用户"字段值。然后,创建了一个Gradio界面(demo
),该界面通过一个文本框接受输入,并返回一个简单的问候语。
接下来,使用gr.mount_gradio_app
函数将这个Gradio界面(demo
)挂载到FastAPI应用上,指定了挂载路径为"/demo"
。在挂载时,get_user
函数被用作认证依赖项,这意味着每个请求到这个Gradio界面的用户都会通过get_user
函数进行用户验证。
最后,如果这个脚本作为主程序运行,则使用uvicorn.run
函数来启动FastAPI应用,从而使得应用可以接收和响应HTTP请求。这样整合FastAPI和Gradio的方式为创建具有交互界面的Web API应用提供了一个有效途径,同时还支持基于请求的用户认证。
在这个例子中,只有包含“用户”头的请求才被允许访问 Gradio 应用程序。当然,这并没有增加太多安全性,因为任何用户都可以在他们的请求中添加这个头。
以下是一个更完整的示例,展示了如何在 Gradio 应用程序中添加 Google OAuth(假设您已经在 Google 开发者控制台上创建了 OAuth 凭据)
import os # 导入os库,用于操作环境变量
from authlib.integrations.starlette_client import OAuth, OAuthError # 导入Authlib库的OAuth集成和异常处理
from fastapi import FastAPI, Depends, Request # 导入FastAPI相关功能
from starlette.config import Config # 导入Starlette的Config,用于处理配置
from starlette.responses import RedirectResponse # 导入重定向响应类型
from starlette.middleware.sessions import SessionMiddleware # 导入会话中间件
import uvicorn # 导入uvicorn,用于运行ASGI应用
import gradio as gr # 导入gradio,用于创建交互式Web界面
app = FastAPI() # 创建FastAPI应用实例
# 以下是OAuth设置,需要使用实际的OAuth客户端ID、客户端秘密和会话秘钥
GOOGLE_CLIENT_ID = "..."
GOOGLE_CLIENT_SECRET = "..."
SECRET_KEY = "..."
# 创建配置字典,并创建Starlette的配置对象
config_data = {'GOOGLE_CLIENT_ID': GOOGLE_CLIENT_ID, 'GOOGLE_CLIENT_SECRET': GOOGLE_CLIENT_SECRET}
starlette_config = Config(environ=config_data)
# 创建OAuth对象并注册Google作为OAuth提供者
oauth = OAuth(starlette_config)
oauth.register(
name='google',
server_metadata_url='https://accounts.google.com/.well-known/openid-configuration',
client_kwargs={'scope': 'openid email profile'},
)
# 设置会话秘钥并添加会话中间件到FastAPI应用
SECRET_KEY = os.environ.get('SECRET_KEY') or "a_very_secret_key"
app.add_middleware(SessionMiddleware, secret_key=SECRET_KEY)
# 定义依赖,用于获取当前用户
def get_user(request: Request):
user = request.session.get('user')
if user:
return user['name']
return None
# 定义路由,进行登录和重定向操作
@app.get('/')
def public(user: dict = Depends(get_user)):
if user:
return RedirectResponse(url='/gradio')
else:
return RedirectResponse(url='/login-demo')
@app.route('/logout')
async def logout(request: Request):
request.session.pop('user', None)
return RedirectResponse(url='/')
@app.route('/login')
async def login(request: Request):
redirect_uri = request.url_for('auth')
# 可选:确保在https环境下使用正确的重定向URI
return await oauth.google.authorize_redirect(request, redirect_uri)
@app.route('/auth')
async def auth(request: Request):
try:
access_token = await oauth.google.authorize_access_token(request)
except OAuthError:
return RedirectResponse(url='/')
request.session['user'] = dict(access_token)["userinfo"]
return RedirectResponse(url='/')
# 创建一个Gradio登录界面
with gr.Blocks() as login_demo:
gr.Button("Login", link="/login")
# 将Gradio登录界面挂载到FastAPI应用
app = gr.mount_gradio_app(app, login_demo, path="/login-demo")
# 定义一个简单的Gradio问候函数
def greet(request: gr.Request):
return f"Welcome to Gradio, {request.username}"
# 创建主Gradio界面
with gr.Blocks() as main_demo:
m = gr.Markdown("Welcome to Gradio!")
gr.Button("Logout", link="/logout")
main_demo.load(greet, None, m)
# 将主Gradio界面挂载到FastAPI应用,并添加用户认证依赖
app = gr.mount_gradio_app(app, main_demo, path="/gradio", auth_dependency=get_user)
if __name__ == '__main__':
uvicorn.run(app)
这段代码创建了一个集成了OAuth登录(以Google为例)的FastAPI应用,并使用Gradio实现了交互式Web界面。它通过Authlib与Starlette集成实现OAuth登录流程,并使用FastAPI作为后端框架。代码中用于OAuth登录的信息(客户端ID、客户端秘密、秘钥)需要替换成实际的有效信息。
主要逻辑包括:
使用Authlib对Google OAuth服务进行配置,并注册为OAuth提供者。
通过SessionMiddleware添加对会话的支持,这对于实现登录状态持久化非常重要。
定义几个路由处理函数来管理登录、登出、授权回调等流程。
定义两个Gradio界面:一个简单的登录界面和一个显示欢迎信息的主界面,分别通过特定的URL路径提供访问。
使用
gr.mount_gradio_app
方法将Gradio界面挂载到FastAPI应用上,允许在同一个应用中既提供REST API服务,又提供Gradio交互式界面。通过依赖注入(Depends)和会话管理来控制用户的登录状态并根据状态进行页面重定向。
整个应用结合了FastAPI的强大后端功能和Gradio的前端交互界面,适合需要用户认证的交互式Web应用场景。
实际上在这个例子中有两个独立的 Gradio 应用程序!一个只是显示登录按钮(这个演示对任何用户都是可访问的),而另一个主要演示只对已登录的用户开放。您可以在这个空间尝试这个例子。https://huggingface.co/spaces/gradio/oauth-example
安全性和文件访问
与他人共享您的 Gradio 应用程序(通过在 Spaces 上托管、在您自己的服务器上托管或通过临时分享链接)会将主机上的某些文件暴露给您的 Gradio 应用程序的用户。
特别是,Gradio应用程序允许用户访问四种文件:
1. Gradio创建的临时文件。这些是Gradio在运行预测功能时创建的文件。例如,如果您的预测功能返回一个视频文件,那么Gradio会将该视频保存到您设备上的一个临时缓存中,然后将文件路径发送到前端。您可以通过设置环境变量GRADIO_TEMP_DIR为一个绝对路径,如/home/usr/scripts/project/temp/,来自定义Gradio创建的临时缓存文件的位置。您可以在应用程序关闭时使用gradio.Blocks、gradio.Interface和gradio.ChatInterface的delete_cache参数删除您的应用程序创建的文件。这个参数是一个整数元组,形式为[frequency, age],其中frequency是删除文件的频率,age是自文件上次修改以来的秒数。
2. Gradio创建的缓存示例。这些是Gradio在缓存示例以加快运行时间时创建的文件,如果您在gr.Interface()、gr.ChatInterface()或gr.Examples()中设置cache_examples=True或cache_examples="lazy"。默认情况下,这些文件保存在您应用程序工作目录中的gradio_cached_examples/子目录中。您可以通过设置环境变量GRADIO_EXAMPLES_CACHE为一个绝对路径或相对于您工作目录的路径,来自定义Gradio创建的缓存示例文件的位置。
3. 您通过launch()中的allowed_paths参数明确允许的文件。此参数允许您传入一个额外目录或确切文件路径的列表,您希望允许用户访问这些文件。(默认情况下,此参数为空列表)。
4. 您通过gr.set_static_paths函数明确设置的静态文件。此参数允许您传入一系列目录或文件名,这些将被视为静态的。这意味着它们不会被复制到缓存中,并将直接从您的计算机提供。这可以帮助节省磁盘空间并减少您的应用程序启动所需的时间,但要注意可能的安全隐患。
Gradio不允许访问:
1. 您通过launch()中的blocked_paths参数明确阻止的文件。您可以传入一个额外目录或确切文件路径的列表到blocked_paths参数中。此参数优先于Gradio默认或通过allowed_paths暴露的文件。
2. 主机上的任何其他路径。用户不应能够访问主机上的其他任意路径。
分享您的Gradio应用程序也将允许用户将文件上传到您的计算机或服务器。您可以设置上传文件的最大大小,以防止滥用并保护磁盘空间。您可以通过.launch的max_file_size参数来实现。例如,以下两个代码片段将文件上传限制为每个文件5兆字节。
import gradio as gr # 导入gradio库,用于创建交互式Web应用界面
# 创建一个Gradio界面,该界面接受一个图片作为输入,并将相同的图片作为输出
demo = gr.Interface(lambda x: x, "image", "image")
# 启动Gradio界面,并设置上传文件的最大尺寸为5MB
demo.launch(max_file_size="5mb")
# 或者,使用gr.FileSize.MB来指定上传文件的最大尺寸为5MB
demo.launch(max_file_size=5 * gr.FileSize.MB)
请确保您运行的是gradio的最新版本,以便这些安全设置生效。
分析
默认情况下,Gradio 会收集某些分析数据,以帮助我们更好地了解 gradio
库的使用情况。这包括以下信息:
- Gradio应用程序正在运行的环境(例如Colab Notebook,Hugging Face Spaces)
- Gradio应用中正在使用的输入/输出组件
- Gradio应用是否在使用某些高级功能,如auth或show_error
- 仅用于衡量使用Gradio的独立开发者数量的IP地址
- 正在运行的Gradio版本
您的Gradio应用的用户信息不会被收集。如果您想完全禁用分析功能,可以通过在gr.Blocks、gr.Interface或gr.ChatInterface中将analytics_enabled参数设置为False来实现。或者,您可以将GRADIO_ANALYTICS_ENABLED环境变量设置为"False",以便将此应用于您系统中创建的所有Gradio应用。
注:此分析政策反映了gradio>=4.32.0版本的情况。
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)