全网最详细Gradio教程系列5——Gradio Client: javascript
- 前言
- 本篇摘要
- 5. Gradio Client的三种使用方式
- 5.2 使用Gradio JavaScript Client
- 5.2.1 安装
- 1. npm方式
- 2. CDN方式
- 3. 在线运行环境:PLAYCODE
- 5.2.2 连接到Gradio程序
- 1. 通过URL或SpaceID连接
- 2. 辅助:duplicate()和hf_token
- 5.2.3 查看API端点
- 1. 函数view_api()
- 2. 页面查看API
- 5.2.4 使用API
- 1. 直接调用.predict()
- 2. 异步调用job
- 参考文献
前言
本系列文章主要介绍WEB界面工具Gradio。Gradio是Hugging Face发布的一个简易的webui开发框架,它基于FastAPI和svelte,便于部署人工智能模型,是当前热门的非常易于开发和展示机器大语言模型及扩散模型的UI框架。本系列文章不仅从概念上介绍Gradio的详细技术架构、历史、应用场景、与其他框架Gradio/NiceGui/StreamLit/Dash/PyWebIO的区别,还进行了大量实践讲解。实践部分先讲解了多种不同的安装、运行和部署方式,然后实践了基础类的Interfaces、Blocks和Custom Components,最后对详解Gradio的多种高级特性,比如Gradio-Lite、Gradio Client和Tabular Data Science And Plots等。
本系列文章目录如下:
- 《全网最详细Gradio教程系列1——Gradio简介》
- 《全网最详细Gradio教程系列2——Gradio的安装与运行》
- 《全网最详细Gradio教程系列3——Gradio的3+1种部署方式实践》
- 《全网最详细Gradio教程系列4——浏览器集成Gradio-Lite》
- 《全网最详细Gradio教程系列5——Gradio Client: python》
- 《全网最详细Gradio教程系列5——Gradio Client: javascript》
- 《全网最详细Gradio教程系列5——Gradio Client: curl》
- 《全网最详细Gradio教程系列6——Interfaces》
- 《全网最详细Gradio教程系列7——Blocks》
- 《全网最详细Gradio教程系列8——Custom Components》
- 《全网最详细Gradio教程系列9——Tabular Data Science And Plots 》
本篇摘要
本章讲解访问API的Gradio Client的三种使用方式:python、javascript和curl。受字数限制,所以分三篇博客发布,本篇讲解javascript方式。
5. Gradio Client的三种使用方式
程序部署完成后,如何将Gradio App作为API访问使用呢,这就用到Gradio Client。Gradio Client的有三种使用方式:python、javascript和curl,以下分别讲解。
5.2 使用Gradio JavaScript Client
通过API使用Gradio应用程序,除了Python方式,还有JavaScript和Curl。JavaScript方式非常易于将任意Gradio应用程序用作API,本小节讲述以JavaScript方式使用Gradio Client,包括两种安装方式npm和CDN、web在线开发环境PLAYCODE、连接Gradio的两种方式URL和SpaceID、查看API的两种方式view_api和页面查看API和使用API的两种方式直接调用.predict()和异步调用job。
由于Python Client部分已经讲述了大部分程序细节,因此本节不再重复解释和编码,而是将对应Python命令替换为JavaScript方式,以减少篇幅。
5.2.1 安装
1. npm方式
Gradio JavaScript Client使用@gradio/client库,@gradio还包括一些其他库,比如lite、audio、image、vedio、chatbot及gallery等。安装@gradio/client库需要用到包管理器npm,而npm被包含在Node.js中,这里要求Node.js版本>=18.0.0。
首先,安装Node.js,其bash脚本如下:
# installs nvm (Node Version Manager)
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
# download and install Node.js (you may need to restart the terminal)
nvm install 20
# verifies the right Node.js version is in the environment
node -v # should print `v20.15.1`
# verifies the right NPM version is in the environment
npm -v # should print `10.7.0`
上述命令也可以直接在终端运行。安装完Node.js后,使用npm或其它包管理器安装@gradio/client:
$ npm i @gradio/client
added 64 packages in 10s
10 packages are looking for funding
run `npm fund` for details
此命令将@gradio/client添加到项目依赖项中,以便将其导入JavaScript或TypeScript文件中。
此时可测试简单的脚本,比如在控制台输出日志,代码如下:
console.log("hello NodeJS")
保存为hello.js,使用命令node运行,输出如下:
$ node hello.js
hello NodeJS
注意:
- 使用Ubuntu系统的apt-get命令并不能安装正确版本的nodejs或npm,安装的版本只有12.XX,并且不包含npm。
- 除了脚本方式,还可以通过Ubuntu software软件中心安装v20.15.1 Node.js。
2. CDN方式
为了将@gradio/client快速添加到web项目,可以使用jsDelivr CDN将最新版的@gradio/client加载到HTML文件中:
<script src="https://cdn.jsdelivr.net/npm/@gradio/client/dist/index.min.js"></script>
整体html文件内容如下所示:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="src/style.css">
<script type="module" src="https://cdn.jsdelivr.net/npm/@gradio/client/dist/index.min.js"></script>
</head>
<body>
<h1 id="header"></h1>
<script type="module" src="src/script.js"></script>
</body>
</html>
在文件script.js可填入javascript脚本内容,如下:
console.log("hello NodeJS")
注意:一定要将<script>内容添加到HTML的<head>标签中,这将安装最新版本的@gradio/client,但建议在生产环境中硬编码该版本。然后将js脚本放在<body>的<script>标签内。这种方法尽管有一定局限性,但非常适合实验或原型制作。更详细的用法可参考jsDelivr的官方网站:https://www.jsdelivr.com/package/npm/@gradio/client。
3. 在线运行环境:PLAYCODE
为了演示CDN方式使用@gradio/client,同时也便于运行使用SpaceID创建的App,我们引入国外的在线运行平台。在众多在线运行环境中,作者推荐PLAYCODE,官网地址:https://playcode.io/。它界面简洁,支持javascript/typescript/html/css/React/vue/svelte等多种格式及和它们相关的运行库,并且可以在线安装所需package,非常方便在线开发小程序。
在线安装@gradio/client后,在运行带有关键字await的示例程序时,会报以下错误:
Top-level await is currently not supported with the "cjs" output format
这是因为默认情况下,顶层的await不会被所有JavaScript运行时或者编译工具所支持。这时只需要用async()方法将await关键字代码包裹即可,我们以查看API代码为例(后面带有await关键字的代码也用同样方式处理即可,限于篇幅,作者不再赘述,请读者自行添加):
import { Client } from "@gradio/client";
(async () => {
const app = await Client.connect("abidlabs/whisper");
const app_info = await app.view_api();
console.log(app_info);
})();
运行截图如下:
这里主要为了演示CDN的使用方式,但本篇命令npm和cdn两种方式均适用。
5.2.2 连接到Gradio程序
1. 通过URL或SpaceID连接
首先,实例化Client时,需将其连接到运行在Hugging Face Spaces或其它在线网络上的Gradio应用程序,可以通过SpaceID和URL两种方式,如下:
import { Client } from "@gradio/client";
# 通过URL连接
const app = Client.connect("https://huggingface.co/spaces/gradio/calculator")
# 通过SpaceID连接
const app = Client.connect("gradio/calculator")
2. 辅助:duplicate()和hf_token
对于Space的无限制访问,可以使用duplicate()。当使用duplicate()或访问私有Space时,需要用到hf_token,示例代码如下:
import { Client } from "@gradio/client";
const response = await fetch(
"https://audio-samples.github.io/samples/mp3/blizzard_unconditional/sample-0.mp3"
);
const audio_file = await response.blob();
const app = await Client.duplicate("abidlabs/whisper", { hf_token: "hf_..." });
const transcription = await app.predict("/predict", [audio_file]);
console.log(transcription.data);
注意:
- 如果之前复制了一个空间,重新运行Client.duplicate()不会创建新的空间。相反,客户端将连接到之前创建的空间。因此,使用相同的空格多次重新运行Client.duplicate()方法是安全的。
- 如果原始Space使用GPU,复制过来的私人Space也将使用GPU,我们的Hugging Face帐户将根据GPU的价格计费。为了尽量减少费用,可以让共享空间将在5分钟不活动后自动进入睡眠状态,或者使用duplicate参数的硬件和超时属性设置硬件,代码如下所示:
import { Client } from "@gradio/client";
const app = await Client.duplicate("abidlabs/whisper", {
hf_token: "hf_...",
timeout: 60,
hardware: "a10g-small" # 免费设置"cpu-basic"
});
5.2.3 查看API端点
与Python一样,javascript也有两种方式查看api:函数view_api()和页面查看API。
1. 函数view_api()
连接到Gradio应用程序后,我们可以通过调用客户端的view_api()方法查看可用的api。对于Whisper Space,由于直接使用SpaceID会出现连接错误,我们可以在本地运行abidlabs/whisper后,使用http://127.0.0.1:7860连接:
import { Client } from "@gradio/client";
const app = await Client.connect("http://127.0.0.1:7860");
const app_info = await app.view_api(all_endpoints=True);
console.log(app_info);
关于/predict及端点的解释请参考前面内容,这里只解释参数all_endpoints。当Gradio应用程序具有未命名的API端点时,可以设置all_endpoints来显示这些端点。
保存后缀为.js的文件后,使用命令node xxx.js运行,可以看到以下输出:
$ node gradio_client_viewapi.js
{
named_endpoints: {
'/predict': { parameters: [Array], returns: [Array], type: [Object] }
},
unnamed_endpoints: { '0': { parameters: [Array], returns: [Array], type: [Object] } }
}
# 官方示例输出
{
"named_endpoints": {
"/predict": {
"parameters": [
{
"label": "text",
"component": "Textbox",
"type": "string"
}
],
"returns": [
{
"label": "output",
"component": "Textbox",
"type": "string"
}
]
}
},
"unnamed_endpoints": {}
}
这里运行的结果和官方示例有一些出入,这里不做深究,了解用法就可以了。
2. 页面查看API
我们也可以单击Gradio应用程序页脚中的“通过api使用”链接,它向我们显示了相同的信息以及示例用法,如下图:
查看API页面还包括一个“API Recorder”,它可以记录我们与Gradio UI的交互信息,并将交互转换为相应的代码,以便与JS客户端一起运行。
5.2.4 使用API
查看完API后,就需要学会使用它们。有两种使用方式:直接调用.predict()和异步调用job。
1. 直接调用.predict()
最简单的使用API进行预测的方法就是使用适当的参数调用.predict()方法:
import { Client } from "@gradio/client";
const app = await Client.connect("abidlabs/en2fr");
const result = await app.predict("/predict", ["Hello"]);
console.log(result);
>>(5) {type: "data", time: Tue Jul 23 2024...}
type:"data"
time:Tue Jul 23 2024 10:42:09 GMT+0800 (中国标准时间)
data:(1) ["Bonjour."]
endpoint:"/predict"
fn_index:0
[[Prototype]]:{}
我们在来看一下其它的参数类型:
多个参数:可以将多个参数作为数组传递给.predict(),并且对结果进行json字符化处理,如下所示:
import { Client } from "@gradio/client";
const app = await Client.connect("gradio/calculator");
const result = await app.predict("/predict", [4, "add", 5]);
console.log(JSON.stringify(result, null, 2));
>>{
"type": "data",
"time": "2024-07-23T07:43:34.380Z",
"data": [
9
],
"endpoint": "/predict",
"fn_index": 0
}
多媒体格式:对于某些多媒体格式,如图像,可以根据需要传入Buffer、Blob或File。在Node节点中,可以使用Buffer或Blob;在浏览器环境中,可使用Blob或File。
import { Client } from "@gradio/client";
const response = await fetch(
"https://audio-samples.github.io/samples/mp3/blizzard_unconditional/sample-0.mp3"
);
const audio_file = await response.blob();
const app = await Client.connect("abidlabs/whisper");
const result = await app.predict("/predict", [audio_file]);
>>(5) {type: "data", time: Tue Jul 23 2024...}
type:"data"
time:Tue Jul 23 2024 10:55:37 GMT+0800 (中国标准时间)
data:(1) ["AutomaticSpeechRecognitionOutput(t...]
0:"AutomaticSpeechRecognitionOutput(text=" My thought I have nobody by a beauty and will as you poured. Mr. Rochester is sir, but that so don't find simpus, and devoted abode, to at might in a", chunks=None)"
[[Prototype]]:[]
endpoint:"/predict"
fn_index:0
[[Prototype]]:{}
由于需要传输.mp3文件,所以示例代码有很大几率失败,多运行几次即可。
2. 异步调用job
因为.predict()是一个阻塞操作,所以当等待API返回结果期间,可以让其在后台运行。我们可以通过创建Job实例并利用其.submit()提交操作,让JOB在后台运行,通过使用可迭代接口获取返回的结果。这对可迭代端点或生成器端点特别有用,它们会随着时间的推移返回一系列的离散响应值。示例代码如下:
import { Client } from "@gradio/client";
function log_result(payload) {
const {
data: [translation]
} = payload;
console.log(`The translated result is: ${translation}`);
}
const app = await Client.connect("abidlabs/en2fr");
const job = app.submit("/predict", ["Hello"]);
for await (const message of job) {
log_result(message);
}
在示例代码中,我们首先利用Client创建JOB,然后循环等待可迭代端点返回结果,最后在平台日志输出返回的结果。输出效果参考下面例子。
事件与状态:在连接Gradio应用程序时,默认返回数据。我们还可以在实例化client时,通过设置事件接口.connect()的参数events,将status和data作为数组传递给events,以便同时获取正在运行job的状态和数据,示例代码如下:
import { Client } from "@gradio/client";
function log_status(status) {
console.log(
`The current status for this job is: ${JSON.stringify(status, null, 2)}.`
);
}
const app = await Client.connect("abidlabs/en2fr", {
events: ["status", "data"]
});
const job = app.submit("/predict", ["Hello"]);
for await (const message of job) {
if (message.type === "status") {
log_status(message);
}
}
运行截图如下所示:
返回的状态包括以下属性:stage(当前作业的可读状态,如"pending" | “generating” | “complete” | “error”),code(gradio的job状态代码),position(此作业在队列中的当前位置),queue(是否排队),size(队列总大小),eta(此作业预计完成的时间),success(作业是否成功完成的布尔值),以及time(Date对象,详细说明状态生成的时间)。
取消作业:job实例还有一个.cancel()方法,用于取消已排队但尚未启动的作业,如下:
import { Client } from "@gradio/client";
const app = await Client.connect("abidlabs/en2fr");
const job_one = app.submit("/predict", ["Hello"]);
const job_two = app.submit("/predict", ["Friends"]);
job_one.cancel();
job_two.cancel();
如果第一个作业已开始处理,则不会取消,但client客户端将不再接受更新信息(丢弃作业)。如果第二个作业尚未启动,它将被成功取消并从队列中删除。
生成器端点:某些Gradio API端点不返回单个值,而是返回一系列值。这时可以使用可迭代接口实时监听这些值:
import { Client } from "@gradio/client";
const app = await Client.connect("gradio/count_generator");
const job = app.submit(0, [9]);
for await (const message of job) {
console.log(message.data);
}
setTimeout(() => {
job.cancel();
}, 3000);
上面代码将输出endpoint端点产生的系列值,然后通过设置超时函数setTimeout(),在3秒后取消迭代输出的job,这时job将立即完成。
参考文献
- Getting Started with the Gradio JavaScript Client