<?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: Prakhar Agrawal</title>
    <description>The latest articles on DEV Community by Prakhar Agrawal (@prakhar0013).</description>
    <link>https://dev.to/prakhar0013</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%2F1145043%2F52ca159d-80b5-4b77-8f39-7008939a57d0.jpeg</url>
      <title>DEV Community: Prakhar Agrawal</title>
      <link>https://dev.to/prakhar0013</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/prakhar0013"/>
    <language>en</language>
    <item>
      <title>My Laptop Is Too Slow to Chat With an AI, So I Gave It the Night Shift</title>
      <dc:creator>Prakhar Agrawal</dc:creator>
      <pubDate>Wed, 21 Oct 2026 17:21:38 +0000</pubDate>
      <link>https://dev.to/prakhar0013/my-laptop-is-too-slow-to-chat-with-an-ai-so-i-gave-it-the-night-shift-1fai</link>
      <guid>https://dev.to/prakhar0013/my-laptop-is-too-slow-to-chat-with-an-ai-so-i-gave-it-the-night-shift-1fai</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;My Downloads folder is where files go to be forgotten. &lt;code&gt;invoice (3).pdf&lt;/code&gt;. &lt;code&gt;Screenshot 2026-10-12 141502.png&lt;/code&gt;. &lt;code&gt;Document1.docx&lt;/code&gt;, which could be anything. Every few months I promise myself I'll clean it up, and every few months I don't.&lt;/p&gt;

&lt;p&gt;So I built &lt;strong&gt;sloth&lt;/strong&gt;, about 200 lines of Python that do it while I sleep. Every night it goes through my Downloads folder with Gemma 4 running on my laptop. It reads each document, looks at each image, gives the file a name a human would pick, and moves it into a folder. In the morning, a report is waiting:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;invoice (3).pdf                  -&amp;gt; Finance/2026-10-01_acme_cloud_services_invoice.pdf
Document1.docx                   -&amp;gt; Work/2026-10-01_sprint_meeting_notes_prakhar_meera_jonas.docx
Screenshot 2026-10-12 141502.png -&amp;gt; Screenshots/2026-10-01_error_message_screenshot_python_app.png
export (1).csv                   -&amp;gt; Finance/2026-10-01_salary_and_transaction_report.csv
VSCodeUserSetup-x64-1.105.1.exe  -&amp;gt; Installers/VSCodeUserSetup-x64-1.105.1.exe
scan0001.pdf                     -&amp;gt; left alone (Gemma wasn't sure)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;If I don't like what it did, &lt;code&gt;python sloth.py undo ~/Downloads&lt;/code&gt; puts everything back.&lt;/p&gt;

&lt;p&gt;Anyone with a Downloads folder gets to be a little lazier, which is everyone. It's especially for people whose laptop, like mine, has no business running AI.&lt;/p&gt;

&lt;p&gt;Here's a real run on my test folder. Read the timings, because they're the point:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ python sloth.py tidy ~/sloth-test
[1/13] 2409.11873v2.pdf -&amp;gt; Reading/2026-10-01_quantization_aware_training_paper.pdf (14s)
[2/13] data.bin -&amp;gt; left alone, unknown type
[3/13] Document1.docx -&amp;gt; Work/2026-10-01_sprint_meeting_notes_prakhar_meera_jonas.docx (13s)
[4/13] e-ticket.pdf -&amp;gt; Personal/2026-10-01_train_ticket_booking_confirmation.pdf (11s)
[5/13] export (1).csv -&amp;gt; Finance/2026-10-01_salary_and_transaction_report.csv (15s)
[6/13] image (2).png -&amp;gt; Images/2026-10-01_quarterly_revenue_by_region_report.png (21s)
[7/13] invoice (3).pdf -&amp;gt; Finance/2026-10-01_acme_cloud_services_invoice.pdf (11s)
[8/13] notes.txt -&amp;gt; Personal/2026-10-01_banana_bread_recipe_personal.txt (11s)
[9/13] photos_backup.zip -&amp;gt; Archives/photos_backup.zip (0s)
[10/13] resume_final2.docx -&amp;gt; Personal/2026-10-01_resume_final_dok_prakhar_a.docx (11s)
[11/13] scan0001.pdf -&amp;gt; left alone, not sure (9s)
[12/13] Screenshot 2026-10-12 141502.png -&amp;gt; Screenshots/2026-10-01_error_message_screenshot_python_app.png (19s)
[13/13] VSCodeUserSetup-x64-1.105.1.exe -&amp;gt; Installers/VSCodeUserSetup-x64-1.105.1.exe (0s)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The folder actually held 15 files. The other two are skipped on purpose: a boarding pass I'd downloaded that day (I might still need it) and a half-finished Ubuntu download.&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/Prakhar0013" rel="noopener noreferrer"&gt;
        Prakhar0013
      &lt;/a&gt; / &lt;a href="https://github.com/Prakhar0013/sloth" rel="noopener noreferrer"&gt;
        sloth
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &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&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;Tidies your Downloads folder overnight with a local Gemma model, so you don't have to.&lt;/p&gt;

&lt;p&gt;While you sleep, sloth reads each file (or looks at it, for images), gives it a real name, and files it into a folder. In the morning there's a &lt;code&gt;SLOTH_REPORT.md&lt;/code&gt; waiting for you, and one command puts everything back if you don't like it.&lt;/p&gt;

&lt;div class="snippet-clipboard-content notranslate position-relative overflow-auto"&gt;&lt;pre class="notranslate"&gt;&lt;code&gt;invoice (3).pdf                   -&amp;gt; Finance/2026-10-01_acme_cloud_services_invoice.pdf
Screenshot 2026-10-12 141502.png  -&amp;gt; Screenshots/2026-10-01_error_message_screenshot_python_app.png
notes.txt                         -&amp;gt; Personal/2026-10-01_banana_bread_recipe_personal.txt
VSCodeUserSetup-x64-1.105.1.exe   -&amp;gt; Installers/VSCodeUserSetup-x64-1.105.1.exe
scan0001.pdf                      -&amp;gt; left alone (Gemma wasn't sure)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Everything runs on your machine through &lt;a href="https://ollama.com" rel="nofollow noopener noreferrer"&gt;Ollama&lt;/a&gt;. No file, filename, or snippet ever leaves it.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Setup&lt;/h2&gt;
&lt;/div&gt;

&lt;p&gt;You need &lt;a href="https://ollama.com" rel="nofollow noopener noreferrer"&gt;Ollama&lt;/a&gt; and Python 3.9+.&lt;/p&gt;

&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;ollama pull gemma4:e2b-it-qat
pip install -r requirements.txt&lt;/pre&gt;

&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Use&lt;/h2&gt;
&lt;/div&gt;

&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;python sloth.py tidy &lt;span class="pl-k"&gt;~&lt;/span&gt;/Downloads --dry-run   &lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; show the plan, move nothing&lt;/span&gt;
python sloth.py tidy &lt;span class="pl-k"&gt;~&lt;/span&gt;/Downloads             &lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; do it&lt;/span&gt;
python sloth.py undo &lt;span class="pl-k"&gt;~&lt;/span&gt;/Downloads             &lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; put the last run back&lt;/span&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Run it every night&lt;/h2&gt;

&lt;/div&gt;

&lt;p&gt;Windows (PowerShell)…&lt;/p&gt;&lt;/div&gt;


&lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/Prakhar0013/sloth" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;


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

&lt;p&gt;&lt;strong&gt;The constraint.&lt;/strong&gt; My laptop has an Intel Core i3-1215U, 8 GB of RAM and no graphics card. While I was testing, Windows had about 1 GB of memory free. Chatting with a model on this machine would be miserable: you type a question and go make tea.&lt;/p&gt;

&lt;p&gt;But tidying files isn't a chat. Nobody is waiting for an answer at 2 a.m. That's the whole project: &lt;strong&gt;slow inference is fine when nobody's waiting for it.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The model.&lt;/strong&gt; Gemma 4 E2B (&lt;code&gt;gemma4:e2b-it-qat&lt;/code&gt;), served by Ollama. It's the smallest Gemma 4, a 4.3 GB download. The QAT build was trained to keep its quality after being squeezed down to 4 bits, which is what lets it fit in 8 GB. It can also see, so a screenshot gets named after what's in it, not after when I took it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The pipeline&lt;/strong&gt;, in the order the script runs it:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Skip what I might still need.&lt;/strong&gt; Anything modified in the last 24 hours stays put, and so does any &lt;code&gt;.crdownload&lt;/code&gt; or &lt;code&gt;.part&lt;/code&gt; file.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Don't think unless you have to.&lt;/strong&gt; Installers, archives, audio and video are sorted by extension and keep their names. The model is never called for them. A sloth saves its energy.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Read a little.&lt;/strong&gt; The first two pages of a PDF (via pypdf), the text inside a &lt;code&gt;.docx&lt;/code&gt; (which is just a zip of XML), the first 1,500 characters of a text file, or the whole image.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hand Gemma a form, not a blank page.&lt;/strong&gt; Ollama can force the reply to match a JSON schema, and the category field is an enum of eight folders. A 4.6B-parameter model can't invent a "Misc Stuff 2" folder, because the schema won't let it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Never trust a model with a date.&lt;/strong&gt; The &lt;code&gt;2026-10-01_&lt;/code&gt; prefix comes from the file's timestamp. Gemma only supplies the words.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Write down every move as it happens.&lt;/strong&gt; If the laptop dies at 3 a.m., &lt;code&gt;undo&lt;/code&gt; still knows what went where.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The heart of it is a single HTTP request to Ollama:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;model&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gemma4:e2b-it-qat&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;messages&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;  &lt;span class="c1"&gt;# prompt, plus the image as base64 if it's a picture
&lt;/span&gt;    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;format&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;SCHEMA&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;       &lt;span class="c1"&gt;# category must be one of 8 folders
&lt;/span&gt;    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;think&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;stream&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;options&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;temperature&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;num_ctx&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;4096&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It uses no framework and no SDK, just the Python standard library plus pypdf.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The one line that made it 7 to 30 times faster.&lt;/strong&gt; When I ran &lt;code&gt;ollama show&lt;/code&gt; on the model, it told me something I didn't expect: Gemma 4 has a thinking mode, and it's on by default. I timed the same three files both ways:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;File&lt;/th&gt;
&lt;th&gt;Thinking off&lt;/th&gt;
&lt;th&gt;Thinking on&lt;/th&gt;
&lt;th&gt;Tokens generated (off / on)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;invoice (3).pdf&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;8.7 s&lt;/td&gt;
&lt;td&gt;59.8 s&lt;/td&gt;
&lt;td&gt;31 / 533&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;export (1).csv&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;4.3 s&lt;/td&gt;
&lt;td&gt;60.4 s&lt;/td&gt;
&lt;td&gt;32 / 535&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Screenshot of a Python error&lt;/td&gt;
&lt;td&gt;2.7 s&lt;/td&gt;
&lt;td&gt;91.7 s&lt;/td&gt;
&lt;td&gt;23 / 667&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;It picked the same folder every time, and the same name for two of the three. On a CPU, 500 tokens of deliberating over whether an invoice is an invoice costs a full minute per file. So &lt;code&gt;"think": False&lt;/code&gt; it is. Even a sloth has limits. (These timings come from a separate test. In full runs, with Windows fighting for memory, it takes 8 to 26 seconds per document and more than a minute for a large photo.)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The bug was in my test, not my code.&lt;/strong&gt; My first prompt gave Gemma two example names: &lt;code&gt;acme_cloud_invoice&lt;/code&gt; and &lt;code&gt;python_import_error&lt;/code&gt;. The first run looked perfect. Then I noticed that my test folder contained an Acme Cloud invoice and a screenshot of a Python import error. I'd been grading the model on how well it could copy my examples. I swapped them for examples that match nothing in the folder (&lt;code&gt;dentist_appointment_reminder&lt;/code&gt;, &lt;code&gt;github_actions_failed_build&lt;/code&gt;). It still put every file in a sensible folder, so it really was reading them. Lesson learned: if your examples look like your test data, your test isn't testing anything.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What it still gets wrong.&lt;/strong&gt; Some names are clumsy. &lt;code&gt;banana_bread_recipe_personal&lt;/code&gt; repeats its own folder, and my résumé came out as &lt;code&gt;resume_final_dok_prakhar_a&lt;/code&gt; (I have no idea what "dok" means). A scanned PDF with no text layer gets left alone, because all Gemma can see is the filename. That's the right call, but a smarter version would turn the page into an image and let Gemma look at it.&lt;/p&gt;

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

&lt;p&gt;Look at what's in a Downloads folder. My little test folder alone had a bank export showing a salary, a résumé, a train ticket with a booking number, and an invoice. My real one is worse.&lt;/p&gt;

&lt;p&gt;A version of this tool built on a closed API would only work by uploading all of that, every night, to a server I don't control. I wasn't going to make that trade to save ten minutes of dragging files around. With Gemma running in Ollama, no file, filename or snippet ever leaves the laptop. I can turn the Wi-Fi off and it still works.&lt;/p&gt;

&lt;p&gt;Then there's cost. It runs every night, on as many files as I've downloaded, for nothing. There's no API key, no rate limit, and no surprise bill if a bug makes it loop.&lt;/p&gt;

&lt;p&gt;And there's control, three kinds of it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;I picked a model that fits my machine.&lt;/strong&gt; I chose the size (E2B) and the quantization (4-bit QAT) specifically so it would fit in 8 GB of RAM.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;I can swap it.&lt;/strong&gt; The model is one constant at the top of the file. When a better small model comes out, upgrading is a one-line change.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;I can share it.&lt;/strong&gt; &lt;code&gt;ollama show&lt;/code&gt; lists Gemma 4's license as Apache 2.0, so anyone can run sloth without signing up for anything.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Where open beat closed.&lt;/strong&gt; A hosted frontier model would be faster per file, and it would probably come up with better names. But speed is the one thing this job doesn't need. At 2 a.m., one second per file and fifteen seconds per file both mean "done before I wake up." Once speed stopped mattering, everything that was left (privacy, cost, working offline, no account) pointed to the model on my own laptop.&lt;/p&gt;

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

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TODO:&lt;/strong&gt; save the session with DevRelay and embed it here, or delete this section.&lt;/p&gt;
&lt;/blockquote&gt;

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

&lt;p&gt;Test folders are polite. So I pointed sloth at my real Downloads folder, with &lt;code&gt;--dry-run&lt;/code&gt; so it planned everything and moved nothing.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;72 files&lt;/strong&gt; were old enough to touch, going back to February.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;36 would have been filed.&lt;/strong&gt; Gemma read 31 of them and gave each a name, and 5 were sorted by extension alone.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;3 were left alone because Gemma wasn't sure what they were.&lt;/strong&gt; That's exactly what I want it to do when it can't tell.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;33 were left alone because sloth can't read them yet:&lt;/strong&gt; 25 Excel files, 6 PowerPoints, an old &lt;code&gt;.doc&lt;/code&gt; and an &lt;code&gt;.mpeg&lt;/code&gt;. Spreadsheets turned out to be the biggest kind of clutter in my real folder, and my test folder had none. That's the most useful thing the run taught me, and reading &lt;code&gt;.xlsx&lt;/code&gt; and &lt;code&gt;.pptx&lt;/code&gt; is next on the list. (Two of those "Excel files" were really Office lock files, &lt;code&gt;~$…&lt;/code&gt;. Sloth skips those now, and &lt;code&gt;.mpeg&lt;/code&gt; now goes to &lt;code&gt;Media&lt;/code&gt;.)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The whole folder took &lt;strong&gt;11.5 minutes&lt;/strong&gt; on the i3, with no GPU. Documents took 8 to 26 seconds each. The two images took 70 and 106 seconds, because big pictures are slow on a CPU. It's a lot of minutes, and I was asleep for all of them.&lt;/p&gt;

&lt;p&gt;The best result was an ebook whose filename was a website prefix followed by six ISBNs. It came out as &lt;code&gt;the_art_of_thinking_clearly_ebook&lt;/code&gt;. It also found two receipts and sent both to &lt;code&gt;Finance&lt;/code&gt;, and it spotted two editions of the same book, filed under filenames that looked nothing alike, and gave them both the same clean title.&lt;/p&gt;

&lt;p&gt;It made mistakes too. Two versions of the same report got identical names, so the only way to tell &lt;code&gt;_v2&lt;/code&gt; apart is the date prefix. &lt;code&gt;El Niño&lt;/code&gt; became &lt;code&gt;el_ni_o&lt;/code&gt;, because my filename cleaner threw away accented letters (my bug, not Gemma's, and it's fixed now). A WhatsApp photo got a name that just repeated its old filename. And it would have filed my coding agent's instruction file, &lt;code&gt;CLAUDE.md&lt;/code&gt;, under &lt;code&gt;Reading&lt;/code&gt;. To be fair, that's technically correct.&lt;/p&gt;

&lt;p&gt;Opening 34 documents one at a time to work out what they are is exactly the chore I'd been putting off since February. Sloth did it in 11 minutes while I did nothing at all.&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;: Gemma 4 E2B runs locally through Ollama and does all of the reading. It reads documents, looks at screenshots, and decides each file's name and folder. Vision is what turns &lt;code&gt;Screenshot 2026-10-12 141502.png&lt;/code&gt; into &lt;code&gt;error_message_screenshot_python_app.png&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>devchallenge</category>
      <category>hf26challenge</category>
      <category>ai</category>
      <category>python</category>
    </item>
    <item>
      <title>Paydar: my laptop reads my invoices and tells me which clients will pay late</title>
      <dc:creator>Prakhar Agrawal</dc:creator>
      <pubDate>Thu, 15 Oct 2026 04:35:28 +0000</pubDate>
      <link>https://dev.to/prakhar0013/paydar-my-laptop-reads-my-invoices-and-tells-me-which-clients-will-pay-late-17oc</link>
      <guid>https://dev.to/prakhar0013/paydar-my-laptop-reads-my-invoices-and-tells-me-which-clients-will-pay-late-17oc</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;Freelancers rarely go under for lack of work. They go under in the gap between doing the work and getting paid for it.&lt;/p&gt;

&lt;p&gt;Meet Maya, the made-up illustrator in Paydar's demo. She has $25,440 in open invoices and $1,400 in the bank. On paper she's having a great month. In practice, Acme Retail always pays about two weeks late, her café client goes quiet at the end of every month when rent is due, and Orbit Media pays small invoices on time but sits on anything over $2,500 until a second manager signs it. None of that is written down anywhere except in her payment history, and none of it is visible from her invoicing tool, which only knows "due" and "overdue".&lt;/p&gt;

&lt;p&gt;By the end of every month, maya gets to feel emptiness because of the lack of funds. These funds often lead her to taking debt for the time period till she gets her operational expenditure. To help her it was decided to build a project.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Paydar&lt;/strong&gt; is a cash-flow radar for people like Maya. You drop invoices into a folder (PDFs, forwarded emails, phone photos) and it:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Reads them on your laptop&lt;/strong&gt; with Gemma 4 (the 2B "E2B" model, about 4.3 GB), pulling out the client, dates, terms, total, and two details that turn out to matter: whether there's a PO number and whether the invoice mentions a late fee.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Predicts how each invoice will actually be paid&lt;/strong&gt; with TabPFN, trained on your own payment history: the chance it lands more than a week late, and a likely pay-date range.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Projects your bank balance week by week&lt;/strong&gt;, under an expected case and a "everyone drags their feet" case, so you see a tight week coming a month out instead of the morning rent bounces.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Drafts reminder emails timed to the risk.&lt;/strong&gt; The trick I like most: clients who are likely to pay late get a friendly heads-up three days &lt;em&gt;before&lt;/em&gt; the due date, not an awkward chase after it.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You can also just ask it things. "Can I afford a $1,500 laptop on November 3rd?" gets checked against the pessimistic forecast, not a gut feeling.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Who pays for this?&lt;/strong&gt; Solo freelancers and two-to-five person studios who invoice a handful of repeat clients and live close to the line between invoices. Because every model runs on the user's own machine (or on a pseudonymized table, more on that below), my cost per customer is close to zero, so it can be priced like a small utility: "$6/month or a one-time $39" rather than another $30/month SaaS seat.&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&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%2F34o3czhbn43hph77p541.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%2F34o3czhbn43hph77p541.png" alt=" " width="800" height="1529"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;![Paydar dashboard]&lt;br&gt;
&lt;em&gt;The dashboard Paydar writes after every run. It's a single HTML file that works offline.&lt;/em&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/Prakhar0013" rel="noopener noreferrer"&gt;
        Prakhar0013
      &lt;/a&gt; / &lt;a href="https://github.com/Prakhar0013/Paydar" rel="noopener noreferrer"&gt;
        Paydar
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &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;Paydar&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;A private cash-flow radar for freelancers.&lt;/strong&gt; Drop your invoices in a folder. A small Gemma model on your laptop reads them, TabPFN learns how each of your clients actually pays, and you get a week-by-week view of your bank balance, which invoices will land late, and reminder emails timed to go out &lt;em&gt;before&lt;/em&gt; the slow payers slip.&lt;/p&gt;
&lt;p&gt;Your invoice PDFs, client names and emails never leave your computer.&lt;/p&gt;
&lt;div class="snippet-clipboard-content notranslate position-relative overflow-auto"&gt;&lt;pre class="notranslate"&gt;&lt;code&gt;$ npm run radar
9 open invoices worth $25,440. About $25,440 should land in the next 30 days.
Likely late: Bluebird Café $640 (100%, expect ~2026-10-21); Acme Retail Group $7,400 (87%, expect ~2026-11-11).
Tight week ahead: if slow payers drag their feet, you'd be down to $14 in the week of 2026-10-26.
3 reminder emails drafted; first one goes out 2026-10-14.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;em&gt;(Output from the demo workspace with the offline baseline. Numbers change with Gemma + TabPFN.)&lt;/em&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;How it works&lt;/h2&gt;

&lt;/div&gt;

  &lt;div class="js-render-enrichment-target"&gt;
    &lt;div class="render-plaintext-hidden"&gt;
      &lt;pre&gt;flowchart LR&lt;/pre&gt;…&lt;/div&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/Prakhar0013/Paydar" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;TypeScript, MIT licensed. &lt;code&gt;npm run demo&lt;/code&gt; creates a synthetic freelancer with 184 paid invoices and nine open ones, so you can try it without your own data. The test suite (26 tests) runs with a fake Gemma and a fake TabPFN server, so CI needs no models or keys.&lt;/p&gt;

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



&lt;pre data-lang="mermaid"&gt;&lt;code&gt;flowchart LR
  subgraph laptop["Your laptop"]
    inbox["inbox/ PDFs, emails, photos"] --&amp;gt; gemma["Gemma 4 E2B (Ollama)"]
    history["history.csv (paid invoices)"] --&amp;gt; feats["features + pseudonymizer"]
    gemma --&amp;gt; feats
    forecast["weekly cash forecast"] --&amp;gt; nudges["reminder drafts (Gemma)"]
    nudges --&amp;gt; report["offline dashboard"]
  end
  feats --&amp;gt;|"C01, C02… + numbers"| tabpfn["TabPFN-3.5 API"]
  tabpfn --&amp;gt;|"P(late), pay-date range"| forecast&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;&lt;strong&gt;Gemma 4 E2B, run locally with Ollama, does all the reading and writing.&lt;/strong&gt; The interesting part was making a 2-billion-parameter model reliable at a form-filling job. Three things did it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Constrained decoding.&lt;/strong&gt; Ollama's &lt;code&gt;format&lt;/code&gt; field takes a JSON Schema and constrains generation to it, so the model &lt;em&gt;cannot&lt;/em&gt; return a malformed date or a missing field. I generate the schema from the same Zod object I use to validate the result, then re-validate anyway and retry once with the error message if something's off.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Thinking off.&lt;/strong&gt; Gemma 4 reasons before answering by default. That's great for hard questions and pure waste for "what's the invoice date" on a laptop CPU, so Paydar sends &lt;code&gt;think: false&lt;/code&gt;, after checking that the model reports thinking as a capability.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Code does the arithmetic, the model doesn't.&lt;/strong&gt; Gemma extracts what's printed. Due dates from "Net 30", client-name matching ("Helix Labs Inc." is "Helix Labs" in your history), and sanity checks happen in plain TypeScript, and anything shaky becomes a visible warning rather than a silent guess.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;TabPFN does the predicting.&lt;/strong&gt; It's a tabular foundation model: you hand it your training table and it predicts in a single forward pass, no training loop, no hyperparameter tuning. That matters here because a freelancer's whole payment history is a couple of hundred rows, which is too little for most models to learn from and exactly where TabPFN shines. Paydar builds 17 features per invoice: the client's track record, their most recent payment, how big this invoice is compared with what they usually get, month-end timing, terms, and the PO and late-fee flags Gemma extracted. Then it makes two calls to the &lt;code&gt;/tabpfn/*&lt;/code&gt; API with &lt;code&gt;model_path: "v3.5_default"&lt;/code&gt;: a classifier for "more than 7 days late?" and a regressor asked for the 10th, 50th and 90th percentile of days late. Those quantiles become the shaded range on the chart.&lt;/p&gt;

&lt;p&gt;One rule I was strict about: &lt;strong&gt;every feature only uses what was knowable on the invoice's issue date.&lt;/strong&gt; A payment that arrived after an invoice was sent can't be used to describe that invoice. Without that rule the backtest would quietly grade itself on the answers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mastra ties it together.&lt;/strong&gt; The pipeline is a Mastra workflow with five typed steps (scan inbox, predict, forecast, draft reminders, write report), which made each step testable on its own and gave me Mastra Studio for free to watch runs step by step. On top sits a Mastra agent, also on local Gemma, with six tools: radar status, refresh, "can I afford", client history, draft a reminder, mark as paid. Its instructions say to quote numbers only from tools, so the small model's job is choosing the right tool and explaining the answer, not doing maths.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Does TabPFN beat common sense?&lt;/strong&gt; The bar isn't zero, it's what a careful freelancer does in a spreadsheet: "this client usually pays N days late." &lt;code&gt;npm run eval&lt;/code&gt; replays the history, trains on invoices paid before a cutoff, predicts the ones issued after, and scores both:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Predictor&lt;/th&gt;
&lt;th&gt;Late? AUC ↑&lt;/th&gt;
&lt;th&gt;Brier ↓&lt;/th&gt;
&lt;th&gt;Pay-date error ↓&lt;/th&gt;
&lt;th&gt;80% range hit rate&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Per-client average (baseline)&lt;/td&gt;
&lt;td&gt;0.58&lt;/td&gt;
&lt;td&gt;0.264&lt;/td&gt;
&lt;td&gt;6.3 days&lt;/td&gt;
&lt;td&gt;0.63&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TabPFN-3.5&lt;/td&gt;
&lt;td&gt;TODO&lt;/td&gt;
&lt;td&gt;TODO&lt;/td&gt;
&lt;td&gt;TODO&lt;/td&gt;
&lt;td&gt;TODO&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;em&gt;Demo data: 132 training invoices, 46 test invoices. The baseline row is real output from &lt;code&gt;npm run eval&lt;/code&gt; on the demo workspace.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The demo clients have habits a per-client average can't see by design: Orbit's $2,500 sign-off threshold, the café's month-end crunch, Helix Labs slowly getting worse over nine months, Kite &amp;amp; Co freezing payments over the holidays. TODO: one or two sentences on which of these TabPFN picked up in your run (look at the Orbit $3,900 invoice and Helix Labs: the baseline rates them "On track" and "Watch"). It's synthetic data, so the honest test is your own history, which is why &lt;code&gt;eval&lt;/code&gt; exists.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Because invoices are the most sensitive documents a freelancer has.&lt;/strong&gt; An invoice carries a client's legal name, address, sometimes bank details, my rates, and what I built for them, often under an NDA. "Upload your invoices to our AI" is a non-starter for a lot of people, and for many it would breach a contract. With Gemma running in Ollama, the documents are read on the same laptop they were written on. Paydar's dashboard ends with a "What left this computer" box that lists exactly which columns went anywhere, and the answer for documents, names and emails is "nothing".&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Because I could choose exactly what to share, and how little.&lt;/strong&gt; TabPFN is used through Prior Labs' hosted API, but what it receives is a table of numbers and pseudonyms: clients become C01, C02, no invoice numbers, no descriptions. The model gets the pattern and none of the identities. With no API key, Paydar falls back to an offline baseline, so you can run the whole thing with Wi-Fi off. TabPFN itself is open too: the package is on GitHub with weights on Hugging Face for non-commercial use.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Because the business only works if each customer costs me almost nothing.&lt;/strong&gt; A tool aimed at people who are short on cash can't carry a per-document API bill. Here the heavy lifting runs on hardware the customer already owns, so the price can be small and flat.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Because I could fit the model to the machine, not the other way around.&lt;/strong&gt; The default is Gemma 4 E2B, quantization-aware trained, about 4.3 GB, chosen because my target user has an ordinary 8 GB laptop, not a GPU. Someone with 16 GB can switch to E4B with one environment variable, and any other Ollama model works the same way. Constrained JSON output, turning thinking off, a retry with the validation error: those are knobs I control because the stack is open.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where open was harder:&lt;/strong&gt; speed. Reading a document on a laptop CPU takes TODO-seconds (measure it on your machine) versus near-instant from a hosted API, so Paydar caches every extraction by file hash and never reads the same document twice. Scanned PDFs without a text layer need to come in as photos so Gemma's vision can read them. For a nightly "how's my cash" check, I'll take that trade.&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;: Gemma 4 E2B runs locally through Ollama to read invoices (text and photos) into a constrained JSON schema, draft reminder emails, and power the chat agent.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of TabPFN&lt;/strong&gt;: TabPFN-3.5 (classifier plus quantile regressor via the &lt;code&gt;/tabpfn/*&lt;/code&gt; API) inside a wider prediction agent, with a walk-forward backtest against a per-client baseline.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of Mastra&lt;/strong&gt;: a five-step Mastra workflow orchestrating open models, plus a Mastra agent with six tools on a local Gemma model.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>devchallenge</category>
      <category>hf26challenge</category>
      <category>ai</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Spotter an app for gym data</title>
      <dc:creator>Prakhar Agrawal</dc:creator>
      <pubDate>Mon, 05 Oct 2026 07:00:16 +0000</pubDate>
      <link>https://dev.to/prakhar0013/spotter-an-app-for-gym-data-2cg0</link>
      <guid>https://dev.to/prakhar0013/spotter-an-app-for-gym-data-2cg0</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/hacktoberfest-weekend-2026-10-01"&gt;Hacktoberfest Weekend Challenge: Build for a Friend&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;Himank Singhvi is 21, trains 3-4 days a week, and is cutting. His current weight is 91 kg. His tracking lives in four places: a calorie app on his phone, a Samsung band syncing sleep and heart rate to Samsung Health, a notes app for his lifts and supplements, and a folder of blood test PDFs he's never looked at twice. He asked me to check the protein diet records and even told that the apps handling data are charging after the trial period.&lt;/p&gt;

&lt;p&gt;None of those places talk to each other. His calorie app doesn't know he slept five hours. His workout notes don't know his resting heart rate jumped. And nobody was looking at the Vitamin D number in his last report.&lt;/p&gt;

&lt;p&gt;So I built him &lt;strong&gt;Spotter&lt;/strong&gt;, an app that runs on his own laptop and pulls all of it together:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Food in plain words.&lt;/strong&gt; He types "3 eggs, 2 rotis with dal, scoop of whey in milk" and gets back an editable table with calories and macros, measured against targets worked out from his body and his chosen deficit.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Supplements&lt;/strong&gt; as a daily checklist, so he can see whether he's actually taking the creatine he pays for.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A workout plan&lt;/strong&gt; built around his training days and equipment, with set logging and an estimated one-rep-max chart. That chart answers the question every cutting lifter has: am I losing fat or losing strength?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Recovery from his Samsung band.&lt;/strong&gt; Spotter imports the Samsung Health data export and turns last night's sleep, resting heart rate and stress into a plain call for today: push, train as planned, or go lighter.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Blood tests.&lt;/strong&gt; He drops in a lab PDF. Spotter extracts every result, flags anything outside the lab's own reference range, tracks markers across reports, and explains flagged results in plain language with questions to take to his doctor.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A weekly check-in&lt;/strong&gt; written like a message from a gym buddy, built from the week's real numbers.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;All demo data is fictional and included in the repo under &lt;code&gt;sample_data/&lt;/code&gt;. His real numbers stay on his laptop, which is rather the point.&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/Prakhar0013" rel="noopener noreferrer"&gt;
        Prakhar0013
      &lt;/a&gt; / &lt;a href="https://github.com/Prakhar0013/Spotter" rel="noopener noreferrer"&gt;
        Spotter
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Built for friend to help with his gym routine
    &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;🏋️ Spotter&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;A local-first training, nutrition and recovery log, built for one friend who lifts.&lt;/p&gt;
&lt;p&gt;Spotter keeps his food, supplements, workouts, Samsung band data and blood test results in one place, on his own laptop. A local open-weight model (via Ollama) reads the messy stuff: meal descriptions, lab PDFs, plan requests. Plain Python does every number.&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;ul&gt;
&lt;li&gt;
&lt;strong&gt;Log food in plain words.&lt;/strong&gt; "3 eggs, 2 rotis with dal, scoop of whey in milk" becomes a checked, editable table with calories and macros.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Targets.&lt;/strong&gt; Maintenance and cut calories from the Mifflin-St Jeor formula, a chosen deficit that never drops below BMR, protein per kg.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Supplements.&lt;/strong&gt; A daily checklist, 14-day adherence, and a timing review that defers to a pharmacist.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Workouts.&lt;/strong&gt; A weekly plan the model builds only from a fixed exercise library, set logging, and estimated 1RM trends to check strength holds during the cut.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Band &amp;amp; recovery.&lt;/strong&gt; Imports…&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/Prakhar0013/Spotter" 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;Spotter is a Streamlit app backed by SQLite, with &lt;strong&gt;Qwen2.5 7B running locally through Ollama&lt;/strong&gt; on my RTX 3050. The 3B model works too on smaller GPUs or CPU only.&lt;/p&gt;

&lt;p&gt;The most important design decision was deciding &lt;strong&gt;what the model is not allowed to do.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A language model is great at reading "had two rotis and a bowl of dal" and terrible at arithmetic. So Spotter splits the work:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The model reads.&lt;/strong&gt; It turns free text into structured JSON: food items with quantities and units, lab markers with values and reference ranges, a workout plan.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Code does every number.&lt;/strong&gt; Food items are fuzzy-matched against a built-in nutrition table with Indian staples (roti, dal, poha, paneer, soya chunks) alongside the usual gym foods. Units like "bowl" or "scoop" convert to grams, and the macros are calculated in Python. Calorie targets come from the Mifflin-St Jeor formula, with a floor at his BMR so the app never suggests eating less than his body burns at rest.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Guardrails catch the model when it's wrong.&lt;/strong&gt; When the model estimates a food that isn't in the table, Spotter checks its calorie number against the macros (4/4/9 kcal per gram). If they disagree by more than 25%, it trusts the macros. Workout plans can only use exercises from a fixed library; anything else gets dropped. Lab values always land in an editable table that he checks against the PDF before anything is saved.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The weekly check-in follows the same rule. Python computes the averages, the days he hit his protein target and the strength trends, and the model's only job is to turn those numbers into a note a friend would actually read.&lt;/p&gt;

&lt;p&gt;The Samsung part was the most difficult, challenging. Samsung Health exports a folder of CSVs with a metadata line on top, column names like &lt;code&gt;com.samsung.health.sleep.start_time&lt;/code&gt;, and timestamps in UTC stored next to a separate offset like &lt;code&gt;UTC+0530&lt;/code&gt;. The parser is deliberately forgiving about all of that, because the format changes between app versions.&lt;/p&gt;

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

&lt;p&gt;Look at what Spotter holds: everything he eats, every supplement he takes, how he slept every night, his resting heart rate, and his blood test results. That's one of the most personal datasets a 21-year-old has.&lt;/p&gt;

&lt;p&gt;With a closed API, every meal description and every lab report would travel to someone else's server. With an open-weight model running in Ollama, &lt;strong&gt;none of it leaves his laptop.&lt;/strong&gt; There's no account, no sync, and no privacy policy to read. The database is one SQLite file he can delete whenever he wants.&lt;/p&gt;

&lt;p&gt;Open made three more things possible:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;It costs nothing to run.&lt;/strong&gt; He'll log food three or four times a day, every day. A per-token bill on a habit app is a slow leak; a local model is free after the download.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;I could pick the model to fit his hardware.&lt;/strong&gt; Qwen2.5 7B fits on his GPU; on a weaker machine it's one environment variable to switch to the 3B model. Nothing else changes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It works offline.&lt;/strong&gt; Gym Wi-Fi is famously bad. Spotter doesn't care.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Where a closed frontier model would have done better: estimating unusual dishes from scratch, and parsing very messy lab PDFs. The answer wasn't a bigger model. It was making the small one's job smaller and checking its work in code. That split ended up being the best design choice in the project.&lt;/p&gt;

&lt;h2&gt;
  
  
  Handing It Over
&lt;/h2&gt;

&lt;p&gt;Once I gave him, he told that he will try using it. Though after testing he told that this one is good. First few times he tried doing it with heavy food items like Chicken and some other stuff. He even recommended an improvement for android compatibility.   &lt;/p&gt;

&lt;p&gt;Team Submissions&lt;br&gt;
Prashant Kumar: &lt;a class="mentioned-user" href="https://dev.to/blackpearl_iiith"&gt;@blackpearl_iiith&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;Thank you for giving us opportunity to participate. &lt;/p&gt;

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