From 9bbb37fae0a0829d9cf85289adf05e5be036ccbd Mon Sep 17 00:00:00 2001 From: Simon Willison Date: Mon, 26 May 2025 13:30:47 -0700 Subject: [PATCH] New default llm_version tool, closes #1096 Refs https://github.com/simonw/llm/issues/1095#issuecomment-2910574597 --- README.md | 1 + docs/tools.md | 20 +++++++++++++++++++- llm/default_plugins/default_tools.py | 12 ++++++++++++ llm/plugins.py | 5 ++++- tests/test_tools.py | 19 +++++++++++++++++++ 5 files changed, 55 insertions(+), 2 deletions(-) create mode 100644 llm/default_plugins/default_tools.py diff --git a/README.md b/README.md index abbce54..7f1f57a 100644 --- a/README.md +++ b/README.md @@ -183,6 +183,7 @@ See also [the llm tag](https://simonwillison.net/tags/llm/) on my blog. * [Extra HTTP headers](https://llm.datasette.io/en/stable/other-models.html#extra-http-headers) * [Tools](https://llm.datasette.io/en/stable/tools.html) * [How tools work](https://llm.datasette.io/en/stable/tools.html#how-tools-work) + * [Trying out tools](https://llm.datasette.io/en/stable/tools.html#trying-out-tools) * [LLM’s implementation of tools](https://llm.datasette.io/en/stable/tools.html#llm-s-implementation-of-tools) * [Tips for implementing tools](https://llm.datasette.io/en/stable/tools.html#tips-for-implementing-tools) * [Schemas](https://llm.datasette.io/en/stable/schemas.html) diff --git a/docs/tools.md b/docs/tools.md index 6cad0c8..f3083f3 100644 --- a/docs/tools.md +++ b/docs/tools.md @@ -14,6 +14,23 @@ A tool is effectively a function that the model can request to be executed. Here 4. LLM prompts the model a second time, this time including the output of the tool execution. 5. The model can then use that output to generate its next response. +## Trying out tools + +LLM comes with a default tool installed, called `llm_version`. You can try that out like this: + +```bash +llm -T llm_version "What version of LLM is this?" --td +``` +The output should look like this: + +``` +Tool call: llm_version({}) + 0.26a0 + +The installed version of the LLM is 0.26a0. +``` +Further tools can be installed using plugins, or you can use the `llm --functions` option to pass tools implemented as PYthon functions directly, as {ref}`described here `. + ## LLM's implementation of tools In LLM every tool is a defined as a Python function. The function can take any number of arguments and can return a string or an object that can be converted to a string. @@ -30,4 +47,5 @@ Consult the {ref}`register_tools() plugin hook ` do If your plugin needs access to API secrets I recommend storing those using `llm keys set api-name` and then reading them using the {ref}`plugin-utilities-get-key` utility function. This avoids secrets being logged to the database as part of tool calls. - \ No newline at end of file + + diff --git a/llm/default_plugins/default_tools.py b/llm/default_plugins/default_tools.py new file mode 100644 index 0000000..2217a17 --- /dev/null +++ b/llm/default_plugins/default_tools.py @@ -0,0 +1,12 @@ +import llm +from importlib.metadata import version + + +def llm_version() -> str: + "Return the installed version of llm" + return version("llm") + + +@llm.hookimpl +def register_tools(register): + register(llm_version) diff --git a/llm/plugins.py b/llm/plugins.py index 5c00b9e..0125ede 100644 --- a/llm/plugins.py +++ b/llm/plugins.py @@ -5,7 +5,10 @@ import pluggy import sys from . import hookspecs -DEFAULT_PLUGINS = ("llm.default_plugins.openai_models",) +DEFAULT_PLUGINS = ( + "llm.default_plugins.openai_models", + "llm.default_plugins.default_tools", +) pm = pluggy.PluginManager("llm") pm.add_hookspecs(hookspecs) diff --git a/tests/test_tools.py b/tests/test_tools.py index 474dc0f..51c603d 100644 --- a/tests/test_tools.py +++ b/tests/test_tools.py @@ -1,6 +1,9 @@ import asyncio +from click.testing import CliRunner +from importlib.metadata import version import json import llm +from llm import cli from llm.migrations import migrate import os import pytest @@ -217,3 +220,19 @@ def test_conversation_with_tools(vcr): ) ).text() assert "841881498" in output2 + + +def test_default_tool_llm_version(): + runner = CliRunner() + result = runner.invoke( + cli.cli, + [ + "-m", + "echo", + "-T", + "llm_version", + json.dumps({"tool_calls": [{"name": "llm_version"}]}), + ], + ) + assert result.exit_code == 0 + assert '"output": "{}"'.format(version("llm")) in result.output