From f879b816e4d5ad03faeaedf5f2a1e11dfe1c31e0 Mon Sep 17 00:00:00 2001 From: Simon Willison Date: Sun, 18 May 2025 14:50:34 -0400 Subject: [PATCH] llm plugins --hook X option, closes #1047 --- docs/help.md | 5 +++-- llm/cli.py | 11 +++++++++-- tests/test_plugins.py | 22 ++++++++++++++++++++++ 3 files changed, 34 insertions(+), 4 deletions(-) diff --git a/docs/help.md b/docs/help.md index 8c6f226..ddb4afb 100644 --- a/docs/help.md +++ b/docs/help.md @@ -811,8 +811,9 @@ Usage: llm plugins [OPTIONS] List installed plugins Options: - --all Include built-in default plugins - --help Show this message and exit. + --all Include built-in default plugins + --hook TEXT Filter for plugins that implement this hook + --help Show this message and exit. ``` (help-install)= diff --git a/llm/cli.py b/llm/cli.py index 6ed5e16..4b1474f 100644 --- a/llm/cli.py +++ b/llm/cli.py @@ -2617,9 +2617,16 @@ def fragments_loaders(): @cli.command(name="plugins") @click.option("--all", help="Include built-in default plugins", is_flag=True) -def plugins_list(all): +@click.option( + "hooks", "--hook", help="Filter for plugins that implement this hook", multiple=True +) +def plugins_list(all, hooks): "List installed plugins" - click.echo(json.dumps(get_plugins(all), indent=2)) + plugins = get_plugins(all) + hooks = set(hooks) + if hooks: + plugins = [plugin for plugin in plugins if hooks.intersection(plugin["hooks"])] + click.echo(json.dumps(plugins, indent=2)) def display_truncated(text): diff --git a/tests/test_plugins.py b/tests/test_plugins.py index 1cd42ee..b9b56c1 100644 --- a/tests/test_plugins.py +++ b/tests/test_plugins.py @@ -290,3 +290,25 @@ def test_register_tools(tmpdir): finally: plugins.pm.unregister(name="ToolsPlugin") assert llm.get_tools() == {} + + +def test_plugins_command(): + runner = CliRunner() + result = runner.invoke(cli.cli, ["plugins"]) + assert result.exit_code == 0 + assert json.loads(result.output) == [ + {"name": "EchoModelPlugin", "hooks": ["register_models"]}, + { + "name": "MockModelsPlugin", + "hooks": ["register_embedding_models", "register_models"], + }, + ] + # Test the --hook option + result2 = runner.invoke(cli.cli, ["plugins", "--hook", "register_embedding_models"]) + assert result2.exit_code == 0 + assert json.loads(result2.output) == [ + { + "name": "MockModelsPlugin", + "hooks": ["register_embedding_models", "register_models"], + }, + ]