Exploring sharp edges with uv using the llm package

The last post about uv and PEP 723 blew up on Hacker News, which indicates that uv is having a moment. However uv is still relatively new and may have some sharp edges. I discovered one such case with a package I am using, which I am documenting below.

llm

llm is a popular package by Simon Willison that is a CLI tool and Python library for interacting with OpenAIAnthropic’s ClaudeGoogle’s GeminiMeta’s Llama and dozens of other Large Language Models.

$ llm who are you
I am an AI language model created by OpenAI. I'm designed to assist with a wide
range of questions and topics by providing information, answering queries, and
engaging in conversation. How can I help you today?

llm also supports a plugin architecture which can be used to extend the tool by adding new commands or models etc. Here is how to install the handy llm-jq plugin, which helps to write and execute jq programs with the help of LLM

$ llm install llm-jq

$ llm plugins
[
  {
    "name": "llm-jq",
    "hooks": [
      "register_commands"
    ],
    "version": "0.1.1"
  }
]

and to demonstrate its usage using an example from the repo

$ curl -s https://api.github.com/repos/simonw/datasette/issues | \
  llm jq 'count by user.login, top 3'
[
  {
    "login": "simonw",
    "count": 7
  },
  {
    "login": "commongeek",
    "count": 4
  },
  {
    "login": "asg017",
    "count": 2
  }
]
map(.user.login) | group_by(.) | map({login: .[0], count: length}) | sort_by(.count) | reverse | .[:3]

Using llm with uv

llm can be installed with uv tool

uv tool install llm

installing the llm-jq plugin is the same as well.

$ llm install llm-jq

$ llm plugins
[
  {
    "name": "llm-jq",
    "hooks": [
      "register_commands"
    ],
    "version": "0.1.1"
  }
]

It’s all good so far. But note that when we do llm install llm-jq we are installing the package via pip. So uv does not know about these dependencies. This will become a problem as we will see in the next section.

Now, if we want to upgrade llm, we can use uv tool upgrade

 $ uv tool upgrade llm
Modified llm environment
 - llm-jq==0.1.1

This output shows that uv upgraded the llm tool and made changes to its environment. The llm-jq plugin was removed from the environment (the minus sign indicates a removal). This likely happened because uv treats llm-jq as an unnecessary package, since it’s not a dependency of llm.

Now, if we try to list the plugins, we discover that we have lost the llm-jq plugin from the new environment.

$ llm plugins
[]

This is actually a bug that (as of June 2025) is currently open in the llm repo: llm loses track of plugins when upgraded (with uv and others) · Issue #575 · simonw/llm · GitHub

Workarounds

The comments under the bug list a bunch of workarounds. I am gonna point out a couple of good ones.

Using llm-uv-tool

llm-uv-tool is itself a plugin for LLM that enables proper plugin management when LLM is installed as a uv tool. It resolves compatibility issues between uv’s isolated environment approach and LLM’s plugin system.

It is best to install it along with llm the first time we install llm

$ uv tool install --with llm-uv-tool llm

and then proceed to install the llm-jq plugin

$ llm install llm-jq

$ llm plugins
[
  {
    "name": "llm-uv-tool",
    "hooks": [
      "register_commands"
    ],
    "version": "0.1.3"
  },
  {
    "name": "llm-jq",
    "hooks": [
      "register_commands"
    ],
    "version": "0.1.1"
  }
]

Now if we try to upgrade, all the plugins will be retained after the upgrade

$ uv tool upgrade llm

$ llm plugins
[
  {
    "name": "llm-uv-tool",
    "hooks": [
      "register_commands"
    ],
    "version": "0.1.3"
  },
  {
    "name": "llm-jq",
    "hooks": [
      "register_commands"
    ],
    "version": "0.1.1"
  }
]

The README in the llm-uv-tool repo has notes on more advanced usage.

Using llm’s own upgrade command

Another option which is a bit inferior imo is to self-upgrade llm using

llm install -U llm

This will upgrade llm within the same environment it was originally installed by uv, and hence preserve the plugins. The reason this is a slightly inferior option is because this won’t work if we want to upgrade llm using uv to another Python version.

uv tool upgrade --python 3.13 llm

This will reinstall llm with Python 3.13, but will not preserve the plugins.