This is a submission for the Hacktoberfest Open-Source AI Challenge Week 3: Slow and Steady
What I Built
slow-sorter is a folder tidier that moves exactly one file per night into a trash folder.
It runs on a Raspberry Pi 2 from 2015: 900 MHz, four cores, 942 MB of RAM, no Wi-Fi. On that board a 150M-parameter open-weight model answers one question about one file, in about nine tokens per second, and then the rules — not the model — decide whether anything happens.
That is the whole product. It does not sort your folder. It removes the moment where you open a folder full of junk, feel tired, close it, and promise to deal with it later. Tonight it looks at one old file. Tomorrow it looks at the next one. Forty junk files take forty nights, at four seconds a night, and nobody has to sit and watch.
The effort it saves is not "sorting 40,000 files". It is the ten seconds of dread before you decide not to start.
Demo
There is no hosted version, because the entire point is that it does not need a host. Here is a real run on the Pi, four consecutive nights, on a folder I made deliberately messy:
--- night 1 ---
blog draft.md
draft blog post about slow sorting
-> kept (cannot prove it is safe to bin)
blog draft copy.md
draft blog post about slow sorting
-> binned (byte-identical duplicate of blog draft.md) -> .slow-sorter-trash/blog draft copy.md
[3199 ms]
--- night 2 ---
empty.txt
file
-> binned (empty file) -> .slow-sorter-trash/empty.txt
[3380 ms]
--- night 3 ---
render.tmp
half a render
-> binned (scratch file (.tmp)) -> .slow-sorter-trash/render.tmp
[5164 ms]
--- night 4 ---
looked at 1 old file(s), binned nothing. slow and steady.
[2714 ms]
blog draft.md is the interesting one. The tool looks at it every single night, decides it cannot prove it is junk, and leaves it alone — while the queue keeps moving underneath it. print.png was two days old, newer than the quiet threshold, so it was never even a candidate.
Reproduce it yourself in about a minute; no model required:
git clone https://github.com/sanpeita/slow-sorter
python3 slow_sorter.py --demo ./junk
python3 slow_sorter.py --nightly ./junk --offline --quiet-days 0
Code
https://github.com/sanpeita/slow-sorter
Pure Python 3, standard library only, MIT. 25 tests, no test dependencies, run with python3 -m unittest discover -s tests.
The part that matters is thirty lines:
def why_disposable(path, root):
"""Only bin what can be proven."""
if path.stat().st_size == 0:
return "empty file"
twin = duplicate_of(path, root) # same SHA-256, older mtime
if twin is not None and twin.stat().st_mtime < path.stat().st_mtime:
return f"byte-identical duplicate of {twin.name}"
if path.suffix.lower() in DISPOSABLE_SUFFIXES: # .tmp .bak .log .part .crdownload
return f"scratch file ({path.suffix})"
return None
None means "kept", and kept is the default that everything has to argue its way out of.
How I Built It
The model is llm-jp-3-150m-instruct3 (Q8_0 GGUF) served by llama-server on loopback, with -c 512 -n 256 --jinja. The client is a single urllib call, so the tool has no dependencies at all and the model can be swapped by changing one endpoint.
The obvious design — scan everything, classify everything, sort everything — dies on this hardware. When I tried a 0.5B model instead, it ran at 1.65–1.85 tokens/second and took 14–31 seconds per answer, with no improvement in quality. The wall on a Pi 2 is not RAM. It is 900 MHz times four cores. So the design is not "a slower version of a fast tool". It is a different tool: one look, one decision, one night.
I also tested whether the model could simply be trusted to decide. It cannot:
- Asked to pick between two options: 0/6 correct.
- Asked a strict yes/no question: 0/16.
- Logprobs on twenty files: "yes" won 20/20, with an almost constant margin.
A 150M model on this board does not have an opinion about your files. It has a prior. So it describes, and the rules decide.
Then the board taught me two things I would not have found on a laptop.
One. The model sometimes answers the instruction instead of the file. The first honest run looked like this:
render.tmp
no preamble
-> binned (scratch file (.tmp))
no preamble was in my prompt. The model echoed the constraint and called it a description. The fix is not a better prompt — it is output validation, because on small models a confident wrong answer and a correct answer are the same shape:
echo = ("preamble", "no quotes", "describe this", "file:", "max 12 words")
if not line or any(marker in line.lower() for marker in echo):
return "" # failure, so the caller falls back to the file's own first line
That is why the transcript above says file and half a render instead of model prose. Those are the fallback, and they are more honest than anything the model produced.
Two. My first nightly loop got stuck. The oldest file could not be proven junk, so every night re-examined it and binned nothing — forever. It took four real nights on real hardware for that to become obvious, and one test (test_unprovable_file_does_not_block_the_queue) to keep it fixed. A tool that is allowed to do one thing per day has to be very careful about what it refuses to do, because refusing is also a daily decision.
Why Does Open Innovation Matter?
Three things that a closed API could not give me here.
It runs with no network. The contest I built this for is a local-first idea, but the stronger reason is boring: a tidier that needs an internet connection is a tidier that stops working on a train, or in a year when the endpoint changes its pricing. The Pi has no Wi-Fi hardware. It does not miss it.
The files never leave the machine. This tool reads filenames and the first line of file contents. That is exactly the category of data you should not paste into someone else's queue, and exactly the category that a "free" tier quietly collects.
The model is not the authority, so I can change it. Because the rules decide and the model only describes, swapping a 150M Japanese-tuned model for a 0.5B English one is a one-line change with no behavioural risk. That is only true because the open weights let me treat the model as a replaceable part rather than a service I am renting.
And the honest part: the model is bad at this. Nine tokens a second, and it sometimes answers the prompt instead of the file. That is fine — that is the design. An open model I can measure, break, and demote is worth more than a closed one I have to trust.
My Agent Session
(Optional. If you save the session with DevRelay and embed it here, this becomes an entry for Best Use of Entire as well. Otherwise delete this section.)
The Lazy Payoff
I ran it for real. Four nights, unattended, on the Pi:
- Night 1: one duplicate binned. 3.2 s.
- Night 2: one empty file binned. 3.4 s.
- Night 3: one abandoned
.tmpbinned. 5.2 s. - Night 4: nothing to do, and it said so.
Total human attention: zero. Total time I spent looking at that folder: zero. The folder got four files lighter while I was asleep, and the one file I actually care about was never touched.
That is the trade I wanted. Not a tool that finishes the job, but a tool that keeps working on a job I will never start.
Prize Categories
No partner technology is used — this runs on a Raspberry Pi and the Python standard library. My Agent Session is the only partner-adjacent section, and it only applies if a DevRelay session is attached (Best Use of Entire). Remove this section if you do not attach one.
Top comments (0)