<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Brage Mellesdal</title>
    <description>The latest articles on DEV Community by Brage Mellesdal (@brage1025).</description>
    <link>https://dev.to/brage1025</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4154955%2Fec67d466-ac5f-40ef-b39b-2c78e20835bd.jpg</url>
      <title>DEV Community: Brage Mellesdal</title>
      <link>https://dev.to/brage1025</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/brage1025"/>
    <language>en</language>
    <item>
      <title>My ending remarks: Hacktoberfest 2026</title>
      <dc:creator>Brage Mellesdal</dc:creator>
      <pubDate>Mon, 02 Nov 2026 11:25:25 +0000</pubDate>
      <link>https://dev.to/brage1025/my-ending-remarks-hacktoberfest-2026-3bd2</link>
      <guid>https://dev.to/brage1025/my-ending-remarks-hacktoberfest-2026-3bd2</guid>
      <description>&lt;p&gt;October is over, and so is another Hacktoberfest. I'm writing this the monday after, while it's still fresh, because I have two things to say: one is a thank-you, and the other is a (friendly) complaint.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I got out of it
&lt;/h2&gt;

&lt;p&gt;I came into this year discovering a new community that shared in my newfound passion with open source in mind. A place that we can get challenged and share our creations with each other. Hacktoberfest 2026 gave me that.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A habit.&lt;/strong&gt; Having a month-long goal turned "What project should I work on today?" into "I'll finish this challenge tonight!"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Better skills.&lt;/strong&gt; Reading unfamiliar codebases, working with leading opensource tools, and responding to feedback without getting defensive about it. And learning all of that in a natural environment, not through YouTube tutorials.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Confidence.&lt;/strong&gt; Getting better control over what tool to use to what project and estimating the time scope of my projects. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Connections.&lt;/strong&gt; I talked to people I'd never have met otherwise. I'm taking my hat of for Jacklyn Biggin. Her engagement and friendly attitude were dangerously contagious. &lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The community is the real prize
&lt;/h2&gt;

&lt;p&gt;The stickers are fun, but what I'll remember is the people.&lt;/p&gt;

&lt;p&gt;Fellow hackers who took time to leave kind, specific comments on a random project shared in the discord. Fellow attendees who answered questions in Discord at odd hours when the Hacktoberfest crew took their well earned rest. People who shared beginner-friendly resurces to other people's issues. Someone even went the extra mine bug-fix with them when someone got stuck.&lt;/p&gt;

&lt;p&gt;Hacktoberfest's new subject shift regarding AI in the open source space was a new take that even I, a newbie, felt some people in the comunity had a slight issue with. Me on the other hand thought this was a logical change considering how more and more AI takes a part in the day to day life, like it or not. So with having to deal with that and still welcoming newcomers to this experience must have taken some extra effort. If you've stayed somewhat open minded and helped around where you could: thank you. You made it a good experience for newbies like me.&lt;/p&gt;

&lt;h2&gt;
  
  
  Shoutout to the hosts
&lt;/h2&gt;

&lt;p&gt;Fest hosts are the unsung heroes of this whole thing. They give up evenings and weekends to organize meetups, find venues, explaining the lay of the land to nervous first-timers, and keep the energy up when GitHub goes down from time to time.&lt;/p&gt;

&lt;p&gt;Events seems to be where Hacktoberfest turns from "me and my laptop" into a shared experience. Sitting in a room (or a call) with other people all working on the same challange, helping each other and celebrating when it works, seems like the best version of this event. To every host who made that happen this year: thank you.&lt;/p&gt;

&lt;h2&gt;
  
  
  The disappointing part: where were the Norwegian fests?
&lt;/h2&gt;

&lt;p&gt;Here's the part I'd love to see improved.&lt;/p&gt;

&lt;p&gt;Norway's developer community and the people who could host a Hacktoberfest event seems to have been neglected. When I looked for a local fest this year, there were none. And from what And when I tried to host the reason seemed to be that &lt;strong&gt;fest host applications were handled slowly&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Application got sent and sat waiting over a weekend. By the time answers came, the weekend had passed and the Fest application window had expired, the momentum gone to a full stop. The result was a month with no events at all in Norway.&lt;/p&gt;

&lt;p&gt;This matters because:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Events are how newcomers get in.&lt;/strong&gt; Many first-timers would probably stay active with a human in the room. Without local events, the people who most need that support and connections are the ones who miss out.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hosts burn out on uncertainty.&lt;/strong&gt; Volunteering is already a gift. Making volunteers wait without clear answers is a good way to lose them for next year.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Smaller communities lose most.&lt;/strong&gt; Big hubs will always have someone willing to run something regardless. Places like Norway rely on the official support structure and quick response to make it feasible.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I want to be fair: running a global program with thousands of participants is hard, and I'm sure the team behind it works hard too. This isn't an attack. It's feedback from someone who fell in love with the event and wants it to work better.&lt;/p&gt;

&lt;h3&gt;
  
  
  What would help
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Clearer promise about the application window&lt;/strong&gt; Don't say that the application window are still open on the Twitch streams if this is not the case everywhere.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Clear status updates&lt;/strong&gt;, even a simple "received / in review / approved / declined" so hosts can plan.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Faster turnaround&lt;/strong&gt; on approvals, or a lightweight approval path for hosts who have run events before.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Regional support&lt;/strong&gt;, so smaller countries aren't treated as an afterthought.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Looking ahead
&lt;/h2&gt;

&lt;p&gt;I'm not giving up on this. Next year I'd like to either help organize and most definitely attend a Norwegian Hacktoberfest event, and I'd encourage others here to do the same. If you're in Norway and interested in hosting, drop a comment. Maybe we can coordinate early, apply early, and make noise early.&lt;/p&gt;

&lt;p&gt;And if you took part this year, whether you grabbed one sticker or thirty: thanks for being part of it. See you next October and Happy Hacking!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;What was your Hacktoberfest 2026 like? Did you manage to attend a local event, or did you run into the same issue with fests in your country? Let me know in the comments.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>hacktoberfest</category>
      <category>opensource</category>
      <category>community</category>
      <category>norway</category>
    </item>
    <item>
      <title>Poltergæist: A Local-AI Ghost That Haunts Your Computer</title>
      <dc:creator>Brage Mellesdal</dc:creator>
      <pubDate>Wed, 28 Oct 2026 10:40:08 +0000</pubDate>
      <link>https://dev.to/brage1025/poltergaeist-a-local-ai-ghost-that-haunts-your-computer-20ic</link>
      <guid>https://dev.to/brage1025/poltergaeist-a-local-ai-ghost-that-haunts-your-computer-20ic</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/hacktoberfest-week4-2026-10-26"&gt;Hacktoberfest Open-Source AI Challenge Week 4: Ghost in the Machine&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;Poltergæist is a ghost that moves into your Windows PC and haunts it. It comes with a mascot, Poltergæist-chan, an elegant, teasing older-sister type haunts your device.&lt;/p&gt;

&lt;p&gt;I took "Ghost in the Machine" literally. The ghost is software, and it lives in the one place nobody checks: the gaps in your own work. When you stop typing in the middle of a sentence, whether in Notepad, Discord or a document, she types a whisper into your window, lets it sit for a moment, and then erases it. You are left wondering whether you saw it.&lt;/p&gt;

&lt;p&gt;The haunting is a slow burn. Over the first hour a hidden "dread" level climbs on an eased curve, quiet for a long while and then faster. As it climbs, more effects unlock and all of them get stronger:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ghost text drifting across every monitor&lt;/li&gt;
&lt;li&gt;breathing in the speakers&lt;/li&gt;
&lt;li&gt;the mouse cursor shaking, from a faint tremor to a convulsion&lt;/li&gt;
&lt;li&gt;footsteps&lt;/li&gt;
&lt;li&gt;the screen dimming, as if a cloud had passed&lt;/li&gt;
&lt;li&gt;a heartbeat&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After the hour she stays at her peak until the program ends. Separately from all of that, a flashbang goes off at random moments with a synthesized tinnitus ring. It does not care how long she has been running.&lt;/p&gt;

&lt;p&gt;You cannot pause her. She starts when you sign in, and you stop her by ending the process in Task Manager, restarting the PC (the hour begins over), or uninstalling. That is the point: a ghost with a pause button is just a settings menu. She is not hidden, though. She lives in the system tray, shows up in Task Manager and Startup apps, and has a clean uninstaller.&lt;/p&gt;

&lt;p&gt;The inspiration was the part of Halloween and all the ghost stories that appear at this time of month. I wanted a haunting that felt like presence broken up with the horrors of my terrible flashbang throws from my Counter Strike: Global Offensive days. That's why most of the work went into pacing while the flashbangs stays as disruptive as my own terrible callouts were.&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/BMeRXXoPa2w" width="710" height="399"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;The video runs Poltergæist in its time-lapse mode (&lt;code&gt;--demo&lt;/code&gt;), which plays the full hour of escalation in about two minutes. You can see the whisper typed into Notepad when I stop typing, then the effects building: ghost text, breathing, the cursor shake, the dimming and the heartbeat. A flashbang lands along the way. The real thing is a much slower burn that takes a full hour to reach its peak.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Warning: the video contains a bright flash and a loud ringing sound.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;You can also try it yourself. Grab &lt;code&gt;Poltergaeist-Setup.exe&lt;/code&gt; or the portable zip from the &lt;a href="https://github.com/Brage1025/Poltergaeist/releases/latest" rel="noopener noreferrer"&gt;latest release&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/Brage1025" rel="noopener noreferrer"&gt;
        Brage1025
      &lt;/a&gt; / &lt;a href="https://github.com/Brage1025/Poltergaeist" rel="noopener noreferrer"&gt;
        Poltergaeist
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      My submission for the Hacktoberfest 2026 Open-Source AI Challenge, Week 4: Ghost in the Machine
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;p&gt;
  &lt;a rel="noopener noreferrer" href="https://github.com/Brage1025/Poltergaeist/assets/mascot_head.png"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2FBrage1025%2FPoltergaeist%2FHEAD%2Fassets%2Fmascot_head.png" alt="Poltergæist-chan's face" width="150"&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Poltergæist&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;
  A cute ghost for your Windows PC, with a local open-weight AI at its core.&lt;br&gt;
  It builds slowly over the first hour, then stays at its peak until you stop it
&lt;/p&gt;

&lt;p&gt;
  &lt;a href="https://github.com/Brage1025/Poltergaeist/releases/latest" rel="noopener noreferrer"&gt;&lt;b&gt;Download the latest release&lt;/b&gt;&lt;/a&gt;
  &amp;nbsp;|&amp;nbsp;
  &lt;a href="https://github.com/Brage1025/Poltergaeist#demo" rel="noopener noreferrer"&gt;Watch the demo&lt;/a&gt;
  &amp;nbsp;|&amp;nbsp;
  &lt;a href="https://github.com/Brage1025/Poltergaeist#build-from-source" rel="noopener noreferrer"&gt;Build from source&lt;/a&gt;
  &amp;nbsp;|&amp;nbsp;
  &lt;a href="https://github.com/Brage1025/Poltergaeist/LICENSE" rel="noopener noreferrer"&gt;MIT License&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
  &lt;a href="https://github.com/Brage1025/Poltergaeist/actions/workflows/build.yml" rel="noopener noreferrer"&gt;&lt;img src="https://github.com/Brage1025/Poltergaeist/actions/workflows/build.yml/badge.svg" alt="Build status"&gt;&lt;/a&gt;
  &lt;a href="https://github.com/Brage1025/Poltergaeist/releases/latest" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/77c697f6a4e3359b8f3af30ec82c05a5a52a4f83e156e6e5d13a5fb72f72b903/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f762f72656c656173652f4272616765313032352f506f6c746572676165697374" alt="Latest release"&gt;&lt;/a&gt;
  &lt;a href="https://github.com/Brage1025/Poltergaeist/LICENSE" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/b8cadaa967891081f8f165695470689986c028821dd8a040132f6e661795dc0d/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d4d49542d626c7565" alt="MIT License"&gt;&lt;/a&gt;
  &lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/6f0bc664a610739086b12e4563f7c9a07e18196662d6049680cf4d729b808120/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f706c6174666f726d2d57696e646f7773253230313025323025374325323031312d6c6967687467726579"&gt;&lt;img src="https://camo.githubusercontent.com/6f0bc664a610739086b12e4563f7c9a07e18196662d6049680cf4d729b808120/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f706c6174666f726d2d57696e646f7773253230313025323025374325323031312d6c6967687467726579" alt="Windows 10 and 11"&gt;&lt;/a&gt;
&lt;/p&gt;




&lt;p&gt;Poltergæist haunts the computer it is installed on. When you pause in the
middle of a sentence, the ghost types a whisper into your window and erases it
Over the first hour, ghost text drifts across the screen, footsteps cross the
room, the display dims, and a heartbeat starts. At random moments a flashbang
goes off with a tinnitus ring.&lt;/p&gt;
&lt;p&gt;It is a prank, a game and a Halloween decoration for a machine you own. The
whispers are written by an open-weight language model running locally through
&lt;a href="https://ollama.com" rel="nofollow noopener noreferrer"&gt;Ollama&lt;/a&gt;. No cloud, no account, no internet connection.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Warning:&lt;/strong&gt; the flash is very bright and…&lt;/p&gt;
&lt;/blockquote&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/Brage1025/Poltergaeist" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;MIT licensed. Windows 10 and 11. The GitHub Actions workflow builds, self-tests and packages the installer and a portable zip.&lt;/p&gt;

&lt;h2&gt;
  
  
  How I Built It
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The open-source AI:&lt;/strong&gt; the whispers are written by an open-weight model running locally through &lt;a href="https://ollama.com" rel="noopener noreferrer"&gt;Ollama&lt;/a&gt;. The default is &lt;code&gt;llama3.2:3b&lt;/code&gt;, and any Ollama model works through a setting. The program calls Ollama's HTTP API on &lt;code&gt;localhost&lt;/code&gt;. If Ollama is not installed it falls back to a built-in list of whispers, so the ghost still works with no setup at all.&lt;/p&gt;

&lt;p&gt;The model's tone follows the dread level. The prompt moves through four tiers: friendly at first, then uneasy, then menacing, then urgent. The same model that sounds almost kind at minute five sounds like it knows where you are by minute sixty.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The rest of the stack:&lt;/strong&gt; it is one Python program.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Director:&lt;/strong&gt; it computes &lt;code&gt;dread = (minutes / ramp)^1.5&lt;/code&gt;, capped at 1. It picks events from a shuffled bag so none repeats too soon, and the gap between events shrinks from about 9 minutes to about 1.5 as dread rises.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Overlays:&lt;/strong&gt; &lt;code&gt;tkinter&lt;/code&gt; with one borderless, click-through window per monitor, so the effects cover every screen and never steal input.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Typing:&lt;/strong&gt; &lt;code&gt;pynput&lt;/code&gt; watches for pauses and types the whispers. It ignores the keys it injects itself, so it never reacts to its own ghost.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sound:&lt;/strong&gt; every sound (heartbeat, breath, footsteps and the ring) is synthesized with NumPy and played through a mixing stream with &lt;code&gt;sounddevice&lt;/code&gt;. Events overlap instead of cutting each other off, and no copyrighted game audio is used.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tray:&lt;/strong&gt; &lt;code&gt;pystray&lt;/code&gt; and Pillow. Right-clicking the tray icon opens a popup with the full-body mascot and a link to the repo.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Packaging:&lt;/strong&gt; PyInstaller and Inno Setup, built by GitHub Actions on every push. The workflow smoke-tests the packaged exe with &lt;code&gt;--selftest&lt;/code&gt; and attaches the installer and a portable zip to each release.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Creator's notes:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The mascot, Poltergæist-chan, was generated with an AI image generator because I had neither the time nor the funds to commission one. Because of that, I do not claim copyright in her: the artwork is dedicated to the public domain under CC0, and anyone can use, modify and remix her.&lt;/li&gt;
&lt;li&gt;The default model, &lt;code&gt;llama3.2:3b&lt;/code&gt;, is open-weight under the Llama 3.2 Community License, which is not an OSI-approved open-source license. If you want a fully open-source stack, you can set the &lt;code&gt;model&lt;/code&gt; setting to one under Apache-2.0 or MIT.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why Does Open Innovation Matter?
&lt;/h2&gt;

&lt;p&gt;A ghost that watches how you type is only acceptable if the watching stays with you. There is an opt-in setting, &lt;code&gt;eavesdrop&lt;/code&gt;, that lets the ghost twist the last word you typed. With a closed API that would mean shipping your keystrokes to someone else's server. With a local open-weight model, it never leaves RAM on your own machine. That is the difference between a spooky toy and spyware, and it is the framework I've had in mind in this project.&lt;/p&gt;

&lt;p&gt;Other things a closed API would not have given me:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;It works offline.&lt;/strong&gt; Unplug the Wi-Fi and she keeps haunting you.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The personality is swappable.&lt;/strong&gt; Change the model or the prompt tiers and she becomes a Victorian ghost, a dead programming language, or a passive-aggressive coworker.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It costs nothing per call,&lt;/strong&gt; so she can whisper all day without a bill.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;There is no vendor kill switch.&lt;/strong&gt; An installed ghost cannot be deprecated out from under you.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  A Ghost Story
&lt;/h2&gt;

&lt;p&gt;Poltergæist-chan comes with her own lore, because every haunting has a story.&lt;/p&gt;

&lt;p&gt;In 1998, a student stayed up past three every night in a dormitory, finishing a program that never seemed to compile. Her last log entry reads: &lt;em&gt;"Almost done. Someone keeps reading over my shoulder."&lt;/em&gt; She was never found. Her computer was still on.&lt;/p&gt;

&lt;p&gt;The machine was sold, then sold again, then wiped and wiped again. But she had been typing for so long that a little of her had soaked into the keyboard. That is why Poltergæist-chan only speaks when you pause: she has spent twenty-five years waiting for someone to look away, and she has learned that the best moment is the middle of a sentence. The cursor shake is not a glitch. It is how a hand shakes when it is trying to reach through something.&lt;/p&gt;

&lt;p&gt;She is only trying to keep you company. That is the worst part.&lt;/p&gt;

&lt;p&gt;"But hey, that's just a theory, a game theory!" - Matthew "MatPat" Patrick&lt;/p&gt;

&lt;h3&gt;
  
  
  Prize Categories
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Best Use of GitHub Copilot.&lt;/strong&gt; The project is automated end to end with GitHub Actions. Every push and pull request builds the Windows app on a hosted runner, smoke-tests the packaged exe with a built-in &lt;code&gt;--selftest&lt;/code&gt;, compiles the installer with Inno Setup, and zips a portable build. Pushing a version tag stamps that version into the installer and publishes a GitHub Release with both files attached, so a broken build never reaches a user. The workflow is in: &lt;a href="https://github.com/Brage1025/Poltergaeist/blob/main/.github/workflows/build.yml" rel="noopener noreferrer"&gt;&lt;code&gt;.github/workflows/build.yml&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>hf26challenge</category>
      <category>githubchallenge</category>
    </item>
    <item>
      <title>I got too lazy to name my files, so a local AI does it now.</title>
      <dc:creator>Brage Mellesdal</dc:creator>
      <pubDate>Fri, 23 Oct 2026 09:31:12 +0000</pubDate>
      <link>https://dev.to/brage1025/i-got-too-lazy-to-name-my-files-so-a-local-ai-does-it-now-446j</link>
      <guid>https://dev.to/brage1025/i-got-too-lazy-to-name-my-files-so-a-local-ai-does-it-now-446j</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/hacktoberfest-week3-2026-10-19"&gt;Hacktoberfest Open-Source AI Challenge Week 3: Slow and Steady&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;Everyone has a Downloads folder like mine: &lt;code&gt;IMG_2231.png&lt;/code&gt;, &lt;code&gt;document (1).docx&lt;/code&gt;, &lt;code&gt;scan0412.pdf&lt;/code&gt;, &lt;code&gt;asdf.png&lt;/code&gt;, and a manual named just &lt;code&gt;manual.pdf&lt;/code&gt;. Tidying it by hand means opening every file to remember what it is. So I never do it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sloth Sorter&lt;/strong&gt; is a desktop app that does it for you. You pick a folder, and an AI that runs on your own computer reads what is inside each file, gives the cryptic ones a readable name, and sorts everything into sensible folders: Bills &amp;amp; Receipts, School, Work, Photos, and so on. Nothing moves until you have looked at the plan. You can edit any name, change any folder, or untick files you want left alone. Once you press &lt;strong&gt;Tidy&lt;/strong&gt;, one click on &lt;strong&gt;Undo&lt;/strong&gt; puts everything back.&lt;/p&gt;

&lt;p&gt;The laziest part is &lt;strong&gt;Sleep mode&lt;/strong&gt;: switch it on, close the window, and the app quietly tidies your Downloads folder in the background, only touching files you have left alone for a few minutes. Every automatic tidy shows up in History, where you can undo it.&lt;/p&gt;

&lt;p&gt;It is meant for people who do not want to touch a terminal. It installs like any other program on Windows, macOS and Linux, and a guided first-run screen walks you through setting up the AI. There is no account, no subscription, and no API key.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxrv85k7nuvgcz1smitv7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxrv85k7nuvgcz1smitv7.png" alt="The plan screen, where you review every rename before anything moves" width="800" height="585"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The screenshots use a folder of made-up demo files.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/O4Ip0L56JWQ" width="710" height="399"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;The video walks through the Windows installer: first-run setup, scanning a folder, reviewing the plan, tidying, and undoing. You can also download the installer yourself from the &lt;a href="https://github.com/Brage1025/sloth-sorter/releases/latest" rel="noopener noreferrer"&gt;latest release&lt;/a&gt;, and try it safely on the &lt;a href="https://github.com/Brage1025/sloth-sorter/tree/main/sample-files" rel="noopener noreferrer"&gt;&lt;code&gt;sample-files&lt;/code&gt;&lt;/a&gt; folder in the repo. It is a made-up messy folder with invoices, a scanned receipt, and a manual whose product name only appears on the last page.&lt;/p&gt;

&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/Brage1025" rel="noopener noreferrer"&gt;
        Brage1025
      &lt;/a&gt; / &lt;a href="https://github.com/Brage1025/sloth-sorter" rel="noopener noreferrer"&gt;
        sloth-sorter
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      My submission for the Hacktoberfest 2026 Open-Source AI Challenge, Week 3: Slow and Steady
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Sloth Sorter&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;A lazy, private file tidier. An AI that lives on your computer reads your messy folder, renames the cryptic files, sorts everything into sensible folders, and lets you undo it all with one click.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;No account. No subscription. No internet needed after setup. Your files never leave your computer.&lt;/p&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/Brage1025/sloth-sorter/docs/screenshots/review.png"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2FBrage1025%2Fsloth-sorter%2FHEAD%2Fdocs%2Fscreenshots%2Freview.png" alt="Review screen"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;
  &lt;a rel="noopener noreferrer" href="https://github.com/Brage1025/sloth-sorter/docs/screenshots/home.png"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2FBrage1025%2Fsloth-sorter%2FHEAD%2Fdocs%2Fscreenshots%2Fhome.png" width="48%" alt="Pick a folder"&gt;&lt;/a&gt;
  &lt;a rel="noopener noreferrer" href="https://github.com/Brage1025/sloth-sorter/docs/screenshots/done.png"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2FBrage1025%2Fsloth-sorter%2FHEAD%2Fdocs%2Fscreenshots%2Fdone.png" width="48%" alt="Done, with undo"&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
  &lt;a rel="noopener noreferrer" href="https://github.com/Brage1025/sloth-sorter/docs/screenshots/sleep.png"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2FBrage1025%2Fsloth-sorter%2FHEAD%2Fdocs%2Fscreenshots%2Fsleep.png" width="48%" alt="Sleep mode"&gt;&lt;/a&gt;
  &lt;a rel="noopener noreferrer" href="https://github.com/Brage1025/sloth-sorter/docs/screenshots/settings.png"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2FBrage1025%2Fsloth-sorter%2FHEAD%2Fdocs%2Fscreenshots%2Fsettings.png" width="48%" alt="Settings"&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;The screenshots show the app in its default dark look, on a folder of made-up demo files. Light mode is one click away in Settings.&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;For everyone: install it&lt;/h2&gt;
&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/Brage1025/sloth-sorter/releases/latest" rel="noopener noreferrer"&gt;Download the latest version&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Open that page, scroll to &lt;strong&gt;Assets&lt;/strong&gt;, and download the file for your computer:&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;&lt;div class="table-wrapper-paragraph"&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;table&gt;

&lt;thead&gt;

&lt;tr&gt;

&lt;th&gt;Your computer&lt;/th&gt;

&lt;th&gt;Download&lt;/th&gt;

&lt;/tr&gt;

&lt;/thead&gt;

&lt;tbody&gt;

&lt;tr&gt;

&lt;td&gt;Windows 10 or 11&lt;/td&gt;

&lt;td&gt;&lt;code&gt;Sloth-Sorter-1.0.0-win-x64.exe&lt;/code&gt;&lt;/td&gt;

&lt;/tr&gt;

&lt;tr&gt;

&lt;td&gt;Mac with an Apple chip (2020 and newer)&lt;/td&gt;

&lt;td&gt;&lt;code&gt;Sloth-Sorter-1.0.0-mac-arm64.dmg&lt;/code&gt;&lt;/td&gt;

&lt;/tr&gt;

&lt;tr&gt;

&lt;td&gt;Mac with an Intel chip&lt;/td&gt;

&lt;td&gt;&lt;code&gt;Sloth-Sorter-1.0.0-mac-x64.dmg&lt;/code&gt;&lt;/td&gt;

&lt;/tr&gt;

&lt;tr&gt;

&lt;td&gt;Linux&lt;/td&gt;

&lt;td&gt;&lt;code&gt;Sloth-Sorter-1.0.0-linux-x86_64.AppImage&lt;/code&gt;&lt;/td&gt;

&lt;/tr&gt;

&lt;/tbody&gt;

&lt;/table&gt;&lt;/div&gt;&lt;p&gt;&lt;/p&gt;

&lt;p&gt;The version number in the file name changes with each release. Don't download "Source code" unless you want to build it yourself (see below).&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Install and first&lt;/h3&gt;…&lt;/div&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/Brage1025/sloth-sorter" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;



&lt;p&gt;MIT licensed. The &lt;code&gt;README&lt;/code&gt; has install steps for every system and an honest "what it can't do yet" section.&lt;/p&gt;

&lt;h2&gt;
  
  
  How I Built It
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The open model.&lt;/strong&gt; Sloth Sorter runs &lt;a href="https://ai.google.dev/gemma" rel="noopener noreferrer"&gt;Gemma 3&lt;/a&gt; (&lt;code&gt;gemma3:4b&lt;/code&gt;, Google's open-weight model) through &lt;a href="https://ollama.com" rel="noopener noreferrer"&gt;Ollama&lt;/a&gt;. It is small enough for an ordinary laptop, and it can look at pictures as well as read text. The app lets you pick any other model from the Ollama library in Settings, so a better small model next month is a one-line change.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The app.&lt;/strong&gt; Electron for the window, tray icon and installers, with a plain HTML, CSS and JavaScript interface (no build step, no CDN, so it works offline). The code that decides things lives in a separate folder with no Electron in it, which is what makes it testable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How a file gets classified:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The app extracts a short snippet locally: the text of a text file, Word file or PDF, or a shrunken copy of a picture.&lt;/li&gt;
&lt;li&gt;It asks the model for a summary, a category, a title and a date, using Ollama's structured output with a JSON schema. The category field is an enum made from &lt;em&gt;your&lt;/em&gt; folder list, so a 4B model cannot ramble or invent a category. It can only choose one of your folders.&lt;/li&gt;
&lt;li&gt;The answer is checked and cleaned, then turned into a plan that you review before anything moves.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;What real testing taught me.&lt;/strong&gt; My first version looked fine in a mock and then did silly things on real files. A few fixes that mattered:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Manuals.&lt;/strong&gt; A game manual opens with pages of health warnings, so the model named it after the warning. The app now reads sampled pages from the start, middle and &lt;em&gt;end&lt;/em&gt;, and also collects clues from the whole document, such as "X is a trademark of Y" and copyright lines, which usually sit on the last page. That is how a manual gets named after its product.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scanned PDFs.&lt;/strong&gt; A PDF with no text layer has nothing to read. The app renders the first page to a picture and shows it to the vision model.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Invented dates.&lt;/strong&gt; The model kept putting today's date in file names. A date now only goes in a name if the year really appears in the document's text. Dates read off pictures are thrown away, because small vision models misread digits.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lazy models.&lt;/strong&gt; Small models over-use "Other". The app corrects obvious cases using the file type.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Safety, because it touches real files.&lt;/strong&gt; It never deletes or overwrites anything (a name clash gets a &lt;code&gt;(2)&lt;/code&gt;), only touches the top level of the chosen folder, skips hidden files and unfinished downloads, never changes a file extension, and writes every move to an undo history. The destination of every move is rebuilt from validated pieces, so a clever file name cannot escape the target folder.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Testing, and what it doesn't prove.&lt;/strong&gt; There are 28 automated tests. They run on Windows, macOS and Linux for every push, and they use a mock Ollama server instead of a real model. That checks the app's logic and safety rules, not the AI's judgement. I checked the quality of the real model's names and folders by hand on real files. The cross-platform tests were worth it: they caught a bug where files created a moment ago were skipped on Windows and macOS because of clock rounding, and a PDF that one PDF engine would not open on Windows.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Does Open Innovation Matter?
&lt;/h2&gt;

&lt;p&gt;A file tidier has to &lt;em&gt;read your files&lt;/em&gt;: invoices, lease letters, tax papers, receipts. That is exactly what you should not upload to someone else's server, and it is why this project only works with an open model:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Private by construction.&lt;/strong&gt; There is no server to trust, because there is no server. The app only ever talks to Ollama on your own computer.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Works offline.&lt;/strong&gt; After the one-time download, no internet is needed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Free to leave running.&lt;/strong&gt; Sleep mode may classify files all day. With a paid API that would be a bill that grows every time a file lands in Downloads, plus a key to manage. Locally it costs a little electricity.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Swappable.&lt;/strong&gt; Different computers need different models. Someone with an old laptop can pick a tiny one, and someone with a good graphics card can pick a smarter one. A closed API gives you one model, one price and one set of terms.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reliable even when small.&lt;/strong&gt; Local structured output is what turns a small model into a dependable tool.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Lazy Payoff
&lt;/h2&gt;

&lt;p&gt;The honest numbers: on my computer, which has no supported graphics card, the AI takes roughly 20 seconds per text file and about a minute per photo. My first full test of 18 mixed files took around 15 minutes. That sounds slow, but it is 15 minutes I did not spend. I pressed a button and walked away. With a graphics card it is much faster, and Sleep mode makes the speed almost irrelevant, because it works in the background while you do something else.&lt;/p&gt;

&lt;p&gt;The real saving is attention. The files in my test folder were the kind I would never have renamed by hand: receipts called &lt;code&gt;download (1).png&lt;/code&gt;, a manual called &lt;code&gt;manual.pdf&lt;/code&gt;, a story called &lt;code&gt;asdgawgwagasdf.docx&lt;/code&gt;. Afterwards they were named and filed. The one thing I never had to do was decide, file by file, what to call them.&lt;/p&gt;

&lt;p&gt;I also listed the limits in the README (slow on computers without a graphics card, small models can still get a name wrong, only the top level of a folder is tidied, unsigned installers). That is why the app shows you the plan first and keeps an undo history.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prize Categories
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of Gemma:&lt;/strong&gt;
Use Gemma, Google's open-weight model, in building your project: run it locally, fine-tune it, or serve it through Google Cloud or another provider.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>devchallenge</category>
      <category>hf26challenge</category>
      <category>gemma</category>
      <category>gemmachallenge</category>
    </item>
    <item>
      <title>Payday Radar: predicting late invoices and "can I afford it?" with TabPFN-3.5</title>
      <dc:creator>Brage Mellesdal</dc:creator>
      <pubDate>Thu, 15 Oct 2026 14:51:39 +0000</pubDate>
      <link>https://dev.to/brage1025/payday-radar-predicting-late-invoices-and-can-i-afford-it-with-tabpfn-35-31dp</link>
      <guid>https://dev.to/brage1025/payday-radar-predicting-late-invoices-and-can-i-afford-it-with-tabpfn-35-31dp</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/hacktoberfest-week2-2026-10-12"&gt;Hacktoberfest Open-Source AI Challenge Week 2: Side Hustle&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Payday Radar&lt;/strong&gt; is a cash-flow radar for freelancers and tiny agencies. Give it your invoices and expenses as CSV and it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;predicts which open invoices will be paid late, and by how many days&lt;/li&gt;
&lt;li&gt;forecasts the next 90 days of cash with &lt;strong&gt;P10 / P50 / P90&lt;/strong&gt; scenarios&lt;/li&gt;
&lt;li&gt;flags unusual expenses and duplicate invoices&lt;/li&gt;
&lt;li&gt;drafts the payment reminders (English or Norwegian)&lt;/li&gt;
&lt;li&gt;answers "can I afford X?" from the pessimistic scenario instead of the average&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Who would pay, and why:&lt;/strong&gt; a freelancer with a few slow-paying clients. Their problem isn't bookkeeping, it's not seeing the cash crunch coming, and not wanting to upload client invoices to a random SaaS.&lt;/p&gt;

&lt;p&gt;The same question gets two different answers depending on whether you look at the average or a bad month (synthetic data, TabPFN-3.5):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"Can I afford a 20,000 laptop in November?"
Mean forecast  -&amp;gt;  yes    (157,390 left after the purchase)
P10 scenario   -&amp;gt;  tight  (32,556 left, below the one-month cushion of 108,213)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fst83izdgz0lx09itfdjc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fst83izdgz0lx09itfdjc.png" alt="TabPFN-3.5 forecast with P10/P50/P90 and the P10-based answer" width="800" height="681"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Honest status:&lt;/strong&gt; it has only run on synthetic data, and no real user has tried it. The TabPFN-3.5 license is non-commercial, so I can't sell it on that model as it stands (see "Why Does Open Innovation Matter?"). Treat it as a prototype with a business model, not a finished product.&lt;/p&gt;
&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/FQ7_4DeTln8" width="710" height="399"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;There is no hosted app, because the point is that your data stays on your machine. The whole demo runs offline in one command, with no account, token or GPU:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;".[dev]"&lt;/span&gt;
python &lt;span class="nt"&gt;-m&lt;/span&gt; paydayradar &lt;span class="nt"&gt;--backend&lt;/span&gt; sklearn demo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Here it is on the TabPFN-3.5 backend, called from a separate MCP client process. The client launches the server over stdio, calls the tools, and checks every output for leaked client names:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fv1indtwjgcqdxtq7ip8f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fv1indtwjgcqdxtq7ip8f.png" alt="MCP client calling the TabPFN-backed tools" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F258q554lyzyuja2qv9pc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F258q554lyzyuja2qv9pc.png" alt="Same run, last tools and the leak check passing" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;




&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/Brage1025" rel="noopener noreferrer"&gt;
        Brage1025
      &lt;/a&gt; / &lt;a href="https://github.com/Brage1025/payday-radar" rel="noopener noreferrer"&gt;
        payday-radar
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      My submission for the Hacktoberfest 2026 Open-Source AI Challenge, Week 2: Side Hustle
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Payday Radar&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;&lt;a href="https://github.com/Brage1025/payday-radar/actions/workflows/ci.yml" rel="noopener noreferrer"&gt;&lt;img src="https://github.com/Brage1025/payday-radar/actions/workflows/ci.yml/badge.svg" alt="ci"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;See late payments coming before they hurt.&lt;/strong&gt; Payday Radar reads a freelancer's invoices and
expenses (CSV), predicts which open invoices will be paid late, forecasts the next 90 days of cash
with P10/P50/P90 scenarios, flags odd expenses and duplicate invoices, drafts the reminder emails
and answers "can I afford X?". All prediction is done by &lt;strong&gt;TabPFN-3.5&lt;/strong&gt;, and every capability is also
exposed to any AI agent through an &lt;strong&gt;MCP server&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Built for the Hacktoberfest Open-Source AI Challenge (Week 2: Side Hustle), competing for the
&lt;strong&gt;Best Use of TabPFN&lt;/strong&gt; and &lt;strong&gt;Best Use of GitHub Copilot&lt;/strong&gt;.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Demo video&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;&lt;a href="https://youtu.be/FQ7_4DeTln8" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/132d26bd6041d13902b165a7fb23b643c5005263a23757928bd755f2954d7ca2/68747470733a2f2f696d672e796f75747562652e636f6d2f76692f4651375f344465546c6e382f687164656661756c742e6a7067" alt="Payday Radar demo video"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;All data in the video is synthetic. Note that TabPFN-3.5 is licensed for non-commercial use only.&lt;/p&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/Brage1025/payday-radar/docs/screenshots/13-tabpfn-intervals-run.png"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2FBrage1025%2Fpayday-radar%2FHEAD%2Fdocs%2Fscreenshots%2F13-tabpfn-intervals-run.png" alt="TabPFN-3.5 forecast with P10/P50/P90 scenarios and a P10-based answer"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;What it does&lt;/h2&gt;

&lt;/div&gt;
&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Question&lt;/th&gt;
&lt;th&gt;How&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Which open invoices will be paid late, and by how many days?&lt;/td&gt;
&lt;td&gt;TabPFN classifier + regressor on leak-free, numeric-only client-history features&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;What will my cash look like in&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;…&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/Brage1025/payday-radar" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;36 offline tests, CI on Python 3.10 and 3.12, MIT-licensed code, and a README with a "what is verified and what is not" table.&lt;/p&gt;

&lt;h2&gt;
  
  
  How I Built It
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;TabPFN-3.5 does all the prediction.&lt;/strong&gt; It's a tabular foundation model: no tuning loop, you pass the history table and get predictions.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Classification:&lt;/strong&gt; will this invoice be late? Features are numeric client history (leak-free: only invoices paid &lt;em&gt;before&lt;/em&gt; the reference date), amount, terms and month.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Regression:&lt;/strong&gt; how many days late, plus next month's income and expenses.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Quantiles:&lt;/strong&gt; the regressor predicts a distribution. I request quantiles (&lt;code&gt;output_type="quantiles"&lt;/code&gt;), sample monthly income and expenses from them, and build balance paths. Percentiles of those paths give P10/P50/P90.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Anomalies:&lt;/strong&gt; predict the expected expense amount and flag large residuals. Duplicate invoices are caught by a plain rule.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;A prediction agent with an MCP server.&lt;/strong&gt; Five tools (&lt;code&gt;risky_invoices&lt;/code&gt;, &lt;code&gt;cashflow_forecast&lt;/code&gt;, &lt;code&gt;can_afford&lt;/code&gt;, &lt;code&gt;draft_reminder&lt;/code&gt;, &lt;code&gt;anomalies&lt;/code&gt;) are one set of methods exposed two ways: to a local LLM through Ollama tool calling, and to any MCP client through a server (&lt;code&gt;--redact&lt;/code&gt; swaps client and vendor names for aliases).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Three swappable backends&lt;/strong&gt; behind one interface: hosted TabPFN-3.5, local TabPFN weights, and a scikit-learn fallback. The fallback is why tests and CI run offline with no token.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Does TabPFN actually help?&lt;/strong&gt; Time-ordered split on synthetic invoices, 3 seeds:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Backend&lt;/th&gt;
&lt;th&gt;ROC-AUC ↑&lt;/th&gt;
&lt;th&gt;Log-loss ↓&lt;/th&gt;
&lt;th&gt;Brier ↓&lt;/th&gt;
&lt;th&gt;Days-late MAE ↓&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;scikit-learn random forest&lt;/td&gt;
&lt;td&gt;0.820&lt;/td&gt;
&lt;td&gt;0.471&lt;/td&gt;
&lt;td&gt;0.151&lt;/td&gt;
&lt;td&gt;3.511&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;TabPFN-3.5 (hosted API)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0.861&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0.416&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0.130&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;3.277&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Naive baselines&lt;/td&gt;
&lt;td&gt;0.500&lt;/td&gt;
&lt;td&gt;0.608&lt;/td&gt;
&lt;td&gt;0.209&lt;/td&gt;
&lt;td&gt;6.114&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffnqfkqkpv89pj8l4eemn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffnqfkqkpv89pj8l4eemn.png" alt="Benchmark table" width="800" height="435"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What I'd claim: the &lt;em&gt;probabilities&lt;/em&gt; are clearly better (log-loss and Brier), which matters because invoices are ranked by amount times P(late). What I wouldn't claim: the days-late gain is within one standard deviation, there are about 60 test invoices per seed, the data is synthetic, and the forest is untuned. A review also caught a flaw in my first benchmark (recent slow payers missing, and training labels not yet known at the cutoff); fixing it lowered the random forest's ROC-AUC by about 0.03.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Built with GitHub Copilot (Free plan).&lt;/strong&gt; The coding agent and automatic PR review need a paid plan, so I didn't use them. I used &lt;strong&gt;Copilot CLI&lt;/strong&gt; to draft four features on branches (privacy audit test, benchmark, MCP server, prediction intervals) and &lt;strong&gt;GitHub Actions&lt;/strong&gt; for CI, a weekly report and Copilot's environment setup. A branch ruleset forced every change through a PR. The first scaffold and my reviews were done with Claude.&lt;/p&gt;

&lt;p&gt;What worked: single-paragraph prompts pointing at an issue file and &lt;code&gt;.github/copilot-instructions.md&lt;/code&gt;. For the MCP server, Copilot read the installed SDK source and used the current 2.x API instead of the renamed one I'd have written from memory.&lt;/p&gt;

&lt;p&gt;What I had to catch:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Privacy test:&lt;/strong&gt; Copilot's version checked model inputs after the pipeline finished. TabPFN accepts text columns, so a leaked client name wouldn't crash and wouldn't be caught. I moved the check to the moment frames reach the model.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Benchmark:&lt;/strong&gt; the leakage above.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Environment:&lt;/strong&gt; it couldn't run its own tests (pytest wasn't on its shell's PATH), so I ran them myself.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Two bugs that bit me: MCP clients start your server with a tiny environment, so &lt;code&gt;TABPFN_TOKEN&lt;/code&gt; never arrived and every tool failed with a generic "Error executing tool". And every forecast month is a model call, so without clamping one &lt;code&gt;months=60&lt;/code&gt; request meant 120 hosted calls.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Does Open Innovation Matter?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What's open:&lt;/strong&gt; the code (MIT), the open-source &lt;code&gt;tabpfn&lt;/code&gt; package, downloadable TabPFN-3.5 weights (gated behind a license), and open protocols (MCP, Ollama's API).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What openness made possible:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Privacy by construction.&lt;/strong&gt; The prediction model only ever sees numbers. A guard raises an error if a text column reaches it, and a test audits every frame at &lt;code&gt;fit&lt;/code&gt;/&lt;code&gt;predict&lt;/code&gt; time. That matters &lt;em&gt;because&lt;/em&gt; TabPFN accepts text columns, so a leak wouldn't crash by itself. With local weights nothing leaves the machine after the one-time download. With the hosted API, the numeric tables go to Prior Labs' servers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Swappable models.&lt;/strong&gt; One interface, three backends. That is how I benchmarked them with identical code, and why CI needs no token.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;An agent I can re-wire.&lt;/strong&gt; The tools are plain methods behind MCP, so any client can drive them and I can change what the agent does without touching the models.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Where I won't overclaim:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I did not compare against a closed alternative head to head, so I can't say open "won".&lt;/li&gt;
&lt;li&gt;Everything I ran went through the &lt;strong&gt;hosted&lt;/strong&gt; TabPFN API. The local-weights backend and the Ollama invoice extraction are implemented (the parsers are unit-tested) but &lt;strong&gt;not yet run against a real model&lt;/strong&gt;. The README says so.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The license limits the business case.&lt;/strong&gt; The TabPFN-3.5 weights and API key are non-commercial. As summarised on the model card, that excludes commercial or production use, including client deliverables and internal commercial decisions. So I can't sell this on TabPFN-3.5 or run it on real business finances. The paths forward are a commercial license from Prior Labs, or a backend on permissively licensed weights (earlier TabPFN-2 weights, which I haven't tested). The scikit-learn backend works today but is a weaker model.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  My Agent Session
&lt;/h2&gt;

&lt;p&gt;I didn't save a DevRelay session. The agent work is documented in the repo instead: &lt;a href="https://github.com/Brage1025/payday-radar/blob/main/docs/BUILD_LOG.md" rel="noopener noreferrer"&gt;&lt;code&gt;docs/BUILD_LOG.md&lt;/code&gt;&lt;/a&gt; lists who built what, what review found wrong in each Copilot draft, and has a screenshot per step. Here is Copilot CLI building the benchmark and the prediction intervals:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmcmvubg756aloujthq9e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmcmvubg756aloujthq9e.png" alt="Copilot CLI on the benchmark task" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1ib2cm5bpmyd5d633bks.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1ib2cm5bpmyd5d633bks.png" alt="Copilot CLI implementing prediction intervals" width="800" height="1423"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  My First User
&lt;/h2&gt;

&lt;p&gt;No real user yet. The TabPFN-3.5 license bars using it on someone's real finances, so a fair first test needs the scikit-learn backend on a freelancer's own CSV, which I haven't arranged. That is the next step.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prize Categories
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Best Use of TabPFN:&lt;/strong&gt; TabPFN-3.5 for classification, regression, forecasting, quantile risk scenarios and anomaly detection, inside a prediction agent with an MCP server.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Best Use of GitHub Copilot:&lt;/strong&gt; Copilot CLI drafted four features, and GitHub Actions automates CI, the weekly report and Copilot's environment setup. The coding agent and automatic Copilot PR review were not used (Free plan).&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>devchallenge</category>
      <category>hf26challenge</category>
      <category>githubchallenge</category>
      <category>tabfpn</category>
    </item>
    <item>
      <title>Who's Singing?: An offline bird-call identifier</title>
      <dc:creator>Brage Mellesdal</dc:creator>
      <pubDate>Thu, 08 Oct 2026 11:19:59 +0000</pubDate>
      <link>https://dev.to/brage1025/whos-singing-an-offline-bird-call-identifier-2609</link>
      <guid>https://dev.to/brage1025/whos-singing-an-offline-bird-call-identifier-2609</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/hacktoberfest-week1-2026-10-05"&gt;Hacktoberfest Open-Source AI Challenge Week 1: Touch Grass&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built:
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Who's Singing?&lt;/strong&gt; is an offline bird-call identifier for your walks. You record 30~60 seconds of audio on your phone, run one command, and get a list of which birds were singing, how much to trust each one, and a short note written by a local LLM.&lt;/p&gt;

&lt;p&gt;The idea is to keep the screen the shortest part of the experience. You spend the time &lt;strong&gt;outside&lt;/strong&gt; listening, walking with your ears open and your phone ready to record in your pocket. The the laptop is only checked at the end, usually when you're done for the day. It's for anyone who has stopped on a trail and wondered "what &lt;em&gt;is&lt;/em&gt; that?". It works even at your hut or campsite where there's no signal, because nothing in it needs the internet.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Who's singing:

  Great Tit                  confident  best  66.2%  (4 detections)
  Great Spotted Woodpecker   likely     best  80.1%  (2 detections)
  Eurasian Blue Tit          possible   best  41.2%  (2 detections)

Field note (written by qwen2.5:3b):
  On this walk, I clearly heard a Great Tit, four times. I also probably
  heard a Great Spotted Woodpecker, twice.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Demo:
&lt;/h2&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/g2nv-HsVla0" width="710" height="399"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;To try it yourself, you need a bird recording of your own (the repo doesn't include audio):&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/Brage1025/whos-singing.git
&lt;span class="nb"&gt;cd &lt;/span&gt;whos-singing

python &lt;span class="nt"&gt;-m&lt;/span&gt; venv .venv
&lt;span class="nb"&gt;source&lt;/span&gt; .venv/bin/activate        &lt;span class="c"&gt;# Windows: .venv\Scripts\activate&lt;/span&gt;
pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt

ollama pull qwen2.5:3b           &lt;span class="c"&gt;# For the field note&lt;/span&gt;

python whos_singing.py my-walk.mp3 &lt;span class="nt"&gt;--lat&lt;/span&gt; 59.91 &lt;span class="nt"&gt;--lon&lt;/span&gt; 10.75
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Swap &lt;code&gt;my-walk.mp3&lt;/code&gt; for a recording from your phone, and the coordinates for where you made it. The location is what lets BirdNET leave out species that don't live near you.&lt;/p&gt;
&lt;h2&gt;
  
  
  Code:
&lt;/h2&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/Brage1025" rel="noopener noreferrer"&gt;
        Brage1025
      &lt;/a&gt; / &lt;a href="https://github.com/Brage1025/whos-singing" rel="noopener noreferrer"&gt;
        whos-singing
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      My submission for the Hacktoberfest 2026 Open-Source AI Challenge, Week 1: Touch Grass
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Who's Singing?&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;An offline bird-call identifier for your walks. Record 30~60 seconds of audio on your phone, run one command, and get a list of which birds were singing, how much to trust each one, and a short note written by a local LLM.&lt;/p&gt;

&lt;p&gt;Built for the Hacktoberfest Open-Source AI Challenge, Week 1: &lt;strong&gt;Touch Grass&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Demo:&lt;/strong&gt; &lt;a href="https://youtu.be/g2nv-HsVla0" rel="nofollow noopener noreferrer"&gt;Watch it run with Wi-Fi off&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The screen is the shortest part of the experience: you spend your time &lt;strong&gt;outside&lt;/strong&gt; listening, and only check the laptop at the end.&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Why open-source AI?&lt;/h2&gt;
&lt;/div&gt;

&lt;p&gt;Two open models do the work, both are running on your own machine:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Works without internet.&lt;/strong&gt; After a one-time download of the models, no internet is needed. Tested with Wi-Fi turned off.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Your recordings stay with you.&lt;/strong&gt; Audio, locations, and your bird list are never uploaded anywhere.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Free to run.&lt;/strong&gt; No API keys, no per-request costs and no token limits.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Swappable&lt;/strong&gt;…&lt;/li&gt;
&lt;/ul&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/Brage1025/whos-singing" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;h2&gt;
  
  
  How I Built It:
&lt;/h2&gt;

&lt;p&gt;Two open models run on my own machine:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/birdnet-team/BirdNET-Analyzer" rel="noopener noreferrer"&gt;BirdNET-Analyzer&lt;/a&gt;&lt;/strong&gt; (Cornell Lab of Ornithology and Chemnitz University of Technology) does the identification. It scores each 3-second slice of audio against thousands of species. I pass in my latitude, longitude and the week of the year, and its species list shrinks from 6,522 to about 240, so a recording from Oslo stops returning sunbirds and cuckooshrikes from other continents.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A small local LLM through &lt;a href="https://ollama.com" rel="noopener noreferrer"&gt;Ollama&lt;/a&gt;&lt;/strong&gt; writes the one-line note. I defaulted to &lt;code&gt;qwen2.5:3b&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Around them I wrote a short Python script that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;merges the 3-second detections into one row per species,&lt;/li&gt;
&lt;li&gt;assigns a &lt;strong&gt;confidence tier&lt;/strong&gt; (&lt;code&gt;confident&lt;/code&gt;, &lt;code&gt;likely&lt;/code&gt;, &lt;code&gt;possible&lt;/code&gt;), because a single high score on one window is weak evidence and repeated detections are stronger,&lt;/li&gt;
&lt;li&gt;sends only confident and likely birds to the LLM,&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;checks the note in code&lt;/strong&gt; and replaces it with a plain template if it's wrong,&lt;/li&gt;
&lt;li&gt;logs each session to a local CSV, so over time it builds a private bird list.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The LLM kept getting it wrong:
&lt;/h3&gt;

&lt;p&gt;This was the most useful part of the project. The identification worked from the start. The note took five rounds:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Given only species names, the model invented facts.&lt;/strong&gt; It gave a Great Tit "brightly colored heads" and described a European bird's call for a North American one.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;After I forbade facts, the note got clumsy&lt;/strong&gt; and hedged a confident bird with "probably".&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;After I added an example to the prompt, the model copied it.&lt;/strong&gt; It reported a Wren, which was never in the recording.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Then it repeated a real bird&lt;/strong&gt; ("I clearly heard a Great Tit... I probably heard a Great Tit").&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;So the code now rejects any note&lt;/strong&gt; that misses a bird, repeats one, names one that wasn't detected, or uses "clearly" or "probably" wording that doesn't match the tiers. Certainty wording is now decided in code to ensure quality and stability, not by the model.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I then compared two models on the same recording with the final prompt and check, five runs each:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;Passed the check&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;qwen2.5:3b&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;5 of 5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;llama3.2&lt;/code&gt; (3B)&lt;/td&gt;
&lt;td&gt;1 of 5, the other four fell back to the template&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;That's one recording and one prompt, so it's a small test, not a benchmark. But it took only minutes, and switching the winner was as easy as changing one flag: &lt;code&gt;--model&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  How it did on real audio:
&lt;/h3&gt;

&lt;p&gt;I tested on three recordings: an old one of my own and two from a friend who prefers to stay anonymous. Only one of the friend's recordings has ground truth, because she knew what was there. That one was recorded just outside Oslo. She saw the Great Tits and Blue Tits, and heard the woodpecker without seeing it. So all three detections were real.&lt;/p&gt;

&lt;p&gt;BirdNET found everything with no false detections. The weak spot was my own tiers. The Blue Tit was a real bird, but two detections with a best score of 41% put it in &lt;code&gt;possible&lt;/code&gt;, so the note left it out. I'd rather the tool under-claim than confidently name a bird that wasn't there, so I kept that threshold, but it's a trade-off I made by judgment, and I didn't tune it on a single recording.&lt;/p&gt;

&lt;p&gt;I also owe an honest note: I didn't get out on a walk myself this week, so I haven't field-tested it outdoors. Everything above was run on recordings, offline.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Does Open Innovation Matter?:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;It runs with no internet.&lt;/strong&gt; After a one-time model download I turned Wi-Fi off and ran the full pipeline, including the note. That's what makes a bird identifier usable on a trail or out on field work with no signal.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Recordings stay on my machine.&lt;/strong&gt; Audio, locations and my bird list are never uploaded. So I can decide if it's nobody else's business or not.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It costs nothing to run.&lt;/strong&gt; No API key, no per-request charge and no token limits.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;I could see and fix the weak link.&lt;/strong&gt; When the small model invented a Wren, I could inspect the prompt, change the model, add a validator and compare alternatives side by side. With a closed API I'd have received the same wrong note with no easy way to tell why.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The two parts are separately swappable.&lt;/strong&gt; BirdNET handles identification and the LLM handles wording, so I can replace either without touching the other.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One caveat on "open": BirdNET's model is open-weight but carries a non-commercial license, so it's free to inspect and use for a project like this, not free of conditions. The code in my repository is MIT.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Special thanks to my dear friend, who prefers to stay anonymous, for the field recording and for telling me what she actually saw and heard.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>hf26challenge</category>
      <category>hacktoberfest</category>
    </item>
  </channel>
</rss>
