<?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: Vaibhav Chaudhari </title>
    <description>The latest articles on DEV Community by Vaibhav Chaudhari  (@vaibhav7549).</description>
    <link>https://dev.to/vaibhav7549</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%2F4159391%2Fef905e28-76bf-4216-b745-55c753fd64e3.jpg</url>
      <title>DEV Community: Vaibhav Chaudhari </title>
      <link>https://dev.to/vaibhav7549</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/vaibhav7549"/>
    <language>en</language>
    <item>
      <title>LedgerLens: A Privacy-First Receipt Sorter That Never Uploads Your Client's Data</title>
      <dc:creator>Vaibhav Chaudhari </dc:creator>
      <pubDate>Tue, 13 Oct 2026 12:41:56 +0000</pubDate>
      <link>https://dev.to/vaibhav7549/ledgerlens-a-privacy-first-receipt-sorter-that-never-uploads-your-clients-data-1de9</link>
      <guid>https://dev.to/vaibhav7549/ledgerlens-a-privacy-first-receipt-sorter-that-never-uploads-your-clients-data-1de9</guid>
      <description>&lt;p&gt;This is a submission for the Hacktoberfest Open-Source AI Challenge Week 2: Side Hustle&lt;/p&gt;

&lt;p&gt;What I Built&lt;br&gt;
LedgerLens is a receipt and invoice sorter for freelancers that runs the expensive part of the pipeline — reading the document — entirely on your own laptop.&lt;/p&gt;

&lt;p&gt;Here's the problem it solves. If you're a freelancer, you already know the drill: every quarter you sit down with a shoebox of receipts and a spreadsheet, and you retype vendor names, dates, amounts, and categories by hand. It's an hour or two of pure tedium, and it's the exact kind of task an LLM should be doing for you.&lt;/p&gt;

&lt;p&gt;But freelancers have a specific reason to be nervous about that. Receipts contain client names, project details, rates, and sometimes partial card numbers. Handing that to a hosted vision API means uploading your clients' financial information to a server you don't control, under a privacy policy you didn't write, on a per-image bill you have to absorb.&lt;/p&gt;

&lt;p&gt;So LedgerLens splits the job:&lt;/p&gt;

&lt;p&gt;The document never leaves your machine. A local Gemma model, running through Ollama, reads the image and extracts vendor, date, amount, and a short neutral summary. No network call.&lt;/p&gt;

&lt;p&gt;Only the anonymized summary goes to the backend. The vendor name stays local; the summary is something like "monthly subscription for design software, billed once." No client names, no card numbers, no images.&lt;/p&gt;

&lt;p&gt;TabPFN classifies it. A backend on Render takes that summary plus the amount and predicts a tax category — Software, Office Supplies, Travel, Meals, Equipment — using a model fitted on the freelancer's own past categorizations.&lt;/p&gt;

&lt;p&gt;You get a CSV. Categorized, ready to hand to your accountant.&lt;/p&gt;

&lt;p&gt;Someone would pay for this because the alternative is either an hour of their Sunday or uploading client data to a company they've never heard of. LedgerLens costs essentially nothing to run, which means it can be sold for $5/month instead of $30/month — the pricing actually works as a side hustle.&lt;/p&gt;

&lt;p&gt;Demo&lt;br&gt;
Deployed frontend: &lt;a href="https://ledgerlens.onrender.com" rel="noopener noreferrer"&gt;https://ledgerlens.onrender.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The demo walks through: dropping in a photo of a receipt, watching the local extraction step (you can see the network tab stays quiet during it), then the classification coming back from Render a second later.&lt;/p&gt;

&lt;p&gt;Code&lt;br&gt;
GitHub repo: &lt;a href="https://github.com/vaibhav7549/ledgerlens" rel="noopener noreferrer"&gt;https://github.com/vaibhav7549/ledgerlens&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;text&lt;br&gt;
ledgerlens/&lt;br&gt;
├── local/&lt;br&gt;
│   ├── extract.py          # Gemma via Ollama — runs on user's machine&lt;br&gt;
│   └── requirements.txt&lt;br&gt;
├── backend/&lt;br&gt;
│   ├── main.py             # FastAPI + TabPFN classifier&lt;br&gt;
│   ├── embed.py            # sentence-transformers for text → vector&lt;br&gt;
│   ├── seed_invoices.csv   # example historical categorizations&lt;br&gt;
│   └── requirements.txt&lt;br&gt;
├── frontend/&lt;br&gt;
│   └── index.html&lt;br&gt;
└── render.yaml&lt;br&gt;
How I Built It&lt;br&gt;
The local layer: Gemma via Ollama.&lt;/p&gt;

&lt;p&gt;The user installs Ollama and pulls a Gemma model:&lt;/p&gt;

&lt;p&gt;bash&lt;br&gt;
ollama pull gemma2:2b&lt;br&gt;
local/extract.py takes an image path and asks Gemma for structured output:&lt;/p&gt;

&lt;p&gt;python&lt;br&gt;
import ollama, json, base64&lt;/p&gt;

&lt;p&gt;PROMPT = """Read this receipt. Return ONLY JSON with these keys:&lt;br&gt;
vendor, date (YYYY-MM-DD), amount (float), summary.&lt;br&gt;
The summary must describe WHAT was purchased in neutral terms.&lt;br&gt;
Do NOT include any client names, project names, or card numbers in the summary."""&lt;/p&gt;

&lt;p&gt;def extract(image_path):&lt;br&gt;
    with open(image_path, "rb") as f:&lt;br&gt;
        img = base64.b64encode(f.read()).decode()&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;resp = ollama.chat(
    model="gemma2:2b",
    messages=[{"role": "user", "content": PROMPT, "images": [img]}],
    format="json",
)
return json.loads(resp["message"]["content"])
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;That Do NOT include line in the prompt is doing real work. It's the boundary between the private half of the pipeline and the public half, and I test it explicitly — see the sanitization test in backend/tests/test_sanitize.py.&lt;/p&gt;

&lt;p&gt;The classification layer: TabPFN on Render.&lt;/p&gt;

&lt;p&gt;The interesting part is that TabPFN is a tabular foundation model, and a receipt summary is text. So the backend converts the summary to a vector first with a small local sentence-transformer, then feeds [embedding..., amount] to TabPFN as a feature row. What TabPFN sees is a table; what the user typed is English.&lt;/p&gt;

&lt;p&gt;python&lt;br&gt;
from fastapi import FastAPI&lt;br&gt;
from tabpfn_client import TabPFNClassifier, set_access_token&lt;br&gt;
from sentence_transformers import SentenceTransformer&lt;br&gt;
import numpy as np, pandas as pd&lt;/p&gt;

&lt;p&gt;set_access_token(os.environ["TABPFN_API_KEY"])&lt;br&gt;
embedder = SentenceTransformer("all-MiniLM-L6-v2")&lt;/p&gt;

&lt;p&gt;app = FastAPI()&lt;/p&gt;

&lt;p&gt;def featurize(summary: str, amount: float) -&amp;gt; list[float]:&lt;br&gt;
    vec = embedder.encode(summary).tolist()&lt;br&gt;
    return vec + [amount]&lt;/p&gt;

&lt;p&gt;def train():&lt;br&gt;
    df = pd.read_csv("seed_invoices.csv")&lt;br&gt;
    X = np.array([featurize(s, a) for s, a in zip(df.summary, df.amount)])&lt;br&gt;
    y = df.category.values&lt;br&gt;
    clf = TabPFNClassifier(model_path="v3.5_default")&lt;br&gt;
    clf.fit(X, y)&lt;br&gt;
    return clf&lt;/p&gt;

&lt;p&gt;classifier = train()&lt;/p&gt;

&lt;p&gt;@app.post("/classify")&lt;br&gt;
def classify(payload: dict):&lt;br&gt;
    x = np.array([featurize(payload["summary"], payload["amount"])])&lt;br&gt;
    return {"category": classifier.predict(x)[0]}&lt;br&gt;
There's a real design decision here worth naming: TabPFN is fitted on the individual freelancer's history, not on a global dataset. Twenty of your own past receipts is enough to get useful predictions. That's a property a general-purpose LLM API can't match, because you can't fine-tune it on twenty rows without paying for a training run.&lt;/p&gt;

&lt;p&gt;The deployment layer: Render.&lt;/p&gt;

&lt;p&gt;The FastAPI service is deployed as a Render Web Service, defined in render.yaml:&lt;/p&gt;

&lt;p&gt;yaml&lt;br&gt;
services:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;type: web
name: ledgerlens-api
runtime: python
buildCommand: pip install -r backend/requirements.txt
startCommand: uvicorn backend.main:app --host 0.0.0.0 --port $PORT
envVars:

&lt;ul&gt;
&lt;li&gt;key: TABPFN_API_KEY
sync: false
Render handles the build, the TLS, and the restarts. The TabPFN API key lives in Render's environment variables and never touches the client.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The connection between the two halves.&lt;/p&gt;

&lt;p&gt;The local script is what orchestrates everything. It extracts locally, strips the vendor name, POSTs the sanitized summary to Render, and appends the returned category to a local CSV. The user's document store and the classification service never share a file.&lt;/p&gt;

&lt;p&gt;python&lt;br&gt;
def process(image_path):&lt;br&gt;
    data = extract(image_path)                       # local only&lt;br&gt;
    safe = {"summary": data["summary"], "amount": data["amount"]}&lt;br&gt;
    r = requests.post(f"{API_URL}/classify", json=safe)&lt;br&gt;
    row = {**data, "category": r.json()["category"]}&lt;br&gt;
    append_to_csv("ledger.csv", row)&lt;br&gt;
Why Does Open Innovation Matter?&lt;br&gt;
Three things about this project are only possible because the models are open.&lt;/p&gt;

&lt;p&gt;Privacy is architectural, not contractual. With a hosted vision API, "we don't train on your data" is a promise in a terms-of-service document. With a local Gemma model, it's a fact about where the bytes went. The receipt images never cross a network boundary because there is no code path that sends them. A freelancer can hand this to a client and say your invoice data stayed on my laptop and mean it literally. No closed API can offer that, because the whole product is the API call.&lt;/p&gt;

&lt;p&gt;The unit economics actually work. Per-image pricing on a hosted vision model means every receipt the user sorts costs me money. At $5/month, a heavy user could put me underwater. Running Gemma locally moves that cost to the user's own hardware, where it's zero. The only paid call in the whole pipeline is the TabPFN classification, which is one small request per receipt instead of one vision request per receipt. This is the difference between a side hustle that can be priced to sell and one that has to be priced to survive.&lt;/p&gt;

&lt;p&gt;The classifier belongs to the user. TabPFN gets fitted on the freelancer's own categorization history, which means their weird personal convention — that they file their phone bill under "Utilities" and their domain renewals under "Software" — becomes part of the model. You can't do that with a closed API without a fine-tuning bill, and you can't do it per-user at all at $5/month. And because the local model is swappable, a user with a beefier laptop can drop in a larger Gemma and get better extraction without asking anyone's permission.&lt;/p&gt;

&lt;p&gt;The last one matters most to me. The open pieces aren't decoration on top of a closed product — remove any of them and the design collapses.&lt;/p&gt;

&lt;p&gt;Prize Categories&lt;br&gt;
Best Use of Gemma — the extraction layer runs Gemma locally via Ollama, and it's what makes the privacy guarantee real.&lt;/p&gt;

&lt;p&gt;Best Use of TabPFN — TabPFN-3.5 is the classifier, fitted per-user on embedded receipt summaries plus amount, inside a wider prediction flow.&lt;/p&gt;

&lt;p&gt;Best Use of Render — the FastAPI service and frontend are deployed as a Render Web Service via render.yaml.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>hf26challenge</category>
    </item>
    <item>
      <title>Night Sky Companion: A Fully Offline Constellation Spotter Powered by Gemma 2B</title>
      <dc:creator>Vaibhav Chaudhari </dc:creator>
      <pubDate>Tue, 06 Oct 2026 19:13:29 +0000</pubDate>
      <link>https://dev.to/vaibhav7549/night-sky-companion-a-fully-offline-constellation-spotter-powered-by-gemma-2b-1nn5</link>
      <guid>https://dev.to/vaibhav7549/night-sky-companion-a-fully-offline-constellation-spotter-powered-by-gemma-2b-1nn5</guid>
      <description>&lt;p&gt;This is a submission for the Hacktoberfest Open-Source AI Challenge Week 1: Touch Grass&lt;/p&gt;

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

&lt;p&gt;Night Sky Companion tells you what's overhead, with no internet connection at all.&lt;/p&gt;

&lt;p&gt;Enter your location and press one button. The app works out which constellations are above your horizon, narrates their mythology using a local open-weight LLM, and gives you a single "look here" direction. Then it switches to a red-light palette to protect your night vision, and an optional pocket mode tells you to put the screen away and look up.&lt;/p&gt;

&lt;p&gt;The whole on-screen experience takes under 15 seconds. The rest is you and the sky.&lt;/p&gt;

&lt;p&gt;I built it for hikers, campers, and anyone who has stared at a dark sky and wondered what they were looking at, without needing a signal or a server farm to find out.&lt;/p&gt;

&lt;p&gt;Demo&lt;/p&gt;

&lt;p&gt;GitHub: &lt;a href="https://github.com/vaibhav7549/Night-Sky-Companion" rel="noopener noreferrer"&gt;https://github.com/vaibhav7549/Night-Sky-Companion&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here's what one scan returns:&lt;/p&gt;

&lt;p&gt;text&lt;br&gt;
🌙 NIGHT SKY COMPANION&lt;br&gt;
─────────────────────────────────────&lt;br&gt;
📍 Location: 28.61°N, 77.21°E&lt;br&gt;
🌕 Moon: Waxing Crescent (17% illuminated)&lt;/p&gt;

&lt;p&gt;✨ Most Prominent Constellations:&lt;/p&gt;

&lt;p&gt;Andromeda (And) — 82° up, toward North&lt;br&gt;
"Andromeda was a princess chained to a rock as a sacrifice&lt;br&gt;
to a sea monster. Perseus rescued her and married her..."&lt;/p&gt;

&lt;p&gt;Pegasus (Peg) — 74° up, toward South&lt;br&gt;
"Pegasus is the winged horse that sprang from the blood&lt;br&gt;
of Medusa when Perseus beheaded her..."&lt;/p&gt;

&lt;p&gt;Cassiopeia (Cas) — 62° up, toward North&lt;br&gt;
"Cassiopeia was a vain queen in Greek mythology..."&lt;/p&gt;

&lt;p&gt;🔴 RED-LIGHT MODE ACTIVE: night vision preserved&lt;br&gt;
📴 POCKET MODE: Put your phone down. The sky is still there.&lt;/p&gt;

&lt;p&gt;Run it yourself:&lt;/p&gt;

&lt;p&gt;bash&lt;br&gt;
git clone &lt;a href="https://github.com/vaibhav7549/Night-Sky-Companion.git" rel="noopener noreferrer"&gt;https://github.com/vaibhav7549/Night-Sky-Companion.git&lt;/a&gt;&lt;br&gt;
cd Night-Sky-Companion&lt;br&gt;
python -m venv .venv&lt;br&gt;
source .venv/bin/activate        # macOS / Linux&lt;/p&gt;

&lt;h1&gt;
  
  
  source .venv/Scripts/activate  # Windows (Git Bash)
&lt;/h1&gt;

&lt;p&gt;pip install -r requirements.txt&lt;br&gt;
ollama pull gemma2:2b&lt;br&gt;
python -m streamlit run app.py&lt;/p&gt;

&lt;p&gt;Then open &lt;a href="http://localhost:8501" rel="noopener noreferrer"&gt;http://localhost:8501&lt;/a&gt;.&lt;/p&gt;

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

&lt;p&gt;Three open-source pieces do the heavy lifting:&lt;/p&gt;

&lt;p&gt;Skyfield computes precise positions of stars and planets for any place and time. After a one-time download of the DE421 ephemeris (~17 MB) and the Hipparcos catalog (~53 MB), it needs no network. It also includes a constellation boundary map for fast lookups.&lt;br&gt;
The Hipparcos star catalog has over 118,000 stars. I keep only those brighter than magnitude 3.0 (easily visible to the naked eye) and group them by constellation.&lt;br&gt;
Ollama + Gemma 2B runs locally and turns my pre-written mythology notes into short, conversational narration and "look here" directions.&lt;/p&gt;

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

&lt;p&gt;Skyfield works out which bright stars are above the horizon for your latitude, longitude, and time.&lt;br&gt;
The constellation map assigns each star to its constellation.&lt;br&gt;
Gemma 2B writes a 2–3 sentence narration for each one, using the mythology notes as context.&lt;br&gt;
The UI applies the red-light palette and, if you like, enters pocket mode.&lt;/p&gt;

&lt;p&gt;The interface is Streamlit with a dark glassmorphism theme, an animated star field, altitude gauges, and compass chips. It all runs on CPU, with no GPU needed. No API keys, no network calls, and no data leaves your machine.&lt;/p&gt;

&lt;p&gt;Why Open Innovation Matters&lt;/p&gt;

&lt;p&gt;This project only works because the AI is open. Its whole purpose is to work where cloud APIs can't: a dark-sky site with no signal.&lt;/p&gt;

&lt;p&gt;Offline: Skyfield's data is stored locally and Gemma runs through Ollama, so the app works on a mountain, in a desert, or at sea.&lt;br&gt;
Private: Your GPS coordinates and observing times never leave your device. A cloud version would quietly build a map of every place you've ever stargazed.&lt;br&gt;
Free: No subscription, no rate limits, no key to manage.&lt;br&gt;
Modifiable: You can swap the model (Gemma, Llama, Mistral, Phi), add constellations, change the narration style, or translate the mythology. A closed API gives you none of that.&lt;/p&gt;

&lt;p&gt;The tool works where you are, not where the vendor's servers are.&lt;/p&gt;

&lt;p&gt;Prize Categories&lt;br&gt;
Grand Prize: Touch Grass track&lt;br&gt;
Best Use of Gemma: Gemma 2B runs locally via Ollama for all constellation narration.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>hf26challenge</category>
    </item>
    <item>
      <title>I Built a Private AI Document Assistant for My Friend (Runs 100% Offline)</title>
      <dc:creator>Vaibhav Chaudhari </dc:creator>
      <pubDate>Sun, 04 Oct 2026 08:25:37 +0000</pubDate>
      <link>https://dev.to/vaibhav7549/i-built-a-private-ai-document-assistant-for-my-friend-runs-100-offline-4poh</link>
      <guid>https://dev.to/vaibhav7549/i-built-a-private-ai-document-assistant-for-my-friend-runs-100-offline-4poh</guid>
      <description>&lt;p&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;/p&gt;

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

&lt;p&gt;My friend is a freelance lawyer who handles sensitive client contracts. She wanted to use AI to quickly find information in long documents, but she couldn't upload them to ChatGPT or other cloud services because of confidentiality rules.&lt;/p&gt;

&lt;p&gt;I built &lt;strong&gt;LocalDoc Assistant&lt;/strong&gt; — a private AI tool that reads her documents and answers questions about them, &lt;strong&gt;completely offline&lt;/strong&gt;. Her data never leaves her laptop.&lt;/p&gt;

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

&lt;p&gt;The screenshot below shows the app correctly answering "When is my birthday?" from an uploaded text file — with zero internet access.&lt;/p&gt;

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

&lt;p&gt;&lt;a href="https://github.com/vaibhav7549/localdoc-assistant" rel="noopener noreferrer"&gt;https://github.com/vaibhav7549/localdoc-assistant&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;I used &lt;strong&gt;Ollama&lt;/strong&gt; to run Google's &lt;strong&gt;Gemma 2 (2B)&lt;/strong&gt; open-weight model locally on my laptop. The interface is built with &lt;strong&gt;Streamlit&lt;/strong&gt;, an open-source Python framework. For PDF reading, I used &lt;strong&gt;PyPDF2&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The entire AI inference runs on the local machine. No API keys, no cloud calls, no data leaving the computer.&lt;/p&gt;

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

&lt;p&gt;This project would be &lt;strong&gt;impossible&lt;/strong&gt; with a closed API. The entire point is privacy — my friend cannot upload client documents to a third-party server. Open-source AI made this possible because:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Privacy:&lt;/strong&gt; The model runs locally. No data is transmitted anywhere.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zero cost:&lt;/strong&gt; No API subscriptions or per-token fees.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Customizable:&lt;/strong&gt; She can swap in a larger model later if she needs better accuracy, without changing any code.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;Best Use of Gemma&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Thanks for reading! Built with ❤️ for a friend who values privacy.&lt;br&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%2Fnmvm6gbay3macybui718.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%2Fnmvm6gbay3macybui718.png" alt=" " width="800" height="375"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>weekendchallenge</category>
      <category>hf26challenge</category>
    </item>
    <item>
      <title>HI Everyone</title>
      <dc:creator>Vaibhav Chaudhari </dc:creator>
      <pubDate>Sat, 03 Oct 2026 13:54:13 +0000</pubDate>
      <link>https://dev.to/vaibhav7549/hi-everyone-alc</link>
      <guid>https://dev.to/vaibhav7549/hi-everyone-alc</guid>
      <description></description>
    </item>
  </channel>
</rss>
