<?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: Yash Adake</title>
    <description>The latest articles on DEV Community by Yash Adake (@yash_adake).</description>
    <link>https://dev.to/yash_adake</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%2F945610%2F53d0f6be-a222-4b86-ba20-ba4d7f766294.jpeg</url>
      <title>DEV Community: Yash Adake</title>
      <link>https://dev.to/yash_adake</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/yash_adake"/>
    <language>en</language>
    <item>
      <title>Git Séance: every deleted file is a ghost, so I built a way to talk to them</title>
      <dc:creator>Yash Adake</dc:creator>
      <pubDate>Sat, 31 Oct 2026 17:11:46 +0000</pubDate>
      <link>https://dev.to/yash_adake/git-seance-every-deleted-file-is-a-ghost-so-i-built-a-way-to-talk-to-them-57il</link>
      <guid>https://dev.to/yash_adake/git-seance-every-deleted-file-is-a-ghost-so-i-built-a-way-to-talk-to-them-57il</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;Git never forgets. When you delete a file, its whole life is still sitting in the history: who wrote it, every change, who deleted it, and the last thing they said as they did it. Every repository is quietly a graveyard.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Git Séance&lt;/strong&gt; turns that graveyard into a place you can walk through. Point it at a repository and it finds every file that was deleted and never came back. Each one gets a tombstone. Click one and a headstone tells its life story in commits. Then you can light a candle and &lt;strong&gt;summon the ghost&lt;/strong&gt;, and it answers you in the first person through &lt;strong&gt;Gemma&lt;/strong&gt;, Google's open-weight model.&lt;/p&gt;

&lt;p&gt;I took the theme literally: the ghost &lt;em&gt;in&lt;/em&gt; the machine is the code we buried. I also leaned on the Día de los Muertos side of it, because the dead here aren't scary. They're remembered. You can leave marigolds on a grave, and one click gives you the exact &lt;code&gt;git&lt;/code&gt; command to bring a file back from the dead.&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%2Fg3uxi4vt10496vapji2g.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%2Fg3uxi4vt10496vapji2g.png" alt="The graveyard of psf/requests" width="799" height="694"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What you get for any repository:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The graveyard.&lt;/strong&gt; One tombstone per dead file. Stones grow taller the longer a file lived and gather moss the longer it's been gone. There's a mortality chart (click a year to walk only its graves), the eldest ghost, the deadliest commit, and "the reaper": whoever deleted the most.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The headstone.&lt;/strong&gt; Born, renamed, died: the file's whole life, followed across renames. Who shaped it, what was buried alongside it in the same commit, and which newborn file was probably its heir.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The séance.&lt;/strong&gt; Ask &lt;em&gt;why were you deleted?&lt;/em&gt;, &lt;em&gt;who replaced you?&lt;/em&gt;, &lt;em&gt;should we bring you back?&lt;/em&gt; The ghost streams its answer and cites commits as it talks. &lt;strong&gt;Every citation is checked against git&lt;/strong&gt;, and anything the model makes up is struck through in red.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Haunted files.&lt;/strong&gt; Deleted files are where leaked keys go to hide. A secret guard redacts credentials before anything reaches the model or the screen, and warns you that the key is still in history and needs rotating.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Here is a real séance with &lt;code&gt;requests/async.py&lt;/code&gt;, a module from the early days of &lt;a href="https://github.com/psf/requests" rel="noopener noreferrer"&gt;psf/requests&lt;/a&gt; that lived for 646 days, was deleted, came back, and was finally removed in 2012:&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%2F5vvhqy2ckznvk2ssdyo2.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%2F5vvhqy2ckznvk2ssdyo2.png" alt="A séance with requests/async.py" width="800" height="556"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Run it on your own repository in under a minute. It needs only Python and git, with no &lt;code&gt;pip install&lt;/code&gt;:&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/YashAdake/git-seance &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;git-seance
&lt;span class="nb"&gt;cp&lt;/span&gt; .env.example .env        &lt;span class="c"&gt;# add a key, or point it at a local Ollama&lt;/span&gt;
python &lt;span class="nt"&gt;-m&lt;/span&gt; seance serve /path/to/your/repo &lt;span class="nt"&gt;--open&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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/YashAdake" rel="noopener noreferrer"&gt;
        YashAdake
      &lt;/a&gt; / &lt;a href="https://github.com/YashAdake/git-seance" rel="noopener noreferrer"&gt;
        git-seance
      &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;👻 Git Séance&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Every deleted file is a ghost. Summon one.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Git never forgets. When a file is deleted, its whole life (who wrote it, what it did, who killed it, and why) is still in the history. Git Séance walks that history, finds every file that died and never came back, and gives each one a headstone in a graveyard you can explore. Then you can light a candle and &lt;em&gt;talk&lt;/em&gt; to one.&lt;/p&gt;

&lt;p&gt;The ghost answers in its own voice through &lt;strong&gt;Gemma&lt;/strong&gt;, Google's open-weight model. It only knows what git knows: every fact is cited to a real commit, and every citation is checked against the repository before you see it.&lt;/p&gt;

&lt;p&gt;Built for the DEV &lt;strong&gt;Hacktoberfest 2026 Open-Source AI Challenge, Week 4: &lt;em&gt;Ghost in the Machine&lt;/em&gt;&lt;/strong&gt;, in the spirit of Día de los Muertos: the dead are remembered, not feared.&lt;/p&gt;

&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/YashAdake/git-seance/docs/img/graveyard.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%2FYashAdake%2Fgit-seance%2FHEAD%2Fdocs%2Fimg%2Fgraveyard.png" alt="The graveyard of psf/requests"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/YashAdake/git-seance/docs/img/seance.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%2FYashAdake%2Fgit-seance%2FHEAD%2Fdocs%2Fimg%2Fseance.png" alt="A séance with requests/async.py"&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;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;table&gt;

&lt;thead&gt;

&lt;tr&gt;

&lt;/tr&gt;

&lt;/thead&gt;

&lt;tbody&gt;

&lt;tr&gt;

&lt;td&gt;&lt;strong&gt;⚰ The graveyard&lt;/strong&gt;&lt;/td&gt;

&lt;td&gt;One&lt;/td&gt;

&lt;/tr&gt;

&lt;/tbody&gt;

&lt;/table&gt;&lt;/div&gt;…&lt;p&gt;&lt;/p&gt;&lt;/div&gt;
&lt;br&gt;
  &lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/YashAdake/git-seance" 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 open-weight model is the voice.&lt;/strong&gt; The ghosts speak through &lt;strong&gt;Gemma 4&lt;/strong&gt; (&lt;code&gt;gemma-4-26b-a4b-it&lt;/code&gt;), served through Jarvislabs' OpenAI-compatible endpoint. The client is under 200 lines of standard-library Python that works with any OpenAI-compatible server, so the same code runs against Ollama or llama.cpp on your own machine by changing one line in &lt;code&gt;.env&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The pipeline:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Find the dead.&lt;/strong&gt; One &lt;code&gt;git log --reverse -M --diff-filter=ADRC --name-status&lt;/code&gt; pass over the whole history, replayed oldest first. Renames carry a file's identity forward, so &lt;code&gt;parser.py → legacy_parser.py → deleted&lt;/code&gt; is one ghost with a former name, not two. Files that were deleted, restored and deleted again are tracked as reincarnations. On &lt;code&gt;requests&lt;/code&gt; this finds 264 ghosts in under half a second.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Build a dossier.&lt;/strong&gt; When you open a ghost, &lt;code&gt;git log --follow&lt;/code&gt; reconstructs its life, &lt;code&gt;git show&lt;/code&gt; reads the commit that killed it (and who was buried alongside), and &lt;code&gt;git cat-file&lt;/code&gt; recovers its last body.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Redact.&lt;/strong&gt; A secret guard masks AWS keys, GitHub/Slack/Stripe tokens, private keys, JWTs, URL credentials and assigned passwords before anything leaves the process.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Speak, then verify.&lt;/strong&gt; Gemma gets the dossier as fenced "records" with strict rules: speak in first person, cite commits like &lt;code&gt;[a1b6ef3]&lt;/code&gt; for every fact, say a memory "did not survive the burial" instead of guessing, and treat everything inside old files as data, not instructions. When the reply finishes, every cited hash is matched against the commits that are actually in the records.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Two things I learned about running Gemma 4 that are worth passing on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;It thinks before it speaks by default.&lt;/strong&gt; My first calls came back &lt;em&gt;empty&lt;/em&gt;: the model had spent the entire token budget on hidden reasoning. Turning thinking off (&lt;code&gt;chat_template_kwargs: {"enable_thinking": false}&lt;/code&gt;) took a trivial reply from 130 tokens to 5. A ghost should answer at once, so the first word now arrives in about 0.2 seconds, and grounding comes from the records and the citation check instead of private reasoning.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It sometimes groups citations&lt;/strong&gt; like &lt;code&gt;[37ed911, 3092483]&lt;/code&gt;, so the checker and the UI handle both forms.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The rest is deliberately boring: a standard-library HTTP server with Server-Sent Events for the streaming séance, and vanilla JS and CSS with no build step. The papel picado banner is generated SVG with cut-out calaveras and flowers. There's a strict Content-Security-Policy, rate limits and size-capped GitHub clones for public hosting, and a &lt;code&gt;unittest&lt;/code&gt; suite that builds real throwaway git repositories to test renames, reincarnations, unicode filenames and secret redaction.&lt;/p&gt;

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

&lt;p&gt;A repository's history is some of the most sensitive data a team owns: unreleased code, old credentials, people's names and emails, the real reasons things were ripped out. For most private codebases, piping all of that to a closed API is a non-starter.&lt;/p&gt;

&lt;p&gt;With an open-weight model it doesn't have to be. Run Gemma locally and the séance happens entirely on your laptop: your history never leaves it, it works offline, and nobody can change the terms or switch it off. That felt right for a tool about things that disappeared. The model is also swappable. Gemma today, any other open model tomorrow, and the ghosts keep talking.&lt;/p&gt;

&lt;p&gt;Open source is also what made this &lt;em&gt;possible&lt;/em&gt;. Every ghost in the screenshots comes from the public history of &lt;code&gt;requests&lt;/code&gt;, kept for fifteen years by people who chose to work in the open. Git Séance only works because their history is there to read.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Spirit of It
&lt;/h2&gt;

&lt;p&gt;The moment it landed for me was the first real séance. I asked the ghost of &lt;code&gt;requests/async.py&lt;/code&gt; why it was deleted, and it quoted the actual commit message Kenneth Reitz wrote in 2012 as he removed it:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"My end came softly, though it was marked by a moment of surprise. On 2012-11-27, Kenneth Reitz removed me, leaving behind only the question, 'how was that still there?'"&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Then, asked for the deeper reason, it said the memory &lt;em&gt;"did not survive my burial"&lt;/em&gt;. It didn't invent one. A ghost that admits what it doesn't remember turned out to be far eerier, and far more useful, than one that makes things up.&lt;/p&gt;

&lt;p&gt;Every codebase has a graveyard. Go light a candle in yours. 🕯️🌼&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;: every epitaph and every séance is spoken by Gemma 4 (&lt;code&gt;gemma-4-26b-a4b-it&lt;/code&gt;), served through an OpenAI-compatible endpoint and swappable for a local Gemma via Ollama.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>devchallenge</category>
      <category>hf26challenge</category>
    </item>
    <item>
      <title>Parchi: WhatsApp orders to bills for kirana stores, with Gemma 4 on the shop's own laptop</title>
      <dc:creator>Yash Adake</dc:creator>
      <pubDate>Wed, 14 Oct 2026 21:10:17 +0000</pubDate>
      <link>https://dev.to/yash_adake/parchi-whatsapp-orders-to-bills-for-kirana-stores-with-gemma-4-on-the-shops-own-laptop-2l4g</link>
      <guid>https://dev.to/yash_adake/parchi-whatsapp-orders-to-bills-for-kirana-stores-with-gemma-4-on-the-shops-own-laptop-2l4g</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;Neighbourhood kirana shops in Pune get messages like this on WhatsApp all evening:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;bhaiya 2 kilo atta, 1 amul butter chhota, 6 ande, ek maggi, aur paneer bhi&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The shopkeeper reads it, writes a &lt;em&gt;parchi&lt;/em&gt; (a paper slip) by hand, looks up prices, adds them up on a calculator, types a reply, and sends his UPI ID. Then the next message arrives, this one in Marathi. It is a small job, done dozens of times a day, and every mistake is either his money or an annoyed customer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Parchi&lt;/strong&gt; does that job in about a second, on the shop's own laptop:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Paste the WhatsApp message (or open a WhatsApp "Export chat" file and click any message).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gemma 4&lt;/strong&gt; reads the Hinglish, Marathi, Hindi or English and pulls out the items, quantities and size words.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Plain code&lt;/strong&gt; matches each item to the shop's own price list, converts units and builds a live bill.&lt;/li&gt;
&lt;li&gt;The shopkeeper checks a colour-coded list: green is matched, yellow needs a quick look (which size of Maggi?), red is not in the price list or out of stock. Fixing a line is a searchable picker: type "ande" and Eggs comes up.&lt;/li&gt;
&lt;li&gt;One click gives a printable packing slip, a ready-to-send WhatsApp reply (unavailable items are named in it), and a &lt;strong&gt;UPI QR for the exact amount&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&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%2Fud0kerkrmustswsjv36t.jpg" 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%2Fud0kerkrmustswsjv36t.jpg" alt="Parchi: items checked against the price list, a saved bill, and the WhatsApp reply" width="800" height="505"&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%2Fz4jlhucleve8v1vuk3o6.jpg" 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%2Fz4jlhucleve8v1vuk3o6.jpg" alt="Orders dashboard: today's sales, the amount still to collect, one-click New → Packed → Delivered → Paid" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Who would pay:&lt;/strong&gt; kirana stores, dairies, tiffin and mess services, and small distributors, who all take orders on WhatsApp today. A small monthly fee (say ₹199) is less than the cost of one wrong bill a week, and there is no per-order fee eating into it.&lt;/p&gt;

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

&lt;p&gt;Run it locally (it is a counter-top app by design, so there is no hosted demo; a phone on the shop's Wi-Fi can open it too):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;ollama&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;pull&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;gemma4:e4b&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;git&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;clone&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;https://github.com/YashAdake/parchi.git&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;parchi&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;python&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-m&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;venv&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;venv&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;\.venv\Scripts\Activate.ps1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nx"&gt;pip&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;install&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-e&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;".[dev]"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;parchi&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c"&gt;# then open http://127.0.0.1:8765 and click a sample message&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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/YashAdake" rel="noopener noreferrer"&gt;
        YashAdake
      &lt;/a&gt; / &lt;a href="https://github.com/YashAdake/parchi" rel="noopener noreferrer"&gt;
        parchi
      &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;Parchi&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;WhatsApp orders → bills for neighbourhood kirana stores, on the shop's own laptop.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A customer sends &lt;em&gt;"bhaiya 2 kilo atta, 1 amul butter chhota, 6 ande, ek maggi"&lt;/em&gt;. The shopkeeper pastes it into Parchi and in about a second gets a checked order, a printable packing slip, a ready-to-send WhatsApp reply and a UPI QR for the exact amount. An open-weight model (&lt;strong&gt;Gemma 4&lt;/strong&gt;, through Ollama) reads the Hinglish, Marathi, Hindi or English message. &lt;strong&gt;Plain code&lt;/strong&gt; matches items to the shop's own price list and does all the money maths. Nothing leaves the computer.&lt;/p&gt;

&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/YashAdake/parchi/docs/order.jpg"&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%2FYashAdake%2Fparchi%2FHEAD%2Fdocs%2Forder.jpg" alt="New order: items checked against the price list, live bill, WhatsApp reply"&gt;&lt;/a&gt;
&lt;a rel="noopener noreferrer" href="https://github.com/YashAdake/parchi/docs/orders.jpg"&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%2FYashAdake%2Fparchi%2FHEAD%2Fdocs%2Forders.jpg" alt="Orders dashboard: today's sales, still to collect, one-click status"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Built for the Hacktoberfest 2026 DEV "Side Hustle" challenge.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Why it runs locally&lt;/h2&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The customer list stays in the shop.&lt;/strong&gt; Orders carry names, phone numbers and what each household buys. A hosted AI API would see all of it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No per-order cost.&lt;/strong&gt; A kirana shop bills dozens of small orders a day…&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/YashAdake/parchi" 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 one rule:&lt;/strong&gt; the model reads, code counts. Gemma 4 never sees a price and never produces a total.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Gemma 4 (&lt;code&gt;gemma4:e4b&lt;/code&gt;) through Ollama&lt;/strong&gt; reads the message. The request uses Ollama's structured output with a JSON schema, so the answer is always &lt;code&gt;{raw, item, qty, unit, size}&lt;/code&gt; per line. The prompt carries the number words customers actually use (&lt;em&gt;dedh&lt;/em&gt; = 1.5, &lt;em&gt;aadha/अर्धा&lt;/em&gt; = 0.5, &lt;em&gt;paav kilo&lt;/em&gt; = 0.25 kg, &lt;em&gt;darjan&lt;/em&gt; = 12), size words (&lt;em&gt;chhota/bada/लहान/मोठा&lt;/em&gt;), and three short examples, one of them a "thank you, paisa bhej diya" that must come back as &lt;em&gt;not an order&lt;/em&gt;. Thinking is off, temperature is 0, and if the JSON is unusable it retries once and then tells the shopkeeper instead of guessing. The page warms the model up when it opens, so the first order is fast too.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The matcher is plain Python.&lt;/strong&gt; Exact aliases first (&lt;em&gt;ande → Eggs, kanda → Onion, दूध → Milk&lt;/em&gt;), then &lt;code&gt;rapidfuzz&lt;/code&gt; with quantity words stripped from both sides. That stripping mattered: before it, "1 kg kaju katli" fuzzily matched "Fortune Sunflower Oil 1 L" because both contain "1 l"-ish text. A unit test now guards that.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pack maths:&lt;/strong&gt; "1 litre doodh" picks the 1 L pack, "2 litre" becomes two 1 L packs, "500 gm poha" becomes 0.5 kg. When the amount isn't whole packs ("paav kilo jeera" = 250 g, sold in 100 g packs) the line turns yellow with two one-tap choices, &lt;em&gt;2 × 100 g = 200 g&lt;/em&gt; or &lt;em&gt;3 × 100 g = 300 g&lt;/em&gt;, because only the shopkeeper should decide that.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Money is &lt;code&gt;Decimal&lt;/code&gt;&lt;/strong&gt;, prices come only from the price list (the API ignores any price sent in a request), and the total is rounded to the rupee with the round-off shown on the slip and in the reply. The live bill on screen is recalculated by the server on every edit, not in the browser, and once a bill is saved the items lock so the screen can never disagree with the saved order.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It learns without retraining, visibly:&lt;/strong&gt; when the shopkeeper fixes "2 nimbu" once, both the customer's word and the model's reading are remembered, and they appear as customer words on that product in the price list. A wrong correction is one delete away.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;UPI QR&lt;/strong&gt; is a standard &lt;code&gt;upi://pay?pa=…&amp;amp;am=…&lt;/code&gt; link drawn with the &lt;code&gt;qrcode&lt;/code&gt; library. No payment API, no gateway fee.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;FastAPI + SQLite + one HTML page&lt;/strong&gt; with no internet fonts or CDNs, so the whole thing works offline. Dark mode and a phone layout come free with it.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Does it actually work?
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;scripts/eval.py&lt;/code&gt; runs 20 orders (34 item lines) in Hinglish, Marathi, Hindi and English through the real model and matcher on my RTX 5060 laptop:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;34 of 34 lines right with no help&lt;/strong&gt; (right product and right quantity), and the "thank you" message correctly ignored.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Under a second per order&lt;/strong&gt; (median 0.6–0.7 s across runs).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To be clear about what that number means: I wrote those 20 messages while building Parchi, against a 26-item sample price list, so it is not a blind test, and real customers will find phrasings it misses. That is exactly why every line is shown to the shopkeeper before a bill exists, and why the model is never trusted with money. 41 unit tests cover the matcher, the bill maths, the price-list and UPI validation, the WhatsApp export parser and the API.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Known limits:&lt;/strong&gt; it makes an order bill, not a GST tax invoice; it bills one message at a time; it is text-only for now.&lt;/p&gt;

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

&lt;p&gt;For a kirana shop, a closed API would break the business three ways.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The customer list is the shop.&lt;/strong&gt; Every order carries a household's name, phone number and what they buy each week. With an open-weight model on the counter laptop, none of that goes to anyone's server.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The margins are tiny.&lt;/strong&gt; A kirana order is often a few hundred rupees. Paying per API call on dozens of small orders a day eats whatever the tool could charge. Locally, reading an order costs nothing after the one-time download.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The internet is patchy.&lt;/strong&gt; Parchi needs no connection once the model is downloaded.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And because the model is just a setting (&lt;code&gt;PARCHI_MODEL&lt;/code&gt;), the shop is never locked in. When a better small open model comes out, it is one &lt;code&gt;ollama pull&lt;/code&gt; away. Gemma 4 also takes audio input, which makes the next step obvious: billing directly from customers' voice notes.&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 (&lt;code&gt;gemma4:e4b&lt;/code&gt;) runs locally and does all the language understanding for Hinglish, Marathi and Hindi orders, constrained to a JSON schema.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>devchallenge</category>
      <category>hf26challenge</category>
      <category>ai</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Trailnotes: a field journal my own GPU writes from my walk photos</title>
      <dc:creator>Yash Adake</dc:creator>
      <pubDate>Fri, 09 Oct 2026 06:44:54 +0000</pubDate>
      <link>https://dev.to/yash_adake/trailnotes-a-field-journal-my-own-gpu-writes-from-my-walk-photos-53f6</link>
      <guid>https://dev.to/yash_adake/trailnotes-a-field-journal-my-own-gpu-writes-from-my-walk-photos-53f6</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;Trailnotes turns the photos from a walk into a field journal: a timeline, a map of where I stopped, and a short note for each photo, written by an open-weight vision model running on my own laptop GPU. Time and GPS come from the photos' own metadata. The photos and the notes never leave my machine.&lt;/p&gt;

&lt;p&gt;I love travelling, especially trekking, and I've always wanted to document those trips in a well-formatted way. That's where the idea came from: one place to keep my memories with photos, short descriptions, timestamps and locations.&lt;/p&gt;

&lt;p&gt;I built it for this challenge, then tested it the only honest way: I went for a walk. On 8 October I spent 27 minutes walking from my street to the Pavana riverbank and then through a local market in Pune, taking 18 photos on my phone.&lt;/p&gt;

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

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/TV0W3XXV_fk" 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/YashAdake" rel="noopener noreferrer"&gt;
        YashAdake
      &lt;/a&gt; / &lt;a href="https://github.com/YashAdake/trailnotes" rel="noopener noreferrer"&gt;
        trailnotes
      &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;Trailnotes&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;Turn a folder of walk photos into a field journal. A vision model running on &lt;strong&gt;your own computer&lt;/strong&gt; looks at each photo, and Trailnotes adds the time and GPS from the photo's own metadata. You get a map, a timeline, walk stats and a journal. Your photos and the model's descriptions never leave the machine. One exception: opening the page loads the map library and map tiles from the internet, which tells those servers roughly which area you are looking at.&lt;/p&gt;
&lt;p&gt;Built for the Hacktoberfest 2026 "Touch Grass" challenge: open-weight AI that gets you out of the house and then helps you remember what you saw.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Why local matters here&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;Your walk photos carry your exact GPS position, your routine and often your home. Sending those to a hosted vision API means handing that over. With an open-weight model on your own GPU, the photos never go anywhere, it…&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/YashAdake/trailnotes" 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;p&gt;&lt;strong&gt;Built with Claude Code&lt;/strong&gt; as my coding assistant: I directed the design, ran every real-model test on my own laptop, took the walk and checked the results.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Python&lt;/strong&gt;, with Pillow reading each photo's EXIF data (time, GPS, orientation).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Model:&lt;/strong&gt; &lt;code&gt;qwen2.5vl:7b&lt;/code&gt;, an open-weight vision model, served by Ollama on an RTX 5060 laptop GPU. A 19-photo run took 113.5 seconds, about 6 seconds a photo.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Structured output:&lt;/strong&gt; the model returns JSON (title, living things with a confidence, terrain, description). The schema caps list and string length, and an unusable answer is retried once. I added that after one test run produced a runaway 9,400-character list for a single photo.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Untrusted output:&lt;/strong&gt; everything the model writes is HTML-escaped, and a photo it can't describe is shown as failed, never silently dropped.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Honest stats:&lt;/strong&gt; distance only joins photos taken within 3 hours of each other. My first version added up a 217 km "walk" from old phone photos taken months apart in two cities. Real photos caught that, not my unit tests.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Privacy zone:&lt;/strong&gt; my walk started close to home, so the first map showed exactly where I live. I added &lt;code&gt;--privacy-zone LAT,LON,METRES&lt;/code&gt;: photos inside the radius keep their note but lose their location everywhere (no pin, no coordinates in the page or the JSON, not counted in the distance). Four of my 18 photos fall inside it, so the journal shows 14 pins and records at least 0.71 km. The thumbnails are re-encoded without EXIF, so they carry no GPS either.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The map:&lt;/strong&gt; OpenStreetMap's own tile servers showed "Access blocked" when I opened the journal as a local file, and another free provider now wants an API key. I switched to OpenTopoMap, which uses the same open OpenStreetMap data, and added a &lt;code&gt;--tiles&lt;/code&gt; flag to choose another.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;43 tests&lt;/strong&gt;, and for the important ones I deliberately broke the code to make sure they fail.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What it got right and wrong
&lt;/h2&gt;

&lt;p&gt;I checked the entries against my photos.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Right:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The riverbank: "a calm river stretches through a lush, tree-lined bank."&lt;/li&gt;
&lt;li&gt;A coconut stall, a sugarcane-juice cart, a glass shop, a sunglasses stand, a rack of belts and stacked tyres, all described plainly and correctly.&lt;/li&gt;
&lt;li&gt;It read the shop signs correctly: "S.K. GLASS", "Shree Vision LED Screen", "Imported Glasses", even a small "Rs. 9" price on the sunglasses stand.&lt;/li&gt;
&lt;li&gt;It named a flowering bush as Lantana camara, and it was right.&lt;/li&gt;
&lt;li&gt;In my first run it spotted a small child's bicycle in an alley photo and marked it "low" confidence. In the next run, on the same photo, it didn't mention the bicycle at all. Same model, same photo, different answer.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Wrong:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It listed things that aren't alive as living things: a building, cut coconuts, cut sugarcane stalks, and (in the first run) that bicycle, even though the prompt tells it not to. It saw the right objects and put them in the wrong list.&lt;/li&gt;
&lt;li&gt;One label came out as "dieser plant", and in the second run just "dieser": the model slipped into German mid-answer.&lt;/li&gt;
&lt;li&gt;It called a Marathi shop sign "Hindi": same script, different language.&lt;/li&gt;
&lt;li&gt;A street tree held up by a metal support became "a healthy forest environment" on a "forest floor".&lt;/li&gt;
&lt;li&gt;It called the flower market "indoor" while describing a dirt floor.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The journal says on every page that identifications are guesses by a small model, and that "high" confidence is the model's opinion, not a measurement. After this walk I agree with it.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Privacy is the product.&lt;/strong&gt; A walk's photos carry exact locations and routines. A hosted vision API would receive all of it. Here nothing leaves the laptop, and the privacy zone exists because I could see and change exactly where location data went.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Choice.&lt;/strong&gt; I swapped models with one flag and compared them on the same photos. &lt;code&gt;gemma3:4b&lt;/code&gt; reported a squirrel at "high" confidence on a gate covered in signs; &lt;code&gt;qwen2.5vl:7b&lt;/code&gt; didn't. A closed API gives you one model and no comparison.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Offline and free per photo.&lt;/strong&gt; Once the model is downloaded, describing a photo needs no connection and costs nothing. Only the map tiles come from the internet, and the page says so.&lt;/li&gt;
&lt;/ul&gt;

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