2025-09-09 18:13:33
The 2025 PSF Board Election is Open!
The Python Software Foundation's annual board member election is taking place right now, with votes (from previously affirmed voting members) accepted from September 2nd, 2:00 pm UTC through Tuesday, September 16th, 2:00 pm UTC.I've served on the board since 2022 and I'm running for a second term. Here's the opening section of my nomination statement.
Hi, I'm Simon Willison. I've been a board member of the Python Software Foundation since 2022 and I'm running for re-election in 2025.
Last year I wrote a detailed article about Things I’ve learned serving on the board of the Python Software Foundation. I hope to continue learning and sharing what I've learned for a second three-year term.
One of my goals for a second term is to help deepen the relationship between the AI research world and the Python Software Foundation. There is an enormous amount of value being created in the AI space using Python and I would like to see more of that value flow back into the rest of the Python ecosystem.
I see the Python Package Index (PyPI) as one of the most impactful projects of the Python Software Foundation and plan to continue to advocate for further investment in the PyPI team and infrastructure.
As a California resident I'm excited to see PyCon return to the West Coast, and I'm looking forward to getting involved in helping make PyCon 2026 and 2027 in Long Beach, California as successful as possible.
I'm delighted to have been endorsed this year by Al Sweigart, Loren Crary and Christopher Neugebauer. If you are a voting member I hope I have earned your vote this year.
You can watch video introductions from several of the other nominees in this six minute YouTube video and this playlist.
2025-09-09 17:31:21
I ran Claude in a loop for three months, and it created a genz programming language called cursed
Geoffrey Huntley vibe-coded an entirely new programming language using Claude:The programming language is called "cursed". It's cursed in its lexical structure, it's cursed in how it was built, it's cursed that this is possible, it's cursed in how cheap this was, and it's cursed through how many times I've sworn at Claude.
Geoffrey's initial prompt:
Hey, can you make me a programming language like Golang but all the lexical keywords are swapped so they're Gen Z slang?
Then he pushed it to keep on iterating over a three month period.
Here's Hello World:
vibe main
yeet "vibez"
slay main() {
vibez.spill("Hello, World!")
}
And here's binary search, part of 17+ LeetCode problems that run as part of the test suite:
slay binary_search(nums normie[], target normie) normie {
sus left normie = 0
sus right normie = len(nums) - 1
bestie (left <= right) {
sus mid normie = left + (right - left) / 2
ready (nums[mid] == target) {
damn mid
}
ready (nums[mid] < target) {
left = mid + 1
} otherwise {
right = mid - 1
}
}
damn -1
}
This is a substantial project. The repository currently has 1,198 commits. It has both an interpreter mode and a compiler mode, and can compile programs to native binaries (via LLVM) for macOS, Linux and Windows.
It looks like it was mostly built using Claude running via Sourcegraph's Amp, which produces detailed commit messages. The commits include links to archived Amp sessions but sadly those don't appear to be publicly visible.
The first version was written in C, then Geoffrey had Claude port it to Rust and then Zig. His cost estimate:
Technically it costs about 5k usd to build your own compiler now because cursed was implemented first in c, then rust, now zig. So yeah, it’s not one compiler it’s three editions of it. For a total of $14k USD.
Via @GeoffreyHuntley
Tags: c, programming-languages, ai, rust, zig, generative-ai, llms, ai-assisted-programming, anthropic, claude, vibe-coding, geoffrey-huntley
2025-09-09 14:47:49
Apollo Global Management's "Chief Economist" Dr. Torsten Sløk released this interesting chart which appears to show a slowdown in AI adoption rates among large (>250 employees) companies:
Here's the full description that accompanied the chart:
The US Census Bureau conducts a biweekly survey of 1.2 million firms, and one question is whether a business has used AI tools such as machine learning, natural language processing, virtual agents or voice recognition to help produce goods or services in the past two weeks. Recent data by firm size shows that AI adoption has been declining among companies with more than 250 employees, see chart below.
(My first thought on seeing that chart is that I hope it represents the peak of inflated expectations leading into the trough of dissillusionment in the Gartner Hype Cycle (which Wikipedia calls "largely disputed, with studies pointing to it being inconsistently true at best"), since that means we might be reaching the end of the initial hype phase and heading towards the slope of enlightenment.)
This is the first I'd heard of the US Census Bureau running a biweekly (that's once every two weeks) survey about AI!
I decided to track down the numbers and see if I could recreate the chart myself. And since GPT-5 is really good at search now I fed it the following prompt to see how well it could do:
> The US Census Bureau conducts a biweekly survey of 1.2 million firms, and one question is whether a business has used AI tools such as machine learning, natural language processing, virtual agents or voice recognition to help produce goods or services in the past two weeks. Recent data by firm size shows that AI adoption has been declining among companies with more than 250 employees, see chart below.
Find me that original data
It took 1m19s to find exactly what I needed:
Here’s the original U.S. Census Bureau source for that series:
- BTOS “Employment size class” time series (XLSX) — biweekly shares of firms answering Yes to “In the last two weeks, did this business use Artificial Intelligence (AI) in producing goods or services?”, broken out by firm-size buckets (incl. 250+ employees). Coverage: Sep 11, 2023 → Aug 24, 2025. (Census.gov) [...]
That Census page was not at all obvious. Thankfully GPT-5 had tipped me off to the "Employment size class" file, this link here:
So I downloaded that file, and confirmed that it was indeed a spreadsheet containing the data I wanted (in among all sorts of other survey questions). Here's a 374KB XLSX copy of the file I downloaded.
So what should I do with it now? I decided to see if GPT-5 could turn the spreadsheet back into that original chart, using Python running in its code interpreter tool.
So I uploaded the XLSX file back to ChatGPT, dropped in a screenshot of the Apollo chart and prompted:
Use this data to recreate this chart using python
I thought this was a pretty tall order, but it's always worth throwing big challenges at an LLM to learn from how well it does.
It really worked hard on this. I didn't time it exactly but it spent at least 7 minutes "reasoning" across 5 different thinking blocks, interspersed with over a dozen Python analysis sessions. It used pandas
and numpy
to explore the uploaded spreadsheet and find the right figures, then tried several attempts at plotting with matplotlib
.
As far as I can tell GPT-5 in ChatGPT can now feed charts it creates back into its own vision model, because it appeared to render a broken (empty) chart and then keep on trying to get it working.
It found a data dictionary in the last tab of the spreadsheet and used that to build a lookup table matching the letters A
through G
to the actual employee size buckets.
At the end of the process it spat out this chart:
At first glance I thought it had nailed it... but then I compared the chart more closely with the Apollo original and spotted some definite discrepancies. GPT-5's chart peaked at 14.5% but the highest value in Apollo's was more like 13.5%. The GPT-5 chart was spikier - and most interestingly it included a clear uptick in the last data point where Apollo's had trended downwards.
I decided it was time to look at the actual data. I opened up the spreadsheet in Numbers, found the AI question columns and manually reviewed them. They seemed to match the GPT-5 chart results - so why the difference to Apollo's?
Then I noticed a crucial detail in the Apollo chart that I had cropped out of my original screenshot!
Note: Data is six-survey moving average.
So I told ChatGPT:
Do the first question, plot it as a six survey rolling average
I asked for the first question because it turned out there were two that were relevant in the survey spreadsheet.
It churned away for a little longer, added this code to the script:
# Compute 6-survey rolling average (biweekly cadence → ~12 weeks) rolled = wide.rolling(window=6, min_periods=6).mean()
And popped out this chart (after I told it to fix the glitch in the title):
I think it's done it! This is a very solid match for the Apollo original, recreated using matplotlib
and pandas
from the same underlying source data from the US Census.
Here's the full Python code it wrote, which I think is quite readable (in as much as Pandas code can be):
import pandas as pd import matplotlib.pyplot as plt from matplotlib.ticker import PercentFormatter path = "/mnt/data/Employment Size Class.xlsx" resp = pd.read_excel(path, sheet_name="Response Estimates") dates = pd.read_excel(path, sheet_name="Collection and Reference Dates") is_current = resp["Question"].astype(str).str.strip().str.startswith("In the last two weeks") ai_yes = resp[is_current & resp["Answer"].astype(str).str.strip().str.lower().eq("yes")].copy() code_to_bucket = {"A":"1-4","B":"5-9","C":"10-19","D":"20-49","E":"50-99","F":"100-249","G":"250 or more employees"} ai_yes["Bucket"] = ai_yes["Empsize"].map(code_to_bucket) period_cols = [c for c in ai_yes.columns if str(c).isdigit() and len(str(c))==6] long = ai_yes.melt(id_vars=["Bucket"], value_vars=period_cols, var_name="Smpdt", value_name="value") dates["Smpdt"] = dates["Smpdt"].astype(str) long["Smpdt"] = long["Smpdt"].astype(str) merged = long.merge(dates[["Smpdt","Ref End"]], on="Smpdt", how="left") merged["date"] = pd.to_datetime(merged["Ref End"], errors="coerce") merged["value"] = pd.to_numeric(long["value"].astype(str).str.replace("%","",regex=False).str.strip(), errors="coerce") order = ["250 or more employees","100-249","50-99","20-49","10-19","5-9","1-4"] wide = merged.pivot_table(index="date", columns="Bucket", values="value", aggfunc="mean").sort_index() wide = wide[[c for c in order if c in wide.columns]] rolled = wide.rolling(window=6, min_periods=6).mean() start, end = pd.Timestamp("2023-11-01"), pd.Timestamp("2025-08-31") rolled_win = rolled.loc[(rolled.index >= start) & (rolled.index <= end)] fig, ax = plt.subplots(figsize=(12, 6)) for col in order: if col in rolled_win.columns: ax.plot(rolled_win.index, rolled_win[col], label=col, linewidth=2) ax.set_title("AI adoption (last two weeks) — 6‑survey rolling average", pad=16) ax.yaxis.set_major_formatter(PercentFormatter(100)) ax.set_ylabel("%") ax.set_xlabel("") ax.grid(True, alpha=0.25, linestyle="--") ax.legend(title=None, loc="upper left", ncols=2, frameon=False) plt.tight_layout() png_path = "/mnt/data/ai_adoption_rolling6_by_firm_size.png" svg_path = "/mnt/data/ai_adoption_rolling6_by_firm_size.svg" plt.savefig(png_path, dpi=200, bbox_inches="tight") plt.savefig(svg_path, bbox_inches="tight")
I like how it generated an SVG version of the chart without me even asking for it.
You can access the ChatGPT transcript to see full details of everything it did.
I had one more challenge to try out. Could I render that same chart entirely in the browser using Pyodide, which can execute both Pandas and Matplotlib?
I fired up a new ChatGPT GPT-5 session and prompted:
Build a canvas that loads Pyodide and uses it to render an example bar chart with pandas and matplotlib and then displays that on the page
My goal here was simply to see if I could get a proof of concept of a chart rendered, ideally using the Canvas feature of ChatGPT. Canvas is OpenAI's version of Claude Artifacts, which lets the model write and then execute HTML and JavaScript directly in the ChatGPT interface.
It worked! Here's the transcript and here's what it built me, exported to my tools.simonwillison.net GitHub Pages site (source code here).
I've now proven to myself that I can render those Python charts directly in the browser. Next step: recreate the Apollo chart.
I knew it would need a way to load the spreadsheet that was CORS-enabled. I uploaded my copy to my /static/cors-allow/2025/...
directory (configured in S3 to serve CORS headers), pasted in the finished plotting code from earlier and told ChatGPT:
Now update it to have less explanatory text and a less exciting design (black on white is fine) and run the equivalent of this:
(... pasted in Python code from earlier ...)
Load the XLSX sheet from https://static.simonwillison.net/static/cors-allow/2025/Employment-Size-Class-Sep-2025.xlsx
It didn't quite work - I got an error about openpyxl
which I manually researched the fix for and prompted:
Use await micropip.install("openpyxl") to install openpyxl - instead of using loadPackage
I had to paste in another error message:
zipfile.BadZipFile: File is not a zip file
Then one about a SyntaxError: unmatched ')'
and a TypeError: Legend.__init__() got an unexpected keyword argument 'ncols'
- copying and pasting error messages remains a frustrating but necessary part of the vibe-coding loop.
... but with those fixes in place, the resulting code worked! Visit tools.simonwillison.net/ai-adoption to see the final result:
Here's the code for that page, 170 lines all-in of HTML, CSS, JavaScript and Python.
This was another of those curiosity-inspired investigations that turned into a whole set of useful lessons.
pyfetch
and openpyxl
:
import micropip await micropip.install("openpyxl") from pyodide.http import pyfetch resp_fetch = await pyfetch(URL) wb_bytes = await resp_fetch.bytes() xf = pd.ExcelFile(io.BytesIO(wb_bytes), engine='openpyxl')
from js import document document.getElementById('plot').src = 'data:image/png;base64,' + img_b64
I will most definitely be using these techniques again in future.
Tags: census, data-journalism, javascript, python, tools, visualization, ai, pyodide, openai, generative-ai, chatgpt, llms, ai-assisted-programming, code-interpreter, llm-reasoning, vibe-coding, ai-assisted-search, gpt-5
2025-09-09 14:28:21
Anthropic status: Model output quality
Anthropic previously reported model serving bugs that affected Claude Opus 4 and 4.1 for 56.5 hours. They've now fixed additional bugs affecting "a small percentage" of Sonnet 4 requests for almost a month, plus a less long-lived Haiku 3.5 issue:Resolved issue 1 - A small percentage of Claude Sonnet 4 requests experienced degraded output quality due to a bug from Aug 5-Sep 4, with the impact increasing from Aug 29-Sep 4. A fix has been rolled out and this incident has been resolved.
Resolved issue 2 - A separate bug affected output quality for some Claude Haiku 3.5 and Claude Sonnet 4 requests from Aug 26-Sep 5. A fix has been rolled out and this incident has been resolved.
They directly address accusations that these stem from deliberate attempts to save money on serving models:
Importantly, we never intentionally degrade model quality as a result of demand or other factors, and the issues mentioned above stem from unrelated bugs.
The timing of these issues is really unfortunate, corresponding with the rollout of GPT-5 which I see as the non-Anthropic model to feel truly competitive with Claude for writing code since their release of Claude 3.5 back in June last year.
Via @theo
Tags: ai, generative-ai, llms, anthropic, claude, claude-4, gpt-5
2025-09-09 07:23:43
Having worked inside AWS I can tell you one big reason [that they don't describe their internals] is the attitude/fear that anything we put in out public docs may end up getting relied on by customers. If customers rely on the implementation to work in a specific way, then changing that detail requires a LOT more work to prevent breaking customer's workloads. If it is even possible at that point.
— TheSoftwareGuy, comment on Hacker News
Tags: aws
2025-09-09 04:53:52
Load Llama-3.2 WebGPU in your browser from a local folder
Inspired by a comment on Hacker News I decided to see if it was possible to modify the transformers.js-examples/tree/main/llama-3.2-webgpu Llama 3.2 chat demo (online here, I wrote about it last November) to add an option to open a local model file directly from a folder on disk, rather than waiting for it to download over the network.I posed the problem to OpenAI's GPT-5-enabled Codex CLI like this:
git clone https://github.com/huggingface/transformers.js-examples
cd transformers.js-examples/llama-3.2-webgpu
codex
Then this prompt:
Modify this application such that it offers the user a file browse button for selecting their own local copy of the model file instead of loading it over the network. Provide a "download model" option too.
Codex churned away for several minutes, even running commands like curl -sL https://raw.githubusercontent.com/huggingface/transformers.js/main/src/models.js | sed -n '1,200p'
to inspect the source code of the underlying Transformers.js library.
After four prompts total (shown here) it built something which worked!
To try it out you'll need your own local copy of the Llama 3.2 ONNX model. You can get that (a ~1.2GB) download) like so:
git lfs install
git clone https://huggingface.co/onnx-community/Llama-3.2-1B-Instruct-q4f16
Then visit my llama-3.2-webgpu page in Chrome or Firefox Nightly (since WebGPU is required), click "Browse folder", select that folder you just cloned, agree to the "Upload" confirmation (confusing since nothing is uploaded from your browser, the model file is opened locally on your machine) and click "Load local model".
Here's an animated demo (recorded in real-time, I didn't speed this up):
I pushed a branch with those changes here. The next step would be to modify this to support other models in addition to the Llama 3.2 demo, but I'm pleased to have got to this proof of concept with so little work beyond throwing some prompts at Codex to see if it could figure it out.
According to the Codex /status
command this used 169,818 input tokens, 17,112 output tokens and 1,176,320 cached input tokens. At current GPT-5 token pricing ($1.25/million input, $0.125/million cached input, $10/million output) that would cost 53.942 cents, but Codex CLI hooks into my existing $20/month ChatGPT Plus plan so this was bundled into that.
Tags: javascript, ai, generative-ai, llama, local-llms, llms, ai-assisted-programming, transformers-js, webgpu, llm-pricing, vibe-coding, gpt-5, codex-cli