llm-gemini 0.33 lands, sqlite-utils 4.2 fixes drops, Codex writes alchemy-utils
Three Python tool releases land today, each more interesting for the fragile layer or absent author sitting beneath the version bump.
llm-gemini 0.33 lands, sqlite-utils 4.2 fixes drops, Codex writes alchemy-utils
TL;DR
- llm-gemini 0.33 wraps Gemini 3.7 Flash with reasoning traces and server-side code execution.
- Gemini 3.7 Flash ships at a 50% intro discount guaranteed only through 2026.
- sqlite-utils 4.2 closes SQLite’s CHECK-constraint introspection gap that
.transform()had silently dropped. - sqlite-utils 4.2.1 shipped hours later to restore a missing
typing_extensionsruntime dep. - alchemy-utils 0.1a1 cuts a DuckDB CSV import from 1 hour to 35 seconds, authored by Codex under TDD.
Three small Python tool releases lead today’s tech pool, and each one is more interesting for the layer directly beneath it than for the version bump on top. llm-gemini 0.33 is a clean wrapper release — but the Gemini 3.7 stack it wraps ships with a 50% intro discount guaranteed only through 2026, Reddit accusations of benchmaxxing, and a fresh cross-provider attack that replays encrypted reasoning traces to recover plaintext scratchpads. sqlite-utils 4.2 finally closes a CHECK-constraint introspection gap that let .transform() silently drop constraints for years, and then shipped 4.2.1 hours later to patch its own missing runtime dep.
The third release is the tell. alchemy-utils 0.1a1 cuts a DuckDB CSV import from an hour to 35 seconds — a real win against SQLAlchemy’s 240× bulk-load overhead — but the library and the perf fix were both written by Codex under red/green TDD, not by the maintainer. Two hand-authored Willison releases and one Codex-authored alpha, sitting side by side.
llm-gemini 0.33 ships atop a fragile Gemini 3 stack
Source: simon-willison · published 2026-08-13
TL;DR
- llm-gemini 0.33 wires up Gemini 3.7 Flash, two embedding models, reasoning traces, and server-side
CodeExecutionin one release. - Gemini 3.7 Flash ships at a 50% intro discount ($0.75/$3.75 per 1M tokens) guaranteed only through 2026.
- Reddit reviewers flag benchmaxxing — AutomationBench jumped 17%→30.4% in 3 weeks.
- A fresh cross-provider attack replays encrypted reasoning traces into weak decoders to recover plaintext scratchpads, including leaked API keys.
What actually shipped
Simon Willison’s llm-gemini 0.33 is a thin plugin release with a wide blast radius. It adds four Gemini models (3.7 Flash, 3.6 Flash, 3.5 Flash-lite, plus gemini-embedding-2 and gemini-embedding-001), and — via compatibility with the LLM 0.32 base — turns on reasoning traces and server-side tools like CodeExecution. The pelican-on-a-bicycle SVG in the post is charming. The substrate underneath is not.
The model is cheaper and contested
VentureBeat pegs Gemini 3.7 Flash at $0.75 input / $3.75 output per 1M tokens — a 50% introductory cut aimed squarely at coding and agent workloads, and guaranteed only through the end of 2026 1. Google’s own docs confirm the three thinking tiers Willison exercised (Low/Medium/High), and confirm that 3.6 Flash’s “minimal” tier is gone 2.
The independent read is cooler. A Reddit benchmark thread flags a jump on AutomationBench from 17% to 30.4% in a 3-week release cycle as more consistent with benchmaxxing than genuine generalization, and notes 3.7 Flash’s hallucination rate still lags Google’s own 3.1 Pro 3. If you’re switching to it for agent work, budget for eval-on-your-own-tasks before trusting the leaderboard delta.
Reasoning traces cut both ways
LLM 0.32 makes a nice Unix-shaped choice: reasoning traces stream to stderr, so pipelines composing llm output stay clean 4. That transparency is genuinely useful for debugging. It also lands the same week researchers showed that the encrypted reasoning blocks from Gemini 3, OpenAI, and Anthropic can be replayed into weaker “fuzzy decoder” models to reconstruct the plaintext chain of thought — surfacing API keys and tokens that appeared only in the model’s private scratchpad, never in the final response 5. If you turn traces on by default, you’re now looking at a channel the providers themselves haven’t fully secured.
Thought signatures: the invisible landmine
The -T CodeExecution example looks tidy — one flag, factorial of 13 times 3, done. The protocol underneath is not. Gemini 3 requires clients to round-trip an opaque, encrypted thought_signature on every follow-up call; missing or malformed signatures return 400 INVALID_ARGUMENT, and signatures generated by 2.5-era models are rejected outright. Middleware like OpenRouter has been observed stripping the field entirely, silently breaking multi-turn tool loops 6.
sequenceDiagram
participant CLI as llm CLI
participant G as Gemini 3.7 Flash
CLI->>G: prompt + tool spec
G-->>CLI: tool_call + thought_signature
Note over CLI: must preserve<br/>signature verbatim
CLI->>G: tool_result + signature
G-->>CLI: final answer
Note over CLI,G: signature stripped →<br/>400 INVALID_ARGUMENT
This is the plumbing llm-gemini 0.33 has to get right for CodeExecution to survive past a single turn, and it’s the most likely source of future bug reports against the plugin.
Takeaway
Treat 0.33 as an on-ramp, not a verdict. The plugin does its job; the model, the trace channel, and the signature protocol underneath all have known sharp edges worth knowing before you point an agent loop at them.
sqlite-utils 4.2 stops silently dropping CHECK constraints
Source: simon-willison · published 2026-08-13
TL;DR
- sqlite-utils 4.2 adds
table.checks,table.column_checks, andtable.table_checks— closing SQLite’s long-standing check-constraint introspection gap. - Before 4.2,
.transform()silently dropped CHECK constraints, UNIQUE constraints, and column comments when rebuilding tables. - 4.2.1 shipped hours later to restore a missing
typing_extensionsruntime dependency that had been hiding as a transitive dev-env import. - The fix ships an
uv run --isolatedsmoke test other Python CLI maintainers should probably steal.
The real change is an introspection fix, not a UX polish
SQLite exposes no PRAGMA for CHECK constraints. That has always been the awkward bit for any tool trying to do a table rebuild: you can enumerate columns and indexes cleanly, but constraint expressions live inside the raw CREATE TABLE DDL and have to be parsed back out. sqlite-utils had been ducking that work — and the consequence, quietly, was that .transform() would drop CHECK constraints, UNIQUE constraints, and column comments on the floor whenever you asked it to modify a schema 7.
Version 4.2 fixes this by adding three first-class introspection properties on the Table object: table.checks, table.column_checks, and table.table_checks 8. Those aren’t just convenience accessors — they’re the primitive that lets .transform() detect existing constraints on the source table and re-apply them to the rebuilt one. Reframed that way, 4.2 is less a feature release than a correctness fix for a class of silent data-integrity-rule loss that many users almost certainly didn’t know was happening to them.
Don’t confuse this with a zero-downtime “shadow table”
The .transform() mechanism — create new table, copy rows, drop old, rename — gets called a “shadow table” pattern in the release notes, and that terminology collides with something different. In production database circles, a shadow table migration means running a parallel table with triggers mirroring live writes, backfilling historical rows in batches, then cutting over with no downtime 9. That’s not what sqlite-utils does. It takes an exclusive lock and swaps atomically, which is fine for local files and dev workflows but will block writers for the duration of a copy on a multi-GB database. If you’re using SQLite in litestream-style production, 4.2’s improved fidelity is welcome, but the rebuild is still a stop-the-world operation.
The 4.2.1 hotfix is a packaging lesson worth stealing
The same-day crash bug had a mundane cause: typing_extensions had quietly become a runtime import but wasn’t declared as a runtime dependency. It kept working in CI and on the maintainer’s machine because pytest or another dev tool was pulling it in transitively 10. Classic “works on my machine because the test env is fatter than the install env.”
The remediation is more interesting than the bug. The project now runs uv run --isolated --no-default-groups sqlite-utils --help as a smoke test, which forces the CLI to boot using only its declared dependencies 11. That’s a two-line addition any Python CLI maintainer using uv can copy today, and it catches exactly the failure mode that a full test suite tends to mask.
Undertone: an AI-assisted release cadence
Willison has been openly documenting heavy LLM assistance across the 4.x cycle, which has drawn mixed reaction — appreciation for the transparency, pushback on treating lines of code as a productivity signal 12. A shipped-and-hotfixed-within-hours release will get read through that lens whether it deserves to or not. The substantive read here is narrower: the CHECK-constraint fix is a real correctness win, and the packaging stumble produced a reusable recipe. Both outcomes stand independent of who or what wrote the diff.
Further reading
- sqlite-utils 4.2.1 — simon-willison
alchemy-utils 0.1a1 cuts a DuckDB CSV import from 1 hour to 35s
Source: simon-willison · published 2026-08-13
TL;DR
- alchemy-utils 0.1a1 collapses a DuckDB CSV import from ~1 hour to ~35 seconds via DuckDB’s native
read_csvpath. - The fast path skips SQLAlchemy’s row-by-row inserts, which cost up to 240× vs Postgres
COPYin bulk-load benchmarks. - Both the library and this perf fix were written by Codex under red/green TDD — not hand-tuned by the maintainer.
- Sqlite-utils parity still lags: no
transform(), FTS, or SpatiaLite one-liners in the alpha.
From an hour to 35 seconds
The changelog reads like a routine alpha bump. It isn’t. A San Francisco street-tree CSV that took nearly an hour to load into DuckDB under 0.1a0 now finishes in about 35 seconds on 0.1a1 13. The mechanism is narrow and specific: when the target dialect is DuckDB and the source is CSV, alchemy-utils stops issuing generic SQLAlchemy inserts and instead executes INSERT INTO ... SELECT * FROM read_csv(...), handing the work to DuckDB’s vectorized engine 13.
That’s a ~100× speedup from a per-dialect escape hatch inside a library whose whole selling point is being database-agnostic. It’s the interesting shape of the release.
Why “database-agnostic” costs so much
Row-by-row ORM inserts are famously expensive, and the tax scales with how far you sit from the database’s native bulk-load path. Published SQLAlchemy benchmarks against a Postgres target give a rough tariff schedule 14:
| Path | Speedup vs session.add() loop |
|---|---|
session.add() loop (ORM baseline) | 1× |
SQLAlchemy 2.0 session.execute(insert()) | ~20× |
Core insert().values(...) | ~40× |
Postgres native COPY | ~240× |
Any wrapper that promises “works with every backend” defaults to the slow end of that table unless it carves out dialect-specific fast paths. 0.1a1 is that carve-out for DuckDB — and it’s the reason a tool that was effectively unusable on medium-sized CSVs a day ago is now competitive with purpose-built loaders. In the ingestion landscape it now sits between dlt (schema-evolving pipelines) and ingestr, whose Go rewrite claims 12× over dlt on 1M-row loads 15; alchemy-utils’ niche is the lightweight, sqlite-utils-shaped middle.
Codex wrote the fix. And the library.
The provenance is worth spelling out. alchemy-utils was bootstrapped as a “research spike” by tasking OpenAI Codex and GPT-5.6 Sol Ultra with reimplementing the sqlite-utils core API — insert, upsert, table introspection — against SQLAlchemy, using red/green TDD with pytest 16. The 0.1a1 optimization pass came from the same workflow: Codex was pointed at the slow import and asked to fix it.
The critique writes itself, and Hacker News commenters have been writing it: “cognitive debt” from maintaining thousands of lines the author didn’t personally write, and a “bug-fix loop” in which AI patches AI-introduced regressions 17. The hour-long import that only a Codex-driven refactor resolved is a clean example of both sides of the argument at once.
What’s still missing
Independent reviewers note that alchemy-utils covers CRUD and introspection (insert, upsert, insert_all, create, update) but not the pieces that make sqlite-utils distinctive: the transform() schema-rewrite trick, FTS, and SpatiaLite integration 18. SQLAlchemy can express these; the alpha just doesn’t expose them as one-liners yet.
For now, the honest pitch is narrower than the README suggests: a fast, ergonomic way to shove CSVs into DuckDB (or into Postgres via smart batching), with the rest of the sqlite-utils surface still catching up — and a maintenance model that some readers will find as interesting as the benchmark.
Footnotes
-
VentureBeat — https://venturebeat.com/technology/googles-gemini-3-7-flash-targets-coding-and-agents-with-a-50-introductory-price-cut
↩Gemini 3.7 Flash targets coding and agents with a 50% introductory price cut… $0.75/$3.75 per 1M tokens, guaranteed only through 2026.
-
Google AI Developer docs (latest-model) — https://ai.google.dev/gemini-api/docs/latest-model
↩Thinking mode exposes three effort tiers — Low (latency-critical), Medium (default, balanced for coding/agentic), High (multi-step planning); the ‘minimal’ tier from 3.6 Flash has been removed.
-
Reddit r/GeminiAI benchmark thread — https://www.reddit.com/r/GeminiAI/comments/1vngq0i/gemini_37_flash_benchmarks/
↩dramatic score increases in short windows (e.g., AutomationBench jumping from 17% to 30.4%) may reflect ‘benchmaxxing’ rather than generalized improvement; hallucination rates still trail Google’s own 3.1 Pro.
-
AI Weekly on LLM 0.32 — https://aiweekly.co/alerts/simon-willisons-llm-032-adds-reasoning-traces-and-agent-tools
↩Reasoning traces are displayed by default to stderr so the model’s internal logic is visible without contaminating stdout — preserving the ability to pipe the final answer into other Unix tools.
-
The Hacker News — https://thehackernews.com/2026/08/openai-anthropic-google-api-flaw-let.html
↩Researchers replayed encrypted reasoning traces from Gemini 3 Pro into weaker ‘fuzzy decoder’ models and forced them to output the hidden reasoning in plaintext, exposing API keys and tokens present in the scratchpad but hidden from the final response.
-
Medium — Migrating to Gemini 3: Thought Signatures — https://medium.com/google-cloud/migrating-to-gemini-3-implementing-stateful-reasoning-with-thought-signatures-4f11b625a8c9
↩Omitting these signatures in Gemini 3 typically results in a 400 INVALID_ARGUMENT error during function calling; middleware like OpenRouter often strips the signature field, breaking multi-turn tool loops.
-
simonwillison.net — sqlite-utils features series (transform history) — https://simonwillison.net/series/sqlite-utils-features/
↩Prior to 4.2, .transform() often dropped edge-case schema details like check constraints, unique constraints and column comments; the new introspection lets it detect and re-apply them to the rebuilt table.
-
simonwillison.net — sqlite-utils features series — https://simonwillison.net/series/sqlite-utils-features/
↩Version 4.2 introduces three new introspection properties to the Python API: table.checks, table.column_checks, and table.table_checks — solving a long-standing challenge because SQLite provides no built-in method to list check constraints.
-
InfoQ — Shadow Table Strategy for Data Migration — https://www.infoq.com/articles/shadow-table-strategy-data-migration/
↩A ‘shadow table’ refers to a parallel table used for zero-downtime migrations. Developers create a shadow version of a production table, use triggers to sync live writes to both, and backfill historical data in batches before performing a final cutover.
-
Simon Willison — sqlite-utils 4.2 release note — https://simonwillison.net/2026/Aug/13/sqlite-utils/
↩It later turned out 4.2 had a crashing bug, fixed in 4.2.1 … the fix explicitly adds typing_extensions back into the project’s dependencies after it escaped detection because it was present as a transitive dev-env dependency.
-
Simon Willison — elsewhere / project log — https://simonwillison.net/elsewhere/
↩To prevent regressions the project now runs
uv run --isolated --no-default-groups sqlite-utils --helpas a smoke test, forcing the CLI to stand up using only its declared dependencies. -
Simon Willison — homepage (AI-assisted development context) — https://simonwillison.net/
↩Willison has been openly documenting heavy LLM assistance across the 4.x cycle, defending ‘lines of code’ as a productivity signal while HN commenters push back that simplicity should remain paramount regardless of how code is generated.
-
Simon Willison — alchemy-utils 0.1a0 post — https://simonwillison.net/2026/Aug/12/alchemy-utils/
↩ ↩2A DuckDB export that originally took nearly an hour was reduced to approximately 35 seconds after Codex applied a bulk-load optimization using DuckDB’s native read_csv path.
-
tutorials.technology — SQLAlchemy bulk insert benchmarks — https://tutorials.technology/tutorials/Fast-bulk-insert-with-sqlalchemy.html
↩PostgreSQL’s native COPY command offers up to a 240x speedup over session.add() loops; Core-level insert().values() gives ~40x, and ORM 2.0 session.execute(insert()) reaches ~20x.
-
muzh.io — M1 Daily — https://muzh.io/article/m1-daily-2026-08-13
↩ingestr v1 (Go rewrite) claimed speeds up to 12 times faster than dlt and other Python-based competitors when loading 1M-row tables… alchemy-utils offers a sqlite-utils-style API backed by SQLAlchemy for simple insert/upsert across PostgreSQL and DuckDB.
-
InjazAI coverage — https://injazai.com/en/news/alchemy-utils-0-1a0-multi-database-python-tool
↩Willison initiated the project as a ‘research spike,’ tasking LLMs like OpenAI Codex and GPT-5.6 Sol Ultra with reimplementing the core sqlite-utils API — insert, upsert, and table introspection — backed by SQLAlchemy, using red/green TDD with pytest.
-
dnorth.net — vibe coding discussion — https://news.dnorth.net/
↩Critics on Hacker News warned of ‘cognitive debt’ — the mental burden of maintaining thousands of lines of code that the developer did not personally write and may not fully grok.
-
dnorth.net roundup — https://news.dnorth.net/
↩sqlite-utils includes a sophisticated transform() method for complex schema changes and native support for FTS and SpatiaLite… these are not yet integrated into the alchemy-utils API with the same ‘one-liner’ simplicity.