DEV Community

Tharun
Tharun

Posted on

Sloth Sort: one small thing, and a memory for the rest

Hacktoberfest Open-Source AI Challenge Week 3: Slow and Steady Submission 🦥

This is a submission for the Hacktoberfest Open-Source AI Challenge Week 3: Slow and Steady.

What I Built

Sloth Sort takes the pile you keep not doing and hands almost all of it back.

You paste up to eight lines. A local Gemma model picks the one small thing that actually has a deadline, and it parks or skips the rest. The minutes you do not spend today are the point. On the sample pile, that was buying a Thursday train ticket, about ten minutes, and 230 minutes handed back on everything else: a dentist email, a Friday bill, Sunday lunch, a downloads folder, a blank-page resume, holiday browsing, and forty newsletters.

The second time a similar chore shows up, the app does not ask you to decide it from scratch. Tiger Data already has the earlier call.

Demo

The model stays on the machine, so this is a local run rather than a hosted URL.

Sloth Sort: sample pile, local sort, and the minutes handed back

Code

GitHub logo tharun634 / sloth-sort

A local Gemma sorter that hands a dreaded pile back to you as one small move, and remembers the call in Tiger Data.

Sloth Sort

Dump the pile you keep putting off. A local Gemma model picks the one small thing worth doing today, and Tiger Data remembers the call so a similar chore does not get re-decided tomorrow.

This is a new project for the Hacktoberfest Open-Source AI Challenge: Week 3, Slow and Steady.

What it saves

A dreaded list is usually eight tasks pretending they all have to happen now. Sloth Sort hands back the minutes on everything except one small move. The first sample pile is there so you can feel that on a real model without inventing chores.

How it is built

flowchart LR
  pile[Pasted pile] --> api[FastAPI]
  api --> embed[EmbeddingGemma via Ollama]
  embed --> tiger[(Tiger Data)]
  tiger --> memory[BM25 plus pgvector]
  memory --> gemma[Gemma 4 E4B via Ollama]
  gemma --> api
  api --> ledger[effort_events hypertable]
  ledger --> chart[minutes_saved_daily]
Loading
  • Gemma 4 E4B (gemma4:e4b in Ollama) is…

github.com/tharun634/sloth-sort. MIT. README.md has the run steps: Ollama, a Tiger Cloud connection string, then uvicorn.

How I Built It

flowchart LR
  pile[Pasted pile] --> api[FastAPI]
  api --> embed[EmbeddingGemma via Ollama]
  embed --> tiger[(Tiger Data)]
  tiger --> memory[BM25 plus pgvector]
  memory --> gemma[Gemma 4 E4B via Ollama]
  gemma --> api
  api --> ledger[effort_events hypertable]
  ledger --> chart[minutes_saved_daily]

Two open models, both through Ollama on localhost, and no SDK. A POST to /api/chat and /api/embed is the whole client. That is the same shape Damiano builds described for a one-shot local call: string in, string out, with a long timeout, because a local model is allowed to be slow.

Gemma 4 E4B (gemma4:e4b) is the sorter. It answers with one JSON object: exactly one do, and the rest split into later and skip. I turn thinking off. With it on, the model spent the token budget on a trace and the JSON came back cut in half.

EmbeddingGemma writes a halfvec(768) for every line. Those rows live in Tiger Data next to a BM25 index from pg_textsearch. For each new line the app runs both searches and fuses them with reciprocal rank fusion, k = 60, which is the hybrid pattern Tiger documents. The fused neighbors go into the prompt as hints, not orders. A dated bill or a ticket still beats a past social reply.

The decision table is a normal table. A vector column and Timescale columnstore do not belong on the same relation. The minutes are a separate append-only hypertable, effort_events, segmented by the decision, with a continuous aggregate minutes_saved_daily that the chart reads, including the rows from today.

pg_textsearch is still marked prerelease upstream. This is a DEV database, and the keyword half of the search is the reason to use it. The index did not answer a bare string. The query has to be item_text <@> to_bm25query(text, index_name).

The stack choice follows what people have already shipped. MindMirror is the same split I wanted for a pile of personal chores: FastAPI in front of Ollama, so the text is not a request to someone else's model.

Why Does Open Innovation Matter?

The pile is the sort of list you would not paste into a hosted chat: dentist, bills, mom, the mess in Downloads. Open weights are what make the laziness cheap enough to use. Gemma runs on this machine, EmbeddingGemma runs on this machine, and a sort does not send the lines to a model API. There is no per-token bill, so leaving the app running for the next pile costs nothing. The models are also swappable. The sorter and the embedder are environment variables. A closed API would have been faster to call and worse at the actual job, which is keeping a private list private.

My Agent Session

The session was recorded in Cursor with Grok. DEV's uploader has no Cursor tool name, so the session is filed under an accepted harness label. The transcript says that in the first reply. Paths, database URLs, and tokens are not in it.

Building Sloth Sort for Hacktoberfest Week 3 grok-4.7
You

Build a web app for the Hacktoberfest Open-Source AI Challenge Week 3, Slow and Steady. I have Python, Ollama, and Tiger Cloud locally. Aim at partner categories the project actually uses. Then publish the DEV post and a scrubbed agent session, create the repo under HobbyProjects, push it to GitHub, and include a GIF of the app.

Agent

This session ran in Cursor with Grok. DEV's uploader has no Cursor label, so the tool name on the session is only the platform's required bucket. Week 3 is live through October 25. The theme is open-weight AI that lets someone be lazier. The machine already had gemma4:e4b in Ollama, and Tiger Cloud already had Timescale, pgvector, and pg_textsearch. Those are the two tracks this project can enter: Best Use of Gemma, and Best Use of Tiger Data.

Sloth Sort takes up to eight lines of a dreaded pile. EmbeddingGemma writes a halfvec for each line. Tiger Data retrieves similar past decisions with BM25 and pgvector, fused with reciprocal rank fusion. Gemma 4 E4B picks one small move and parks or skips the rest. Each line is also an effort_events hypertable row, rolled up by a continuous aggregate of minutes handed back. The decision table is not a hypertable, because a vector column and columnstore do not mix.

Agent

The first live sort failed in two local ways. The BM25 operator needed to_bm25query with the index name. Gemma 4 also spent its token budget on a thinking trace and returned truncated JSON. Setting think to false made it return one complete JSON object.

You

Publish the repository, the scrubbed session, and the article.

The Lazy Payoff

I loaded the sample pile and sorted it on this machine.

  • 15.9 seconds on Gemma 4 E4B, after thinking was turned off.
  • One move: buy the Thursday train ticket, 10 minutes, because it was the nearest hard deadline.
  • Held for later: the dentist email, the electricity bill due Friday, and Sunday lunch.
  • Skipped: cleaning Downloads, rewriting a resume from a blank page, researching holidays, and clearing newsletters.
  • 230 minutes handed back on that sort. The chart's bar for the day is that number, read from the continuous aggregate.

Prize Categories

  • Best Use of Gemma. Gemma 4 E4B decides the pile. EmbeddingGemma is the memory. Both run locally through Ollama.
  • Best Use of Tiger Data. Past decisions are retrieved with hybrid BM25 and pgvector search, and the minutes handed back are a hypertable plus a continuous aggregate.

Top comments (0)