【Gradio】构建自定义多模态聊天机器人

55a77da8d3b7cae7ed98c7fa8c6f539e.png

这是我们构建自定义多模态聊天机器人组件两部分系列的第一部分。在第一部分中,我们将修改 Gradio 聊天机器人组件,使其能够在同一消息中显示文本和媒体文件(视频、音频、图片)。在第二部分中,我们将构建一个自定义的文本框组件,该组件能够向聊天机器人发送多模态消息(文本和媒体文件)。

您可以跟随这篇文章的作者,通过以下 YouTube 视频实现聊天机器人组件!

这里是我们的多模态聊天机器人组件将会是什么样子的预览:

ab509c942f7d8496538aea0f2d2f01fa.png

第 1 部分 - 创建我们的项目 

对于这个演示,我们将调整现有的 Gradio Chatbot 组件,以便在同一消息中显示文本和媒体文件。让我们通过模板化 Chatbot 组件源代码来创建一个新的自定义组件目录。

gradio cc create MultimodalChatbot --template Chatbot

我们准备好出发了!

✍️ 提示:确保在 pyproject.toml 文件中修改 Author 键。

第 2a 部分 - 后端数据模型 

在您最喜欢的代码编辑器中打开 multimodalchatbot.py 文件,让我们开始修改组件的后端。

我们将要做的第一件事是创建我们组件的 data_model 。 data_model 是您的 Python 组件将接收并发送给运行 UI 的 JavaScript 客户端的数据格式。您可以在后端指南中了解更多关于 data_model 的信息。

对于我们的组件,每个聊天机器人消息将包含两个键:一个 text 键用于显示文本消息,以及一个可选的媒体文件列表,可以显示在文本下方。

从 gradio.data_classes 导入 FileData 和 GradioModel 类,并修改现有的 ChatbotData 类,使其看起来如下:

# 定义文件消息类,继承自 GradioModel
class FileMessage(GradioModel):
    # 文件数据,类型为 FileData
    file: FileData
    # 可选的替代文本,类型为可选的字符串
    alt_text: Optional[str] = None


# 定义多模态消息类,继承自 GradioModel
class MultimodalMessage(GradioModel):
    # 文本消息,类型为可选的字符串
    text: Optional[str] = None
    # 文件消息,类型为 FileMessage 类的列表,是可选的
    files: Optional[List[FileMessage]] = None


# 定义聊天机器人数据类,继承自 GradioRootModel
class ChatbotData(GradioRootModel):
    # 聊天数据,类型为包含多模态消息类的元组的列表
    root: List[Tuple[Optional[MultimodalMessage], Optional[MultimodalMessage]]]


# 定义多模态聊天机器人组件,继承自 Component
class MultimodalChatbot(Component):
    ...
    # 多模态聊天机器人组件的数据模型为聊天机器人数据类
    data_model = ChatbotData

✍️ 提示:`data_model`是使用`Pydantic V2`实现的。请阅读此处的文档。

我们已经完成了最困难的部分!

第 2b 部分 - 预处理和后处理方法 

对于 preprocess 方法,我们将保持简单,将一个 MultimodalMessage 列表传递给使用此组件作为输入的 python 函数。这将允许我们组件的用户使用 .text 和 .files 属性访问聊天机器人数据。这是您可以在实现中修改的设计选择!我们可以像这样返回带有 root 属性的 ChatbotData 的消息列表:

# 定义预处理方法,输入参数为聊天数据,类型为 ChatbotData 或 None
def preprocess(
    self,
    payload: ChatbotData | None,
) -> List[MultimodalMessage] | None:
    # 如果输入参数为空,则直接返回
    if payload is None:
        return payload
    # 如果存在聊天数据,则返回其中的 root 属性,即所有的聊天消息
    return payload.root

✍️ 提示:了解`preprocess`和`postprocess`方法背后的原理,请参阅关键概念指南

在 postprocess 方法中,我们将强制 python 函数返回的每条消息都是一个 MultimodalMessage 类。我们还将清理 text 字段中的任何缩进,以便它可以在前端正确显示为 markdown。

我们可以保留 postprocess 方法并修改 _postprocess_chat_messages

# 定义后处理聊天消息的方法,输入参数为多模态消息,类型为 MultimodalMessage 或 字典 或 None
def _postprocess_chat_messages(
    self, chat_message: MultimodalMessage | dict | None
) -> MultimodalMessage | None:
    # 如果聊天消息为空,则直接返回 None
    if chat_message is None:
        return None
    # 如果聊天消息是一个字典,则将其转化为 MultimodalMessage 类型
    if isinstance(chat_message, dict):
        chat_message = MultimodalMessage(**chat_message)
    # 清理聊天消息的文本,使用 cleandoc 方法对文本进行清理,如果文本不存在,则返回一个空字符串
    chat_message.text = inspect.cleandoc(chat_message.text or "")
    # 遍历消息中的所有文件
    for file_ in chat_message.files:
        # 使用 get_mimetype 方法获取文件的 MIME 类型,并设置给文件的 mime_type 属性
        file_.file.mime_type = client_utils.get_mimetype(file_.file.path)
    # 返回处理过后的聊天消息
    return chat_message

在我们结束后端代码之前,让我们修改 example_value 和 example_payload 方法,以返回 ChatbotData 的有效字典表示形式:

# 定义example_value方法,返回一个任意类型的值
def example_value(self) -> Any:
    # 返回一个示例聊天信息(仅包含文本“Hello!”)的列表
    return [[{"text": "Hello!", "files": []}, None]]


# 定义example_payload方法,返回一个任意类型的值
def example_payload(self) -> Any:
    # 返回一个示例聊天信息(仅包含文本“Hello!”)的列表
    return [[{"text": "Hello!", "files": []}, None]]

恭喜 - 后端已完成!

第 3a 部分 - Index.svelte 文件 

Chatbot 组件的前端分为两部分 - Index.svelte 文件和 shared/Chatbot.svelte 文件。 Index.svelte 文件对从服务器接收的数据进行一些处理,然后将对话的渲染委托给 shared/Chatbot.svelte 文件。首先我们将修改 Index.svelte 文件,以对后端将返回的新数据类型进行处理。

让我们开始将我们的自定义类型从我们的 Python data_model 移植到 TypeScript。打开 frontend/shared/utils.ts ,并在文件顶部添加以下类型定义:

// 定义了一个名为FileMessage的类型,
// 其中包含一个必需的file属性,类型为FileData,
// 和一个可选的alt_text属性,类型为字符串(string)。
export type FileMessage = {
    file: FileData;
    alt_text?: string;
};


// 定义了一个名为MultimodalMessage的类型,
// 其中包含一个必需的text属性,类型为字符串(string),
// 和一个可选的files属性,类型为FileMessage类型的数组。
export type MultimodalMessage = {
    text: string;
    files?: FileMessage[];
}

现在让我们在 Index.svelte 中导入它们,并修改 value 和 _value 的类型注释。

// 从 "shared/utils" 导入 FileMessage 和 MultimodalMessage 类型
import type { FileMessage, MultimodalMessage } from "./shared/utils";


// 定义一个名为 value 的变量,
// 其类型为一个数组,数组中的每个元素是一个长度为2的元组(Tuple),
// 且元组的两个元素都是 MultimodalMessage 或 null。
export let value: [
    MultimodalMessage | null,
    MultimodalMessage | null
][] = [];


// 定义一个名为 _value 的变量,其类型与 value 变量相同
// 但该 _value 变量并未被赋初始值
let _value: [
    MultimodalMessage | null,
    MultimodalMessage | null
][];

我们需要规范化每条消息,以确保每个文件都有一个正确的 URL 来获取其内容。我们还需要在 text 键中格式化任何嵌入的文件链接。让我们添加一个 process_message 实用程序函数,并在 value 发生变化时应用它。

// 定义一个函数process_message,
// 接受一个MultimodalMessage或null作为参数,
// 返回一个MultimodalMessage或null
function process_message(msg: MultimodalMessage | null): MultimodalMessage | null {
    // 如果传入的消息为null,则直接返回null
    if (msg === null) {
        return msg;
    }
    // 将消息文本重定向至某个URL
    // (具体实现取决于redirect_src_url函数,
    // 在此未给出)
    msg.text = redirect_src_url(msg.text); 
    // 对消息中的files进行标准化处理
    // (具体实现取决于normalize_messages函数,
    // 在此未给出)
    msg.files = msg.files.map(normalize_messages); 
    // 返回处理后的消息对象
    return msg; 
}


// 创建一个响应式变量_value,
// 它的值取决于value变量
// 如果value为空,则_value也为空数组;
// 否则,_value的值为value数组中的每一对
// [user_msg, bot_msg]经过process_message函数处理后的结果。
$: _value = value
  ? value.map(([user_msg, bot_msg]) => [
          process_message(user_msg),
          process_message(bot_msg)
     ])
  : [];

以上代码的功能是接收输入消息(用户或bot的消息),并进行相应的处理(包括重定向文本的URL和标准化文件消息),然后返回处理后的消息。其中_value是一个响应式变量,它会随着value的变化而相应变化。

第 3b 部分 - Chatbot.svelte 文件 

让我们开始类似于 Index.svelte 文件,首先修改类型注释。在 <script> 部分的顶部导入 Mulimodal 消息,并使用它来为 value 和 old_value 变量进行类型定义。

// 从 "utils" 导入 MultimodalMessage 类型
import type { MultimodalMessage } from "./utils";


// 定义一个名为value的变量,
// 它的类型可以是一个数组(其中每个元素为一个元组,元组的长度为2,元素类型可以是MultimodalMessage或null),
// 也可以是null。
export let value:
    | [
            MultimodalMessage | null,
            MultimodalMessage | null
        ][]
    | null;


// 定义一个名为old_value的变量,它的类型与value变量一样,初始值为null。
let old_value:
    | [
            MultimodalMessage | null,
            MultimodalMessage | null
        ][]
    | null = null;

我们还需要修改 handle_select 和 handle_like 函数:

// 定义一个名为handle_select的函数,这个函数接收三个参数:
// i 和 j 分别为两个数字,表示操作的索引位置,
// message 是一个 MultimodalMessage 对象或者 null,
// 这个函数不返回任何值。
// 函数的功能是,当选中一个消息时,触发一个名为 "select" 的事件,并将相关信息作为事件的参数。
function handle_select(
    i: number,
    j: number,
    message: MultimodalMessage | null
): void {
    dispatch("select", { // 触发 select 事件
        index: [i, j], // 将 i 和 j 作为索引位置信息
        value: message // 将 message 作为选中的消息传入
    });
}


// 定义一个名为handle_like的函数,这个函数接收四个参数:
// i 和 j 分别为两个数字,表示操作的索引位置,
// message 是一个 MultimodalMessage 对象或者 null,
// liked 是一个布尔值,表示是否喜欢。
// 这个函数不返回任何值。
// 函数的功能是,当对一个消息进行 "喜欢/不喜欢" 操作时,触发一个名为 "like" 的事件,并将相关信息作为事件的参数。
function handle_like(
    i: number,
    j: number,
    message: MultimodalMessage | null,
    liked: boolean
): void {
    dispatch("like", { // 触发 like 事件
        index: [i, j], // 将 i 和 j 作为索引位置信息
        value: message, // 将 message 作为操作的消息传入
        liked: liked // 将 liked 作为操作的类型传入 (喜欢或不喜欢)
    });
}

现在开始有趣的部分,实际上在同一消息中渲染文本和文件!

你应该会看到类似以下的代码,它根据消息的类型确定是显示一个文件还是一个 markdown 消息:

// 如果消息类型为字符串
{#if typeof message === "string"}
    // 使用Markdown组件来渲染字符串类型的消息
    <Markdown
        {message} // 传入消息文本
        {latex_delimiters} // LaTeX分隔符,用于渲染LaTeX内容
        {sanitize_html} // 清理HTML,防止跨站脚本攻击
        {render_markdown} // 渲染Markdown,将Markdown格式的文本转换为 HTML
        {line_breaks} // 处理换行
        on:load={scroll} // 当内容加载完成时,执行滚动操作,以便查看新加载的内容
    />


// 否则如果消息不为null,且消息中的文件类型包含 "audio"
{:else if message !== null && message.file?.mime_type?.includes("audio")}
    // 使用HTML的audio元素来渲染音频类型的消息
    <audio
        data-testid="chatbot-audio" // 设置数据测试id为 "chatbot-audio",便于进行单元测试
        controls // 显示媒体播放器的默认控制器
        preload="metadata" // 在页面加载时加载音频的元数据,但不加载音频文件本身
        ...

我们将修改这段代码,使其总是显示文本消息,然后遍历文件并显示所有存在的文件:

// 使用Markdown元素来渲染消息文本
<Markdown
    message={message.text}
    {latex_delimiters}
    {sanitize_html}
    {render_markdown}
    {line_breaks}
    on:load={scroll}
/>
// 循环遍历message中的文件数组
{#each message.files as file, k}
    // 如果文件类型包含 "audio"
    {#if file !== null && file.file.mime_type?.includes("audio")}
        // 渲染audio元素来播放音频文件
        <audio
            data-testid="chatbot-audio"
            controls
            preload="metadata"
            src={file.file?.url}
            title={file.alt_text}
            on:play
            on:pause
            on:ended
        />
    // 如果文件类型包含 "video"
    {:else if message !== null && file.file?.mime_type?.includes("video")}
        // 渲染video元素来播放视频文件
        <video
            data-testid="chatbot-video"
            controls
            src={file.file?.url}
            title={file.alt_text}
            preload="auto"
            on:play
            on:pause
            on:ended
        >
            <track kind="captions" />
        </video>
    // 如果文件类型包含 "image"
    {:else if message !== null && file.file?.mime_type?.includes("image")}
        // 渲染img元素来显示图片文件
        <img
            data-testid="chatbot-image"
            src={file.file?.url}
            alt={file.alt_text}
        />
    // 如果文件不是音频、视频或图片文件
    {:else if message !== null && file.file?.url !== null}
        // 渲染a元素来提供文件下载链接
        <a
            data-testid="chatbot-file"
            href={file.file?.url}
            target="_blank"
            download={window.__is_colab__
                ? null
                : file.file?.orig_name || file.file?.path}
        >
            {file.file?.orig_name || file.file?.path}
        </a>
    // 如果消息仍在发送中
    {:else if pending_message && j === 1}
        // 渲染Pending元素表示消息仍在发送中
        <Pending {layout} />
    {/if}
{/each}

我们做到了!🎉

第 4 部分 - 演示 

对于这个教程,让我们保持演示的简单性,只展示一个假想用户和一个机器人之间的静态对话。这个演示将展示用户和机器人如何发送文件。在这个教程系列的第 2 部分,我们将构建一个功能齐全的聊天机器人演示!

演示代码将如下所示:

# 导入 gradio 库
import gradio as gr
# 导入来自 gradio_multimodalchatbot 的 MultimodalChatbot 类
from gradio_multimodalchatbot import MultimodalChatbot
# 导入来自 gradio.data_classes 的 FileData 类
from gradio.data_classes import FileData


# 先定义一些需要用到的用户和机器人的消息
# 这里的第一组消息是文字和图像
user_msg1 = {"text": "Hello, what is in this image?",
             "files": [{"file": FileData(path="https://gradio-builds.s3.amazonaws.com/diffusion_image/cute_dog.jpg")}]
             }
bot_msg1 = {"text": "It is a very cute dog",
            "files": []}


# 第二组消息是文字和音频文件
user_msg2 = {"text": "Describe this audio clip please.",
             "files": [{"file": FileData(path="cantina.wav")}]}
bot_msg2 = {"text": "It is the cantina song from Star Wars",
            "files": []}


# 第三组消息是文字和视频文件以及音频文件
user_msg3 = {"text": "Give me a video clip please.",
             "files": []}
bot_msg3 = {"text": "Here is a video clip of the world",
            "files": [{"file": FileData(path="world.mp4")},
                      {"file": FileData(path="cantina.wav")}]}


# 聊天记录存放在 conversation 中
conversation = [[user_msg1, bot_msg1], [user_msg2, bot_msg2], [user_msg3, bot_msg3]]


# 使用 gr.Blocks 构建交互界面
with gr.Blocks() as demo:
    # 生成一个多模态聊天机器人的模块,设置值为聊天记录,设置窗口高度为 800
    MultimodalChatbot(value=conversation, height=800)


# 启动交互界面
demo.launch()

提示:更改文件路径以对应于您的计算机上的文件。另外,如果您处于开发模式,请确保文件位于自定义组件目录的顶层。

第 5 部分 - 部署与总结 

让我们用 gradio cc build 和 gradio cc deploy 构建并部署我们的演示!

您可以在 HuggingFace Spaces 查看我们部署的组件,所有源代码都可以在这里https://huggingface.co/spaces/freddyaboulton/gradio_multimodalchatbot/tree/main找到https://huggingface.co/spaces/freddyaboulton/gradio_multimodalchatbot/tree/main/src。

记录自定义组件

146f1a69fc43d44a24fc2bd925e9062a.png

在 4.15 中,我们向 Gradio CLI 添加了一个新的 gradio cc docs 命令,以生成丰富的自定义组件文档。这个命令将自动为用户生成文档,但要充分利用它,您需要做一些事情。

我该如何使用它? 

当运行 gradio cc build 时,文档将被生成。您可以传递 --no-generate-docs 参数来关闭此行为。

还有一个独立的 docs 命令,允许进行更大的自定义。如果您手动运行此命令,应该在您的 pyproject.toml 中的 version 被提升后但在构建组件之前运行。

所有参数都是可选的。

这段是关于使用 gradio cc 命令进行自定义组件文档生成的命令行参数说明。
path : 自定义组件的目录。
--demo-dir : 演示目录的路径。
--demo-name : 演示文件的名称。
--space-url : 用于链接的 Hugging Face Space 的 URL。
--generate-space : 创建一个文档空间。
--no-generate-space : 不创建一个文档空间。
--readme-path : README.md 文件的路径。
--generate-readme : 创建一个 README.md 文件。
--no-generate-readme : 不创建一个 README.md 文件。
--suppress-demo-check :抑制验证检查和警告。
以上这些参数都是为了帮助用户创建和配置自定义组件的文档,并能够快捷地进行演示和发布到 Hugging Face Space。

生成了什么? 

gradio cc docs 命令将生成一个交互式的 Gradio 应用程序和一个具有各种功能的静态 README 文件。您可以在这里看到一个例子:

  • 部署在 Hugging Face Spaces 的 Gradio 应用https://www.gradio.app/guides/documenting-custom-components

  • 由 GitHub 渲染的 README.md https://www.gradio.app/guides/documenting-custom-components

README.md 和空间都具有以下特点:

  •  一个描述。

  • 安装说明。

  • 一个完全功能的代码片段。

  • 可选的链接到 PyPi、GitHub 和 Hugging Face Spaces。

  • 包括以下内容的 API 文档:

    • 用于组件初始化的参数表,显示类型、默认值和描述。

    • 该组件如何影响用户的预测功能的描述。

    • 事件及其描述的表格。

    • 在初始化或预处理器或后处理器中可能使用的任何额外接口或类。

此外,Gradio 包括:

  •  一个现场演示。

  • 参数表的一个更丰富、互动的版本。

  •  更好的样式!

我需要做什么? 

文档生成器使用现有标准来提取必要信息,即类型提示和文档字符串。没有特定于 Gradio 的 API 用于文档,因此遵循最佳实践通常会产生最好的结果。

如果您在组件源代码中已经使用了类型提示和文档字符串,那么您不需要做太多工作就能从这个功能中受益,但有一些细节您需要注意。

 Python 版本 

为了获得最佳的文档体验,您在生成文档时需要使用 Python 3.10 或更高版本。这是因为一些用于生成文档的自省功能是在 3.10 中才添加的。

 类型提示 

Python 类型提示被广泛用于为用户提供有用的信息。 什么是类型提示?

我需要添加提示到哪里? 

您不需要为代码的每个部分添加类型提示。为了使文档正确工作,您需要为以下组件方法添加类型提示:

  • __init__ 参数应该被打字。

  • postprocess 参数和返回值应该被打字。

  • preprocess 参数和返回值应该被键入。

如果您正在使用 gradio cc create ,这些类型应该已经存在,但您可能需要根据您所做的任何更改来调整它们。

__init__

在这里,您只需要键入参数。如果您已经用 ` gradio cc create` 克隆了一个模板,这些应该已经到位。您只需要为您添加或更改的任何内容添加新的提示:

def __init__(
  self,
  # "value" 参数用于接受一个字符串输入,其默认值为 None
  # "value" 可以接受的类型是 字符串 (str) 或者 None
  value: str | None = None,
  *,
  # "sources" 参数用于选择音频来源,可以是 "upload" (上传)或者 "microphone" (麦克风)
  # "sources" 可以接受的值是 "upload" 或者 "microphone"
  sources: Literal["upload", "microphone"] = "upload,
  # "every" 参数表示每隔一段时间进行一次操作,其单位是秒,其默认值为 None
  # "every" 可以接受的类型是 浮点型 (float) 或者 None
  every: float | None = None,
  ...
):
  # 函数体内容省略
  ...
preprocess 和 postprocess 

preprocess 和 postprocess 方法确定传递给用户函数的值以及需要返回的值。

即使你的组件设计主要是作为输入或输出,添加类型提示到输入参数和返回值也是值得的,因为 Gradio 无法限制组件的使用方式。

在这种情况下,我们特别关心:

  • 返回类型为 preprocess 。

  • postprocess 的输入类型。

# "preprocess" 函数,用于预处理数据
# 接受一个 "payload" 参数,类型可以为 FileData 或 None,表示输入是可选的
# 函数返回的类型可以为包含整型和字符串的元组,或者字符串,或者 None
def preprocess(
  self, payload: FileData | None 
) -> tuple[int, str] | str | None:
  # 函数体省略
  ...


# "postprocess" 函数,用于后处理数据
# 接受一个 "value" 参数,类型可以为包含整型和字符串的元组,或者 None
# 函数返回的类型可以为 FileData,或者字节串,或者 None,表示返回值是可选的
def postprocess(
  self, value: tuple[int, str] | None
) -> FileData | bytes | None:
  # 函数体省略
  ...

 文档字符串 

文档字符串也被广泛用于提取 API 某些部分更有意义、更易于人阅读的描述。

什么是文档字符串?

虽然文档字符串没有任何语法要求,但我们需要一个特定的结构来进行文档编写目的。

就像类型提示一样,我们关心的具体信息如下:

  • __init__ 参数文档字符串。

  •   preprocess 返回文档字符串。

  • postprocess 输入参数文档字符串。

其他一切都是可选的。

文档字符串应始终采用此格式,以便由文档生成器捕获:

Classes
"""
A description of the class.


This can span multiple lines and can _contain_ *markdown*.
"""

 事件 

在自定义组件中,事件被表示为存储在组件类的 events 字段上的列表。虽然我们不需要事件的类型,但我们确实需要一个人类可读的描述,以便用户可以理解事件的行为。

为了方便这一点,我们必须以特定的方式创建事件。

有两种方法可以向自定义组件添加事件。

 内置事件 

Gradio 附带了多种内置事件,这些可能已足够您的组件使用。如果您正在使用内置事件,您无需进行任何操作,因为我们可以提取它们已有的描述:

# 从 gradio.events 模块中导入 Events 类
from gradio.events import Events


# 定义一个名为 ParamViewer 的类,继承自 Component 类
class ParamViewer(Component):
  # 类的其他定义部分省略...


  # 在类中定义一个名为 EVENTS 的列表,列表中包含了两个由 Events 类定义的事件:change 和 upload
  EVENTS = [
    Events.change,   # change 事件,通常会在表单值改变时触发
    Events.upload,   # upload 事件,通常会在上传文件完成时触发
  ]
 自定义事件 

如果内置事件不适合您的用例,您可以定义一个自定义事件。这是一个简单的过程,但您必须以这种方式创建事件,以便 docstrings 能够正确工作:

# 从 gradio.events 模块中导入 Events 和 EventListener 类
from gradio.events import Events, EventListener


# 定义一个名为 ParamViewer 的类,继承自 Component 类
class ParamViewer(Component):
  # 类的其他定义部分省略...


  # 在类中定义一个名为 EVENTS 的列表
  EVENTS = [
    # 包含了一个叫做 change 的事件,通常会在表单值改变时触发
    Events.change,
    # 使用 EventListener 创建了一个名为 "bingbong" 的事件监听器
    # 当用户做出一次 "bingbong" 操作时,这个事件监听器就会触发
    # "doc" 参数用来给这个事件监听器添加一段文档字符串,用于描述这个事件监听器的用途和作用
    EventListener(
        "bingbong",
        doc="This listener is triggered when the user does a bingbong."
      ),
  ]

 演示 

demo/app.py 经常用于开发组件,生成实时演示和代码片段。这里唯一的严格规则是 demo.launch() 命令必须包含在如下的 __name__ == "__main__" 条件中:

if __name__ == "__main__":
  demo.launch()

文档生成器将扫描此类条款,并在缺少时报错。如果您不在 demo/app.py 内启动演示,那么您可以传递 --suppress-demo-check 来关闭此检查。

演示推荐 

虽然没有额外的规则,但有一些最佳实践你应该记住,以便从文档生成器中获得最佳体验。

这些只是指导原则,每种情况都是独特的,但它们是值得记住的健全原则。

保持演示紧凑 

紧凑的演示看起来更好,也使用户更容易理解演示的作用。尽可能移除多余的 UI 元素,以便将用户的注意力集中在核心用例上。

有时,为文档专门设置一个 demo/app.py ,并为你的测试目的准备一个更复杂的应用程序是有意义的。你也可以创建其他空间,展示更复杂的示例,并通过主类文档字符串或 pyproject.toml 描述将它们链接起来。

保持代码简洁 

“入门”代码片段使用了演示代码,应尽可能简短,以保持用户参与并避免混淆。

样本代码片段的任务不是展示整个 API;这个片段应该是新用户成功的最短路径。它应该易于键入或复制粘贴,并且容易理解。解释性评论应该简洁明了。

避免外部依赖 

如上所述,用户应能够复制粘贴代码片段并拥有一个完全工作的应用程序。尝试避免第三方库依赖以便做到这一点。

您应该仔细考虑任何示例;避免需要额外文件的示例或对环境作出假设的示例通常是个好主意。

确保 demo 目录是自包含的 

只有在某些情况下, demo 目录会被上传到 Hugging Face 空间,因为如果可能的话,组件会通过 PyPi 安装。至关重要的是,这个目录是自包含的,任何需要正确运行演示的文件都必须存在。

 额外的 URLs 

文档生成器将生成一些按钮,为用户提供有用的信息和链接。它们在某些情况下会自动获得,但有些需要在 pyproject.yaml 中明确包含。

  • PyPi 版本和链接 - 这是自动生成的。

  • GitHub 仓库 - 这是通过 pyproject.toml 的 project.urls.repository 填充的。

  • 拥抱脸空间 - 这是通过 pyproject.toml 的 project.urls.space 填充的。

一个例子 pyproject.toml 网址部分可能看起来像这样:

[project.urls]
repository = "https://github.com/user/repo-name"
space = "https://huggingface.co/spaces/user/space-name"

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/734045.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

3D视觉引导机器人提升生产线的自动化水平和智能化程度

随着智能化技术的不断发展&#xff0c;汽车制造企业正积极寻求提升智能化水平的途径。富唯智能的3D视觉引导机器人抓取技术为汽车制造企业提供了一种高效、智能的自动化解决方案。 项目目标 某汽车制造企业希望通过引入智能化技术提升生产线的自动化水平和智能化程度。他们希望…

哎呦我, HashMap KeySet有序? 好像是哈

背景&#xff1a;有8个格子&#xff0c;上架物品时需要从第一个格子开始上架&#xff0c;不能跳格子&#xff0c;也就是说 如果格子1空着&#xff0c;就不能把物品放到格子2。有这么个顺序的情况 前人模块功能实现&#xff1a; 用HashMap 初始化格子信息&#xff0c;然后用 Ke…

2024年【T电梯修理】免费试题及T电梯修理考试总结

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 T电梯修理免费试题参考答案及T电梯修理考试试题解析是安全生产模拟考试一点通题库老师及T电梯修理操作证已考过的学员汇总&#xff0c;相对有效帮助T电梯修理考试总结学员顺利通过考试。 1、【多选题】TSGT7005-2012《…

基于vue3 + ant-design 使用阿里图标库iconfont.cn

对于使用 iconfont.cn 的用户&#xff0c;通过设置 createFromIconfontCN 方法参数对象中的 scriptUrl 字段&#xff0c; 即可轻松地使用已有项目中的图标。 组件封装 IconFont <template><IconFont :type"iconType" /> </template><script se…

联想Y7000P 2023款拆机教程及升级内存教程

0.电脑参数介绍 联想Y7000P 2023电脑&#xff0c;笔者电脑CPU为i7-13700H&#xff0c;14核20线程&#xff1b;标配内存为三星的DDR5-5600MHz-8GB*2&#xff0c;由于电脑CPU限制&#xff0c;实际内存跑的频率为5200MHz; 2个内存插槽&#xff0c;2个固态硬盘插槽。每个内存插槽最…

【Linux基础】SSH登录

SSH简介 安全外壳协议&#xff08;Secure Shell Protocol&#xff0c;简称SSH&#xff09;是一种加密的网络传输协议&#xff0c;可在不安全的网络中为网络服务提供安全的传输环境。 SSH通过在网络中建立安全隧道来实现SSH客户端与服务器之间的连接。 SSH最常见的用途是远程登…

python自动化办公工具:自动批量生成奖状的工具(可视化)

&#x1f446;点击关注 获取更多编程干货&#x1f446; 不知道大家有没有注意到一种趋势&#xff0c;现在即便是那些非程序员&#xff0c;甚至对计算机一窍不通的人&#xff0c;也开始学习Python了&#xff0c;其“普及程度”实在让人感到有些惊讶。 那么&#xff0c;对于那些…

Github 2024-06-22 开源项目日报 Top10

根据Github Trendings的统计,今日(2024-06-22统计)共有10个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量TypeScript项目3JavaScript项目2Python项目2HTML项目1Rust项目1Dart项目1Dockerfile项目1Shell项目1C++项目1Swift项目1RustDesk: 用Rust编写的…

一个电商创业者眼中的618:平台大变局

战役结束了&#xff0c;战斗还在继续。 一位朋友去年5月创业&#xff0c;网上卖咖啡&#xff0c;这个赛道很拥挤&#xff0c;时机也不好&#xff0c;今年是他参加第一个618。朋友说&#xff0c;今年的目标是锤炼团队&#xff0c;总结方法&#xff0c;以及最重要的——活下去。…

getPhysicalNumberOfCells获取列数不是合并前实际列数

问题就是:有的导入复杂表头被合并的单元格有默认空字符串&#xff0c;有的直接不存在这个单元格 实际我需要下面这种情况 断点可以看到这个导入第一行合并了&#xff0c;被合并单元格还有默认的空字符串 解决办法就是在合并单元格里面判断&#xff0c;不是第一行第一列都设置…

FENDI CLUB精酿啤酒与小麦的不解之缘

FENDI CLUB精酿啤酒与小麦之间这种联系体现在啤酒的酿造原料、口感特色以及文化内涵等多个方面。以下是关于这两者之间关系的详细分析&#xff1a; 一、酿造原料的紧密联系 小麦作为关键原料&#xff1a;FENDI CLUB精酿啤酒在酿造过程中&#xff0c;小麦是不可或缺的原料之一…

压缩包文件密码破解软件 Ziperello 下载及使用教程

使用 Ziperello https://qweree.cn/index.php/416/ 对加了密码的压缩包进行密码破解&#xff0c;教程如下&#xff1a; 第一步&#xff0c;双击运行 Ziperello双击我打开程序.exe&#xff0c;如下图&#xff1a; 第二步&#xff0c;打开一个加了密的 ZIP 压缩包&#xff0c;再…

idea导入项目右侧maven不显示的解决办法

不显示情况&#xff1a; 原因可能是读取项目出错&#xff0c;未正确加载pom文件造成的。 解决方案一&#xff1a; 关闭idea在项目目录中删除.idea文件夹重新打开项目&#xff0c;重新加载。 解决犯案二&#xff1a; 直接在pom文件中右键选择add as maven project。 解决方案三…

【JavaEE】Cookie和Session详解

一.Cookie 首先我们知道HTTP协议本身是’‘无状态’‘的, 这里的’‘无状态’指的是:默认情况下HTTP协议的客户端和服务器之间的这次通信,和下次通信之间没有直接的联系. 但是在实际的开发过程之中, 我们很多时候是需要知道请求之间的关联关系的. 例如登陆网站成功后,第二次访…

『大模型笔记』如何让小型语言模型发挥作用!

如何让小型语言模型发挥作用! 文章目录 一. 如何让小型语言模型发挥作用!不可能的可能性小模型的潜力创新方法与突破实践与验证过滤系统与数据质量小模型的逐步改进信息理论蒸馏方法(新工作InfoSum)总结与展望Infini-Gram与N-gram模型的新时代后缀数组与高速计算二. 参考文献…

若依框架集成微信支付

1. 添加微信支付相关依赖 <!-- 微信支付 --> <dependency><groupId>com.github.wxpay</groupId><artifactId>wxpay-sdk</artifactId><version>0.0.3</version> </dependency> <dependency><groupId>com.gi…

免费开源的地图解析工具【快速上手】

视频学习地址 这篇文章和【Nominatim】是相呼应的&#xff0c;在尝试了OSM数据一直有问题之后&#xff0c;通过别人的指点是不是可以换个思路&#xff0c;我的数据只需要精确到市级别&#xff0c;也可以不用OSM这样全的数据&#xff08;主要原因还是OSM太过庞大了&#xff09; …

神经网络学习6-线性层

归一化用的较少 正则化用来解决过拟合&#xff0c;处理最优化问题&#xff0c;批量归一化加快速度 正则化&#xff08;Regularization&#xff09;&#xff1a; 作用&#xff1a;正则化是一种用来防止过拟合的技术&#xff0c;通过向模型的损失函数中添加惩罚项&#xff0c;使…

全网最全!25届最近5年上海理工大学自动化考研院校分析

上海理工大学 目录 一、学校学院专业简介 二、考试科目指定教材 三、近5年考研分数情况 四、近5年招生录取情况 五、最新一年分数段图表 六、历年真题PDF 七、初试大纲复试大纲 八、学费&奖学金&就业方向 一、学校学院专业简介 二、考试科目指定教材 1、考试…

数据挖掘与分析——数据预处理

数据探索 波士顿房价数据集&#xff1a;卡内基梅隆大学收集&#xff0c;StatLib库&#xff0c;1978年&#xff0c;涵盖了麻省波士顿的506个不同郊区的房屋数据。 一共含有506条数据。每条数据14个字段&#xff0c;包含13个属性&#xff0c;和一个房价的平均值。 数据读取方法…