插件钩子#

插件使用插件钩子来定制 LLM 的行为。这些钩子由Pluggy 插件系统提供支持。

每个插件都可以使用 @hookimpl 装饰器针对本页描述的一个或多个钩子函数名称来实现一个或多个钩子。

LLM 模仿了 Datasette 插件系统。Datasette 插件文档描述了插件的工作原理。

register_commands(cli)#

此钩子向 llm CLI 工具添加新命令 - 例如 llm extra-command

此示例插件添加了一个新的 hello-world 命令,它会打印“Hello world!”

from llm import hookimpl
import click

@hookimpl
def register_commands(cli):
    @cli.command(name="hello-world")
    def hello_world():
        "Print hello world"
        click.echo("Hello world!")

这个新命令将添加到 llm --help 中,可以使用 llm hello-world 运行。

register_models(register)#

此钩子可用于注册一个或多个附加模型。

import llm

@llm.hookimpl
def register_models(register):
    register(HelloWorld())

class HelloWorld(llm.Model):
    model_id = "helloworld"

    def execute(self, prompt, stream, response):
        return ["hello world"]

如果您的模型包含异步版本,您也可以注册它

class AsyncHelloWorld(llm.AsyncModel):
    model_id = "helloworld"

    async def execute(self, prompt, stream, response):
        return ["hello world"]

@llm.hookimpl
def register_models(register):
    register(HelloWorld(), AsyncHelloWorld(), aliases=("hw",))

这演示了如何注册一个同时包含同步和异步版本的模型,以及如何为该模型指定别名。

模型插件教程详细描述了如何使用此钩子。异步模型此处有描述

register_template_loaders(register)#

插件可以使用 register_template_loaders 钩子注册新的模板加载器

模板加载器使用 llm -t prefix:name 语法。prefix 指定加载器,然后注册的加载器函数会以 name 作为参数被调用。加载器函数应返回一个 llm.Template() 对象。

此示例插件将 my-prefix 注册为一个新的模板加载器。安装后可以这样使用

llm -t my-prefix:my-template

以下是 Python 代码

import llm

@llm.hookimpl
def register_template_loaders(register):
    register("my-prefix", my_template_loader)

def my_template_loader(template_path: str) -> llm.Template:
    """
    Documentation for the template loader goes here. It will be displayed
    when users run the 'llm templates loaders' command.
    """
    try:
        # Your logic to fetch the template content
        # This is just an example:
        prompt = "This is a sample prompt for {}".format(template_path)
        system = "You are an assistant specialized in {}".format(template_path)

        # Return a Template object with the required fields
        return llm.Template(
            name=template_path,
            prompt=prompt,
            system=system,
        )
    except Exception as e:
        # Raise a ValueError with a clear message if the template cannot be found
        raise ValueError(f"Template '{template_path}' could not be loaded: {str(e)}")

请查阅 llm/templates.py 中的最新代码,了解该 llm.Template 类的详细信息。

如果找不到模板或加载不正确,加载器函数应引发 ValueError,并提供明确的错误消息。

register_fragment_loaders(register)#

插件可以使用 register_template_loaders 钩子注册新的片段加载器。然后可以将它们与 llm -f prefix:argument 语法一起使用。

片段加载器插件与模板加载器插件的不同之处在于,您可以在同一个 prompt 中堆叠多个片段加载器调用。

片段加载器可以返回一个或多个字符串片段或附件,或者两者的混合。片段将被连接到 prompt 字符串中,而任何附件将被添加到要发送给模型的附件列表中。

prefix 指定加载器。argument 将传递给该注册的回调函数。

回调函数的工作方式与模板加载器非常相似,但返回单个 llm.Fragment 对象、llm.Fragment 对象列表、单个 llm.Attachment 对象,或一个可以混合 llm.Attachmentllm.Fragment 对象的列表。

llm.Fragment 构造函数接受一个必需的字符串参数(片段的内容)和一个可选的第二个 source 参数,该参数是一个可能显示为调试信息的字符串。对于文件,这是路径;对于 URL,这是 URL。您的插件可以对 source 值使用任何您喜欢的内容。

请参阅Python API 附件文档,了解 llm.Attachment 类的详细信息。

以下是一些示例代码

import llm

@llm.hookimpl
def register_fragment_loaders(register):
    register("my-fragments", my_fragment_loader)


def my_fragment_loader(argument: str) -> llm.Fragment:
    """
    Documentation for the fragment loader goes here. It will be displayed
    when users run the 'llm fragments loaders' command.
    """
    try:
        fragment = "Fragment content for {}".format(argument)
        source = "my-fragments:{}".format(argument)
        return llm.Fragment(fragment, source)
    except Exception as ex:
        # Raise a ValueError with a clear message if the fragment cannot be loaded
        raise ValueError(
            f"Fragment 'my-fragments:{argument}' could not be loaded: {str(ex)}"
        )

# Or for the case where you want to return multiple fragments and attachments:
def my_fragment_loader(argument: str) -> list[llm.Fragment]:
    "Docs go here."
    return [
        llm.Fragment("Fragment 1 content", "my-fragments:{argument}"),
        llm.Fragment("Fragment 2 content", "my-fragments:{argument}"),
        llm.Attachment(path="/path/to/image.png"),
    ]

像这样的插件可以这样调用

llm -f my-fragments:argument

如果返回多个片段,它们将像用户向命令传递了多个 -f X 参数一样被使用。

多个片段对于返回目录中所有文件等插件特别有用。如果这些文件被插件连接在一起,则单个文件的更改将使整个片段的重复数据删除缓存失效。为每个文件提供自己的片段意味着如果只有一个文件发生更改,我们可以避免存储该完整集合的多个副本。