LLM 0.32: Reasoning Traces, Server-Side Tools Arrive
> LLM 0.32 adds visible reasoning traces, server-side tools for OpenAI and Claude, a new Python streaming events API, and Git-style content-addressable logs.
🎧 Listen — ~6 min
Ready · LLM 0.32: Reasoning Traces, Serv
Simon Willison shipped LLM 0.32 on August 4, 2026, calling it "the most significant new version of LLM since the initial launch of the project." That is not marketing fluff. The release rewires how the CLI and Python library represent prompts, responses, tool calls, and logs — and it quietly repositions LLM as an agent framework, whether Willison wanted to use that word or not.
If you build on top of LLM, or you use it daily to talk to GPT, Claude, Gemini, or a local model through LM Studio, here is what actually changed and why it matters.
Reasoning traces you can finally see
Reasoning models have always been a black box in the terminal. You'd fire off a prompt, wait, and get an answer with no visibility into the thinking that produced it. LLM 0.32 fixes that by streaming reasoning traces to stderr while the model works.
llm -m gpt-5.6-luna "Explain why quicksort is worst-case O(n^2)"
Run that against a reasoning-capable model and you'll see the model's intermediate thinking scroll past on stderr, separate from the final answer on stdout. That separation matters: you can pipe the clean output into another tool without your reasoning trace polluting the pipeline, but you still get to watch the model work when you want to. Add -R or --hide-reasoning if you'd rather not see it at all.
LLM also ships GPT-5.6 support out of the box now, and the default model for a bare llm "prompt" has shifted to GPT-5.6 Luna — OpenAI's cheap, fast tier that got an 80% price cut on July 30. That's a deliberate choice: Willison wants the zero-config path to be inexpensive by default.
Server-side tools: the model's provider does the work
This is the headline architectural change. Previously, every tool LLM ran executed locally — your machine did the work, then fed results back to the model. 0.32 adds support for server-side tools, where the provider's own infrastructure executes the tool call, and you never leave the request/response cycle.
OpenAI models get a hosted CodeInterpreter and WebSearch:
llm --tool CodeInterpreter "Show current python and SQLite versions"
The llm-anthropic plugin (also updated, to 0.26) adds four server-side tools for Claude: WebSearch, WebFetch, CodeExecution, and AnthropicMCP. That last one is the most interesting — it lets a single API call to Anthropic reach out and execute Model Context Protocol calls against a remote MCP server on your behalf:
1llm -m claude-sonnet-5 -T 'AnthropicMCP("https://datasette.simonwillison.net/-/mcp")' \
2 'how many rows in the blog_blogmark table?'That's a full MCP round-trip — model reasoning, MCP tool discovery, tool execution, and a synthesized answer — happening inside Anthropic's own infrastructure, triggered by one CLI invocation. No local MCP client, no local process spawning. For teams building agents against internal MCP servers, this removes an entire category of client-side plumbing.
A generic endpoint command for anything OpenAI-compatible
The new llm openai endpoint command turns any OpenAI-compatible API into a one-liner target, without installing LLM as a dependency:
1uvx --with llm-tools-quickjs \
2 llm openai endpoint http://localhost:1234/v1 -m google/gemma-4-12b \
3 -T QuickJS 'Use QuickJS to multiply 3434 * 2434'That example hits a local Gemma 4 12B model running in LM Studio, using uvx to grab LLM and the QuickJS tool plugin on the fly, with zero permanent install. It's a genuinely useful pattern for testing local model deployments — LocalAI, vLLM, Ollama's OpenAI-compatible endpoint, or any self-hosted inference server — against LLM's tool-calling machinery without committing to a full install.
Calls made through llm openai endpoint aren't logged, which is a deliberate design choice for one-off exploratory prompts against arbitrary endpoints.
The Python API grows up
LLM's Python API used to make you build a conversation and send messages one at a time — an abstraction that leaked once real usage got complicated. 0.32 adds a model.prompt(messages=[]) parameter that accepts a full message history directly:
1import llm
2from llm import user, assistant, system
3
4model = llm.get_model("gpt-5.6-luna")
5response = model.prompt(messages=[
6 system("You are a helpful pirate."),
7 user("What is the capital of France?"),
8 assistant("Paris, matey."),
9 user("And Germany?"),
10])
11print(response.text())More significantly, LLM now exposes a stream_events() iterator that reflects what models actually return in 2026: a mix of reasoning chunks, output text, tool calls, and image attachments — not just a flat string.
1for event in model.prompt("Explain cats").stream_events():
2 if event.type == "reasoning":
3 print(f"[thinking] {event.chunk}", end="", flush=True)
4 elif event.type == "text":
5 print(event.chunk, end="", flush=True)
6 else:
7 print(f"Other event: {event}")If you've been hand-rolling event-type detection against raw API responses to build a custom chat UI or logging pipeline, this replaces that code with a stable, provider-agnostic interface.
Content-addressable logs, modeled after Git
Supporting append-only conversation history at scale exposed a logging problem: every turn was re-serializing the entire message history as duplicate JSON. LLM 0.32 replaces that with a content-addressable SQLite message store — each message stored once, referenced by hash, similar to how Git stores blobs. llm logs and llm logs --json transparently read both the legacy schema and the new one, so existing tooling built against llm logs --json output should keep working without changes.
This is the kind of unglamorous infrastructure work that matters more than it sounds like. If you're running LLM against long agent loops or multi-day conversations, the old approach meant your log database grew roughly quadratically with conversation length. The new store grows linearly.
Why this looks like an agent framework now
Willison has resisted the word "agent" for years, citing how vague the term became. In the announcement, he notes he came around to a working definition — "an LLM agent runs tools in a loop to achieve a goal" — and admits LLM is starting to look agent-shaped: tool chains can now pause for human approval mid-execution and resume from stored message history, both capabilities pulled directly from the needs of his in-progress Datasette Agent project.
Combined with the new llm-chat-completions-server plugin — which spins up a local OpenAI-compatible chat completions server backed by any model LLM supports — you can now stand up a local proxy that speaks the standard chat completions dialect while routing through LLM's plugin ecosystem underneath:
1llm install llm-chat-completions-server
2llm chat-completions-server --port 9000Point any tool that expects an OpenAI-shaped endpoint at http://127.0.0.1:9000/v1 and it'll work against whatever model and provider you've configured in LLM, tool calls and all.
Should you upgrade?
Existing LLM plugins keep working, but plugins that register new models need an upgrade to 0.32 to participate in the streaming events system. If you maintain a model plugin, the "Structured messages and streaming events" guide in LLM's docs is the place to start. For everyone else: pip install -U llm or uv tool install llm, and the reasoning-trace streaming alone is worth the update if you work with GPT-5.6, Claude 5, or any other reasoning-capable model day to day.
Related reading
Keep reading
Related reading
⚡ Daily AI Model Drop — Get Kimi K3 benchmarks before Twitter
Join 2,400+ AI engineers. 1 email/day, no spam, unsubscribe anytime