Fresh installs of llm stopped working. Not because Simon Willison changed anything, but because the OpenAI Python library stopped using httpx, and llm had been getting httpx for free as a transitive dependency of openai. The code imported it. The package never declared it. For however long that arrangement held, it worked perfectly, which is the problem.

The fix on August 21 was 0.32.1: pin openai and buy time. The fix on August 22 was 0.33: upgrade to the OpenAI Python library 3.x and move off httpx entirely, onto httpx2. Two releases, two days, one upstream decision nobody downstream got a vote on.

Undeclared is not free

Every Python project has some version of this. You install a package, it drags in six others, and your code starts using one of them without ever writing it down. Nothing complains. Tests pass. CI is green. The dependency graph is doing you a favor and you’ve mistaken the favor for a contract.

The cost only lands when the upstream package decides its own HTTP client is an implementation detail it would like to change. Which it is. openai never promised anyone httpx. It just happened to ship it, and a lot of code quietly built on that.

What makes this case instructive rather than embarrassing is the timeline. Willison shipped the stopgap the same day it broke and the real fix the next. That’s the whole benefit of a small, actively maintained project with one person who understands the entire dependency tree. Plenty of larger projects with the same undeclared-import problem will discover it weeks from now, in a bug report from someone whose Docker build stopped working.

The httpx2 question

0.33 doesn’t just re-declare httpx. It switches to httpx2, following where openai went. That’s the pragmatic call. Aligning with your largest dependency’s HTTP client means one less version resolution fight for anyone installing llm alongside the OpenAI SDK.

It also means the coupling is still there, just declared now instead of accidental. If httpx2 and openai diverge later, llm gets to have this conversation again. Declaring a dependency doesn’t insulate you from the ecosystem, it only means you find out on your own terms.

The other 0.33 changes point at something more mundane and probably more useful day to day: llm embed and llm embed-multi now take --key, and the Python-side EmbeddingModel.embed(), embed_multi(), and the Collection equivalents accept a key too. Embeddings had been the part of the API where you couldn’t pass credentials inline the way you could elsewhere. Now you can.

Go run pip install --upgrade llm and check what your own pyproject.toml claims you depend on versus what your imports actually reach for. The gap is usually not zero.