<?xml version="1.0" encoding="utf-8"?>
<feed xml:lang="en-us" xmlns="http://www.w3.org/2005/Atom"><title>Simon Willison's Weblog: carlton-gibson</title><link href="http://simonwillison.net/" rel="alternate"/><link href="http://simonwillison.net/tags/carlton-gibson.atom" rel="self"/><id>http://simonwillison.net/</id><updated>2025-04-14T04:54:35+00:00</updated><author><name>Simon Willison</name></author><entry><title>Using LLMs as the first line of support in Open Source</title><link href="https://simonwillison.net/2025/Apr/14/llms-as-the-first-line-of-support/#atom-tag" rel="alternate"/><published>2025-04-14T04:54:35+00:00</published><updated>2025-04-14T04:54:35+00:00</updated><id>https://simonwillison.net/2025/Apr/14/llms-as-the-first-line-of-support/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://noumenal.es/posts/llms-for-user-support/WZb/"&gt;Using LLMs as the first line of support in Open Source&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
From reading the title I was nervous that this might involve automating the initial response to a user support query in an issue tracker with an LLM, but Carlton Gibson has better taste than that.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The &lt;strong&gt;open contribution&lt;/strong&gt; model engendered by GitHub — where anonymous (to the project) users can create issues, and comments, which are almost always extractive support requests — results in an effective &lt;strong&gt;denial-of-service attack against maintainers&lt;/strong&gt;. [...]&lt;/p&gt;
&lt;p&gt;For anonymous users, who really just want help almost all the time, the pattern I’m settling on is to facilitate them getting their answer from their LLM of choice. [...] we can generate a file that we offer users to download, then we tell the user to pass this to (say) Claude with a simple prompt for their question.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This resonates with the concept proposed by &lt;a href="https://llmstxt.org/"&gt;llms.txt&lt;/a&gt; - making LLM-friendly context files available for different projects.&lt;/p&gt;
&lt;p&gt;My &lt;a href="https://github.com/simonw/docs-for-llms"&gt;simonw/docs-for-llms&lt;/a&gt; contains my own early experiment with this: I'm running a build script to create LLM-friendly concatenated documentation for several of my projects, and my &lt;a href="https://github.com/simonw/llm-docs"&gt;llm-docs&lt;/a&gt; plugin (&lt;a href="https://simonwillison.net/2025/Apr/7/long-context-llm/#asking-questions-of-llm-s-documentation"&gt;described here&lt;/a&gt;) can then be used to ask questions of that documentation.&lt;/p&gt;
&lt;p&gt;It's possible to pre-populate the Claude UI with a prompt by linking to &lt;code&gt;https://claude.ai/new?q={PLACE_HOLDER}&lt;/code&gt;, but it looks like there's quite a short length limit on how much text can be passed that way. It would be neat if you could pass a URL to a larger document instead.&lt;/p&gt;
&lt;p&gt;ChatGPT also supports &lt;code&gt;https://chatgpt.com/?q=your-prompt-here&lt;/code&gt; (again with a short length limit) and directly executes the prompt rather than waiting for you to edit it first(!)

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="https://fosstodon.org/@carlton/114329734119743735"&gt;@carlton&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/open-source"&gt;open-source&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ai"&gt;ai&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/chatgpt"&gt;chatgpt&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/llms"&gt;llms&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/claude"&gt;claude&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/carlton-gibson"&gt;carlton-gibson&lt;/a&gt;&lt;/p&gt;



</summary><category term="open-source"/><category term="ai"/><category term="generative-ai"/><category term="chatgpt"/><category term="llms"/><category term="claude"/><category term="carlton-gibson"/></entry><entry><title>2025 DSF Board Nominations</title><link href="https://simonwillison.net/2024/Oct/16/2025-dsf-board-nominations/#atom-tag" rel="alternate"/><published>2024-10-16T23:01:22+00:00</published><updated>2024-10-16T23:01:22+00:00</updated><id>https://simonwillison.net/2024/Oct/16/2025-dsf-board-nominations/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://www.djangoproject.com/weblog/2024/sep/25/2025-dsf-board-nominations/"&gt;2025 DSF Board Nominations&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
The Django Software Foundation board elections are coming up. There are four positions open, seven directors total. Terms last two years, and the deadline for submitting a nomination is October 25th (the date of the election has not yet been decided).&lt;/p&gt;
&lt;p&gt;Several community members have shared "DSF initiatives I'd like to see" documents to inspire people who may be considering running for the board:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://gist.github.com/sarahboyce/68ffaaeae24d2501cf27a914f77fb97c"&gt;Sarah Boyce&lt;/a&gt; (current Django Fellow) wants a marketing strategy, better community docs, more automation and a refresh of the Django survey.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.better-simple.com/django/2024/10/13/dsf-initiatives-i-would-like-to-see/"&gt;Tim Schilling&lt;/a&gt; wants one big sponsor, more community recognition and a focus on working groups.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://noumenal.es/posts/dsf-board-election/N8W/"&gt;Carlton Gibson&lt;/a&gt; wants an Executive Director, an updated website and better integration of the community into that website.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://jacobian.org/2024/oct/18/dsf-board-2025/"&gt;Jacob Kaplan-Moss&lt;/a&gt; wants effectively all of the above.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There's also a useful FAQ &lt;a href="https://forum.djangoproject.com/t/2025-dsf-board-elections/35253/7"&gt;on the Django forum&lt;/a&gt; by Thibaud Colas.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/django"&gt;django&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/jacob-kaplan-moss"&gt;jacob-kaplan-moss&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/dsf"&gt;dsf&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/carlton-gibson"&gt;carlton-gibson&lt;/a&gt;&lt;/p&gt;



</summary><category term="django"/><category term="jacob-kaplan-moss"/><category term="dsf"/><category term="carlton-gibson"/></entry><entry><title>Weeknotes: DjangoCon, SQLite in Django, datasette-gunicorn</title><link href="https://simonwillison.net/2022/Oct/23/datasette-gunicorn/#atom-tag" rel="alternate"/><published>2022-10-23T19:58:00+00:00</published><updated>2022-10-23T19:58:00+00:00</updated><id>https://simonwillison.net/2022/Oct/23/datasette-gunicorn/#atom-tag</id><summary type="html">
    &lt;p&gt;I spent most of this week at &lt;a href="https://2022.djangocon.us/"&gt;DjangoCon&lt;/a&gt; in San Diego - my first outside-of-the-Bay-Area conference since the before-times.&lt;/p&gt;
&lt;p&gt;It was a most excellent event. I spent a lot of time in the corridor track - actually the sitting-outside-in-the-sunshine track, catching up with people I haven't seen in several years.&lt;/p&gt;
&lt;p&gt;I gave a talk titled "&lt;a href="https://2022.djangocon.us/talks/massively-increase-your-productivity-on/"&gt;Massively increase your productivity on personal projects with comprehensive documentation and automated tests&lt;/a&gt;", with the alternative title "Coping strategies for the serial project hoarder". I'll do a full write-up of this once the video is made available in a few weeks time, but in the meantime the talk materials can be found here:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/simonw/djangocon-2022-productivity"&gt;Supporting notes and links&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://speakerdeck.com/simon/massively-increase-your-productivity-on-personal-projects-with-comprehensive-documentation-and-automated-tests"&gt;Slides on Speaker Deck&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://djangoconus2022.loudswarm.com/session/massively-increase-your-productivity-on-personal-projects-with-comprehensive-documentation-and-automated-tests"&gt;Video for paying DjangoCon attendees&lt;/a&gt; (public video coming soon)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I also gave a lightning talk about AI and magic, which was effectively the five minute oral version of my recent blog post &lt;a href="https://simonwillison.net/2022/Oct/5/spell-casting/"&gt;Is the AI spell-casting metaphor harmful or helpful?&lt;/a&gt;&lt;/p&gt;
&lt;h4 id="benchmarking-sqlite"&gt;Benchmarking SQLite in Django&lt;/h4&gt;
&lt;p&gt;I also hung around for the first day of the DjangoCon sprints.&lt;/p&gt;
&lt;p&gt;For over a decade, the Django documentation has warned against using SQLite in production - recommending PostgreSQL or MySQL instead.&lt;/p&gt;
&lt;p&gt;I asked Django Fellow &lt;a href="https://twitter.com/carltongibson"&gt;Carlton Gibson&lt;/a&gt; what it would take to update that advice for 2022. He suggested that what we really needed was a solid idea for how well modern SQLite performs with Django, against a variety of different settings.&lt;/p&gt;
&lt;p&gt;So I spent some time running benchmarks, using my new &lt;a href="https://github.com/simonw/django_sqlite_benchmark"&gt;django_sqlite_benchmark&lt;/a&gt; repository.&lt;/p&gt;
&lt;p&gt;You can follow the full details of my experiments in these issues:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/simonw/django_sqlite_benchmark/issues/2"&gt;#2: Locust test to exercise /counter/xxx endpoint&lt;/a&gt; which runs benchmarks against a simple Django view that increments a counter stored in a SQLite table&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/simonw/django_sqlite_benchmark/issues/3"&gt;#3: Load test for larger writes&lt;/a&gt; runs a benchmark using a script that inserts larger JSON objects into a database table. I also &lt;a href="https://github.com/simonw/django_sqlite_benchmark/issues/3#issuecomment-1287598057"&gt;tried this against PostgreSQL&lt;/a&gt;, getting very similar numbers to SQLite.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/simonw/django_sqlite_benchmark/issues/4"&gt;#4: Benchmark endpoint that doesn't interact with database&lt;/a&gt; benchmarks a simple "hello world" view that doesn't use SQLite at all - as a baseline for comparison&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I used &lt;a href="https://locust.io"&gt;Locust&lt;/a&gt; for all of these tests, and wrote up &lt;a href="https://til.simonwillison.net/python/locust"&gt;a TIL about using it&lt;/a&gt; as well.&lt;/p&gt;
&lt;p&gt;Here's the TLDR version of the results: SQLite in its default "journal" mode starts returning "database locked" errors pretty quickly as the write load increases. But... if you switch to "wal" mode (&lt;a href="https://til.simonwillison.net/sqlite/enabling-wal-mode"&gt;here's how&lt;/a&gt;) those errors straight up vanish!&lt;/p&gt;
&lt;p&gt;I was expecting WAL mode to improve things, but I thought I'd still be able to hit errors even with it enabled. No - it turns out that, at least for the amount of traffic I could generate on may laptop, WAL mode proved easily capable of handling the load.&lt;/p&gt;
&lt;p&gt;Even without WAL mode, bumping the SQLite "timeout" option up to 20s solved most of the errors.&lt;/p&gt;
&lt;p&gt;Even more interestingly: I tried using Gunicorn (and Uvicorn) to run multiple Django workers at once. I was certain this would lead to problems, as SQLite isn't designed to handle writes from multiple processes at once... or so I thought. It turned out SQLite's use of file locking meant everything worked far better than I expected - and upping the number of worker processes from 1 to 4 resulted in approximately a 4x increase in throughput.&lt;/p&gt;
&lt;p&gt;I shouldn't be surprised by this, if only because every time I've tried to push SQLite in a new direction it's impressed me with how much more capable it is than I expected.&lt;/p&gt;
&lt;p&gt;But still, these results are very exciting. This problem still needs more thorough testing and more eyes than just mine, but I think this indicates that SQLite should absolutely be considered a viable option for running Django in production in 2022.&lt;/p&gt;
&lt;h4&gt;datasette-gunicorn&lt;/h4&gt;
&lt;p&gt;Datasette has always run as a single process. It uses &lt;a href="https://www.uvicorn.org/"&gt;Uvicorn&lt;/a&gt; to serve requests, but it hard-codes Uvicorn to a single worker (&lt;a href="https://github.com/simonw/datasette/blob/0.62/datasette/cli.py#L617-L619"&gt;here&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;Based on my experiments with SQLite and Django - in particular how running multiple worker processes gave me an increase in how much traffic I could handle - I decided to try the same thing with Datasette itself.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://gunicorn.org/"&gt;Gunicorn&lt;/a&gt; remains one of the most well regarded options for deploying Python web applications. It acts as a process monitor, balancing requests between different workers and restarting anything that fails with an error.&lt;/p&gt;
&lt;p&gt;I decided to experiment with this through the medium of a Datasette plugin. So I built &lt;a href="https://datasette.io/plugins/datasette-gunicorn"&gt;datasette-gunicorn&lt;/a&gt;, a plugin that adds an extra command to Datasette that lets you start it like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;datasette gunicorn my.db --workers 4
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It takes &lt;a href="https://datasette.io/plugins/datasette-gunicorn#user-content-datasette-gunicorn---help"&gt;most of the same arguments&lt;/a&gt; as Datasette's regular &lt;code&gt;datasette serve&lt;/code&gt; command, plus that new &lt;code&gt;-w/--workers&lt;/code&gt; option for setting the number of workers.&lt;/p&gt;
&lt;p&gt;Initial benchmarks &lt;a href="https://github.com/simonw/datasette-gunicorn/issues/1#issuecomment-1287905177"&gt;were very positive&lt;/a&gt;: 21 requests a second with a single worker, increasing to 75 requests/second with four! Not bad for an initial experiment. I also &lt;a href="https://github.com/simonw/datasette-gunicorn/issues/4"&gt;tested it serving a static page&lt;/a&gt; through Datasette and got up to over 500 requests a second with a warning that Locust needed to be moved to a separate machine for a full load test.&lt;/p&gt;
&lt;p&gt;In writing the plugin I had to figure out how to build a new command that mostly copied parameters from the existing &lt;code&gt;datasette serve&lt;/code&gt; Click command - I wrote &lt;a href="https://til.simonwillison.net/datasette/plugin-modifies-command"&gt;a TIL&lt;/a&gt; about how I ended up doing that.&lt;/p&gt;
&lt;h4&gt;shot-scraper 1.0&lt;/h4&gt;
&lt;p&gt;Also this week: I released &lt;a href="https://github.com/simonw/shot-scraper/releases/tag/1.0"&gt;shot-scraper 1.0&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Despite the exciting version number this actually only has two small new features. Here's the full changelog:&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;New &lt;code&gt;shot-scraper html URL&lt;/code&gt; command (&lt;a href="https://shot-scraper.datasette.io/en/stable/html.html"&gt;documented here&lt;/a&gt;) for outputting the final HTML of a page, after JavaScript has been executed. &lt;a href="https://github.com/simonw/shot-scraper/issues/96"&gt;#96&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;shot-scraper javascript&lt;/code&gt; has a new &lt;code&gt;-r/--raw&lt;/code&gt; option for outputting the result of the JavaScript expression as a raw string rather than JSON encoded (&lt;a href="https://shot-scraper.datasette.io/en/stable/javascript.html"&gt;shot-scraper javascript documentation&lt;/a&gt;). &lt;a href="https://github.com/simonw/shot-scraper/issues/95"&gt;#95&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Tutorial: &lt;a href="https://simonwillison.net/2022/Oct/14/automating-screenshots/"&gt;Automating screenshots for the Datasette documentation using shot-scraper&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;I bumped it to 1.0 because &lt;code&gt;shot-scraper&lt;/code&gt; is mature enough now that I'm ready to commit to not breaking existing features (at least without shipping a 2.0, which I hope to avoid for as long as possible).&lt;/p&gt;
&lt;p&gt;I'm always trying to get more brave when it comes to stamping a 1.0 release on my main projects.&lt;/p&gt;
&lt;p&gt;(I really, really need to get Datasette 1.0 shipped soon.)&lt;/p&gt;
&lt;h4&gt;Releases this week&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/simonw/datasette-gunicorn"&gt;datasette-gunicorn&lt;/a&gt;&lt;/strong&gt;: &lt;a href="https://github.com/simonw/datasette-gunicorn/releases/tag/0.1"&gt;0.1&lt;/a&gt; - 2022-10-22
&lt;br /&gt;Plugin for running Datasette using Gunicorn&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/simonw/shot-scraper"&gt;shot-scraper&lt;/a&gt;&lt;/strong&gt;: &lt;a href="https://github.com/simonw/shot-scraper/releases/tag/1.0"&gt;1.0&lt;/a&gt; - (&lt;a href="https://github.com/simonw/shot-scraper/releases"&gt;23 releases total&lt;/a&gt;) - 2022-10-15
&lt;br /&gt;A command-line utility for taking automated screenshots of websites&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/simonw/asgi-gzip"&gt;asgi-gzip&lt;/a&gt;&lt;/strong&gt;: &lt;a href="https://github.com/simonw/asgi-gzip/releases/tag/0.2"&gt;0.2&lt;/a&gt; - (&lt;a href="https://github.com/simonw/asgi-gzip/releases"&gt;2 releases total&lt;/a&gt;) - 2022-10-13
&lt;br /&gt;gzip middleware for ASGI applications, extracted from Starlette&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;TIL this week&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://til.simonwillison.net/python/too-many-open-files-psutil"&gt;Using psutil to investigate "Too many open files"&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://til.simonwillison.net/shot-scraper/subset-of-table-columns"&gt;shot-scraper for a subset of table columns&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://til.simonwillison.net/gpt3/guessing-amazon-urls"&gt;Guessing Amazon image URLs using GitHub Copilot&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://til.simonwillison.net/django/datasette-django"&gt;Adding a Datasette ASGI app to Django&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://til.simonwillison.net/python/locust"&gt;Simple load testing with Locust&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://til.simonwillison.net/datasette/plugin-modifies-command"&gt;Writing a Datasette CLI plugin that mostly duplicates an existing command&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/django"&gt;django&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/djangocon"&gt;djangocon&lt;/a&gt;, &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/my-talks"&gt;my-talks&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/gunicorn"&gt;gunicorn&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/datasette"&gt;datasette&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/weeknotes"&gt;weeknotes&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/shot-scraper"&gt;shot-scraper&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/carlton-gibson"&gt;carlton-gibson&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="django"/><category term="djangocon"/><category term="projects"/><category term="sqlite"/><category term="my-talks"/><category term="gunicorn"/><category term="datasette"/><category term="weeknotes"/><category term="shot-scraper"/><category term="carlton-gibson"/></entry><entry><title>Quoting Carlton Gibson</title><link href="https://simonwillison.net/2020/Sep/27/carlton-gibson/#atom-tag" rel="alternate"/><published>2020-09-27T15:09:26+00:00</published><updated>2020-09-27T15:09:26+00:00</updated><id>https://simonwillison.net/2020/Sep/27/carlton-gibson/#atom-tag</id><summary type="html">
    &lt;blockquote cite="https://noumenal.es/posts/weeknotes-wk-39/rX/"&gt;&lt;p&gt;Inevitably we got round to talking about async.&lt;/p&gt;
&lt;p&gt;As much of an unneeded complication as it is for so many day-to-day use-cases, it’s important for Python because, if and when you do need the high throughput handling of these io-bound use-cases, you don’t want to have to switch language.&lt;/p&gt;
&lt;p&gt;The same for Django: most of what you’re doing has no need of async but you don’t want to have to change web framework just because you need a sprinkling of non-blocking IO.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p class="cite"&gt;&amp;mdash; &lt;a href="https://noumenal.es/posts/weeknotes-wk-39/rX/"&gt;Carlton Gibson&lt;/a&gt;&lt;/p&gt;

    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/async"&gt;async&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/django"&gt;django&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/python"&gt;python&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/carlton-gibson"&gt;carlton-gibson&lt;/a&gt;&lt;/p&gt;



</summary><category term="async"/><category term="django"/><category term="python"/><category term="carlton-gibson"/></entry></feed>