<?xml version="1.0" encoding="utf-8"?>
<feed xml:lang="en-us" xmlns="http://www.w3.org/2005/Atom"><title>Simon Willison's Weblog: datasette</title><link href="http://simonwillison.net/" rel="alternate"/><link href="http://simonwillison.net/tags/datasette.atom" rel="self"/><id>http://simonwillison.net/</id><updated>2026-06-11T15:26:49+00:00</updated><author><name>Simon Willison</name></author><entry><title>datasette 1.0a33</title><link href="https://simonwillison.net/2026/Jun/11/datasette/#atom-tag" rel="alternate"/><published>2026-06-11T15:26:49+00:00</published><updated>2026-06-11T15:26:49+00:00</updated><id>https://simonwillison.net/2026/Jun/11/datasette/#atom-tag</id><summary type="html">
    
        &lt;p&gt;&lt;strong&gt;Release:&lt;/strong&gt; &lt;a href="https://github.com/simonw/datasette/releases/tag/1.0a33"&gt;datasette 1.0a33&lt;/a&gt;&lt;/p&gt;
        &lt;p&gt;This alpha is a significant step on the road to a stable 1.0, finally extending the &lt;code&gt;?_extra=&lt;/code&gt; pattern I introduced &lt;a href="https://docs.datasette.io/en/1.0a3/changelog.html#a3-2023-08-09"&gt;in Datasette 1.0a3&lt;/a&gt; to cover queries and rows in addition to tables. That pattern is also &lt;a href="https://docs.datasette.io/en/latest/json_api.html#expanding-json-responses"&gt;now documented&lt;/a&gt;!&lt;/p&gt;
&lt;p&gt;I wrote a whole lot more about the new release on the Datasette project blog: &lt;strong&gt;&lt;a href="http://datasette.io/blog/2026/api-extras/"&gt;Datasette 1.0a33 with JSON extras in the API&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Because API explorer tools are almost free to build now I had Claude Fable 5 in Claude Code (for &lt;a href="https://gist.github.com/simonw/d8bf1a8f36e28fbd595cede946e0ab6d"&gt;the plan&lt;/a&gt;) and GPT-5.5 xhigh in Codex Desktop (for &lt;a href="https://gist.github.com/simonw/12d5e09797072a6807d7b9cfcc8ff6b7"&gt;the implementation&lt;/a&gt;) build me this &lt;a href="https://tools.simonwillison.net/datasette-extras-explorer"&gt;custom extras API explorer&lt;/a&gt; to help demonstrate the feature:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Screenshot of a web application titled &amp;quot;Datasette extras explorer&amp;quot;. A URL input field contains https://latest.datasette.io/fixtures/facetable.json with a teal Explore button next to it. Below, a left panel labeled EXTRAS (30) lists checkboxes: all_columns - All columns in the table, regardless of _col/_nocol filtering; column_types - Column type assignments for this table; columns (checked) - Column names returned by this query; count - Total count of rows matching these filters; count_sql - SQL query used to calculate the total count; custom_table_templates - Custom template names considered for this table; database - Database name; database_color - Color assigned to the database. A right panel labeled RESPONSE shows GET /fixtures/fac… with Copy JSON and Copy URL buttons, then a dark JSON viewer showing 200 - 9.9 KB - 114ms and JSON: &amp;quot;ok&amp;quot;: true, &amp;quot;next&amp;quot;: null, &amp;quot;columns&amp;quot;: (highlighted array) &amp;quot;pk&amp;quot;, &amp;quot;created&amp;quot;, &amp;quot;planet_int&amp;quot;, &amp;quot;on_earth&amp;quot;, &amp;quot;state&amp;quot;, &amp;quot;_city_id&amp;quot;, &amp;quot;_neighborhood&amp;quot;, &amp;quot;tags&amp;quot;, &amp;quot;complex_array&amp;quot;, &amp;quot;distinct_some_null&amp;quot;, &amp;quot;n&amp;quot;, &amp;quot;rows&amp;quot;: list of objects." src="https://static.simonwillison.net/static/2026/extras-explorer.png" /&gt;&lt;/p&gt;
    
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/projects"&gt;projects&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/datasette"&gt;datasette&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/annotated-release-notes"&gt;annotated-release-notes&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ai-assisted-programming"&gt;ai-assisted-programming&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="projects"/><category term="datasette"/><category term="annotated-release-notes"/><category term="ai-assisted-programming"/></entry><entry><title>datasette-agent 0.2a0</title><link href="https://simonwillison.net/2026/Jun/10/datasette-agent/#atom-tag" rel="alternate"/><published>2026-06-10T23:57:27+00:00</published><updated>2026-06-10T23:57:27+00:00</updated><id>https://simonwillison.net/2026/Jun/10/datasette-agent/#atom-tag</id><summary type="html">
    
        &lt;p&gt;&lt;strong&gt;Release:&lt;/strong&gt; &lt;a href="https://github.com/datasette/datasette-agent/releases/tag/0.2a0"&gt;datasette-agent 0.2a0&lt;/a&gt;&lt;/p&gt;
        &lt;p&gt;Highlights from the release notes:&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;Tools can now ask the user questions mid-execution. Tools that declare a &lt;code&gt;context&lt;/code&gt; parameter receive a &lt;code&gt;ToolContext&lt;/code&gt; object, and &lt;code&gt;await context.ask_user(...)&lt;/code&gt; can ask a yes/no, multiple-choice (&lt;code&gt;options=[...]&lt;/code&gt;) or free-text (&lt;code&gt;free_text=True&lt;/code&gt;) question. While a question is unanswered the agent turn suspends: the question renders as a form in the chat UI and persists to the internal database, so suspended conversations survive a server restart. Once answered, the tool re-executes from the top with stored answers replayed, so call &lt;code&gt;ask_user()&lt;/code&gt; before performing side effects. &lt;a href="https://github.com/datasette/datasette-agent/pull/20"&gt;#20&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;New built-in &lt;code&gt;save_query&lt;/code&gt; tool: the agent can save SQL it has written as a &lt;a href="https://docs.datasette.io/en/latest/sql_queries.html#saved-queries"&gt;Datasette stored query&lt;/a&gt;. Saving always requires human approval - the agent shows the full SQL plus the proposed name, database and visibility, and nothing is stored until you click Yes. &lt;a href="https://github.com/datasette/datasette-agent/pull/20"&gt;#20&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;The &lt;code&gt;ask_user()&lt;/code&gt; feature was enabled by the new LLM alpha I &lt;a href="https://simonwillison.net/2026/Jun/9/claude-fable-5/#adding-features-to-datasette-agent-and-llm-using-claude-code"&gt;built yesterday&lt;/a&gt; with the help of Claude Fable 5.&lt;/p&gt;
    
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/ai"&gt;ai&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/datasette"&gt;datasette&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/generative-ai"&gt;generative-ai&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/llms"&gt;llms&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/datasette-agent"&gt;datasette-agent&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="ai"/><category term="datasette"/><category term="generative-ai"/><category term="llms"/><category term="datasette-agent"/></entry><entry><title>datasette-agent-edit 0.1a0</title><link href="https://simonwillison.net/2026/Jun/7/datasette-agent-edit/#atom-tag" rel="alternate"/><published>2026-06-07T23:56:38+00:00</published><updated>2026-06-07T23:56:38+00:00</updated><id>https://simonwillison.net/2026/Jun/7/datasette-agent-edit/#atom-tag</id><summary type="html">
    
        &lt;p&gt;&lt;strong&gt;Release:&lt;/strong&gt; &lt;a href="https://github.com/datasette/datasette-agent-edit/releases/tag/0.1a0"&gt;datasette-agent-edit 0.1a0&lt;/a&gt;&lt;/p&gt;
        &lt;p&gt;I'm planning several plugins for &lt;a href="https://agent.datasette.io/"&gt;Datasette Agent&lt;/a&gt; which can make edits to existing pieces of text - things like collaborative Markdown editing, updating large SQL queries, and editing SVG files.&lt;/p&gt;
&lt;p&gt;Agentic editing of text is a little tricky to get right. My favorite published design for this is for the &lt;a href="https://platform.claude.com/docs/en/agents-and-tools/tool-use/text-editor-tool#use-the-text-editor-tool"&gt;Claude text editor&lt;/a&gt;, which implements the following tools:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;view&lt;/code&gt; - view sections of a file, with line numbers added to every line.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;str_replace&lt;/code&gt; - find an exact &lt;code&gt;old_str&lt;/code&gt; and replace it with &lt;code&gt;new_str&lt;/code&gt; - fail if the original string is not unique&lt;/li&gt;
&lt;li&gt;&lt;code&gt;insert&lt;/code&gt; - insert the specified text after the specified line number&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Rather than recreate these patterns for every plugin that needs them I decided to create this base plugin, &lt;code&gt;datasette-agent-edit&lt;/code&gt;, which implements the core tools in a way that allows them to be adapted for other plugins.&lt;/p&gt;
    
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/ai"&gt;ai&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/datasette"&gt;datasette&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/generative-ai"&gt;generative-ai&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/llms"&gt;llms&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/llm-tool-use"&gt;llm-tool-use&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/datasette-agent"&gt;datasette-agent&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="ai"/><category term="datasette"/><category term="generative-ai"/><category term="llms"/><category term="llm-tool-use"/><category term="datasette-agent"/></entry><entry><title>Running Python code in a sandbox with MicroPython and WASM</title><link href="https://simonwillison.net/2026/Jun/6/micropython-in-a-sandbox/#atom-tag" rel="alternate"/><published>2026-06-06T03:53:34+00:00</published><updated>2026-06-06T03:53:34+00:00</updated><id>https://simonwillison.net/2026/Jun/6/micropython-in-a-sandbox/#atom-tag</id><summary type="html">
    &lt;p&gt;I've been experimenting with different approaches to running code in a sandbox for several years now, but my latest attempt feels like it might finally have all of the characteristics I've been looking for. I've released it as an alpha package called &lt;a href="https://github.com/simonw/micropython-wasm"&gt;micropython-wasm&lt;/a&gt;, and I'm using it for a code execution sandbox plugin for &lt;a href="https://github.com/datasette/datasette-agent"&gt;Datasette Agent&lt;/a&gt; called &lt;a href="https://github.com/datasette/datasette-agent-micropython"&gt;datasette-agent-micropython&lt;/a&gt;.&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href="https://simonwillison.net/2026/Jun/6/micropython-in-a-sandbox/#why-do-i-want-a-sandbox-"&gt;Why do I want a sandbox?&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://simonwillison.net/2026/Jun/6/micropython-in-a-sandbox/#what-i-want-from-a-sandbox"&gt;What I want from a sandbox&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://simonwillison.net/2026/Jun/6/micropython-in-a-sandbox/#webassembly-looks-really-promising-here"&gt;WebAssembly looks really promising here&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://simonwillison.net/2026/Jun/6/micropython-in-a-sandbox/#micropython-in-webassembly"&gt;MicroPython in WebAssembly&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://simonwillison.net/2026/Jun/6/micropython-in-a-sandbox/#building-the-first-version"&gt;Building the first version&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://simonwillison.net/2026/Jun/6/micropython-in-a-sandbox/#try-it-yourself"&gt;Try it yourself&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://simonwillison.net/2026/Jun/6/micropython-in-a-sandbox/#should-you-trust-my-vibe-coded-sandbox-"&gt;Should you trust my vibe-coded sandbox?&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="why-do-i-want-a-sandbox-"&gt;Why do I want a sandbox?&lt;/h4&gt;
&lt;p&gt;My key open source projects - &lt;a href="https://datasette.io/"&gt;Datasette&lt;/a&gt;, &lt;a href="https://llm.datasette.io/"&gt;LLM&lt;/a&gt;, even &lt;a href="https://sqlite-utils.datasette.io/"&gt;sqlite-utils&lt;/a&gt; - all support plugins.&lt;/p&gt;
&lt;p&gt;I absolutely love plugins as a mechanism for extending software. A carefully designed plugin system reduces the risk involved in trying new things to almost nothing - even the wildest ideas won't leave a lasting influence on the core application itself. My software can grow a new feature overnight and I don't even have to review a pull request!&lt;/p&gt;
&lt;p&gt;There's one major drawback: my plugin systems all use Python and &lt;a href="https://pluggy.readthedocs.io/en/latest/"&gt;Pluggy&lt;/a&gt;, and plugin code executes with full privileges within my applications. A buggy or malicious plugin could break everything or leak private data.&lt;/p&gt;
&lt;p&gt;I'd love to be able to run plugin-style code in an environment where it is unable to read unapproved files, connect to a network, or generally operate in a way that's risky or harmful to the rest of the application or the user's computer.&lt;/p&gt;
&lt;p&gt;My interest covers more than just plugins. For Datasette in particular there are many features I'd like to support where arbitrary code execution would be useful. I've already experimented with this for &lt;a href="https://enrichments.datasette.io/"&gt;Datasette Enrichments&lt;/a&gt;, where code can be used to transform values stored in a table. I'd love to build a mechanism where you can run code on a schedule that fetches JSON from an approved location, runs a tiny bit of code to reformat it into a list of dictionaries, then inserts those as rows in a SQLite database table.&lt;/p&gt;
&lt;h4 id="what-i-want-from-a-sandbox"&gt;What I want from a sandbox&lt;/h4&gt;
&lt;p&gt;My goal is to execute code safely within my own Python applications. Here's what I need:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Dependencies that &lt;strong&gt;cleanly install from PyPI&lt;/strong&gt;, including binary wheels across multiple platforms if necessary. I don't want people using my software to have to take any extra steps beyond directly installing my Python package.&lt;/li&gt;
&lt;li&gt;Executed code must be subject to both &lt;strong&gt;memory&lt;/strong&gt; and &lt;strong&gt;CPU&lt;/strong&gt; limits. I don't want &lt;code&gt;while True: s += "longer string"&lt;/code&gt; to crash my application or the user's computer.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;File access must be strictly controlled&lt;/strong&gt;. Either no filesystem access at all or I get to define exactly which files can be read and which files can be written to.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Network access is controlled as well&lt;/strong&gt;. Sandboxed code should not be able to communicate with anything without going through a layer I fully control.&lt;/li&gt;
&lt;li&gt;Support for interaction with &lt;strong&gt;host functions&lt;/strong&gt;. A sandbox isn't much use if I can't carefully expose selected platform features to the code that it's running.&lt;/li&gt;
&lt;li&gt;It has to be &lt;strong&gt;robust, supported, and clearly documented&lt;/strong&gt;. I've lost count of the number of sandbox projects I've seen in repos with warnings that they aren't actively maintained!&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="webassembly-looks-really-promising-here"&gt;WebAssembly looks really promising here&lt;/h4&gt;
&lt;p&gt;Web browsers operate in the most hostile environment imaginable when it comes to malicious code. Their job is to download &lt;em&gt;and execute&lt;/em&gt; untrusted code from the web on almost every page load.&lt;/p&gt;
&lt;p&gt;Given this, JavaScript engines should be excellent candidates for sandboxes. Sadly those engines are also extremely complicated, and are not designed for easy embedding in other projects. Most of the V8-in-Python projects I've seen are infrequently maintained and come with warnings not to use them with completely untrusted code.&lt;/p&gt;
&lt;p&gt;WebAssembly is a &lt;em&gt;much better&lt;/em&gt; candidate. It was designed from the start to support all of the characteristics I care about and has been tested in browsers for nearly a decade. The &lt;a href="https://pypi.org/project/wasmtime"&gt;wasmtime&lt;/a&gt; Python library brings WASM to Python, is actively maintained, and has binary wheels.&lt;/p&gt;
&lt;h4 id="micropython-in-webassembly"&gt;MicroPython in WebAssembly&lt;/h4&gt;
&lt;p&gt;WebAssembly engines like wasmtime run WebAssembly binaries. Some programming languages like Rust are easy to compile directly to WebAssembly. Dynamic languages like JavaScript and Python are harder - they support language primitives like &lt;code&gt;eval()&lt;/code&gt;, which means they need a full interpreter available at runtime.&lt;/p&gt;
&lt;p&gt;To run Python we need a full Python interpreter compiled to WebAssembly, wired up in a way that makes it easy to feed it code, hook up host functions and access the results.&lt;/p&gt;
&lt;p&gt;Pyodide offers an outstanding package for running Python using WebAssembly in the browser, but using Pyodide in server-side Python isn't supported. The most recent advice I could find was &lt;a href="https://github.com/pyodide/pyodide/discussions/5145"&gt;from October 2024&lt;/a&gt; stating "Pyodide is built by the Emscripten toolchain and can only run in a browser or Node.js".&lt;/p&gt;
&lt;p&gt;The other day I decided to take a look at &lt;a href="https://micropython.org"&gt;MicroPython&lt;/a&gt; as an option for this. The MicroPython site says:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;MicroPython is a lean and efficient implementation of the Python 3 programming language that includes a small subset of the Python standard library and is optimised to run on microcontrollers and in constrained environments.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;WebAssembly sure feels like a constrained environment to me!&lt;/p&gt;
&lt;h4 id="building-the-first-version"&gt;Building the first version&lt;/h4&gt;
&lt;p&gt;I had GPT-5.5 Pro &lt;a href="https://chatgpt.com/share/6a1e2a5c-58b8-8328-ba1c-0e6aadb0a051"&gt;do some research for me&lt;/a&gt;, which turned up &lt;a href="https://github.com/micropython/micropython/pull/13676"&gt;this PR against MicroPython&lt;/a&gt; by &lt;a href="https://github.com/yamt"&gt;Yamamoto Takahashi&lt;/a&gt; titled "Experimental WASI support for ports/unix".&lt;/p&gt;
&lt;p&gt;It then produced this &lt;a href="https://github.com/simonw/micropython-wasm/blob/c08fbd2276b15dc8c9bdff82845f750971f45647/research.md"&gt;research.md document&lt;/a&gt;, so I let Codex Desktop and GPT-5.5 high &lt;a href="https://gist.github.com/simonw/27461a16d76f28f8619c609444d544fe"&gt;loose on it&lt;/a&gt; to see what would happen:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;read the research.md document and build this. You will probably need to write a script that compiles a custom WASM version of MicroPython as part of this project - fetch the MicroPython code to a /tmp directory for this as part of that script.&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;It worked. I now had a prototype Python library that could execute Python code inside a WebAssembly sandbox!&lt;/p&gt;
&lt;p&gt;The trickiest piece to solve was persistent interpreter state. The WASM build we are using here exposes a single entry point which starts the interpreter, runs the code and then stops the interpreter at the end.&lt;/p&gt;
&lt;p&gt;This works fine for one-off scripts, but for Datasette Agent I want variables and functions to stay resident in memory so I can reuse them across multiple code execution calls.&lt;/p&gt;
&lt;p&gt;A neat thing about working with coding agents is that you can get from an idea to a proof of concept quickly. I prompted:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;For keeping variables resident: what if we ran code inside micropython itself which called a host function get_next_python_code() and then passed that to eval() - and that host function blocked until new code was available, maybe by running in a thread with a queue? Could that or a similar idea help here?&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;After some iteration we got to a version of this that works! In Python code you can now do this:&lt;/p&gt;
&lt;pre&gt;&lt;span class="pl-k"&gt;from&lt;/span&gt; &lt;span class="pl-s1"&gt;micropython_wasm&lt;/span&gt; &lt;span class="pl-k"&gt;import&lt;/span&gt; &lt;span class="pl-v"&gt;MicroPythonSession&lt;/span&gt;

&lt;span class="pl-k"&gt;with&lt;/span&gt; &lt;span class="pl-en"&gt;MicroPythonSession&lt;/span&gt;() &lt;span class="pl-k"&gt;as&lt;/span&gt; &lt;span class="pl-s1"&gt;session&lt;/span&gt;:
    &lt;span class="pl-en"&gt;print&lt;/span&gt;(&lt;span class="pl-s1"&gt;session&lt;/span&gt;.&lt;span class="pl-c1"&gt;run&lt;/span&gt;(&lt;span class="pl-s"&gt;"x = 10&lt;span class="pl-cce"&gt;\n&lt;/span&gt;print(x)"&lt;/span&gt;).&lt;span class="pl-c1"&gt;stdout&lt;/span&gt;)
    &lt;span class="pl-en"&gt;print&lt;/span&gt;(&lt;span class="pl-s1"&gt;session&lt;/span&gt;.&lt;span class="pl-c1"&gt;run&lt;/span&gt;(&lt;span class="pl-s"&gt;"x += 5&lt;span class="pl-cce"&gt;\n&lt;/span&gt;print(x)"&lt;/span&gt;).&lt;span class="pl-c1"&gt;stdout&lt;/span&gt;)
    &lt;span class="pl-en"&gt;print&lt;/span&gt;(&lt;span class="pl-s1"&gt;session&lt;/span&gt;.&lt;span class="pl-c1"&gt;run&lt;/span&gt;(&lt;span class="pl-s"&gt;"print(x * 2)"&lt;/span&gt;).&lt;span class="pl-c1"&gt;stdout&lt;/span&gt;)&lt;/pre&gt;
&lt;p&gt;Under the hood this starts a thread, sets up a request queue and then sends messages to that queue for the &lt;code&gt;session.run()&lt;/code&gt; command, each time waiting on a reply queue for the result of that execution. Inside WASM the MicroPython interpreter blocks waiting for a &lt;code&gt;__session_next__()&lt;/code&gt; host function to return the next line of code, which it runs &lt;code&gt;eval()&lt;/code&gt; on before calling &lt;code&gt;__session_result__({"id": request_id, "ok": True})&lt;/code&gt; when each block has been successfully executed.&lt;/p&gt;
&lt;p&gt;The other piece of complexity was supporting host functions, so my Python library could selectively expose functions that could then be called by code running in MicroPython.&lt;/p&gt;
&lt;p&gt;Codex ended up solving this with &lt;a href="https://github.com/simonw/micropython-wasm/blob/0.1a1/micropython_wasm/usercmodule/host/hostmodule.c"&gt;78 lines of C&lt;/a&gt;, which ends up compiled into the &lt;a href="https://github.com/simonw/micropython-wasm/blob/0.1a1/micropython_wasm/artifacts/micropython-wasi.wasm"&gt;362KB WebAssembly blob&lt;/a&gt; I'm distributing with the package.&lt;/p&gt;
&lt;p&gt;I am by no means a C programmer, but I've read the C and had two different models explain it to me (here's &lt;a href="https://claude.ai/share/62f74371-cc3c-44f2-b406-33d03513de9e"&gt;Claude's explanation&lt;/a&gt;) and I've subjected it to a barrage of tests.&lt;/p&gt;
&lt;p&gt;The great thing about working with WebAssembly is that if the C turns out to be fatally flawed the worst that can happen is the WebAssembly execution will fail with an exception. I can live with that risk.&lt;/p&gt;
&lt;p&gt;Memory limits are directly supported by wasmtime. CPU limits are a little harder: wasmtime offers a "fuel" concept to limit how many operations a WebAssembly call can execute, and that's the correct fit for this problem, but the units are hard to reason about. I'm experimenting with a 20 million default "fuel" setting now but I'm not confident that it's the most appropriate value.&lt;/p&gt;
&lt;h4 id="try-it-yourself"&gt;Try it yourself&lt;/h4&gt;
&lt;p&gt;The &lt;code&gt;micropython-wasm&lt;/code&gt; alpha is now &lt;a href="https://pypi.org/project/micropython-wasm"&gt;live on PyPI&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;You can try it from your own Python code as &lt;a href="https://github.com/simonw/micropython-wasm"&gt;described in the README&lt;/a&gt;. I've also added a simple CLI mode in &lt;a href="https://github.com/simonw/micropython-wasm/releases/tag/0.1a2"&gt;version 0.1a2&lt;/a&gt; which means you can try it using &lt;code&gt;uvx&lt;/code&gt; without first installing it like so:&lt;/p&gt;
&lt;div class="highlight highlight-source-shell"&gt;&lt;pre&gt;uvx micropython-wasm -c &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;'&lt;/span&gt;print("Hello world")&lt;span class="pl-pds"&gt;'&lt;/span&gt;&lt;/span&gt;
&lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; To see it run out of fuel:&lt;/span&gt;
uvx micropython-wasm -c &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;'&lt;/span&gt;s = ""; while True: s += "longer"&lt;span class="pl-pds"&gt;'&lt;/span&gt;&lt;/span&gt;
&lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; Outputs: micropython-wasm: guest exited with code 1&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You can also try it in &lt;a href="https://agent.datasette.io/"&gt;Datasette Agent&lt;/a&gt; like this:&lt;/p&gt;
&lt;div class="highlight highlight-source-shell"&gt;&lt;pre&gt;uvx llm keys &lt;span class="pl-c1"&gt;set&lt;/span&gt; openai
&lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; Paste in an OpenAI key, then:&lt;/span&gt;
uvx --with datasette-agent \
  --with datasette-agent-micropython \
  --prerelease allow \
  datasette --internal internal.db \
    -s plugins.datasette-llm.default_model gpt-5.5 \
    --root -o&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Then navigate to &lt;a href="http://127.0.0.1:8001/-/agent"&gt;http://127.0.0.1:8001/-/agent&lt;/a&gt; and run the prompt:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;show me some micropython&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;img src="https://static.simonwillison.net/static/2026/micropython-in-datasette-agent.jpg" style="max-width: 100%" alt="Screenshot of a chat application interface with a dark blue-grey header reading &amp;quot;home&amp;quot; on the left and &amp;quot;root&amp;quot; with a hamburger menu icon on the right. Below is a navigation row with &amp;quot;← Back&amp;quot; and &amp;quot;Chat&amp;quot; on the left and an &amp;quot;EXPORT&amp;quot; button on the right. A blue user message bubble reads &amp;quot;show me some micropython&amp;quot;. Below it a collapsed thinking section reads &amp;quot;▸Thinking: … to show the result clearly. After that, I can wrap up with a brief explanation!&amp;quot; followed by a &amp;quot;▶ Tool: execute_micropython&amp;quot; label. A code block follows: &amp;quot;# A tiny MicroPython example: blink-style logic + Fibonacci&amp;quot; / &amp;quot;def fib(n):&amp;quot; / &amp;quot;    a, b = 0, 1&amp;quot; / &amp;quot;    out = []&amp;quot; / &amp;quot;    for _ in range(n):&amp;quot; / &amp;quot;        out.append(a)&amp;quot; / &amp;quot;        a, b = b, a + b&amp;quot; / &amp;quot;    return out&amp;quot; / &amp;#39;print(&amp;quot;Hello from MicroPython!&amp;quot;)&amp;#39; / &amp;#39;print(&amp;quot;First 10 Fibonacci numbers:&amp;quot;, fib(10))&amp;#39; / &amp;quot;# MicroPython often runs on microcontrollers, e.g.:&amp;quot; / &amp;quot;# from machine import Pin&amp;quot; / &amp;quot;# led = Pin(2, Pin.OUT)&amp;quot; / &amp;quot;# led.value(1)  # turn LED on&amp;quot; / &amp;quot;# led.value(0)  # turn LED off&amp;quot;. Below a horizontal divider is the output: &amp;quot;Hello from MicroPython!&amp;quot; / &amp;quot;First 10 Fibonacci numbers: [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]&amp;quot;, followed by a &amp;quot;▶ Result: execute_micropython&amp;quot; label. At the bottom is a text input field with placeholder &amp;quot;Type a message...&amp;quot; and a blue &amp;quot;Send&amp;quot; button." /&gt;&lt;/p&gt;

&lt;p&gt;You can try a live demo of that plugin running in Datasette Agent by signing into &lt;a href="https://agent.datasette.io"&gt;agent.datasette.io&lt;/a&gt; with your GitHub account.&lt;/p&gt;

&lt;h4 id="should-you-trust-my-vibe-coded-sandbox-"&gt;Should you trust my vibe-coded sandbox?&lt;/h4&gt;
&lt;p&gt;Having complained about immature, loosely-maintained sandboxing libraries, it's deeply ironic that I've now built my own!&lt;/p&gt;
&lt;p&gt;I deliberately slapped an alpha release version on it, and I'm not ready to recommend it to anyone who isn't willing to take a significant risk.&lt;/p&gt;
&lt;p&gt;I've put it through enough testing that I'm OK using it myself. I've shipped my first plugin that uses it, &lt;a href="https://github.com/datasette/datasette-agent-micropython"&gt;datasette-agent-micropython&lt;/a&gt;. I've also locked GPT-5.5 xhigh in that Datasette Agent plugin and &lt;a href="https://gist.github.com/simonw/5de497c44d25f9fd459c8aa2c959fe4a"&gt;challenged it to break out of the sandbox&lt;/a&gt; and so far it has not managed to.&lt;/p&gt;
&lt;p&gt;I'm hoping this implementation can convince some companies with professional security teams and high-stakes problems to commit to using Python in WebAssembly as a sandboxing approach and open source their own solutions.&lt;/p&gt;
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/python"&gt;python&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/sandboxing"&gt;sandboxing&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ai"&gt;ai&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/datasette"&gt;datasette&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/webassembly"&gt;webassembly&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/generative-ai"&gt;generative-ai&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/llms"&gt;llms&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ai-assisted-programming"&gt;ai-assisted-programming&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/codex"&gt;codex&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/datasette-agent"&gt;datasette-agent&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/micropython"&gt;micropython&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="python"/><category term="sandboxing"/><category term="ai"/><category term="datasette"/><category term="webassembly"/><category term="generative-ai"/><category term="llms"/><category term="ai-assisted-programming"/><category term="codex"/><category term="datasette-agent"/><category term="micropython"/></entry><entry><title>datasette-agent-micropython 0.1a0</title><link href="https://simonwillison.net/2026/Jun/2/datasette-agent-micropython/#atom-tag" rel="alternate"/><published>2026-06-02T19:28:36+00:00</published><updated>2026-06-02T19:28:36+00:00</updated><id>https://simonwillison.net/2026/Jun/2/datasette-agent-micropython/#atom-tag</id><summary type="html">
    
        &lt;p&gt;&lt;strong&gt;Release:&lt;/strong&gt; &lt;a href="https://github.com/datasette/datasette-agent-micropython/releases/tag/0.1a0"&gt;datasette-agent-micropython 0.1a0&lt;/a&gt;&lt;/p&gt;
        &lt;p&gt;I want &lt;a href="https://agent.datasette.io"&gt;Datasette Agent&lt;/a&gt; to be able to generate and execute Python code safely. This alpha is looking promising so far. GPT-5.5 has so far failed to break out of the sandbox!&lt;/p&gt;
    
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/python"&gt;python&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/sandboxing"&gt;sandboxing&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/datasette"&gt;datasette&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/webassembly"&gt;webassembly&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/datasette-agent"&gt;datasette-agent&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/micropython"&gt;micropython&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="python"/><category term="sandboxing"/><category term="datasette"/><category term="webassembly"/><category term="datasette-agent"/><category term="micropython"/></entry><entry><title>datasette 1.0a32</title><link href="https://simonwillison.net/2026/May/31/datasette/#atom-tag" rel="alternate"/><published>2026-05-31T23:23:38+00:00</published><updated>2026-05-31T23:23:38+00:00</updated><id>https://simonwillison.net/2026/May/31/datasette/#atom-tag</id><summary type="html">
    
        &lt;p&gt;&lt;strong&gt;Release:&lt;/strong&gt; &lt;a href="https://github.com/simonw/datasette/releases/tag/1.0a32"&gt;datasette 1.0a32&lt;/a&gt;&lt;/p&gt;
        &lt;p&gt;A minor bugfix release. Fixes a bug with &lt;code&gt;INSERT ... RETURNING&lt;/code&gt; queries via the &lt;a href="https://datasette.io/blog/2026/sql-write-queries/"&gt;new /db/-/execute-write endpoint&lt;/a&gt; and a bunch of &lt;a href="https://docs.datasette.io/en/latest/settings.html#setting-base-url"&gt;base_url&lt;/a&gt; issues which showed up when I was &lt;a href="https://simonwillison.net/2026/May/30/pyodide-asgi-browser/"&gt;experimenting with Service Workers&lt;/a&gt; yesterday.&lt;/p&gt;
    
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/datasette"&gt;datasette&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/annotated-release-notes"&gt;annotated-release-notes&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="datasette"/><category term="annotated-release-notes"/></entry><entry><title>Running Python ASGI apps in the browser via Pyodide + a service worker</title><link href="https://simonwillison.net/2026/May/30/pyodide-asgi-browser/#atom-tag" rel="alternate"/><published>2026-05-30T15:34:00+00:00</published><updated>2026-05-30T15:34:00+00:00</updated><id>https://simonwillison.net/2026/May/30/pyodide-asgi-browser/#atom-tag</id><summary type="html">
    
        &lt;p&gt;&lt;strong&gt;Research:&lt;/strong&gt; &lt;a href="https://github.com/simonw/research/tree/main/pyodide-asgi-browser#readme"&gt;Running Python ASGI apps in the browser via Pyodide + a service worker&lt;/a&gt;&lt;/p&gt;
        &lt;p&gt;&lt;a href="https://lite.datasette.io/"&gt;Datasette Lite&lt;/a&gt; is my version of Datasette that runs entirely in the browser using Pyodide in WebAssembly.&lt;/p&gt;
&lt;p&gt;When I first built it &lt;a href="https://simonwillison.net/2022/May/4/datasette-lite/"&gt;four years ago&lt;/a&gt; I used Web Workers and code that intercepts navigation operations and fetches the generated HTML by running the Python app.&lt;/p&gt;
&lt;p&gt;This worked, but had the disadvantage that any JavaScript in &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tags would not be executed - breaking some Datasette functionality and a whole lot of Datasette plugins.&lt;/p&gt;
&lt;p&gt;This morning I &lt;a href="https://github.com/simonw/research/pull/112"&gt;set Claude Opus 4.8 the task&lt;/a&gt; (in Claude Code for web) of figuring out how to run Python ASGI apps in Pyodide using Service Workers instead, and it seems to work! Here's a &lt;a href="https://simonw.github.io/research/pyodide-asgi-browser/"&gt;basic ASGI FastCGI demo&lt;/a&gt; and here's &lt;a href="https://simonw.github.io/research/pyodide-asgi-browser/datasette.html"&gt;a demo that runs Datasette 1.0a31&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I'm still getting my head around exactly how it works, but once I've done that I plan to upgrade Datasette Lite itself.&lt;/p&gt;
    
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/javascript"&gt;javascript&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/python"&gt;python&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/datasette"&gt;datasette&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/asgi"&gt;asgi&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/webassembly"&gt;webassembly&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/service-workers"&gt;service-workers&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/pyodide"&gt;pyodide&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/datasette-lite"&gt;datasette-lite&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/claude-code"&gt;claude-code&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="javascript"/><category term="python"/><category term="datasette"/><category term="asgi"/><category term="webassembly"/><category term="service-workers"/><category term="pyodide"/><category term="datasette-lite"/><category term="claude-code"/></entry><entry><title>datasette 1.0a31</title><link href="https://simonwillison.net/2026/May/29/datasette/#atom-tag" rel="alternate"/><published>2026-05-29T03:32:02+00:00</published><updated>2026-05-29T03:32:02+00:00</updated><id>https://simonwillison.net/2026/May/29/datasette/#atom-tag</id><summary type="html">
    
        &lt;p&gt;&lt;strong&gt;Release:&lt;/strong&gt; &lt;a href="https://github.com/simonw/datasette/releases/tag/1.0a31"&gt;datasette 1.0a31&lt;/a&gt;&lt;/p&gt;
        &lt;p&gt;Another significant alpha release, with two new headline features.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Datasette now offers users with the necessary permissions the ability to both &lt;strong&gt;execute write queries&lt;/strong&gt; against their database and to &lt;strong&gt;save stored queries&lt;/strong&gt; (renamed from "canned queries") both privately and for use by other members of their Datasette instance.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;There's more detail in &lt;a href="https://datasette.io/blog/2026/sql-write-queries/"&gt;SQL write queries and stored queries in Datasette 1.0a31&lt;/a&gt; on the Datasette blog, which now has &lt;a href="https://datasette.io/blog/"&gt;three posts introducing new features&lt;/a&gt; since the blog launched two weeks ago.&lt;/p&gt;
&lt;p&gt;Here's an animated demo from &lt;a href="https://datasette.io/blog/2026/sql-write-queries/"&gt;the blog post&lt;/a&gt; showing how the new execute query interface lets people get started with templated insert/update/delete queries from tables they have permission to edit:&lt;/p&gt;
&lt;p&gt;&lt;img alt="The user starts on the data database page, selects actions and &amp;quot;Execute write SQL&amp;quot;, then selects the insert document template on the next page and executes it with a title of &amp;quot;My document!&amp;quot;. Also demonstrates that a create table statement cannot be executed because the user does not have create-table permission." src="https://datasette.io/static/blog/2026/sql-write-ui.gif" /&gt;&lt;/p&gt;
    
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/projects"&gt;projects&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/sql"&gt;sql&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/sqlite"&gt;sqlite&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/datasette"&gt;datasette&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/annotated-release-notes"&gt;annotated-release-notes&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="projects"/><category term="sql"/><category term="sqlite"/><category term="datasette"/><category term="annotated-release-notes"/></entry><entry><title>I think Anthropic and OpenAI have found product-market fit</title><link href="https://simonwillison.net/2026/May/27/product-market-fit/#atom-tag" rel="alternate"/><published>2026-05-27T16:38:35+00:00</published><updated>2026-05-27T16:38:35+00:00</updated><id>https://simonwillison.net/2026/May/27/product-market-fit/#atom-tag</id><summary type="html">
    &lt;p&gt;Anthropic are &lt;a href="https://techcrunch.com/2026/05/20/anthropic-says-its-about-to-have-its-first-profitable-quarter/"&gt;strongly rumored&lt;/a&gt; to be about to have their first profitable quarter. Stories &lt;a href="https://www.theinformation.com/newsletters/applied-ai/uber-cto-shows-claude-code-can-blow-ai-budgets"&gt;are circulating&lt;/a&gt; of companies surprised at how expensive their LLM bills are becoming from usage by their staff. I think this is because OpenAI and Anthropic have both found product-market fit.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href="https://simonwillison.net/2026/May/27/product-market-fit/#enterprise-customers-are-now-paying-api-prices"&gt;Enterprise customers are now paying API prices&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://simonwillison.net/2026/May/27/product-market-fit/#i-think-they-ve-found-product-market-fit"&gt;I think they've found product-market fit&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://simonwillison.net/2026/May/27/product-market-fit/#and-they-re-ramping-up"&gt;And they're ramping up&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://simonwillison.net/2026/May/27/product-market-fit/#the-ai-failure-stories-around-this-are-pretty-thin"&gt;The AI-failure stories around this are pretty thin&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://simonwillison.net/2026/May/27/product-market-fit/#we-also-know-the-labs-are-spending-a-lot"&gt;We also know the labs are spending a lot&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://simonwillison.net/2026/May/27/product-market-fit/#api-revenue-is-becoming-less-important"&gt;API revenue is becoming less important&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://simonwillison.net/2026/May/27/product-market-fit/#april-is-a-new-inflection-point"&gt;April is a new inflection point&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id="enterprise-customers-are-now-paying-api-prices"&gt;Enterprise customers are now paying API prices&lt;/h4&gt;
&lt;p&gt;I currently subscribe to the $100/month Max plan from Anthropic and the $100/month Pro plan from OpenAI. If you are a heavy user of coding agents these plans are a fantastic deal. I just ran the &lt;a href="https://github.com/ryoppippi/ccusage"&gt;ccusage&lt;/a&gt; tool on my laptop to get an estimate of how much I would have spent if I were to pay for API tokens in the past 30 days and got:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;$1,199.79 for Anthropic Claude Code&lt;/li&gt;
&lt;li&gt;$980.37 for OpenAI Codex&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That's $2,180.16 worth of tokens for $200 - not bad at all! I'm a moderately heavy user of these tools, but I'm certainly not running agents every hour of the day and night.&lt;/p&gt;
&lt;p&gt;I had assumed that companies making extensive use of agents were getting similar discounts. It turns out I &lt;em&gt;could not have been more wrong&lt;/em&gt; about that.&lt;/p&gt;
&lt;p&gt;I haven't been able to track down the exact date, but at some point in the last six months Anthropic switched their Enterprise plan (originally &lt;a href="https://www.anthropic.com/news/claude-code-on-team-and-enterprise"&gt;"Claude seats include enough usage for a typical workday" back in August 2025&lt;/a&gt;) to $20/seat/month plus API pricing for usage. This story about the change &lt;a href="https://www.theinformation.com/articles/anthropic-changes-pricing-bill-firms-based-ai-use-amid-compute-crunch"&gt;from The Information&lt;/a&gt; is dated Apr 14, 2026, but cites an Anthropic spokesperson claiming that the pricing change occurred in November 2025. Existing customers are finding out about the change as they renew their contracts.&lt;/p&gt;
&lt;p&gt;OpenAI made a similar pricing change in April. The &lt;a href="https://help.openai.com/en/articles/20001106-codex-rate-card"&gt;Codex rate card&lt;/a&gt; (&lt;a href="https://web.archive.org/web/20260519062438/https://help.openai.com/en/articles/20001106-codex-rate-card"&gt;Internet Archive copy&lt;/a&gt;) currently says:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: On April 2, 2026, we updated Codex pricing to align with API token usage, instead of per-message pricing. This change was applicable to new and existing Plus, Pro, ChatGPT Business and new ChatGPT Enterprise plans.&lt;/p&gt;
&lt;p&gt;On April 23, 2026, we made this update for all existing ChatGPT Enterprise plans as well, inclusive of Edu, Health, Gov, and ChatGPT for Teachers.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;It's a little harder to decode as they quote prices in "credits", but as far as I can tell those credit costs are an exact match for the API token costs listed for those models.&lt;/p&gt;
&lt;p&gt;All of which is to say that as of April 2026 the "Enterprise" cost for both OpenAI Codex and Anthropic Claude Code/Cowork is the same as the listed API price.&lt;/p&gt;
&lt;p&gt;GPT-5.5 (released April 23rd) is 2x the API price of GPT-5.4. Opus 4.7 (April 16th) is &lt;a href="https://simonwillison.net/2026/Apr/20/claude-token-counts/"&gt;around 1.4x&lt;/a&gt; the price of Opus 4.6 when you take their new tokenizer into account.&lt;/p&gt;
&lt;p&gt;So April saw both leading model companies release new frontier models with a higher API price, &lt;em&gt;and&lt;/em&gt; both companies now have measures to lock their enterprise customers (who tend to sign year-long deals) at those API prices, not the previous extreme discounts.&lt;/p&gt;
&lt;h4 id="i-think-they-ve-found-product-market-fit"&gt;I think they've found product-market fit&lt;/h4&gt;
&lt;p&gt;Why these sudden aggressive moves on pricing? Both Anthropic and OpenAI are planning to IPO, but I suspect there's a more important factor here: I think they've finally found product-market fit, with the coding/general-purpose agent products embodied by Claude Code/Cowork and Codex.&lt;/p&gt;
&lt;p&gt;Tools like ChatGPT are wildly popular, but that wild popularity has been difficult to turn into revenue. In February &lt;a href="https://finance.yahoo.com/news/chatgpt-almost-1-billion-weekly-212157499.html"&gt;OpenAI boasted&lt;/a&gt; more than 900 million weekly active users for ChatGPT, but only 50 million - 5.6% of that - were paying consumer subscribers.&lt;/p&gt;
&lt;p&gt;Charging $10-$20/month per user is an OK business, but you'd need 1-2 billion subscribers sticking around for four years to cover &lt;a href="https://openai.com/global-affairs/seizing-the-ai-opportunity/"&gt;$1 trillion in infrastructure&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Companies spending $200+/month/user will get you there a whole lot faster - and as noted above, as a power-user I'm at ~$1,000/month in API costs per vendor already.&lt;/p&gt;
&lt;p&gt;Coding agents really did change everything. These are tools which burn &lt;em&gt;vastly&lt;/em&gt; more tokens, but are also quickly becoming daily drivers for the work carried out by extremely well-compensated professionals. Right now that's still mostly software engineers, but a coding agent is a tool that can automate anything you can do by typing commands into a computer... so they are clearly applicable to a much wider set of skilled knowledge workers.&lt;/p&gt;
&lt;p&gt;As I've &lt;a href="https://simonwillison.net/tags/november-2025-inflection/"&gt;discussed on this site at length&lt;/a&gt;, the models released in November 2025 elevated agents to being genuinely useful. We've had six months to get used to that idea now - it's no wonder companies are beginning to spend real money on this technology.&lt;/p&gt;
&lt;p&gt;You could argue that ChatGPT achieved product-market fit when it became the &lt;a href="https://www.reuters.com/technology/chatgpt-sets-record-fastest-growing-user-base-analyst-note-2023-02-01/"&gt;fastest-growing consumer app in history&lt;/a&gt; back in February 2023... but it certainly wasn't making any actual money back then. Coding agents plus enterprise pricing marks the point when these companies start making &lt;em&gt;very&lt;/em&gt; real revenue. Maybe even enough to start covering their costs!&lt;/p&gt;
&lt;h4 id="and-they-re-ramping-up"&gt;And they're ramping up&lt;/h4&gt;
&lt;p&gt;As further evidence that enterprise agents represent product-market fit for these companies, consider their open job listings.&lt;/p&gt;
&lt;p&gt;OpenAI have &lt;a href="https://openai.com/careers/search/"&gt;703 open jobs&lt;/a&gt; right now, of which I'd categorize 229 (32.6%) as relating to enterprise sales and support - account executives, "Go To Market", "Forward Deployed Engineers" and the like.&lt;/p&gt;
&lt;p&gt;Anthropic have &lt;a href="https://www.anthropic.com/careers/jobs"&gt;390 open jobs&lt;/a&gt;, 105 (26.9%) of which look enterprisey to me.&lt;/p&gt;
&lt;p&gt;It's pleasingly ironic that these AI labs have picked a business model with such a heavy demand on human labor - enterprise sales contracts don't close themselves without a whole lot of humans in the mix!&lt;/p&gt;
&lt;p&gt;&lt;small&gt;(I ran this analysis by scraping their job sites with Claude Code, then having it use Datasette's &lt;a href="https://docs.datasette.io/en/latest/json_api.html"&gt;JSON API&lt;/a&gt; to pipe that data into Datasette Cloud where I used &lt;a href="https://agent.datasette.io/"&gt;Datasette Agent&lt;/a&gt; for the analysis, &lt;a href="https://gist.github.com/simonw/5632d208d76b3c8b34f1fdbaf69eb1b8#agent-4"&gt;exported here&lt;/a&gt;. Dogfood!)&lt;/small&gt;&lt;/p&gt;
&lt;h4 id="the-ai-failure-stories-around-this-are-pretty-thin"&gt;The AI-failure stories around this are pretty thin&lt;/h4&gt;
&lt;p&gt;I started digging into this in response to &lt;a href="https://news.ycombinator.com/item?id=48287025#48287219"&gt;a growing volume&lt;/a&gt; of stories claiming that large companies were sounding the alarm because their AI usage costs had grown so large.&lt;/p&gt;
&lt;p&gt;The most widely cited of these stories appear quite overblown to me.&lt;/p&gt;
&lt;p&gt;The most discussed has been Uber, based on &lt;a href="https://www.theinformation.com/newsletters/applied-ai/uber-cto-shows-claude-code-can-blow-ai-budgets"&gt;this report&lt;/a&gt; where CTO Praveen Neppalli Naga indicated that Uber had "maxed out its full year AI budget just a few months into 2026", mostly thanks to Claude Code.&lt;/p&gt;
&lt;p&gt;Given that Claude Code only got &lt;em&gt;really&lt;/em&gt; good in November it's entirely unsurprising to me that a budget set in 2025 may have failed to predict demand for that tool in 2026!&lt;/p&gt;
&lt;p&gt;That Uber story was further fueled by comments made by Uber's COO, Andrew Macdonald, on the Rapid Response podcast. I tracked down &lt;a href="https://www.youtube.com/watch?v=y_mQ6xLcKyc&amp;amp;t=1616s"&gt;the segment&lt;/a&gt; and there really isn't much there. Here's what Andrew said:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;But then you sometimes go and talk to your senior engineering leaders and you're saying, OK, how many projects that were on the cutting room floor got moved above the line because of the productivity gains because 25% of our code commits were via Claude Code last quarter?&lt;/p&gt;
&lt;p&gt;That link is not there yet, right? I think maybe implicitly there's more that is getting shipped. But it's very hard to draw a line between one of those stats and, OK, now we're actually producing like 25% more useful consumer features, right? And that line is hard to draw.&lt;/p&gt;
&lt;p&gt;[...] And so if you're not actually able to draw a direct line to how much useful features and functionality you're shipping to your users, that trade becomes harder to justify.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Somehow this fragment turned into headlines like &lt;a href="https://www.businessinsider.com/uber-coo-andrew-macdonald-ai-token-spending-harder-justify-2026-5"&gt;Uber's COO says it's getting harder to justify the money spent on AI tokenmaxxing&lt;/a&gt;, because the market for stories about AI failures remains enormous.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;Update 29th May 2026&lt;/strong&gt;: I edited the above quote to add that last paragraph ending in "becomes harder to justify" on &lt;a href="https://x.com/MadisonMills22/status/2060343512936186240"&gt;the suggestion of Madison Mills&lt;/a&gt; - previously my quoted section stopped at "hard to draw". Here's the &lt;a href="https://gist.github.com/simonw/59096a338c82f6f95e40e3d7c7b5bad9"&gt;full unedited transcript&lt;/a&gt; from MacWhisper.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The other popular story around this is &lt;a href="https://www.theverge.com/tech/930447/microsoft-claude-code-discontinued-notepad"&gt;Microsoft starts canceling Claude Code licenses&lt;/a&gt;, ostensibly to encourage their engineers to dogfood their own Copilot CLI agent instead - but The Verge reporter Tom Warren says "sources tell me the decision is also a financial one", triggered by the June 30th end of Microsoft's financial year.&lt;/p&gt;
&lt;p&gt;I think both of these stories support my "product-market fit" hypothesis. The best advice I ever heard on pricing a product was that your customer should &lt;em&gt;suck air through their teeth&lt;/em&gt; and then say yes. Uber's budget overrun and Microsoft's seat cancellations look like that effect playing out in practice.&lt;/p&gt;
&lt;h4 id="we-also-know-the-labs-are-spending-a-lot"&gt;We also know the labs are spending a lot&lt;/h4&gt;
&lt;p&gt;The big AI labs spend billions of dollars on both training and inference. Credible figures are hard to come by, but we did get one huge hint as to the figures involved from, oddly enough, the recent &lt;a href="https://www.sec.gov/Archives/edgar/data/1181412/000162828026036936/spaceexplorationtechnologi.htm"&gt;SpaceX S-1&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;[...] in May 2026, we entered into &lt;strong&gt;Cloud Services Agreements with Anthropic PBC&lt;/strong&gt; (“Anthropic”), an AI research and development public benefit corporation, with respect to access to &lt;strong&gt;compute capacity across COLOSSUS and COLOSSUS II&lt;/strong&gt;. Pursuant to these agreements, the customer &lt;strong&gt;has agreed to pay us $1.25 billion per month&lt;/strong&gt; through May 2029 [...]&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The &lt;a href="https://www.anthropic.com/news/higher-limits-spacex"&gt;Anthropic announcement&lt;/a&gt; said that this deal meant they could "increase our usage limits for Claude Code and the Claude API", heavily implying that Colossus is being used for inference, not model training.&lt;/p&gt;
&lt;p&gt;Anthropic already have vast amounts of compute from other providers. The fact that they're willing to spend $1.25 billion per month for extra capacity from just &lt;em&gt;one&lt;/em&gt; of their vendors hints at how big these inference budgets have become.&lt;/p&gt;
&lt;h4 id="api-revenue-is-becoming-less-important"&gt;API revenue is becoming less important&lt;/h4&gt;
&lt;p&gt;Over the past two years my impression has been that OpenAI made more of their income from subscription revenue while Anthropic made more from their API.&lt;/p&gt;
&lt;p&gt;Anthropic's API revenue was historically quite dependent on a small number of large API customers - &lt;a href="https://venturebeat.com/ai/anthropic-revenue-tied-to-two-customers-as-ai-pricing-war-threatens-margins"&gt;this VentureBeat story from August 2025&lt;/a&gt; quotes "sources familiar with the matter" suggesting that just Cursor and GitHub Copilot were responsible for $1.2 billion of the company's then-$4 billion revenue.&lt;/p&gt;
&lt;p&gt;Today Anthropic are rumored to hit &lt;a href="https://www.wsj.com/tech/ai/mind-blowing-growth-is-about-to-propel-anthropic-into-its-first-profitable-quarter-7edbf2f4"&gt;$10.9 billion in the second quarter&lt;/a&gt;, potentially even operating at a profit for the first time.&lt;/p&gt;
&lt;p&gt;This pivot-to-Enterprise suggests that the labs have realized that the real money lies in cutting out the middlemen. Anthropic's Claude Code directly competes with Cursor and Copilot. No wonder Cursor are &lt;a href="https://cursor.com/blog/composer-2"&gt;investing in their own models&lt;/a&gt;!&lt;/p&gt;
&lt;h4 id="april-is-a-new-inflection-point"&gt;April is a new inflection point&lt;/h4&gt;
&lt;p&gt;I've called November 2025 the &lt;a href="https://simonwillison.net/tags/november-2025-inflection/"&gt;November inflection point&lt;/a&gt; because that was when GPT-5.1 and Opus 4.5, combined with their respective coding agent harnesses, got &lt;em&gt;good&lt;/em&gt; - good enough that we've spent the last six months adapting to agent systems that can reliably get useful work done.&lt;/p&gt;
&lt;p&gt;I think April 2026 is a new inflection point where the revenue implications of this have started to land, to the benefit of the frontier AI labs and with material impacts on the budgets of large companies.&lt;/p&gt;
&lt;p&gt;We'll know for sure how real this moment is when the S-1 documents for the upcoming Anthropic and OpenAI IPOs give us some real, audited numbers to get our teeth into.&lt;/p&gt;
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/ai"&gt;ai&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/datasette"&gt;datasette&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/openai"&gt;openai&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/generative-ai"&gt;generative-ai&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/llms"&gt;llms&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/anthropic"&gt;anthropic&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/llm-pricing"&gt;llm-pricing&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/coding-agents"&gt;coding-agents&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/claude-code"&gt;claude-code&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/codex"&gt;codex&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/claude-cowork"&gt;claude-cowork&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/november-2025-inflection"&gt;november-2025-inflection&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/datasette-agent"&gt;datasette-agent&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/uber"&gt;uber&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="ai"/><category term="datasette"/><category term="openai"/><category term="generative-ai"/><category term="llms"/><category term="anthropic"/><category term="llm-pricing"/><category term="coding-agents"/><category term="claude-code"/><category term="codex"/><category term="claude-cowork"/><category term="november-2025-inflection"/><category term="datasette-agent"/><category term="uber"/></entry><entry><title>datasette 1.0a30</title><link href="https://simonwillison.net/2026/May/24/datasette/#atom-tag" rel="alternate"/><published>2026-05-24T23:52:37+00:00</published><updated>2026-05-24T23:52:37+00:00</updated><id>https://simonwillison.net/2026/May/24/datasette/#atom-tag</id><summary type="html">
    
        &lt;p&gt;&lt;strong&gt;Release:&lt;/strong&gt; &lt;a href="https://github.com/simonw/datasette/releases/tag/1.0a30"&gt;datasette 1.0a30&lt;/a&gt;&lt;/p&gt;
        &lt;p&gt;The big new feature in this alpha is a new customizable "Jump to..." menu, described in detail in &lt;a href="https://datasette.io/blog/2026/jump-menu/"&gt;The extensible "Jump to" menu in Datasette 1.0a30&lt;/a&gt; on the Datasette blog. You can try it out by hitting &lt;code&gt;/&lt;/code&gt; on &lt;a href="https://latest.datasette.io/"&gt;latest.datasette.io&lt;/a&gt; - it looks like this:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Animated demo - the Jump to menu appears, and as the user types it filters to specific databases and tables and debug options" src="https://static.simonwillison.net/static/2026/menu.gif" /&gt;&lt;/p&gt;
&lt;p&gt;The new &lt;a href="https://docs.datasette.io/en/latest/plugin_hooks.html#jump-items-sql-datasette-actor-request"&gt;jump_items_sql()&lt;/a&gt; plugin hook allows plugins to add their own items to the set that's searched by the plugin.&lt;/p&gt;
    
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/projects"&gt;projects&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/datasette"&gt;datasette&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/annotated-release-notes"&gt;annotated-release-notes&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="projects"/><category term="datasette"/><category term="annotated-release-notes"/></entry><entry><title>datasette-agent 0.1a4</title><link href="https://simonwillison.net/2026/May/24/datasette-agent/#atom-tag" rel="alternate"/><published>2026-05-24T23:19:34+00:00</published><updated>2026-05-24T23:19:34+00:00</updated><id>https://simonwillison.net/2026/May/24/datasette-agent/#atom-tag</id><summary type="html">
    
        &lt;p&gt;&lt;strong&gt;Release:&lt;/strong&gt; &lt;a href="https://github.com/datasette/datasette-agent/releases/tag/0.1a4"&gt;datasette-agent 0.1a4&lt;/a&gt;&lt;/p&gt;
        &lt;p&gt;Taking advantage of the new &lt;a href="https://docs.datasette.io/en/latest/javascript_plugins.html#javascript-plugins-makejumpsections"&gt;makeJumpSections()&lt;/a&gt; JavaScript plugin hook added in &lt;a href="https://docs.datasette.io/en/latest/changelog.html#a30-2026-05-24"&gt;Datasette 1.0a30&lt;/a&gt;, &lt;code&gt;datasette-agent&lt;/code&gt; now presents this "Start a new agent chat" interface as part of the Jump to menu, any time you hit &lt;code&gt;/&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Animated demo - this time the demo starts on agent.datasette.io and when the menu opens it has a new Start chat box below the search box - entering 'count entries' and hitting the button causes it to start an agent conversation that counts the number of entries and returns 3300." src="https://static.simonwillison.net/static/2026/menu-agent.gif" /&gt;&lt;/p&gt;
&lt;p&gt;You can try this out by signing into &lt;a href="https://agent.datasette.io/"&gt;agent.datasette.io&lt;/a&gt; using your GitHub account.&lt;/p&gt;
    
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/datasette"&gt;datasette&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/datasette-agent"&gt;datasette-agent&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="datasette"/><category term="datasette-agent"/></entry><entry><title>datasette-fixtures 0.1a0</title><link href="https://simonwillison.net/2026/May/24/datasette-fixtures/#atom-tag" rel="alternate"/><published>2026-05-24T21:38:32+00:00</published><updated>2026-05-24T21:38:32+00:00</updated><id>https://simonwillison.net/2026/May/24/datasette-fixtures/#atom-tag</id><summary type="html">
    
        &lt;p&gt;&lt;strong&gt;Release:&lt;/strong&gt; &lt;a href="https://github.com/datasette/datasette-fixtures/releases/tag/0.1a0"&gt;datasette-fixtures 0.1a0&lt;/a&gt;&lt;/p&gt;
        &lt;p&gt;One of the smaller features in &lt;a href="https://docs.datasette.io/en/latest/changelog.html#a30-2026-05-24"&gt;Datasette 1.0a30&lt;/a&gt; is this:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;New documented &lt;a href="https://docs.datasette.io/en/latest/testing_plugins.html#datasette-fixtures-populate-fixture-database"&gt;datasette.fixtures.populate_fixture_database(conn)&lt;/a&gt; helper for creating the fixture database tables used by Datasette's own tests, intended for plugin test suites.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This new plugin takes advantage of that API. You can try it out using &lt;code&gt;uvx&lt;/code&gt; without even installing Datasette like this:&lt;/p&gt;
&lt;pre&gt;uvx --prerelease=allow \
  --with datasette-fixtures datasette \
  --get /fixtures/roadside_attractions.json&lt;/pre&gt;
&lt;p&gt;Which outputs:&lt;/p&gt;
&lt;pre&gt;{
  &lt;span class="pl-ent"&gt;"ok"&lt;/span&gt;: &lt;span class="pl-c1"&gt;true&lt;/span&gt;,
  &lt;span class="pl-ent"&gt;"next"&lt;/span&gt;: &lt;span class="pl-c1"&gt;null&lt;/span&gt;,
  &lt;span class="pl-ent"&gt;"rows"&lt;/span&gt;: [
    {&lt;span class="pl-ent"&gt;"pk"&lt;/span&gt;: &lt;span class="pl-c1"&gt;1&lt;/span&gt;, &lt;span class="pl-ent"&gt;"name"&lt;/span&gt;: &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;The Mystery Spot&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;, &lt;span class="pl-ent"&gt;"address"&lt;/span&gt;: &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;465 Mystery Spot Road, Santa Cruz, CA 95065&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;, &lt;span class="pl-ent"&gt;"url"&lt;/span&gt;: &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;https://www.mysteryspot.com/&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;, &lt;span class="pl-ent"&gt;"latitude"&lt;/span&gt;: &lt;span class="pl-c1"&gt;37.0167&lt;/span&gt;, &lt;span class="pl-ent"&gt;"longitude"&lt;/span&gt;: &lt;span class="pl-c1"&gt;-122.0024&lt;/span&gt;},
    {&lt;span class="pl-ent"&gt;"pk"&lt;/span&gt;: &lt;span class="pl-c1"&gt;2&lt;/span&gt;, &lt;span class="pl-ent"&gt;"name"&lt;/span&gt;: &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;Winchester Mystery House&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;, &lt;span class="pl-ent"&gt;"address"&lt;/span&gt;: &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;525 South Winchester Boulevard, San Jose, CA 95128&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;, &lt;span class="pl-ent"&gt;"url"&lt;/span&gt;: &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;https://winchestermysteryhouse.com/&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;, &lt;span class="pl-ent"&gt;"latitude"&lt;/span&gt;: &lt;span class="pl-c1"&gt;37.3184&lt;/span&gt;, &lt;span class="pl-ent"&gt;"longitude"&lt;/span&gt;: &lt;span class="pl-c1"&gt;-121.9511&lt;/span&gt;},
    {&lt;span class="pl-ent"&gt;"pk"&lt;/span&gt;: &lt;span class="pl-c1"&gt;3&lt;/span&gt;, &lt;span class="pl-ent"&gt;"name"&lt;/span&gt;: &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;Burlingame Museum of PEZ Memorabilia&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;, &lt;span class="pl-ent"&gt;"address"&lt;/span&gt;: &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;214 California Drive, Burlingame, CA 94010&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;, &lt;span class="pl-ent"&gt;"url"&lt;/span&gt;: &lt;span class="pl-c1"&gt;null&lt;/span&gt;, &lt;span class="pl-ent"&gt;"latitude"&lt;/span&gt;: &lt;span class="pl-c1"&gt;37.5793&lt;/span&gt;, &lt;span class="pl-ent"&gt;"longitude"&lt;/span&gt;: &lt;span class="pl-c1"&gt;-122.3442&lt;/span&gt;},
    {&lt;span class="pl-ent"&gt;"pk"&lt;/span&gt;: &lt;span class="pl-c1"&gt;4&lt;/span&gt;, &lt;span class="pl-ent"&gt;"name"&lt;/span&gt;: &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;Bigfoot Discovery Museum&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;, &lt;span class="pl-ent"&gt;"address"&lt;/span&gt;: &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;5497 Highway 9, Felton, CA 95018&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;, &lt;span class="pl-ent"&gt;"url"&lt;/span&gt;: &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;https://www.bigfootdiscoveryproject.com/&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;, &lt;span class="pl-ent"&gt;"latitude"&lt;/span&gt;: &lt;span class="pl-c1"&gt;37.0414&lt;/span&gt;, &lt;span class="pl-ent"&gt;"longitude"&lt;/span&gt;: &lt;span class="pl-c1"&gt;-122.0725&lt;/span&gt;}
  ],
  &lt;span class="pl-ent"&gt;"truncated"&lt;/span&gt;: &lt;span class="pl-c1"&gt;false&lt;/span&gt;
}&lt;/pre&gt;
    
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/datasette"&gt;datasette&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/uv"&gt;uv&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="datasette"/><category term="uv"/></entry><entry><title>datasette-llm-limits 0.1a1</title><link href="https://simonwillison.net/2026/May/21/datasette-llm-limits/#atom-tag" rel="alternate"/><published>2026-05-21T21:04:59+00:00</published><updated>2026-05-21T21:04:59+00:00</updated><id>https://simonwillison.net/2026/May/21/datasette-llm-limits/#atom-tag</id><summary type="html">
    
        &lt;p&gt;&lt;strong&gt;Release:&lt;/strong&gt; &lt;a href="https://github.com/datasette/datasette-llm-limits/releases/tag/0.1a1"&gt;datasette-llm-limits 0.1a1&lt;/a&gt;&lt;/p&gt;
        &lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;Improved design of the &lt;code&gt;/-/llm-limits&lt;/code&gt; page, now using the base template. #2&lt;/li&gt;
&lt;li&gt;Now shown in application menu for users with the &lt;code&gt;datasette-llm-limits-view&lt;/code&gt; permission.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
    
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/datasette"&gt;datasette&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="datasette"/></entry><entry><title>Datasette Agent</title><link href="https://simonwillison.net/2026/May/21/datasette-agent/#atom-tag" rel="alternate"/><published>2026-05-21T19:52:19+00:00</published><updated>2026-05-21T19:52:19+00:00</updated><id>https://simonwillison.net/2026/May/21/datasette-agent/#atom-tag</id><summary type="html">
    &lt;p&gt;We just &lt;a href="https://datasette.io/blog/2026/datasette-agent/"&gt;announced the first release of Datasette Agent&lt;/a&gt;, a new extensible AI assistant for Datasette. I've been working on my &lt;a href="https://llm.datasette.io/"&gt;LLM&lt;/a&gt; Python library for just over three years now, and Datasette Agent represents the moment that LLM and &lt;a href="https://datasette.io/"&gt;Datasette&lt;/a&gt; finally come together. I'm really excited about it!&lt;/p&gt;
&lt;p&gt;Datasette Agent provides a conversational interface for asking questions of the data you have stored in Datasette. Add the &lt;a href="https://github.com/datasette/datasette-agent-charts"&gt;datasette-agent-charts&lt;/a&gt; plugin and it can generate charts of your data as well.&lt;/p&gt;
&lt;h4 id="the-demo"&gt;The demo&lt;/h4&gt;
&lt;p&gt;The &lt;a href=""&gt;announcement post&lt;/a&gt; (on the new Datasette project blog) includes this &lt;a href="https://www.youtube.com/watch?v=AFZKp6hbFjI"&gt;demo video&lt;/a&gt;:&lt;/p&gt;

&lt;iframe style="margin-bottom: 1.5em;" width="560" height="315" src="https://www.youtube-nocookie.com/embed/AFZKp6hbFjI" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="allowfullscreen"&gt; &lt;/iframe&gt;


&lt;p&gt;I recorded the video against the new &lt;a href="https://agent.datasette.io/"&gt;agent.datasette.io&lt;/a&gt; live demo instance, which runs Datasette Agent against example databases including the classic &lt;a href="https://datasette.io/global-power-plants"&gt;global-power-plants&lt;/a&gt; by &lt;a href="https://www.wri.org/research/global-database-power-plants"&gt;WRI&lt;/a&gt;, and a copy of the &lt;a href="https://datasette.simonwillison.net/"&gt;Datasette backup&lt;/a&gt; of my blog.&lt;/p&gt;
&lt;p&gt;The live demo runs on &lt;a href="https://ai.google.dev/gemini-api/docs/models/gemini-3.1-flash-lite"&gt;Gemini 3.1 Flash-Lite&lt;/a&gt; - it's cheap, fast and has no trouble writing SQLite queries.&lt;/p&gt;
&lt;p&gt;A question I asked in the demo was:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;when did Simon most recently see a pelican?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Which ran &lt;a href="https://datasette.simonwillison.net/simonwillisonblog?sql=SELECT+title%2C+commentary%2C+created+FROM+blog_beat+WHERE+beat_type+%3D+%27sighting%27+AND+%28title+LIKE+%27%25pelican%25%27+OR+commentary+LIKE+%27%25pelican%25%27%29+ORDER+BY+created+DESC+LIMIT+5"&gt;this SQL query&lt;/a&gt;:&lt;/p&gt;
&lt;div class="highlight highlight-source-sql"&gt;&lt;pre&gt;&lt;span class="pl-k"&gt;SELECT&lt;/span&gt;
  title, commentary, created
&lt;span class="pl-k"&gt;FROM&lt;/span&gt;
  blog_beat
&lt;span class="pl-k"&gt;WHERE&lt;/span&gt;
  beat_type &lt;span class="pl-k"&gt;=&lt;/span&gt; &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;'&lt;/span&gt;sighting&lt;span class="pl-pds"&gt;'&lt;/span&gt;&lt;/span&gt;
  &lt;span class="pl-k"&gt;AND&lt;/span&gt; (title &lt;span class="pl-k"&gt;LIKE&lt;/span&gt; &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;'&lt;/span&gt;%pelican%&lt;span class="pl-pds"&gt;'&lt;/span&gt;&lt;/span&gt; &lt;span class="pl-k"&gt;OR&lt;/span&gt; commentary &lt;span class="pl-k"&gt;LIKE&lt;/span&gt; &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;'&lt;/span&gt;%pelican%&lt;span class="pl-pds"&gt;'&lt;/span&gt;&lt;/span&gt;)
&lt;span class="pl-k"&gt;ORDER BY&lt;/span&gt;
  created &lt;span class="pl-k"&gt;DESC&lt;/span&gt;
&lt;span class="pl-k"&gt;LIMIT&lt;/span&gt;
  &lt;span class="pl-c1"&gt;5&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And replied:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The most recent sighting of a pelican by Simon was recorded on &lt;strong&gt;May 20, 2026&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;The observation included a California Brown Pelican, along with a Common Loon, Canada Goose, Striped Shore Crab, and a California Sea Lion.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Here's &lt;a href="https://simonwillison.net/2026/May/20/sighting-363395265/"&gt;that sighting on my blog&lt;/a&gt;, and the &lt;a href="https://gist.github.com/simonw/a46d17b69659a4866adb1d868280091d"&gt;Markdown export&lt;/a&gt; of the full conversation transcript.&lt;/p&gt;
&lt;h4 id="the-plugins"&gt;The plugins&lt;/h4&gt;
&lt;p&gt;My favorite feature of Datasette Agent is that, like the rest of Datasette, it's extensible using plugins.&lt;/p&gt;
&lt;p&gt;We've shipped three plugins so far:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/datasette/datasette-agent-charts"&gt;datasette-agent-charts&lt;/a&gt;, shown in the video, adds charts to Datasette Agent, powered by &lt;a href="https://observablehq.com/plot/"&gt;Observable Plot&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/datasette/datasette-agent-openai-imagegen"&gt;datasette-agent-openai-imagegen&lt;/a&gt; adds an image generation tool to Datasette Agent using &lt;a href="https://openai.com/index/introducing-chatgpt-images-2-0/"&gt;ChatGPT Images 2.0&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/datasette/datasette-agent-sprites"&gt;datasette-agent-sprites&lt;/a&gt; provides tools for executing code in a &lt;a href="https://sprites.dev/"&gt;Fly Sprites&lt;/a&gt; persistent sandbox.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Building plugins is &lt;em&gt;really fun&lt;/em&gt;. I have a bunch more prototypes that aren't quite alpha-quality yet.&lt;/p&gt;
&lt;p&gt;Claude Code and OpenAI Codex are both proving excellent at writing plugins - just point them at a checkout of the &lt;a href="https://github.com/datasette/datasette-agent"&gt;datasette-agent repo&lt;/a&gt; for reference and tell them what you want to build!&lt;/p&gt;
&lt;h4 id="running-it-against-local-models"&gt;Running it against local models&lt;/h4&gt;
&lt;p&gt;I've also been having fun running the new plugin against local models. Here's a &lt;code&gt;uv&lt;/code&gt; one-liner to run the plugin against &lt;a href="https://huggingface.co/google/gemma-4-26B-A4B"&gt;gemma-4-26b-a4b&lt;/a&gt; in &lt;a href="https://lmstudio.ai"&gt;LM Studio&lt;/a&gt; on a Mac:&lt;/p&gt;
&lt;div class="highlight highlight-source-shell"&gt;&lt;pre&gt;uvx --prerelease=allow \
  --with datasette-agent --with llm-lmstudio \
  datasette --internal internal.db --root \
  -s plugins.datasette-llm.default_model lmstudio/google/gemma-4-26b-a4b \
  data.db&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Datasette Agent needs reliable tool calls and the ability for a model to produce SQL queries that run against SQLite. The open weight models released in the past six months are increasingly able to handle that.&lt;/p&gt;
&lt;h4 id="what-s-next"&gt;What's next&lt;/h4&gt;
&lt;p&gt;Datasette Agent opens up &lt;em&gt;so many&lt;/em&gt; opportunities for the LLM and Datasette ecosystem in general.&lt;/p&gt;
&lt;p&gt;It's already informed &lt;a href="https://simonwillison.net/2026/Apr/29/llm/"&gt;the major LLM 0.32a0 refactor&lt;/a&gt; which I'm nearly ready to roll into a stable release, maybe with some additional "LLM agent" abstractions extracte from Datasette Agent itself.&lt;/p&gt;
&lt;p&gt;I've been exploring my own take on the Claude Artifacts, which is shaping up nicely as a plugin.&lt;/p&gt;
&lt;p&gt;I'm excited to use Datasette Agent to build my own &lt;a href="https://simonwillison.net/2026/May/19/5-minute-llms/#5-minutes-llms.013.jpeg"&gt;Claw&lt;/a&gt; - a personal AI assistant built around data imported from different parts of my digital life, which is a neat excuse to revisit my older &lt;a href="https://dogsheep.github.io"&gt;Dogsheep&lt;/a&gt; family of tools.&lt;/p&gt;
&lt;p&gt;We'll also be rolling out Datasette Agent for users of &lt;a href="https://datasette.cloud/"&gt;Datasette Cloud&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Join our &lt;a href="https://discord.gg/hdxyusUFv"&gt;#datasette-agent Discord channel&lt;/a&gt; if you'd like to talk about the project.&lt;/p&gt;
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/projects"&gt;projects&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/sqlite"&gt;sqlite&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ai"&gt;ai&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/datasette"&gt;datasette&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/generative-ai"&gt;generative-ai&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/llms"&gt;llms&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/llm"&gt;llm&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/uv"&gt;uv&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/datasette-agent"&gt;datasette-agent&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="projects"/><category term="sqlite"/><category term="ai"/><category term="datasette"/><category term="generative-ai"/><category term="llms"/><category term="llm"/><category term="uv"/><category term="datasette-agent"/></entry><entry><title>datasette-agent-sprites 0.1a0</title><link href="https://simonwillison.net/2026/May/21/datasette-agent-sprites/#atom-tag" rel="alternate"/><published>2026-05-21T18:21:07+00:00</published><updated>2026-05-21T18:21:07+00:00</updated><id>https://simonwillison.net/2026/May/21/datasette-agent-sprites/#atom-tag</id><summary type="html">
    
        &lt;p&gt;&lt;strong&gt;Release:&lt;/strong&gt; &lt;a href="https://github.com/datasette/datasette-agent-sprites/releases/tag/0.1a0"&gt;datasette-agent-sprites 0.1a0&lt;/a&gt;&lt;/p&gt;
        &lt;p&gt;A Datasette Agent plugin for running commands in a &lt;a href="https://sprites.dev"&gt;Fly Sprites&lt;/a&gt; sandbox.&lt;/p&gt;
    
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/sandboxing"&gt;sandboxing&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/datasette"&gt;datasette&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/fly"&gt;fly&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/datasette-agent"&gt;datasette-agent&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="sandboxing"/><category term="datasette"/><category term="fly"/><category term="datasette-agent"/></entry><entry><title>datasette-agent-charts 0.1a2</title><link href="https://simonwillison.net/2026/May/21/datasette-agent-charts/#atom-tag" rel="alternate"/><published>2026-05-21T15:15:58+00:00</published><updated>2026-05-21T15:15:58+00:00</updated><id>https://simonwillison.net/2026/May/21/datasette-agent-charts/#atom-tag</id><summary type="html">
    
        &lt;p&gt;&lt;strong&gt;Release:&lt;/strong&gt; &lt;a href="https://github.com/datasette/datasette-agent-charts/releases/tag/0.1a2"&gt;datasette-agent-charts 0.1a2&lt;/a&gt;&lt;/p&gt;
        &lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;"View SQL query" buttons below rendered charts.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
    
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/datasette"&gt;datasette&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/datasette-agent"&gt;datasette-agent&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="datasette"/><category term="datasette-agent"/></entry><entry><title>datasette-agent 0.1a3</title><link href="https://simonwillison.net/2026/May/21/datasette-agent-2/#atom-tag" rel="alternate"/><published>2026-05-21T15:04:09+00:00</published><updated>2026-05-21T15:04:09+00:00</updated><id>https://simonwillison.net/2026/May/21/datasette-agent-2/#atom-tag</id><summary type="html">
    
        &lt;p&gt;&lt;strong&gt;Release:&lt;/strong&gt; &lt;a href="https://github.com/datasette/datasette-agent/releases/tag/0.1a3"&gt;datasette-agent 0.1a3&lt;/a&gt;&lt;/p&gt;
        &lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;"View SQL query" buttons for both visible tables and collapsed SQL result tool calls.&lt;/li&gt;
&lt;li&gt;Don't display empty reasoning chunks&lt;/li&gt;
&lt;li&gt;Improved handling of truncated responses - table still displays to the user even if the SQL results were truncated when showing the agent.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;See &lt;a href="https://datasette.io/blog/2026/datasette-agent/"&gt;Datasette Agent, an extensible AI assistant for Datasette&lt;/a&gt;.&lt;/p&gt;
    
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/datasette"&gt;datasette&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/datasette-agent"&gt;datasette-agent&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="datasette"/><category term="datasette-agent"/></entry><entry><title>datasette-agent-charts 0.1a1</title><link href="https://simonwillison.net/2026/May/20/datasette-agent-charts/#atom-tag" rel="alternate"/><published>2026-05-20T14:52:16+00:00</published><updated>2026-05-20T14:52:16+00:00</updated><id>https://simonwillison.net/2026/May/20/datasette-agent-charts/#atom-tag</id><summary type="html">
    
        &lt;p&gt;&lt;strong&gt;Release:&lt;/strong&gt; &lt;a href="https://github.com/datasette/datasette-agent-charts/releases/tag/0.1a1"&gt;datasette-agent-charts 0.1a1&lt;/a&gt;&lt;/p&gt;
        &lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;More color! Bar and waffle charts without a color column are shaded by magnitude with a sequential color scheme; color columns holding text values use the &lt;code&gt;observable10&lt;/code&gt; categorical scheme. #2&lt;/li&gt;
&lt;li&gt;Now checks &lt;code&gt;execute-sql&lt;/code&gt; permission before running the query to find the column names.&lt;/li&gt;
&lt;li&gt;Charts now display interactive tooltips.&lt;/li&gt;
&lt;li&gt;Fixed a bug where &lt;code&gt;waffleY&lt;/code&gt; charts were not described to the agent.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
    
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/datasette"&gt;datasette&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/datasette-agent"&gt;datasette-agent&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="datasette"/><category term="datasette-agent"/></entry><entry><title>datasette-llm-accountant 0.1a4</title><link href="https://simonwillison.net/2026/May/19/datasette-llm-accountant/#atom-tag" rel="alternate"/><published>2026-05-19T20:45:43+00:00</published><updated>2026-05-19T20:45:43+00:00</updated><id>https://simonwillison.net/2026/May/19/datasette-llm-accountant/#atom-tag</id><summary type="html">
    
        &lt;p&gt;&lt;strong&gt;Release:&lt;/strong&gt; &lt;a href="https://github.com/datasette/datasette-llm-accountant/releases/tag/0.1a4"&gt;datasette-llm-accountant 0.1a4&lt;/a&gt;&lt;/p&gt;
        &lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;Fixed bug tracking chains of responses. Refs &lt;a href="https://github.com/datasette/datasette-llm/issues/7"&gt;datasette-llm#7&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
    
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/datasette"&gt;datasette&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/llm"&gt;llm&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="datasette"/><category term="llm"/></entry><entry><title>datasette-llm-limits 0.1a0</title><link href="https://simonwillison.net/2026/May/15/datasette-llm-limits/#atom-tag" rel="alternate"/><published>2026-05-15T00:42:09+00:00</published><updated>2026-05-15T00:42:09+00:00</updated><id>https://simonwillison.net/2026/May/15/datasette-llm-limits/#atom-tag</id><summary type="html">
    
        &lt;p&gt;&lt;strong&gt;Release:&lt;/strong&gt; &lt;a href="https://github.com/datasette/datasette-llm-limits/releases/tag/0.1a0"&gt;datasette-llm-limits 0.1a0&lt;/a&gt;&lt;/p&gt;
        &lt;p&gt;This plugin works in conjunction with &lt;a href="https://github.com/datasette/datasette-llm"&gt;datasette-llm&lt;/a&gt; and &lt;a href="https://github.com/datasette/datasette-llm-accountant"&gt;datasette-llm-accountant&lt;/a&gt; to let you configure a per-user (or global) spending limit for LLM usage inside of Datasette. Configuration looks something like this:&lt;/p&gt;
&lt;pre&gt;&lt;span class="pl-ent"&gt;plugins&lt;/span&gt;:
  &lt;span class="pl-ent"&gt;datasette-llm-limits&lt;/span&gt;:
    &lt;span class="pl-ent"&gt;limits&lt;/span&gt;:
      &lt;span class="pl-ent"&gt;per-user-daily&lt;/span&gt;:
        &lt;span class="pl-ent"&gt;scope&lt;/span&gt;: &lt;span class="pl-s"&gt;actor&lt;/span&gt;
        &lt;span class="pl-ent"&gt;window&lt;/span&gt;: &lt;span class="pl-s"&gt;rolling-24h&lt;/span&gt;
        &lt;span class="pl-ent"&gt;amount_usd&lt;/span&gt;: &lt;span class="pl-c1"&gt;1.00&lt;/span&gt;
&lt;/pre&gt;
    
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/datasette"&gt;datasette&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/llm"&gt;llm&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="datasette"/><category term="llm"/></entry><entry><title>datasette-agent 0.1a2</title><link href="https://simonwillison.net/2026/May/15/datasette-agent/#atom-tag" rel="alternate"/><published>2026-05-15T00:03:00+00:00</published><updated>2026-05-15T00:03:00+00:00</updated><id>https://simonwillison.net/2026/May/15/datasette-agent/#atom-tag</id><summary type="html">
    
        &lt;p&gt;&lt;strong&gt;Release:&lt;/strong&gt; &lt;a href="https://github.com/datasette/datasette-agent/releases/tag/0.1a2"&gt;datasette-agent 0.1a2&lt;/a&gt;&lt;/p&gt;
        &lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;Tool availability can now be attached to a &lt;code&gt;required_permission&lt;/code&gt;. The default background agent tools now require the new &lt;code&gt;datasette-agent-background&lt;/code&gt; permission. #10&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
    
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/datasette"&gt;datasette&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/datasette-agent"&gt;datasette-agent&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="datasette"/><category term="datasette-agent"/></entry><entry><title>datasette-agent 0.1a1</title><link href="https://simonwillison.net/2026/May/14/datasette-agent/#atom-tag" rel="alternate"/><published>2026-05-14T22:01:42+00:00</published><updated>2026-05-14T22:01:42+00:00</updated><id>https://simonwillison.net/2026/May/14/datasette-agent/#atom-tag</id><summary type="html">
    
        &lt;p&gt;&lt;strong&gt;Release:&lt;/strong&gt; &lt;a href="https://github.com/datasette/datasette-agent/releases/tag/0.1a1"&gt;datasette-agent 0.1a1&lt;/a&gt;&lt;/p&gt;
        &lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;Now uses the &lt;code&gt;execute-sql&lt;/code&gt; permission when deciding which tables to list to the user. #8&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
    
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/datasette"&gt;datasette&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/datasette-agent"&gt;datasette-agent&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="datasette"/><category term="datasette-agent"/></entry><entry><title>datasette-ip-rate-limit 0.1a0</title><link href="https://simonwillison.net/2026/May/14/datasette-ip-rate-limit/#atom-tag" rel="alternate"/><published>2026-05-14T04:10:23+00:00</published><updated>2026-05-14T04:10:23+00:00</updated><id>https://simonwillison.net/2026/May/14/datasette-ip-rate-limit/#atom-tag</id><summary type="html">
    
        &lt;p&gt;&lt;strong&gt;Release:&lt;/strong&gt; &lt;a href="https://github.com/datasette/datasette-ip-rate-limit/releases/tag/0.1a0"&gt;datasette-ip-rate-limit 0.1a0&lt;/a&gt;&lt;/p&gt;
        &lt;p&gt;The &lt;a href="https://datasette.io/"&gt;datasette.io&lt;/a&gt; site was being hammered by poorly-behaved crawlers, so I had Codex (GPT-5.5 xhigh) build a configurable rate limiting plugin to block IPs that were hammering specific areas of the site too quickly.&lt;/p&gt;
&lt;p&gt;Here's &lt;a href="https://github.com/simonw/datasette.io/blob/b6022bf9987661b94a26d3143028193a6cabfdcf/datasette.yml#L103-L116"&gt;the production configuration&lt;/a&gt; I'm using on that site for the new plugin:&lt;/p&gt;
&lt;pre&gt;  &lt;span class="pl-ent"&gt;datasette-ip-rate-limit&lt;/span&gt;:
    &lt;span class="pl-ent"&gt;header&lt;/span&gt;: &lt;span class="pl-s"&gt;Fly-Client-IP&lt;/span&gt;
    &lt;span class="pl-ent"&gt;max_keys&lt;/span&gt;: &lt;span class="pl-c1"&gt;10000&lt;/span&gt;
    &lt;span class="pl-ent"&gt;exempt_paths&lt;/span&gt;:
    - &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;/static/*&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;
    - &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;/-/turnstile*&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;
    &lt;span class="pl-ent"&gt;rules&lt;/span&gt;:
    - &lt;span class="pl-ent"&gt;name&lt;/span&gt;: &lt;span class="pl-s"&gt;demo-databases&lt;/span&gt;
      &lt;span class="pl-ent"&gt;paths&lt;/span&gt;:
      - &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;/global-power-plants/*&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;
      - &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;/legislators/*&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;
      &lt;span class="pl-ent"&gt;window_seconds&lt;/span&gt;: &lt;span class="pl-c1"&gt;60&lt;/span&gt;
      &lt;span class="pl-ent"&gt;max_requests&lt;/span&gt;: &lt;span class="pl-c1"&gt;60&lt;/span&gt;
      &lt;span class="pl-ent"&gt;block_seconds&lt;/span&gt;: &lt;span class="pl-c1"&gt;20&lt;/span&gt;&lt;/pre&gt;
    
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/rate-limiting"&gt;rate-limiting&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/datasette"&gt;datasette&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/codex"&gt;codex&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="rate-limiting"/><category term="datasette"/><category term="codex"/></entry><entry><title>Welcome to the Datasette blog</title><link href="https://simonwillison.net/2026/May/13/welcome-to-the-datasette-blog/#atom-tag" rel="alternate"/><published>2026-05-13T23:59:39+00:00</published><updated>2026-05-13T23:59:39+00:00</updated><id>https://simonwillison.net/2026/May/13/welcome-to-the-datasette-blog/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://datasette.io/blog/2026/new-blog/"&gt;Welcome to the Datasette blog&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
We have a bunch of neat Datasette announcements in the pipeline so we decided it was time the project grew an official blog.&lt;/p&gt;
&lt;p&gt;I built this using OpenAI Codex desktop, which turns out to have the Markdown session transcript export feature I've always wanted. Here's &lt;a href="https://gist.github.com/simonw/885b11eee46822622b8031a1f4e5f3a3"&gt;the session that built the blog&lt;/a&gt;. See also &lt;a href="https://github.com/simonw/datasette.io/issues/179"&gt;issue 179&lt;/a&gt;.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/ai"&gt;ai&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/datasette"&gt;datasette&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/generative-ai"&gt;generative-ai&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/llms"&gt;llms&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ai-assisted-programming"&gt;ai-assisted-programming&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/codex"&gt;codex&lt;/a&gt;&lt;/p&gt;



</summary><category term="ai"/><category term="datasette"/><category term="generative-ai"/><category term="llms"/><category term="ai-assisted-programming"/><category term="codex"/></entry><entry><title>datasette 1.0a29</title><link href="https://simonwillison.net/2026/May/12/datasette/#atom-tag" rel="alternate"/><published>2026-05-12T23:41:06+00:00</published><updated>2026-05-12T23:41:06+00:00</updated><id>https://simonwillison.net/2026/May/12/datasette/#atom-tag</id><summary type="html">
    
        &lt;p&gt;&lt;strong&gt;Release:&lt;/strong&gt; &lt;a href="https://github.com/simonw/datasette/releases/tag/1.0a29"&gt;datasette 1.0a29&lt;/a&gt;&lt;/p&gt;
        &lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;New &lt;code&gt;TokenRestrictions.abbreviated(datasette)&lt;/code&gt; &lt;a href="https://docs.datasette.io/en/latest/internals.html#tokenrestrictions"&gt;utility method&lt;/a&gt; for creating &lt;code&gt;"_r"&lt;/code&gt; dictionaries. &lt;a href="https://github.com/simonw/datasette/issues/2695"&gt;#2695&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Table headers and column options are now visible even if a table contains zero rows. &lt;a href="https://github.com/simonw/datasette/issues/2701"&gt;#2701&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Fixed bug with display of column actions dialog on Mobile Safari. &lt;a href="https://github.com/simonw/datasette/issues/2708"&gt;#2708&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Fixed bug where tests could crash with a segfault due to a race condition between &lt;code&gt;Datasette.close()&lt;/code&gt; and &lt;code&gt;Database.close()&lt;/code&gt;. &lt;a href="https://github.com/simonw/datasette/issues/2709"&gt;#2709&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;That segfault bug was &lt;em&gt;gnarly&lt;/em&gt;. I added a mechanism to Datasette recently that would automatically close connections at the end of each test, but it turned out that introduced a race condition where an in-flight query could sometimes be executing in a thread against a connection while it was being closed. I ended up solving that by having Codex CLI (with GPT-5.5 xhigh) create &lt;a href="https://github.com/simonw/datasette/issues/2709#issuecomment-4435604727"&gt;a minimal Dockerfile&lt;/a&gt; that recreated the bug.&lt;/p&gt;
    
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/projects"&gt;projects&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/datasette"&gt;datasette&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="projects"/><category term="datasette"/></entry><entry><title>datasette-agent-openai-imagegen 0.1a1</title><link href="https://simonwillison.net/2026/May/12/datasette-agent-openai-imagegen/#atom-tag" rel="alternate"/><published>2026-05-12T22:03:22+00:00</published><updated>2026-05-12T22:03:22+00:00</updated><id>https://simonwillison.net/2026/May/12/datasette-agent-openai-imagegen/#atom-tag</id><summary type="html">
    
        &lt;p&gt;&lt;strong&gt;Release:&lt;/strong&gt; &lt;a href="https://github.com/datasette/datasette-agent-openai-imagegen/releases/tag/0.1a1"&gt;datasette-agent-openai-imagegen 0.1a1&lt;/a&gt;&lt;/p&gt;
        
    
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/datasette"&gt;datasette&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/openai"&gt;openai&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/text-to-image"&gt;text-to-image&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/datasette-agent"&gt;datasette-agent&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="datasette"/><category term="openai"/><category term="text-to-image"/><category term="datasette-agent"/></entry><entry><title>datasette-agent-charts 0.1a0</title><link href="https://simonwillison.net/2026/May/12/datasette-agent-charts/#atom-tag" rel="alternate"/><published>2026-05-12T21:41:08+00:00</published><updated>2026-05-12T21:41:08+00:00</updated><id>https://simonwillison.net/2026/May/12/datasette-agent-charts/#atom-tag</id><summary type="html">
    
        &lt;p&gt;&lt;strong&gt;Release:&lt;/strong&gt; &lt;a href="https://github.com/datasette/datasette-agent-charts/releases/tag/0.1a0"&gt;datasette-agent-charts 0.1a0&lt;/a&gt;&lt;/p&gt;
        
    
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/datasette"&gt;datasette&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/observable-plot"&gt;observable-plot&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/datasette-agent"&gt;datasette-agent&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="datasette"/><category term="observable-plot"/><category term="datasette-agent"/></entry><entry><title>datasette-agent 0.1a0</title><link href="https://simonwillison.net/2026/May/12/datasette-agent/#atom-tag" rel="alternate"/><published>2026-05-12T21:29:33+00:00</published><updated>2026-05-12T21:29:33+00:00</updated><id>https://simonwillison.net/2026/May/12/datasette-agent/#atom-tag</id><summary type="html">
    
        &lt;p&gt;&lt;strong&gt;Release:&lt;/strong&gt; &lt;a href="https://github.com/datasette/datasette-agent/releases/tag/0.1a0"&gt;datasette-agent 0.1a0&lt;/a&gt;&lt;/p&gt;
        &lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;Initial (silent) alpha release.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
    
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/datasette"&gt;datasette&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/datasette-agent"&gt;datasette-agent&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="datasette"/><category term="datasette-agent"/></entry><entry><title>datasette-referrer-policy 0.1</title><link href="https://simonwillison.net/2026/May/5/datasette-referrer-policy/#atom-tag" rel="alternate"/><published>2026-05-05T23:44:27+00:00</published><updated>2026-05-05T23:44:27+00:00</updated><id>https://simonwillison.net/2026/May/5/datasette-referrer-policy/#atom-tag</id><summary type="html">
    
        &lt;p&gt;&lt;strong&gt;Release:&lt;/strong&gt; &lt;a href="https://github.com/datasette/datasette-referrer-policy/releases/tag/0.1"&gt;datasette-referrer-policy 0.1&lt;/a&gt;&lt;/p&gt;
        &lt;p&gt;The OpenStreetMap tiles on the Datasette &lt;a href="https://datasette.io/global-power-plants/global-power-plants"&gt;global-power-plants demo&lt;/a&gt; weren't displaying correctly. This turned out to be caused by two bugs.&lt;/p&gt;
&lt;p&gt;The first is that the CAPTCHA &lt;a href="https://github.com/simonw/datasette-turnstile"&gt;I added&lt;/a&gt; to that site a few weeks ago was triggering for the &lt;code&gt;.json&lt;/code&gt; fetch requests used by the map plugin, and since those weren't HTML the user was not being asked to solve them. Here's &lt;a href="https://github.com/simonw/datasette.io/commit/23a1c8596b75b2094db46035a3b4280109fb3df3"&gt;the fix&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The second was that OpenStreetMap quite reasonably &lt;a href="https://wiki.openstreetmap.org/wiki/Referer"&gt;block tile requests&lt;/a&gt; from sites that use a &lt;code&gt;Referrer-Policy: no-referrer&lt;/code&gt; header.&lt;/p&gt;
&lt;p&gt;Datasette does this by default, and I didn't want to change that default on people without warning - so I had Codex + GPT-5.5 &lt;a href="https://gisthost.github.io/?402f2f23ee3dbfa251bf0d216e0224f7"&gt;build me&lt;/a&gt; a new plugin to help set that header to another value.&lt;/p&gt;
    
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/http"&gt;http&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/openstreetmap"&gt;openstreetmap&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/datasette"&gt;datasette&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="http"/><category term="openstreetmap"/><category term="datasette"/></entry><entry><title>datasette-llm 0.1a7</title><link href="https://simonwillison.net/2026/May/5/datasette-llm/#atom-tag" rel="alternate"/><published>2026-05-05T01:56:55+00:00</published><updated>2026-05-05T01:56:55+00:00</updated><id>https://simonwillison.net/2026/May/5/datasette-llm/#atom-tag</id><summary type="html">
    
        &lt;p&gt;&lt;strong&gt;Release:&lt;/strong&gt; &lt;a href="https://github.com/datasette/datasette-llm/releases/tag/0.1a7"&gt;datasette-llm 0.1a7&lt;/a&gt;&lt;/p&gt;
        &lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;Mechanism for &lt;a href="https://github.com/datasette/datasette-llm/blob/main/README.md#configuration"&gt;configuring default options&lt;/a&gt; for specific models.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;Part of Datasette's evolving support mechanism for plugins that use LLMs. It's now possible to configure a model with default options, e.g. to say all &lt;a href="https://github.com/datasette/datasette-enrichments-llm"&gt;enrichment&lt;/a&gt; operations should use a specific model with temperature set to 0.5.&lt;/p&gt;
    
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/datasette"&gt;datasette&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/llm"&gt;llm&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="datasette"/><category term="llm"/></entry></feed>