<?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: PARITOSH</title>
    <description>The latest articles on DEV Community by PARITOSH (@kparitosh760).</description>
    <link>https://dev.to/kparitosh760</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%2F4159550%2Ff73b88aa-dd80-4793-9c7e-879f9269c183.jpg</url>
      <title>DEV Community: PARITOSH</title>
      <link>https://dev.to/kparitosh760</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/kparitosh760"/>
    <language>en</language>
    <item>
      <title>Sloth Mode: open-weight AI that does the chores, and a journal that undoes them</title>
      <dc:creator>PARITOSH</dc:creator>
      <pubDate>Tue, 20 Oct 2026 16:09:55 +0000</pubDate>
      <link>https://dev.to/kparitosh760/sloth-mode-open-weight-ai-that-does-the-chores-and-a-journal-that-undoes-them-28a5</link>
      <guid>https://dev.to/kparitosh760/sloth-mode-open-weight-ai-that-does-the-chores-and-a-journal-that-undoes-them-28a5</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;&lt;strong&gt;Sloth Mode&lt;/strong&gt; is a command-line tool (with a small local web GUI) that runs an&lt;br&gt;
open-weight model you host yourself and points it at three jobs everybody&lt;br&gt;
postpones:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;sloth tidy&lt;/code&gt;&lt;/strong&gt; — reads your junk-drawer folder, proposes a home and a better
name for every file, and shows you the plan. Nothing moves until you say so.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;sloth brief&lt;/code&gt;&lt;/strong&gt; — reads an overnight pile of group chats and hands back two
sentences plus the things that actually need &lt;em&gt;you&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;sloth fridge&lt;/code&gt;&lt;/strong&gt; — one photo of the fridge, door closed, becomes a shopping
list.
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;sloth tidy ~/Downloads
&lt;span class="go"&gt;
Admin/Invoices/
  ACME Invoice March 2024.pdf   → acme-invoice-march-2024.pdf   [model 99%]

Reading/Notes/
  notes.txt                     → garden-boiler-notes.txt       [model 85%]

_Unsure/
  thing.qqq                                                     [model 30%]

dry run. re-run with --apply to move these files.
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Two details carry the whole design. &lt;code&gt;notes.txt&lt;/code&gt; became &lt;code&gt;garden-boiler-notes.txt&lt;/code&gt;&lt;br&gt;
because the model &lt;em&gt;read the file&lt;/em&gt;. And &lt;code&gt;thing.qqq&lt;/code&gt; went to &lt;code&gt;_Unsure&lt;/code&gt; at 30%&lt;br&gt;
rather than to a confident-looking wrong folder — &lt;strong&gt;"I don't know" is a valid&lt;br&gt;
answer&lt;/strong&gt;, and the threshold enforces it.&lt;/p&gt;
&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/dSTvF1jNGNo" width="710" height="399"&gt;
  &lt;/iframe&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/Pager-dot" rel="noopener noreferrer"&gt;
        Pager-dot
      &lt;/a&gt; / &lt;a href="https://github.com/Pager-dot/sloth-mode" rel="noopener noreferrer"&gt;
        sloth-mode
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Local open-weight AI that does your chores while you nap. Zero dependencies, nothing leaves your machine.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div&gt;
&lt;div class="snippet-clipboard-content notranslate position-relative overflow-auto"&gt;&lt;pre class="notranslate"&gt;&lt;code&gt;      (
       \     ___
        \  .'   `.        s l o t h   m o d e
         \/  o o  \       ----------------------
         (   \_/   )      it does the work
          \  ---  /       you do the nap
       ,--' )   ( `--.
      (___.'     `.___)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Local, open-weight AI for the chores you keep putting off.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Zero dependencies. Runs on Gemma. Local by default.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/Pager-dot/sloth-mode#why" rel="noopener noreferrer"&gt;Why&lt;/a&gt; · &lt;a href="https://github.com/Pager-dot/sloth-mode#install" rel="noopener noreferrer"&gt;Install&lt;/a&gt; · &lt;a href="https://github.com/Pager-dot/sloth-mode#the-three-agents" rel="noopener noreferrer"&gt;The agents&lt;/a&gt; · &lt;a href="https://github.com/Pager-dot/sloth-mode#the-gui" rel="noopener noreferrer"&gt;The GUI&lt;/a&gt; · &lt;a href="https://github.com/Pager-dot/sloth-mode#how-it-works" rel="noopener noreferrer"&gt;How it works&lt;/a&gt; · &lt;a href="https://github.com/Pager-dot/sloth-mode#config" rel="noopener noreferrer"&gt;Config&lt;/a&gt; · &lt;a href="https://github.com/Pager-dot/sloth-mode#the-undo-guarantee" rel="noopener noreferrer"&gt;Safety&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/Pager-dot/sloth-mode/docs/demo/sloth-demo.gif"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fraw.githubusercontent.com%2FPager-dot%2Fsloth-mode%2FHEAD%2Fdocs%2Fdemo%2Fsloth-demo.gif" alt="Sloth Mode tidying a folder, undoing it, and summarising a night of group chats"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Real run against &lt;code&gt;gpt-oss:120b&lt;/code&gt;. Plan → Apply → Undo → morning brief, 22 seconds.
📺 &lt;strong&gt;&lt;a href="https://youtu.be/REPLACE_WITH_YOUR_VIDEO_ID" rel="nofollow noopener noreferrer"&gt;Watch the full video&lt;/a&gt;&lt;/strong&gt; ·
&lt;a href="https://github.com/Pager-dot/sloth-mode/docs/demo/sloth-demo.mp4" rel="noopener noreferrer"&gt;download the MP4&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;

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

&lt;/div&gt;
&lt;p&gt;Sloths are not lazy, they are &lt;em&gt;efficient&lt;/em&gt;. They do the minimum that keeps them
alive and spend the rest of the day not wasting energy. That is a design
principle, not a personality flaw.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;sloth&lt;/code&gt; is a command line tool that applies it to your computer. It runs an…&lt;/p&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/Pager-dot/sloth-mode" 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;&lt;strong&gt;The open pieces are not decoration — they are the engine.&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Open piece&lt;/th&gt;
&lt;th&gt;What it does here&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Gemma 3&lt;/strong&gt; (&lt;code&gt;gemma3:4b&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;the local default; multimodal, so one 3 GB pull covers all three agents&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;gpt-oss:120b&lt;/strong&gt; (Apache-2.0)&lt;/td&gt;
&lt;td&gt;120B parameters via Ollama Cloud, no GPU, no download&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Gemma 4&lt;/strong&gt; (&lt;code&gt;gemma4:31b&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;the vision model behind &lt;code&gt;fridge&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Ollama&lt;/strong&gt; / &lt;strong&gt;llama.cpp&lt;/strong&gt; / &lt;strong&gt;vLLM&lt;/strong&gt; / &lt;strong&gt;LM Studio&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;inference, your choice of host&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Hugging Face Inference Providers&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;the same open weights on a free tier&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;The Python standard library&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;the &lt;em&gt;only&lt;/em&gt; dependency&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Zero runtime dependencies is itself a laziness feature: &lt;code&gt;pip install&lt;/code&gt; and you are&lt;br&gt;
done. &lt;code&gt;urllib&lt;/code&gt; talks to the model, &lt;code&gt;http.server&lt;/code&gt; serves the GUI, &lt;code&gt;tomllib&lt;/code&gt; reads&lt;br&gt;
the config, and the undo journal is JSONL you can read with &lt;code&gt;tail&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Four backends sit behind one &lt;code&gt;Model&lt;/code&gt; protocol, so &lt;code&gt;--api cloud&lt;/code&gt;, &lt;code&gt;--api hf&lt;/code&gt; or a&lt;br&gt;
bare local Ollama are one flag apart. I wrote a small &lt;strong&gt;agent harness&lt;/strong&gt;&lt;br&gt;
(~250 lines) rather than pulling in a framework: tools are plain functions with a&lt;br&gt;
JSON-schema argument spec, the loop is bounded by steps &lt;em&gt;and&lt;/em&gt; a wall clock, and&lt;br&gt;
&lt;strong&gt;every tool is read-only unless writes are explicitly allowed&lt;/strong&gt;. An agent can&lt;br&gt;
look at anything and propose anything; only the apply step touches the disk.&lt;/p&gt;

&lt;p&gt;The spine of the whole thing is: &lt;strong&gt;the model is never trusted.&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It can only choose from your configured taxonomy. Invent &lt;code&gt;Vibes/Whatever&lt;/code&gt; and
the answer is discarded for a keyword/extension heuristic.&lt;/li&gt;
&lt;li&gt;It cannot change a file extension.&lt;/li&gt;
&lt;li&gt;It cannot escape the root directory with a &lt;code&gt;../../../etc/passwd&lt;/code&gt; filename.&lt;/li&gt;
&lt;li&gt;Below the confidence threshold, nobody guesses.&lt;/li&gt;
&lt;li&gt;And if no model is running at all, every agent except &lt;code&gt;fridge&lt;/code&gt; still works on
heuristics — &lt;code&gt;fridge&lt;/code&gt; says so instead of inventing a shopping list.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Then I ran it against a real model, and four things broke
&lt;/h3&gt;

&lt;p&gt;I had 189 passing tests before the first real inference call. All green. Here is&lt;br&gt;
what they had failed to notice:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. The JSON schema was being ignored outright.&lt;/strong&gt; Every call ships one. I&lt;br&gt;
assumed it was honoured. It is not — I measured it: a request carrying a schema&lt;br&gt;
that &lt;em&gt;requires&lt;/em&gt; &lt;code&gt;{"ok": boolean}&lt;/code&gt;, with a prompt asking for prose, came back as&lt;br&gt;
&lt;code&gt;'Sloths move slowly through the treetops...'&lt;/code&gt;. Not loosely enforced. Ignored.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. So the morning brief silently degraded.&lt;/strong&gt; The model answered the map step&lt;br&gt;
with &lt;code&gt;{"action_items": [...]}&lt;/code&gt; where the schema said &lt;code&gt;{"points": [...]}&lt;/code&gt;. My code&lt;br&gt;
read only the expected key, got an empty list, and fell back to keyword&lt;br&gt;
extraction — while printing &lt;em&gt;"no model running"&lt;/em&gt; with a 31B model very much&lt;br&gt;
running. &lt;strong&gt;A silent failure that looks like success is worse than a crash.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. &lt;code&gt;--deep&lt;/code&gt; was 100% dead.&lt;/strong&gt; gpt-oss writes its entire planned trajectory as&lt;br&gt;
several JSON objects in one reply. The parser raised &lt;code&gt;Extra data&lt;/code&gt;, the harness&lt;br&gt;
retried four times, hit max steps, and returned nothing. Every unsure file, every&lt;br&gt;
run, invisibly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. &lt;code&gt;fridge&lt;/code&gt; dropped every item it saw&lt;/strong&gt; — the model answered &lt;code&gt;[{"item": "milk"}]&lt;/code&gt;&lt;br&gt;
where the schema said &lt;code&gt;name&lt;/code&gt;, so the shopping list cheerfully told you to buy the&lt;br&gt;
milk that was visible in the photo.&lt;/p&gt;

&lt;p&gt;Same root cause, four faces. The fix was to stop treating the schema as a&lt;br&gt;
contract: accept the field under another name, unwrap markdown fences, take the&lt;br&gt;
first complete JSON value, state the shape in the prompt &lt;em&gt;as well&lt;/em&gt;, and force the&lt;br&gt;
summary into the sentence count that was asked for. The suite is now &lt;strong&gt;250 tests&lt;/strong&gt;&lt;br&gt;
that need no weights and no network — a &lt;code&gt;FakeModel&lt;/code&gt; for logic, a throwaway HTTP&lt;br&gt;
server for the wire format.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Because I could measure the thing instead of believing it.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That schema discovery is the entire argument. Against a closed API I would have&lt;br&gt;
read the docs, seen that structured output is supported, and shipped a brief that&lt;br&gt;
quietly produced keyword slop for a subset of users. What let me find it was that&lt;br&gt;
every layer was inspectable: I could send a byte-identical request with a valid&lt;br&gt;
schema, a deliberately bogus schema, and no schema at all, diff the three&lt;br&gt;
replies, and &lt;em&gt;know&lt;/em&gt;. Then I could fix it in my own parser. No ticket, no waiting&lt;br&gt;
for a vendor, no "we don't comment on model internals."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Open weights let the work happen where the files already are.&lt;/strong&gt; The entire&lt;br&gt;
value proposition — tidy the folder full of invoices and contracts — collapses if&lt;br&gt;
step one is "upload your invoices and contracts." Running locally is not a&lt;br&gt;
privacy feature bolted on; it is the only reason the tool is usable by the people&lt;br&gt;
who need it most. The tool is honest about the trade, too: pick a hosted backend&lt;br&gt;
and &lt;code&gt;sloth doctor&lt;/code&gt; prints &lt;code&gt;prompts are sent to https://ollama.com&lt;/code&gt; in yellow,&lt;br&gt;
every single run.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The open ecosystem converged on two wire formats, so swapping is one flag.&lt;/strong&gt;&lt;br&gt;
Gemma 3 on a laptop, gpt-oss:120b in the cloud, anything on a llama.cpp server —&lt;br&gt;
the same code, because &lt;code&gt;/api/chat&lt;/code&gt; and &lt;code&gt;/v1/chat/completions&lt;/code&gt; are conventions&lt;br&gt;
anyone can implement. That is also an honest escape hatch: nothing here is&lt;br&gt;
load-bearing on one vendor's continued goodwill or pricing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;And it scales down to nothing.&lt;/strong&gt; &lt;code&gt;gemma3:1b&lt;/code&gt; runs on almost any machine.&lt;br&gt;
&lt;code&gt;--no-model&lt;/code&gt; runs on none at all. A tool built on a closed API has a hard floor:&lt;br&gt;
no key, no product. This one degrades to keyword heuristics and keeps working on&lt;br&gt;
a train with no signal — which is, after all, the most sloth-like property&lt;br&gt;
available: it does not even need the internet to be awake.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>hf26challenge</category>
      <category>ai</category>
      <category>opensource</category>
    </item>
    <item>
      <title>LedgerLocal - your receipts never leave your laptop</title>
      <dc:creator>PARITOSH</dc:creator>
      <pubDate>Fri, 16 Oct 2026 16:39:32 +0000</pubDate>
      <link>https://dev.to/kparitosh760/ledgerlocal-your-receipts-never-leave-your-laptop-542k</link>
      <guid>https://dev.to/kparitosh760/ledgerlocal-your-receipts-never-leave-your-laptop-542k</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;LedgerLocal&lt;/strong&gt; — a CLI that turns a freelancer's folder of invoices and receipts into a sorted, accountant-ready archive, entirely on their own laptop.&lt;/p&gt;

&lt;p&gt;Point it at a folder: it reads each document with a local vision model, extracts vendor / date / tax / currency / total, categorises it against rules you control, files it into a &lt;code&gt;year/quarter/category&lt;/code&gt; tree, and exports the CSV your accountant asks for.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Who pays:&lt;/strong&gt; freelancers and one-person studios with 300 unsorted receipts and a lost weekend every tax season. Plenty of SaaS does this — all of it wants the documents uploaded. Receipts name your clients, expose your rates, and carry card digits and tax IDs. For consultants under NDA or anyone under data-residency rules, uploading isn't a price question, it's a non-starter. Their real choice is &lt;em&gt;local tool vs. the weekend&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;The best part isn't extraction, it's distrust: every document is audited after the model answers. In the demo the model misreads &lt;code&gt;$65.09&lt;/code&gt; as &lt;code&gt;$65.00&lt;/code&gt; and the tool catches it from the arithmetic — nobody told it to look.&lt;/p&gt;

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

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/XqbKvyEZOjA" width="710" height="399"&gt;
  &lt;/iframe&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/Pager-dot" rel="noopener noreferrer"&gt;
        Pager-dot
      &lt;/a&gt; / &lt;a href="https://github.com/Pager-dot/ledgerlocal" rel="noopener noreferrer"&gt;
        ledgerlocal
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Local-first invoice &amp;amp; receipt sorter for freelancers, powered by open-weight models.
    &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;LedgerLocal&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Sort a year of invoices and receipts into a clean, accountant-ready archive — on your own laptop, with an open-weight model, with no network access at all.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Point it at the folder where your receipts pile up. It reads each one with a local vision model, pulls out vendor, date, tax and total, categorises it against rules you control, files it into a sane folder tree, and exports the CSV your accountant actually wants.&lt;/p&gt;
&lt;div class="highlight highlight-text-shell-session notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;$ &lt;span class="pl-s1"&gt;ledgerlocal ingest &lt;span class="pl-k"&gt;~&lt;/span&gt;/Downloads/receipts&lt;/span&gt;
&lt;span class="pl-c1"&gt;  read    hetzner-feb.pdf&lt;/span&gt;
&lt;span class="pl-c1"&gt;  read    coffee-with-northwind.jpg&lt;/span&gt;
&lt;span class="pl-c1"&gt;  read    adobe-jan.pdf&lt;/span&gt;
&lt;span class="pl-c1"&gt;  read    legal-invoice.pdf&lt;/span&gt;

&lt;span class="pl-c1"&gt;  2026-02-14  Hetzner Online GmbH             32.73 EUR  hosting&lt;/span&gt;
&lt;span class="pl-c1"&gt;  2026-04-03  Blue Fox Coffee                 11.77 EUR  meals&lt;/span&gt;
&lt;span class="pl-c1"&gt;  2026-01-07  Adobe Inc.                      65.00 USD  software      review&lt;/span&gt;
&lt;span class="pl-c1"&gt;  2026-03-02  Bright Path Legal LLP          990.00 USD  professional_fees&lt;/span&gt;

&lt;span class="pl-c1"&gt;4 ingested, 0 already known, 0 failed, 1 need review&lt;/span&gt;

$ &lt;span class="pl-s1"&gt;ledgerlocal review&lt;/span&gt;

&lt;span class="pl-c1"&gt;1 document(s) the pipeline is not confident about:&lt;/span&gt;

&lt;span class="pl-c1"&gt;  2026-01-07  Adobe Inc.                       65.00 USD&lt;/span&gt;
&lt;span class="pl-c1"&gt;      · subtotal + tax differs from total&lt;/span&gt;&lt;/pre&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/Pager-dot/ledgerlocal" 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;ul&gt;
&lt;li&gt;
&lt;strong&gt;Open-weight models:&lt;/strong&gt; Qwen2.5-VL 7B (photos, scans) and Qwen2.5 7B (text-layer PDFs), served locally by &lt;strong&gt;Ollama&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Two local runtimes:&lt;/strong&gt; a three-method provider interface with an Ollama client and an OpenAI-compatible one (llama.cpp, vLLM, LM Studio). Swapping models or pointing at a fine-tune is one config line.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Schema-constrained decoding&lt;/strong&gt; is load-bearing — the JSON schema goes to the runtime itself, so the model physically can't emit malformed JSON. Available &lt;em&gt;because&lt;/em&gt; I control the decoder.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The model never formats anything.&lt;/strong&gt; It finds the total; Python decides &lt;code&gt;"1.234,56 €"&lt;/code&gt; is &lt;code&gt;Decimal("1234.56")&lt;/code&gt;. Small models are good at finding, unreliable at formatting.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Audit, then one repair pass.&lt;/strong&gt; Subtotal + tax must equal total; failures go back to the model with the specific complaint, best answer wins.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zero required dependencies&lt;/strong&gt; — the whole model client is stdlib &lt;code&gt;urllib&lt;/code&gt; against localhost. When the pitch is "audit this yourself," a 40-package tree undermines it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;SQLite keyed on content hash (re-running a folder is a no-op). ~3,600 lines, 89 tests, no GPU or network needed. Apache-2.0: &lt;strong&gt;github.com/Pager-dot/ledgerlocal&lt;/strong&gt;&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;The headline feature is one a closed API cannot ship.&lt;/strong&gt; Rather than ask users to trust a privacy policy, I removed the capability. Every command installs a process-wide socket allowlist first:&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;socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_connection&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;api.openai.com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;443&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;  &lt;span class="c1"&gt;# OfflineViolation
&lt;/span&gt;&lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_connection&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;127.0.0.1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;11434&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;     &lt;span class="c1"&gt;# fine — that's your model
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A hosted version &lt;strong&gt;can't implement this&lt;/strong&gt;, because the egress it blocks is the call the product depends on. Not a cheaper substitute — a product that otherwise can't exist.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Unmetered inference changed the engineering, not just the bill.&lt;/strong&gt; The repair pass runs on every uncertain document; on a metered API you'd ration that. &lt;code&gt;recategorise&lt;/code&gt; replays three years of documents against new rules in under a second with &lt;strong&gt;zero&lt;/strong&gt; model calls — there's a test asserting the provider is never touched. With per-document pricing you simply don't change your mind, and the rule system dies unused.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Swappability is leverage you keep.&lt;/strong&gt; A 3B on battery, a 32B at year-end, a LoRA fine-tuned on one client's bizarre invoice layout. With a closed API that last one is a feature request to a vendor who'll never prioritise it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where closed wins, honestly:&lt;/strong&gt; a frontier model reads a crumpled thermal receipt better than a 7B. I closed most of that gap with structure instead of scale — constrained decoding, deterministic parsing, arithmetic audits — and surfaced the rest in a &lt;code&gt;review&lt;/code&gt; queue. The discipline a small model &lt;em&gt;forced&lt;/em&gt; made a better product, because a frontier model would have tempted me to skip the checks.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>hf26challenge</category>
      <category>ai</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Checking AQI Instead of Doomscrolling: My Laptop's New Job Is Getting Me Outside</title>
      <dc:creator>PARITOSH</dc:creator>
      <pubDate>Sat, 10 Oct 2026 02:59:28 +0000</pubDate>
      <link>https://dev.to/kparitosh760/checking-aqi-instead-of-doomscrolling-my-laptops-new-job-is-getting-me-outside-7e8</link>
      <guid>https://dev.to/kparitosh760/checking-aqi-instead-of-doomscrolling-my-laptops-new-job-is-getting-me-outside-7e8</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;AeroVision&lt;/strong&gt; is an AI-powered air quality app that answers one question before you walk out the door: &lt;em&gt;is it actually safe to be outside right now?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;It has two parts:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;An AQI predictor&lt;/strong&gt; trained on 5+ years of pollution data from 26 Indian cities (29,531 records). Give it pollutant concentrations (PM2.5, PM10, NO2, CO, etc.) and it predicts the Air Quality Index and health category using four different ML models (Random Forest, XGBoost, Gradient Boosting, Extra Trees) side by side.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A live AQI view&lt;/strong&gt; — the part that actually gets you outside. Search any city (or use your current location), and it pulls real-time pollutant data, runs it through the trained model, and shows you the current AQI, a 24-hour forecast, and current weather — so you can decide &lt;em&gt;when&lt;/em&gt; in the next day is actually the best window to go for a run, walk the dog, or just open the windows.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It's for anyone living somewhere air quality swings hard day to day — which, if you've spent time in Delhi, Ahmedabad, or Patna during peak pollution season, is most of India. Instead of guessing from haze outside your window, you get a number and an actual forecast window.&lt;/p&gt;

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

&lt;p&gt;&lt;a href="https://youtu.be/MDjNqBvu1vM" rel="noopener noreferrer"&gt;https://youtu.be/MDjNqBvu1vM&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/Pager-dot" rel="noopener noreferrer"&gt;
        Pager-dot
      &lt;/a&gt; / &lt;a href="https://github.com/Pager-dot/AeroVision" rel="noopener noreferrer"&gt;
        AeroVision
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      AI-Powered Air Quality Index Prediction &amp;amp; Monitoring System
    &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;AeroVision - AI-Powered Air Quality Prediction &amp;amp; Monitoring System&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;A machine learning web application that predicts &lt;strong&gt;Air Quality Index (AQI)&lt;/strong&gt; from pollutant concentrations and provides interactive dashboards to monitor air pollution trends across &lt;strong&gt;26 Indian cities&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Built with &lt;strong&gt;Flask&lt;/strong&gt;, &lt;strong&gt;scikit-learn&lt;/strong&gt;, &lt;strong&gt;XGBoost&lt;/strong&gt;, &lt;strong&gt;Bootstrap 5&lt;/strong&gt;, and &lt;strong&gt;Chart.js&lt;/strong&gt;.&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Table of Contents&lt;/h2&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/Pager-dot/AeroVision#problem-statement" rel="noopener noreferrer"&gt;Problem Statement&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Pager-dot/AeroVision#what-is-aqi" rel="noopener noreferrer"&gt;What is AQI?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Pager-dot/AeroVision#dataset" rel="noopener noreferrer"&gt;Dataset&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Pager-dot/AeroVision#pollutants-explained" rel="noopener noreferrer"&gt;Pollutants Explained&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/Pager-dot/AeroVision#ml-pipeline" rel="noopener noreferrer"&gt;ML Pipeline&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/Pager-dot/AeroVision#1-data-loading--cleaning" rel="noopener noreferrer"&gt;1. Data Loading &amp;amp; Cleaning&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Pager-dot/AeroVision#2-handling-missing-values" rel="noopener noreferrer"&gt;2. Handling Missing Values&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Pager-dot/AeroVision#3-outlier-removal-iqr-method" rel="noopener noreferrer"&gt;3. Outlier Removal (IQR Method)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Pager-dot/AeroVision#4-feature-engineering--scaling" rel="noopener noreferrer"&gt;4. Feature Engineering &amp;amp; Scaling&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Pager-dot/AeroVision#5-model-training" rel="noopener noreferrer"&gt;5. Model Training&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Pager-dot/AeroVision#6-model-evaluation" rel="noopener noreferrer"&gt;6. Model Evaluation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Pager-dot/AeroVision#project-structure" rel="noopener noreferrer"&gt;Project Structure&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Pager-dot/AeroVision#how-to-run" rel="noopener noreferrer"&gt;How to Run&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Pager-dot/AeroVision#features" rel="noopener noreferrer"&gt;Features&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Pager-dot/AeroVision#key-findings" rel="noopener noreferrer"&gt;Key Findings&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Pager-dot/AeroVision#technologies-used" rel="noopener noreferrer"&gt;Technologies Used&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Problem Statement&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;Air pollution is one of the most critical environmental challenges in India. Cities like Delhi, Ahmedabad, and Patna frequently experience hazardous pollution levels, directly impacting public health.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; Build a system that can:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Predict&lt;/strong&gt; the AQI value given pollutant concentrations (PM2.5, NO2, CO, etc.)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Classify&lt;/strong&gt; the predicted AQI into…&lt;/li&gt;
&lt;/ol&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/Pager-dot/AeroVision" 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;AeroVision is built around open-source ML, not a closed API:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;scikit-learn (Random Forest, Extra Trees, Gradient Boosting) and XGBoost for the actual AQI prediction — all trained locally on an open dataset (Kaggle's Air Quality Data in India), with the full pipeline (cleaning, IQR outlier removal, StandardScaler, train/test split) transparent and reproducible in train_model.py.&lt;/li&gt;
&lt;li&gt;Flask serves the models and the live view.&lt;/li&gt;
&lt;li&gt;For live readings, I use OpenWeatherMap's free Air Pollution API to get real pollutant concentrations for any city, then feed those straight into the same trained model — so live, real-world air quality gets scored on the same scale as the historical dataset, not just OpenWeather's own 1-5 index. The app shows both side by side.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The model itself has no dependency on any proprietary AI service — the entire prediction pipeline is open-source, inspectable, and retrainable by anyone who clones the repo.&lt;/p&gt;

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

&lt;p&gt;Because this is a public-health tool, not a toy. If the model were hidden behind a closed API, nobody could check why it predicted what it predicted, retrain it on their own city's data, or catch bias in it (e.g. is it systematically off for cities with sparse data?). Open tooling means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Anyone can see exactly how AQI is derived from raw pollutants (train_model.py is the whole pipeline, no black box).&lt;/li&gt;
&lt;li&gt;Anyone can swap in their own country's dataset and retrain — this doesn't have to be India-only.&lt;/li&gt;
&lt;li&gt;No API cost or rate-limit wall stands between someone and a decision that affects their health today.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>devchallenge</category>
      <category>hf26challenge</category>
      <category>ai</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Spend Review: I read my friend's bank statement so she never has to</title>
      <dc:creator>PARITOSH</dc:creator>
      <pubDate>Sat, 03 Oct 2026 16:25:54 +0000</pubDate>
      <link>https://dev.to/kparitosh760/spend-review-i-read-my-friends-bank-statement-so-she-never-has-to-2jo</link>
      <guid>https://dev.to/kparitosh760/spend-review-i-read-my-friends-bank-statement-so-she-never-has-to-2jo</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;I built this for my friend Sagrika, who spends like the money is not real and then&lt;br&gt;
arrives at the end of the month genuinely puzzled about where it went. Not reckless,&lt;br&gt;
exactly just a hundred small decisions that never felt like decisions, and no way to&lt;br&gt;
see them stacked up. Her bank sends her a statement every month. She has never once&lt;br&gt;
read it, and having now read one myself, I understand why.&lt;/p&gt;

&lt;p&gt;Every Indian bank hands you a statement and calls it insight. HDFC's is a 342-row&lt;br&gt;
&lt;code&gt;.xls&lt;/code&gt; where the first 20 rows are your address, the transaction you care about is&lt;br&gt;
described as &lt;code&gt;UPI-SOMTHING HOSPITALITY PVT-Q6245659728@YBL-YESB0YBLUPI-645845694696-PAID&lt;br&gt;
VIA SUPERMONEY&lt;/code&gt;, and nothing anywhere tells you that you spent ₹6,755 on food.&lt;/p&gt;

&lt;p&gt;The usual fix is to upload the statement to a budgeting site. That is the part I&lt;br&gt;
would not do. A bank statement is a complete record of where you were and who you&lt;br&gt;
paid for six months — it is not a file to hand to a free web service in exchange for&lt;br&gt;
a pie chart.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Spend Review&lt;/strong&gt; is a single-file Streamlit app that reads the statement, finds the&lt;br&gt;
real transaction table inside the bank's formatting, and sorts six months of spending&lt;br&gt;
into 14 categories. It holds nothing. The file is parsed in memory and gone when you&lt;br&gt;
close the tab.&lt;/p&gt;

&lt;p&gt;Upload, and you get:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Total spent, total received, transaction count&lt;/li&gt;
&lt;li&gt;Spend per category with each category's share&lt;/li&gt;
&lt;li&gt;Top 20 payees, with the bank's narration string cleaned into an actual name&lt;/li&gt;
&lt;li&gt;A filterable table of every transaction, downloadable as CSV&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's the whole app. It doesn't set budgets, it doesn't send reminders, and it will&lt;br&gt;
never tell her to stop buying things. It just makes six months of small decisions&lt;br&gt;
visible in one screen, which turns out to be the part that was missing.&lt;/p&gt;
&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

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

&lt;p&gt;Hosted demo: &lt;strong&gt;&lt;a href="https://spend-review.onrender.com" rel="noopener noreferrer"&gt;https://spend-review.onrender.com&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Free tier, so the first load takes about a minute while the service wakes up — give it&lt;br&gt;
a moment before deciding it's broken. Nothing you upload is stored, by the service or&lt;br&gt;
by me; the file is parsed in memory and dropped when you close the tab.&lt;/p&gt;

&lt;p&gt;In the recording: six months of an HDFC statement goes in as the bank exported it,&lt;br&gt;
342 rows of letterhead and footer and all, and 294 categorised transactions come out.&lt;br&gt;
The surprise is not the food row. It's that the subscriptions row is more than twice&lt;br&gt;
as large, spread over fourteen charges small enough that none of them ever felt like&lt;br&gt;
spending money.&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/Pager-dot" rel="noopener noreferrer"&gt;
        Pager-dot
      &lt;/a&gt; / &lt;a href="https://github.com/Pager-dot/spend-review" rel="noopener noreferrer"&gt;
        spend-review
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Streamlit app that categorises HDFC bank statement spending
    &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;Spend Review&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;A small Streamlit app that reads an HDFC Bank account statement (&lt;code&gt;.xls&lt;/code&gt; / &lt;code&gt;.xlsx&lt;/code&gt;)
and groups the transactions into spending categories.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Live demo: &lt;a href="https://spend-review.onrender.com" rel="nofollow noopener noreferrer"&gt;https://spend-review.onrender.com&lt;/a&gt;&lt;/strong&gt; — free tier, so the first load
takes about a minute while the service wakes up. Nothing you upload is stored.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Run locally&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;pip install -r requirements.txt
streamlit run app.py&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Then upload your statement in the browser.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;What it shows&lt;/h2&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Total spent, total received, transaction count&lt;/li&gt;
&lt;li&gt;Spend by category, with each category's share&lt;/li&gt;
&lt;li&gt;Top payees&lt;/li&gt;
&lt;li&gt;A filterable transaction table, downloadable as CSV&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Categories&lt;/h2&gt;

&lt;/div&gt;
&lt;p&gt;Categorisation is keyword matching on the transaction narration. The rules live in
the &lt;code&gt;RULES&lt;/code&gt; dict at the top of &lt;code&gt;app.py&lt;/code&gt; — add keywords there to tune it.&lt;/p&gt;
&lt;p&gt;Person-to-person UPI transfers fall into a single &lt;code&gt;Transfers to People&lt;/code&gt; bucket,
since those narrations carry only a name and no merchant information.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Privacy&lt;/h2&gt;

&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Uploaded statements are parsed in memory and never written…&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/Pager-dot/spend-review" 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;Streamlit, pandas, and about 200 lines of Python. No model, no API, no database. The&lt;br&gt;
interesting parts were all in the data.&lt;/p&gt;

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

&lt;p&gt;The honest version of my answer is that open innovation let me not use an API at all.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It costs nothing and cannot be taken away.&lt;/strong&gt; No key, no account, no quota, no&lt;br&gt;
pricing-page change in eighteen months. &lt;code&gt;pip install -r requirements.txt&lt;/code&gt; and it runs,&lt;br&gt;
today or in five years, with the Wi-Fi off. Free-tier API keys are the one component&lt;br&gt;
of a weekend project most likely to be dead before anyone else tries to run it.&lt;/p&gt;

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

&lt;p&gt;Built in an evening with Claude Code. The useful division of labour turned out to be&lt;br&gt;
that it did the archaeology and I did the judgement calls.&lt;/p&gt;

&lt;p&gt;It opened the &lt;code&gt;.xls&lt;/code&gt;, found the header at row 20, worked out that &lt;code&gt;xlrd&lt;/code&gt; was required,&lt;br&gt;
built the keyword rules from the actual narration strings in my statement rather than&lt;br&gt;
from a guess about Indian merchants, and caught the &lt;code&gt;$PORT&lt;/code&gt; binding issue before the&lt;br&gt;
first Render deploy instead of after it.&lt;/p&gt;

&lt;p&gt;Two moments worth recording. When I asked it to make a public repo it checked what was&lt;br&gt;
staged and grepped the committed history for my account number and card digits before&lt;br&gt;
pushing — the statement was gitignored, but it verified rather than assumed. And when I&lt;br&gt;
said &lt;em&gt;"make sure nothing is stored"&lt;/em&gt;, it came back with the &lt;code&gt;cache_data&lt;/code&gt; decorator it&lt;br&gt;
had itself added two steps earlier, and explained why that was the one thing in the app&lt;br&gt;
that broke the promise. I would not have found that.&lt;/p&gt;

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

&lt;p&gt;Best Use of Render&lt;/p&gt;

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