Sloth FM 讓本機模型夜間分類並朗讀 GitHub 通知
I Hired a Sloth to Read My GitHub Inbox While I Sleep
作者打造 Sloth FM,讓它在手提電腦上逐一分類 GitHub 通知,並以 Kokoro 語音在早上播放摘要。實測 76 則通知中,程式碼規則處理 57 則,Gemma 3 4B 在本機判斷其餘 19 則,耗時約 2.3 分鐘;晨間節目約 100 秒。安全公告及私人儲存庫內容不會被朗讀,公開頁面亦會遮蓋相關細節;處理過程不使用雲端模型或 API key。
This is a submission for the Hacktoberfest Open-Source AI Challenge Week 3: Slow and Steady
This morning, GitHub had 76 things to tell me. I listened to 100 seconds of them, with my eyes closed, and went back to sleep for five more minutes.
That is Sloth FM.
What I Built
Sloth FM is a night shift for your GitHub notifications, run by a sloth that lives on your laptop.
While you sleep, it reads your notifications one at a time with Gemma 3 running locally, works out which ones actually need you, and in the morning an open-weight voice reads you a short radio show, with a page of links for anything you want to open.
I built it because my inbox had stopped being useful. Over the last three months GitHub sent me 184 notification threads. 98 of them were CI runs. Buried in the rest were the three that mattered: a contributor licence agreement I had to sign, a maintainer waiting on my answer, and a security advisory. Reading all of it is a chore. Ignoring all of it is how those three get missed.
So Sloth FM sorts every notification into four piles:
- Needs you today: someone is blocked on you.
- Yours this week: it is your move, but nobody is waiting yet.
- Good news: merged, approved, fixed.
- Noise: passed CI, releases you only watch, threads you merely follow.
It is slow on purpose, in the best sloth tradition. It only works when the laptop is plugged in. It rests between notifications so the fans stay quiet. If the lid closes or the process dies, it picks up exactly where it stopped. And nothing it reads ever leaves the machine.
Who gets to be lazier: anyone who contributes to open source and has learned to dread the little blue dot. Maintainers drowning in CI emails. Me, mostly.
Demo
- Project page, with this morning's real show: vignesh2027.github.io/slothfm
- This morning's page (public version, private items redacted): sample morning
This is the start of the show it read me this morning, word for word:
Good morning. This is Sloth FM. While you slept, I read 76 GitHub notifications, slowly, on your own laptop, so you would not have to.
5 things need you today.
First: You need to address the bug report regarding instrumentation spans leaking in the opentelemetry-go-compile-instrumentation repo.
...
13 more are yours this week, but nobody is blocked on them. They are on your morning page.
Some good news: 9 things went well. In opentelemetry-operator, your pull request was merged.
The other 49 were noise: 28 CI runs that need nothing from you, 16 threads you were only following, 5 releases from repositories you watch. You can ignore them.
Rest is not wasted time; it's the quiet foundation for a deliberate life.
That is all. Go back to sleep for five more minutes. You have earned it.
Yes, the line about rest is the sloth's own wisdom. Gemma writes one of those every morning. It is the only creative liberty the model gets.
Code
vignesh2027
/
slothfm
Your GitHub inbox, read overnight by a sloth on your own laptop: Gemma 3 locally, Kokoro voice, Temporal durability. No cloud, no keys.
Sloth FM
Your GitHub inbox, read overnight by a sloth on your own laptop, and read back to you at breakfast.
Project page and this morning's real show · 2-minute demo video
Sloth FM is a slow, private, open-weight night shift for developers. While you sleep, it works through your GitHub notifications one at a time with Gemma 3 running locally, decides what needs you, what went well and what is noise, and in the morning Kokoro (an open-weight voice) reads you a short radio show with a page of links Nothing leaves your machine: no API keys, no cloud model, no cost.
It is built to be slow on purpose. It works only when the laptop is plugged in, rests between notifications so the fans stay quiet, and if the lid closes or the process is killed, it carries on from the next notification. That durability comes from…
How I Built It
The stack, all open, all local
| Part | What | Licence | Where it runs |
|---|---|---|---|
| The judgement | Gemma 3 4B through Ollama | Gemma Terms of Use | my laptop |
| The voice |
Kokoro 82M (bm_george, a calm British voice) |
Apache-2.0 | my laptop |
| The durability | Temporal | MIT | my laptop (temporal server start-dev) |
| The inbox | the GitHub CLI (gh api notifications) |
MIT | borrows my existing login, stores no token |
The laptop is an 8 GB M1 MacBook Air. Nothing here needs a GPU or a cloud account.
Code decides first. Gemma judges the rest. Code counts.
The most important design decision was not to send everything to the model. A small model on a laptop is slow and occasionally silly, so I gave it only the notifications that actually need judgement:
gh notifications -> SQLite journal -> code decides the obvious -> Gemma 3 judges the rest -> code writes every number -> Kokoro reads the show
Code handles anything that does not need thinking: passed or cancelled CI, releases from repositories I only watch, merged pull requests, CI failures on a pull request that has since been merged or closed, and pull requests where I spoke last and am waiting on the maintainers. Of last night's 76 notifications, code decided 57. Gemma read 19, in about 2.3 minutes of compute.
For the rest, Gemma gets the facts a person would glance at (who opened it, its state, who spoke last, which branch a failing workflow ran on, whose move it is) and must answer through a JSON schema with a bucket, an urgency, a short reason and one spoken sentence. Ollama enforces the schema, so the model cannot ramble.
Every number in the show comes from code. Gemma writes sentences; it never counts. A radio host that invents "seven" when it means "five" is worse than no host at all.
The night shift is a Temporal workflow
This is where the sloth gets its patience. Each notification is one Temporal activity with retries, and the workflow does three things a cron job cannot:
- It waits for the charger. Before each notification it asks whether the laptop is on AC power. If not, it waits on a durable timer. It does not drain my battery to read CI emails.
- It survives anything. I killed the worker halfway through, at notification 20. When I started it again, it carried on at 21. Every decision is in a journal, so a crash costs one retry, not the night.
-
It answers questions while it works.
python -m slothfm progressis a Temporal query, andpython -m slothfm wakeis a signal that says "I know I'm on battery, start anyway".
What went wrong (and made it better)
My first real night was a disaster, in an instructive way. Gemma marked 41 of 75 notifications as "needs you". The morning show cheerfully announced that 31 things needed me today. That is not a lazy morning. That is a panic attack with a nice voice.
Reading through its decisions, the model was not wrong so much as missing facts:
- One pull request, five emails. A single branch of mine had failing Integration Tests, E2E Tests, Unit Tests, Check Consistency and Benchmarks, and each one was its own notification. Now code collapses CI runs per branch, and folds them into the pull request's own thread when GitHub sent one.
- Old news. Several "failures" were on pull requests that had already been merged. Code now looks up the pull request for each failing branch, and if it is merged or closed, the failure is noise.
-
Whose move is it? Gemma kept reading "awaiting review" as "you need to review", when it meant I was waiting for a maintainer. Now code computes
whose_movefrom facts (did I open it, did I speak last, is a review requested from me, is a bot asking me to sign a CLA) and the model is told to trust it. - Everything is urgent to a small model. I added urgency, today versus this week, with two hard rules in code: a review requested from you is always today, and a red check on your own branch is never today, because nobody else is blocked by it.
After those four changes, the same inbox produced 5 things for today, 13 for this week, 9 pieces of good news and 49 noise. That is the show I actually want to hear.
Two smaller lessons. A 4B model told to say "you" sometimes says "You, for the prometheus/common repo, have a mention", so a tiny cleanup function smooths the common phrasings. And it occasionally uses my GitHub username as if it were my name, so that gets removed before Kokoro ever sees it.
Private things stay private
Security advisories and private repositories are never read aloud. The radio says "there is a security advisory waiting for you, I kept the details off the radio", and the --public version of the page redacts them entirely. That is how I can publish this morning's real page without leaking anything.
Why Does Open Innovation Matter?
Because of those security advisories.
Two of the notifications Sloth FM handled last night were comments on draft repository security advisories: vulnerability reports that stay private until the maintainers publish them. A hosted triage service would have received at least their titles, and probably the discussion too. Sloth FM recognised them in code, put them at the top of my page, kept them off the radio, and nothing about them left the laptop. For a tool whose whole job is to read everything, that is not a nice-to-have. It is the only acceptable design.
Open weights also made the rest possible:
- It costs nothing to run, every night, forever. A sloth that bills you per token is not lazy enough. No API key, no quota, no surprise invoice.
- It works offline. Gemma, Kokoro and Temporal all run locally. Only fetching from GitHub needs the network.
- I could change how it thinks. I tuned the prompt, constrained the output with a schema, swapped the voice for a calmer one, slowed it to 0.92x, and moved decisions from the model into code wherever code was more reliable. A closed API would have given me a black box and a bill.
- The voice is open too. The radio host is Kokoro, an 82M-parameter Apache-2.0 model that speaks on the CPU. The whole show is recorded on the laptop's CPU in about a minute.
Where open did worse: Gemma 3 4B is a small model, and it needed help. But the help it needed was facts, and facts are cheap to compute. The combination of a small open model and a little deterministic code beat what I expected from either alone.
The Lazy Payoff
I used it this morning, for real, on my own inbox. Here is what it did for me:
| Notifications read overnight | 76 |
| Decided by code, with no model call | 57 |
| Read by Gemma 3, locally | 19, in about 2.3 minutes of compute |
| Words of titles and comments I did not have to read | 1,255 |
| My morning | a 100 second show, then 5 things to do |
| Cloud calls, API keys, cost | 0 |
The honest version of the payoff: it did not save me hours. It saved me the worst five minutes of the morning, the scroll through 76 lines of red crosses and bot comments, hunting for the few that matter. Now a calm voice tells me the five that matter today, and I open those.
I am keeping it. Tonight I start it before bed, it waits for the charger like a good sloth, and tomorrow I find out what it thinks of my pull requests.
Prize Categories
- Best Use of Gemma: Gemma 3 4B, running locally through Ollama, is the judgement at the core of Sloth FM: it triages every notification code cannot decide, through a JSON schema, and writes every sentence of the show.
-
Best Use of Temporal: the night shift is a Temporal workflow: one activity per notification with retries, a durable timer that waits for the charger, a
wakesignal, aprogressquery, and it resumed exactly where it stopped after I killed it mid-run.
Thanks for reading. I am going back to sleep.
來源:DEV Community · dev.to


