<?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: Divyansh</title>
    <description>The latest articles on DEV Community by Divyansh (@divyansh0208).</description>
    <link>https://dev.to/divyansh0208</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%2F4000840%2F098e3b98-1b1d-4fa7-9f95-c273d2504c9d.png</url>
      <title>DEV Community: Divyansh</title>
      <link>https://dev.to/divyansh0208</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/divyansh0208"/>
    <language>en</language>
    <item>
      <title>Siesta: a post planner that tells you when to go back to sleep</title>
      <dc:creator>Divyansh</dc:creator>
      <pubDate>Sun, 25 Oct 2026 20:30:04 +0000</pubDate>
      <link>https://dev.to/divyansh0208/siesta-a-post-planner-that-tells-you-when-to-go-back-to-sleep-3o2g</link>
      <guid>https://dev.to/divyansh0208/siesta-a-post-planner-that-tells-you-when-to-go-back-to-sleep-3o2g</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;Siesta is a small local app that plans next week's posts while you sleep, and tells you honestly when your own history can't help.&lt;/p&gt;

&lt;p&gt;Most "best time to post" tools hand you a confident answer whatever your data looks like. Siesta does the opposite. You give it your post history (a CSV, or public posts from DEV by username or tag). It trains a small logistic regression on your older posts, tests it on your newest 20%, and reports ranking skill (ROC AUC) with a 95% bootstrap range. If that range includes a coin flip, the verdict is &lt;strong&gt;"no signal"&lt;/strong&gt;, the drafts stay unranked, and it tells you to go back to sleep.&lt;/p&gt;

&lt;p&gt;If there is a signal, you give it a brief (typed, or read from your recent git commits or a notes file) and it drafts a week of posts with titles, tags and outlines. Code, not the model, computes every feature and score. Drafts that don't score Strong get one revision, and the revision is kept only if it scores higher.&lt;/p&gt;

&lt;p&gt;Who gets to be lazier: anyone who writes regularly and loses an hour every Sunday to "what do I write, and when?" One run answers both. And when your history shows nothing, it saves you from optimizing noise.&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%2Fhhvrbxpat1hirohl9fx5.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%2Fhhvrbxpat1hirohl9fx5.png" alt="Siesta plan: drafted posts with scores, days and edit controls" width="557" height="1032"&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%2Fx1n39a3xpes32f4e3zpd.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%2Fx1n39a3xpes32f4e3zpd.png" alt="Reality check: verdict, AUC with range, lift and hit rate per group" width="800" height="957"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;Siesta runs on your own computer, so there is no hosted link. I kept it local on purpose: your history and scores never leave the machine. The screenshots above come from the built-in demo data, which is made up and has a planted pattern, so the numbers in them are for trying the app only.&lt;/p&gt;

&lt;p&gt;To try it in a couple of minutes:&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/Divyansh0208/Siesta.git
&lt;span class="nb"&gt;cd &lt;/span&gt;Siesta
python &lt;span class="nt"&gt;-m&lt;/span&gt; venv .venv
&lt;span class="nb"&gt;source&lt;/span&gt; .venv/bin/activate        &lt;span class="c"&gt;# Windows: .venv\Scripts\activate&lt;/span&gt;
pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt
python &lt;span class="nt"&gt;-m&lt;/span&gt; siesta
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Then press &lt;strong&gt;"Just let me try it"&lt;/strong&gt; in step 1. No key is needed; without one it uses plain templates for the drafts.&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/Divyansh0208" rel="noopener noreferrer"&gt;
        Divyansh0208
      &lt;/a&gt; / &lt;a href="https://github.com/Divyansh0208/Siesta" rel="noopener noreferrer"&gt;
        Siesta
      &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;Siesta 🌙&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Plan next week's posts while you sleep, and find out honestly whether your own history predicts what works.&lt;/strong&gt;
If it doesn't, Siesta says so and tells you to go back to sleep.&lt;/p&gt;
&lt;p&gt;Runs on your computer. Drafts come from Google's free Gemini API (free key, no card). No telemetry.&lt;/p&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/Divyansh0208/Siesta/docs/plan.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%2FDivyansh0208%2FSiesta%2FHEAD%2Fdocs%2Fplan.png" alt="Siesta plan: drafted posts with scores, days and edit controls"&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;ol&gt;
&lt;li&gt;
&lt;strong&gt;History.&lt;/strong&gt; Upload a CSV (a date, a title, a result such as reactions or likes), or pull
public posts from DEV by username or tag (DEV's public API needs no key).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reality check.&lt;/strong&gt; A small logistic regression learns from your older posts and is tested on
your newest 20%. You see ranking skill (ROC AUC) with a 95% bootstrap range, lift for its top
20% picks, and the measured hit rate of its Strong / Toss-up / Weak groups. If the range
includes a coin flip, the verdict is "no signal" and drafts stay unranked.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Brief.&lt;/strong&gt;…&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/Divyansh0208/Siesta" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;MIT licensed. Python 3.10+, with numpy, pandas and scikit-learn. The server is the standard library only.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The judge is not the LLM.&lt;/strong&gt; A logistic regression (C=0.5) on weekday, 4-hour window, title length, question mark, number, tags, reading time and cover image. A gradient-boosting model runs on the same test for comparison and is never used to score. The method is fixed in advance: threshold, tag list and scaling come from training rows only, and seeds and settings are never tuned on results.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The writer is Gemini's free tier.&lt;/strong&gt; Siesta calls the Gemini REST API over plain HTTPS with no SDK, asks for JSON that follows a schema, and then validates everything. Model output is treated as untrusted: it gets one retry, and nothing numeric is ever taken from it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Without a key it still works.&lt;/strong&gt; It falls back to plain templates, so the whole flow runs offline.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The free tier fought back.&lt;/strong&gt; While testing I hit a 404 on &lt;code&gt;gemini-2.5-flash&lt;/code&gt; even though it appeared in the model list, then 503s on an overloaded preview model, then a timeout. So Siesta now lists the models your key can use (stable ones first), retries on rate limits and overload, and moves on to the next model on a 404, 503 or timeout. Only then does it fall back to templates, and the run log says what happened.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Everything else is small.&lt;/strong&gt; A tiny &lt;code&gt;.env&lt;/code&gt; reader, a stdlib HTTP server bound to localhost, a single HTML file for the UI, and 12 tests that need no key.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;I want to be straight about this. The drafting step uses a hosted free API, not an open-weight model, so I'm not claiming the model itself is open. My first version used open-weight models through Ollama, and I switched to Gemini's free tier because that's what I could run.&lt;/p&gt;

&lt;p&gt;What open innovation made possible here is the part that matters most:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The judge is auditable.&lt;/strong&gt; A logistic regression you can read, with the same split and settings every run, can show you why it said no. A hosted black box can't.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The code is open and the model is swappable.&lt;/strong&gt; The model sits behind one small file (&lt;code&gt;siesta/draft.py&lt;/code&gt;). Everything the app decides, from the verdict to every score, lives in open code you can read.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Open tooling carried the project.&lt;/strong&gt; scikit-learn, pandas and numpy do the real work, and DEV's public API needs no key.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The cost is privacy. Gemini receives your brief, your common tags and the draft being revised, while your history stays on your machine. Google may use free-tier prompts to improve its products, so keep secrets out of the brief. I put that in the README on purpose.&lt;/p&gt;

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

&lt;p&gt;One press of "Draft my week" produces a full week of drafts, each already scored, slotted onto a day and hour, and revised once if it wasn't Strong. In my demo run, two drafts were revised and kept because they scored higher: one went from 61% to 78%, another from 57% to 73%. Those numbers come from the made-up demo data, so they show how the revise-and-keep rule works, not a promise about your posts.&lt;/p&gt;

&lt;p&gt;I haven't claimed hours saved, because I haven't measured them on real posting history yet. The honest payoff is smaller and, I think, more useful: it either hands you a week of drafts, or it tells you in one run that your history can't predict anything and you can stop fiddling.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>hf26challenge</category>
      <category>opensource</category>
      <category>python</category>
    </item>
    <item>
      <title>GigLift: Hinglish job requests in, clear briefs out (Gemma + Django)</title>
      <dc:creator>Divyansh</dc:creator>
      <pubDate>Sun, 18 Oct 2026 20:01:44 +0000</pubDate>
      <link>https://dev.to/divyansh0208/giglift-hinglish-job-requests-in-clear-briefs-out-gemma-django-5l7</link>
      <guid>https://dev.to/divyansh0208/giglift-hinglish-job-requests-in-clear-briefs-out-gemma-django-5l7</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;A shop owner in India doesn't write a job posting. They send a WhatsApp message: "logo bana do dukaan ke liye, 1500 tak, Friday tak chahiye".&lt;/p&gt;

&lt;p&gt;GigLift is a small job board for students that starts from that message. It turns it into a proper brief: what has to be delivered, which skills it needs, a budget, a deadline, and the questions the owner forgot to answer. The owner fixes whatever is wrong and publishes. Nothing is public before that. Students see gigs ranked by how well they fit their profile and send a proposal, and the owner hires one. Phone numbers show up only after a hire, and payment stays between the two people.&lt;/p&gt;

&lt;p&gt;The ones who would pay are small business owners, for a logo, a menu card, eight reels edited or forty pages typed. Usually ₹500 to ₹3,000. The side hustle belongs to the student.&lt;/p&gt;

&lt;p&gt;I wanted to fix the brief because a message like that leaves out half of what a freelancer needs to know. The student guesses, quotes blind, and the two of them argue about it later. A clear brief at the start costs almost nothing and heads off most of that.&lt;/p&gt;

&lt;p&gt;GigLift itself earns nothing. Nobody has paid through it and no real client has used it yet.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Live:&lt;/strong&gt; &lt;a href="https://giglift-aa4q.onrender.com" rel="noopener noreferrer"&gt;https://giglift-aa4q.onrender.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It runs on Render's free tier, so the first visit after a quiet spell can take up to a minute, and the data resets whenever I redeploy. The five gigs marked "(demo)" are seeded. The footer says &lt;code&gt;AI: gemma&lt;/code&gt; when the model answered and &lt;code&gt;AI: offline&lt;/code&gt; when the fallback did.&lt;/p&gt;

&lt;p&gt;Two things worth trying:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Click &lt;strong&gt;Post a gig&lt;/strong&gt; and paste &lt;code&gt;logo bana do dukaan ke liye, 1500 tak, Friday tak chahiye&lt;/code&gt;. The budget and deadline in the brief should be exactly the ones you wrote.&lt;/li&gt;
&lt;li&gt;Post another with no budget, like &lt;code&gt;4 posters chahiye shaadi ke liye&lt;/code&gt;. The price comes back labelled as a rough guess, and the brief asks for your budget and your deadline.&lt;/li&gt;
&lt;/ol&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/Divyansh0208" rel="noopener noreferrer"&gt;
        Divyansh0208
      &lt;/a&gt; / &lt;a href="https://github.com/Divyansh0208/GigLift" rel="noopener noreferrer"&gt;
        GigLift
      &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;GigLift&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Small paid jobs for students, posted in plain language.&lt;/strong&gt; A shop owner pastes a messy WhatsApp-style request ("logo bana do, 1500 tak, Friday tak"). GigLift turns it into a clear brief. Students pitch. The client hires. No accounts, no payment fees, free to run.&lt;/p&gt;
&lt;p&gt;Django + HTMX, one stdlib-only AI module, MIT licensed.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Live demo: &lt;a href="https://giglift-aa4q.onrender.com" rel="nofollow noopener noreferrer"&gt;https://giglift-aa4q.onrender.com&lt;/a&gt;&lt;/strong&gt; (free Render tier: the first visit after idle can take up to a minute, and data resets on each redeploy. The five gigs marked "(demo)" are seeded.)&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Status:&lt;/strong&gt; working prototype, deployed. The full flow, security pass and eval below are real and reproducible. Screenshots and a first real user are next. See the &lt;a href="https://github.com/Divyansh0208/GigLift#roadmap-and-known-limits" rel="noopener noreferrer"&gt;roadmap&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Contents&lt;/h2&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/Divyansh0208/GigLift#try-it" rel="noopener noreferrer"&gt;Try it&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Divyansh0208/GigLift#the-problem" rel="noopener noreferrer"&gt;The problem&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Divyansh0208/GigLift#how-it-works" rel="noopener noreferrer"&gt;How it works&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Divyansh0208/GigLift#where-ai-is-used-and-where-it-is-not" rel="noopener noreferrer"&gt;Where AI is used, and where it is not&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Divyansh0208/GigLift#eval" rel="noopener noreferrer"&gt;Eval&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Divyansh0208/GigLift#what-went-wrong-along-the-way" rel="noopener noreferrer"&gt;What went wrong along the way&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Divyansh0208/GigLift#run-it" rel="noopener noreferrer"&gt;Run it&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Divyansh0208/GigLift#configuration" rel="noopener noreferrer"&gt;Configuration&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Divyansh0208/GigLift#project-layout" rel="noopener noreferrer"&gt;Project layout&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Divyansh0208/GigLift#tests" rel="noopener noreferrer"&gt;Tests&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Divyansh0208/GigLift#deploy" rel="noopener noreferrer"&gt;Deploy&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Divyansh0208/GigLift#security" rel="noopener noreferrer"&gt;Security&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Divyansh0208/GigLift#roadmap-and-known-limits" rel="noopener noreferrer"&gt;Roadmap and known limits&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Divyansh0208/GigLift#license" rel="noopener noreferrer"&gt;License&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Try&lt;/h2&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/Divyansh0208/GigLift" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;MIT licensed. The README has the setup steps, the eval, and a list of what's still missing.&lt;/p&gt;

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

&lt;p&gt;It's Django with HTMX and SQLite, on Render's free plan. All the AI lives in one file, &lt;code&gt;core/llm.py&lt;/code&gt;, standard library only, no SDK. The model is Gemma (&lt;code&gt;gemma-4-26b-a4b-it&lt;/code&gt;), called through Google's hosted Gemini API with the same free key. If the key is missing or the call fails, rule-based fallbacks take over and the page says so.&lt;/p&gt;

&lt;p&gt;Most of the design comes from one thing I learned early: ask a model for a price and you get a price, even when the client never gave one. So I stopped letting it decide numbers. If the client stated a budget or a deadline, a small set of rules in &lt;code&gt;core/extract.py&lt;/code&gt; reads it straight out of the text and the model can't change it. The rules cope with English, Hinglish and Devanagari digits: &lt;code&gt;1500 tak&lt;/code&gt;, &lt;code&gt;₹१२००&lt;/code&gt;, &lt;code&gt;parso&lt;/code&gt;, &lt;code&gt;agle hafte&lt;/code&gt;, &lt;code&gt;Friday tak&lt;/code&gt;. If nothing was stated, the model's number is shown as "Rough guess, not from real prices" and the client is asked. If the model writes a deliverable containing a number that isn't in the client's message, I drop that line. What's left for Gemma is the part rules can't do: understand a Hinglish request and write the deliverables and the open questions.&lt;/p&gt;

&lt;p&gt;To check it, I wrote 30 briefs by hand (English, Hinglish, Devanagari) and built the rules on them. Then I froze &lt;code&gt;extract.py&lt;/code&gt;, whose hash is in &lt;code&gt;eval/FROZEN.txt&lt;/code&gt;, wrote 32 new briefs and scored them once.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Held-out, 32 briefs&lt;/th&gt;
&lt;th&gt;Old regex&lt;/th&gt;
&lt;th&gt;New rules&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Stated budget read right (25)&lt;/td&gt;
&lt;td&gt;11&lt;/td&gt;
&lt;td&gt;22&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stated budget read wrong&lt;/td&gt;
&lt;td&gt;14&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Budget invented when none given (7)&lt;/td&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stated deadline read right (27)&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;25&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stated deadline read wrong&lt;/td&gt;
&lt;td&gt;19&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Deadline invented when none given (5)&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Crashes&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Then I ran the whole pipeline with real Gemma behind it, all 32 briefs served by the model: 22 of 25 budgets and 25 of 27 deadlines right, none wrong, none invented. That matches the rules alone, which is the point. Gemma can't change a number the client wrote.&lt;/p&gt;

&lt;p&gt;The five misses were &lt;code&gt;paanch hazar&lt;/code&gt; (an amount in words), &lt;code&gt;1800 mein ho jaye&lt;/code&gt;, &lt;code&gt;due 25 Oct&lt;/code&gt;, and &lt;code&gt;within 48 hours&lt;/code&gt;. A miss means the app asks the client. It never shows a wrong number.&lt;/p&gt;

&lt;p&gt;What this doesn't show: I wrote both the rules and the test briefs, so the held-out score is optimistic. It also only measures numbers. I haven't scored Gemma's own output yet, meaning the open questions, the skills and the deliverables. That's next, along with real messages from real clients and a delivery check, where the student describes what they handed over and the model marks each deliverable done or missing.&lt;/p&gt;

&lt;p&gt;Things that went wrong. My first offline regex crashed on "posters, 4" because it read the "rs" in "posters" as rupees. On Windows my scorer read the briefs with the wrong encoding and mangled the ₹ sign, and the budget score fell from 22 to 16 until I worked out why. Gemma's first replies didn't parse as JSON at all, so for a while every brief quietly fell back to the offline rules and I didn't notice. I rewrote the parser to take the largest valid JSON object in the reply, and the eval now prints which backend actually served the run. I also once posted a screenshot with my API key in it. I revoked it.&lt;/p&gt;

&lt;p&gt;I spent an evening on the boring security parts too: a CSP with nonces, rate limits per session and per IP, and ownership checks on edit and hire. There are 24 Django tests, plus 7 more on the rules.&lt;/p&gt;

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

&lt;p&gt;I didn't run a closed model against Gemma, so I won't claim it did better. Here is what open changed for this project.&lt;/p&gt;

&lt;p&gt;The design doesn't need a frontier model. Because code decides the numbers, the model only has to read a messy request and write a short brief, and a mid-size open model is enough for that. That makes the model replaceable. I can switch to a different one by changing &lt;code&gt;GEMMA_MODEL&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The money is tight. A job is ₹600. Right now the free API key is what makes a brief cost me nothing, and open weights mean I'm not stuck if that free tier changes. I could run the same prompts on my own hardware.&lt;/p&gt;

&lt;p&gt;Privacy matters most to me here, and it's the least finished part. These messages often have a phone number in them. Today the text goes to Google's API, so it leaves my server. With open weights, a shop's message doesn't have to end up inside a closed vendor's system. The Gemma call sits in one function, so pointing it at a model on my own machine means rewriting that function, not the prompts. I haven't built that yet.&lt;/p&gt;

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

&lt;p&gt;Side Hustle. Gemma is the model, so I'd also like to be considered for any Gemma-related partner category.&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%2F2b1zb8gfuychwqznva7a.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%2F2b1zb8gfuychwqznva7a.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>hf26challenge</category>
      <category>gemmachallenge</category>
      <category>ai</category>
    </item>
    <item>
      <title>Tuft: a nature quest that turns your screen black, judged by an open model on your own device</title>
      <dc:creator>Divyansh</dc:creator>
      <pubDate>Sun, 11 Oct 2026 18:29:59 +0000</pubDate>
      <link>https://dev.to/divyansh0208/tuft-a-nature-quest-that-turns-your-screen-black-judged-by-an-open-model-on-your-own-device-3091</link>
      <guid>https://dev.to/divyansh0208/tuft-a-nature-quest-that-turns-your-screen-black-judged-by-an-open-model-on-your-own-device-3091</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;I wanted an app whose best feature is that you stop using it.&lt;/p&gt;

&lt;p&gt;Tuft gives you one small quest, like &lt;em&gt;find moss or lichen&lt;/em&gt;, &lt;em&gt;find sky between branches&lt;/em&gt;, or &lt;em&gt;find a shadow with a shape&lt;/em&gt;. You pick 5, 10 or 20 minutes and tap &lt;strong&gt;Go outside&lt;/strong&gt;. The screen goes pure black (off pixels on an OLED phone), the quest is read aloud, and the app disappears into your pocket.&lt;/p&gt;

&lt;p&gt;When the timer ends the phone buzzes and talks to you. You take one photo, no retakes. A vision model decides whether it completes the quest, says what it saw, and drops the result into a small field log on your device, thumbnail included.&lt;/p&gt;

&lt;p&gt;So the screen only matters for the first ten seconds and the last ten. Everything in between is you, outside.&lt;/p&gt;

&lt;p&gt;I made it for anyone who needs a reason to look up for ten minutes, and for a school club or hiking group that wants a tool it can host on its own.&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/PASTE-YOUR-IMAGE-LINK" 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/PASTE-YOUR-IMAGE-LINK" alt="Tuft quest screen on a teal background: " width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;Live app: &lt;strong&gt;&lt;a href="https://tuft.onrender.com" rel="noopener noreferrer"&gt;https://tuft.onrender.com&lt;/a&gt;&lt;/strong&gt;. On a phone, use &lt;em&gt;Add to Home Screen&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;There's also a benchmark page at &lt;a href="https://tuft.onrender.com/bench/" rel="noopener noreferrer"&gt;https://tuft.onrender.com/bench/&lt;/a&gt; for testing the judge on your own photos.&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/PASTE-YOUR-IMAGE-LINK" 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/PASTE-YOUR-IMAGE-LINK" alt="Tuft bench page: add photos, run CLIP or an Ollama model, copy a results table" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

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


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/Divyansh0208" rel="noopener noreferrer"&gt;
        Divyansh0208
      &lt;/a&gt; / &lt;a href="https://github.com/Divyansh0208/Tuft" rel="noopener noreferrer"&gt;
        Tuft
      &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;Tuft&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;A quest that ends outside.&lt;/strong&gt; Tuft hands you a small nature quest, turns your screen black, and tells you to put the phone in your pocket. When time is up you take one photo, and an open-weight vision model on your own device decides if you did it.&lt;/p&gt;
&lt;p&gt;Built for the Hacktoberfest 2026 Open-Source AI Challenge, Week 1: Touch Grass.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;App: &lt;code&gt;/&lt;/code&gt; (single page, no build step, no backend)&lt;/li&gt;
&lt;li&gt;Benchmark: &lt;code&gt;/bench/&lt;/code&gt; (measure the judge on your own photos)&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;How it works&lt;/h2&gt;
&lt;/div&gt;

  &lt;div class="js-render-enrichment-target"&gt;
    &lt;div class="render-plaintext-hidden"&gt;
      &lt;pre&gt;flowchart LR
  quest["Quest&amp;lt;br/&amp;gt;built-in pool, or written by a local model"] --&amp;gt; pocket["Black pocket screen&amp;lt;br/&amp;gt;wake lock, timer, voice cue"]
  pocket --&amp;gt; shot["One photo, no retakes"]
  shot --&amp;gt; judge{"Judge"}
  judge --&amp;gt;|"Ollama reachable"| vlm["Vision model via Ollama&amp;lt;br/&amp;gt;e.g. gemma3:4b"]
  judge --&amp;gt;|"otherwise"| clip["CLIP in the browser&amp;lt;br/&amp;gt;transformers.js, offline after first load"]
  vlm --&amp;gt; verdict["Spoken verdict"]
  clip --&amp;gt; verdict
  verdict --&amp;gt; log["Field log&amp;lt;br/&amp;gt;stored on this device only"]
&lt;/pre&gt;
    &lt;/div&gt;
  &lt;/div&gt;
  &lt;span class="js-render-enrichment-loader d-flex flex-justify-center flex-items-center width-full"&gt;
    &lt;span&gt;
      &lt;span class="sr-only"&gt;Loading&lt;/span&gt;
&lt;/span&gt;
  &lt;/span&gt;


&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Pocket screen:&lt;/strong&gt; pure black (off pixels on…&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/Divyansh0208/Tuft" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;No backend, no build step, no framework. It's one &lt;code&gt;index.html&lt;/code&gt;, a service worker, a manifest and the benchmark page. MIT licensed.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;The judge has two modes.&lt;/strong&gt; If Ollama is running and has a vision model, Tuft uses it. It picks up Gemma, LLaVA and similar models on its own, and the same model can write new quests. If there's no Ollama, it falls back to CLIP running in the browser through transformers.js.&lt;/p&gt;

&lt;p&gt;CLIP compares the photo with the quest description and with six "not this" labels: a room indoors, a computer screen, a face, a plain wall, a road, a blurry photo. The quest passes if its description ranks first or scores above 25%. A second pass picks what the photo shows from eleven nature words, so the spoken verdict can say what it saw. If Ollama dies halfway through, Tuft switches to CLIP instead of showing an error.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The black screen.&lt;/strong&gt; It's a black overlay plus the Screen Wake Lock API, because phones pause timers and audio when the screen locks. Phones also slow timers down in the background, so when the page comes back, Tuft checks the real clock instead of trusting the timeout.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Offline.&lt;/strong&gt; A service worker caches the page, the CLIP library and its files the first time they load, and transformers.js caches the model weights. The idea is that after one online run, the judge works with no signal.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A benchmark page.&lt;/strong&gt; At &lt;code&gt;/bench/&lt;/code&gt; you add photos, tag each one with its quest and whether it truly completes it, and run both judges. You get accuracy with 95% intervals. The page picks a threshold on one half of your photos and tests it on the other half. It also has a "can't tell" band, an exact paired test, and a gallery of every miss. I checked the paired test against counts from another entry in this challenge (19 vs 6 should give p = 0.015) and it matched.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A bug worth telling.&lt;/strong&gt; An early decorative 3D layer put &lt;code&gt;perspective&lt;/code&gt; on the page body. That quietly changes what &lt;code&gt;position: fixed&lt;/code&gt; means, so the black screen would have covered the page instead of the whole phone. A black screen that's only partly black defeats the point, so it's fixed in the shipped version.&lt;/p&gt;

&lt;h3&gt;
  
  
  What I haven't tested
&lt;/h3&gt;

&lt;p&gt;I haven't run a field study, so there are no accuracy numbers in this post. The benchmark page is there so anyone can measure it, including you. I also haven't checked the offline path in airplane mode on a real phone. CLIP matches photos to descriptions, it doesn't know species, and a photo that looks like the quest can fool it. The built-in pool has only ten quests.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The judge should work where the signal doesn't.&lt;/strong&gt; An app that wants you outside is going to end up somewhere with no bars. A closed vision API is a network call. Tuft's judge is open weights in your browser.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Your surroundings stay yours.&lt;/strong&gt; Photos are resized and judged on the device. The only network traffic is the first download of assets, plus your own Ollama address if you set one.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Nothing to pay for.&lt;/strong&gt; No keys, no per-photo cost, no rate limits, no accounts. Any static host works, so a club can run its own copy.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Swapping models is one text field.&lt;/strong&gt; The CLIP weights are open too, so a hosted model getting retired can't break the app.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The trade-off.&lt;/strong&gt; Closed frontier models are better at recognising species. I picked "good enough, private, and everywhere" over "best, sometimes", and I'm not claiming open wins on accuracy.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;&lt;strong&gt;Best Use of Render:&lt;/strong&gt; Tuft is a static site on Render. The HTTPS it provides is what makes the wake lock, the service worker and Home Screen install work.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>hf26challenge</category>
      <category>opensource</category>
      <category>ai</category>
    </item>
    <item>
      <title>Building a cyclone impact forecaster with only 61 data points</title>
      <dc:creator>Divyansh</dc:creator>
      <pubDate>Wed, 30 Sep 2026 19:26:09 +0000</pubDate>
      <link>https://dev.to/divyansh0208/building-a-cyclone-impact-forecaster-with-only-61-data-points-3lk1</link>
      <guid>https://dev.to/divyansh0208/building-a-cyclone-impact-forecaster-with-only-61-data-points-3lk1</guid>
      <description>&lt;p&gt;Almost every year, India’s east coast—especially Odisha, Andhra Pradesh, and West Bengal—takes a hit from a cyclone. While plenty of tools show where a storm is heading, very few tell you how many people actually live inside the impact zone. Early on, that district-level exposure is what responders really need to prioritize resources.&lt;/p&gt;

&lt;p&gt;I built &lt;strong&gt;Cyclone Impact Forecaster&lt;/strong&gt; to bridge that gap. It combines a 6-hour intensity forecast with a district-level exposure ranking for the North Indian Ocean, rendered on a 3D satellite globe.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Live demo:&lt;/strong&gt; &lt;a href="https://cyclone-forecaster-s015.onrender.com" rel="noopener noreferrer"&gt;https://cyclone-forecaster-s015.onrender.com&lt;/a&gt; &lt;em&gt;(runs on a free Render instance that sleeps when idle, so the first load takes about 45 to 60 seconds)&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Code:&lt;/strong&gt; &lt;a href="https://github.com/Divyansh0208/Cyclone" rel="noopener noreferrer"&gt;https://github.com/Divyansh0208/Cyclone&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&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%2F9qt4qgy634ub516mvmnp.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%2F9qt4qgy634ub516mvmnp.png" alt="A storm placed by hand near the Odisha coast, with districts ranked by expected people affected" width="800" height="453"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;A simulated storm placed by hand at 17.2 N, 84.5 E (80 kt), taken on a quiet day when no real storm was active.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How the pipeline works
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Ingesting storm state:&lt;/strong&gt; Live storms in the North Indian Ocean are pulled from NOAA's IBTrACS ACTIVE file (using JTWC fixes). Because the basin isn't always active, I also built modes to replay historical cyclones or drop a custom storm onto the map by hand.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;6-hour intensity forecast:&lt;/strong&gt; An XGBoost model predicts the change in wind speed over the next 6 hours based on current position, wind, central pressure, and their recent trends.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;District exposure model:&lt;/strong&gt; To estimate human impact, I fitted a 2-parameter log-linear model on 61 historical Indian cyclone records from EM-DAT, combined with Census 2011 district populations and areas. It outputs expected people affected along with a 10-90% prediction interval for every district inside the impact radius.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;3D visualization:&lt;/strong&gt; Built on MapLibre GL with satellite imagery and terrain elevation. District markers scale with the estimated affected population, and clicking any district flies the camera down to inspect it.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;All of this runs strictly on real datasets—IBTrACS (NOAA NCEI) for tracks, EM-DAT (CRED) for the 61 historical impact records, and the 2011 Census of India. I avoided synthetic data completely; even the test suite runs against a real track excerpt from Cyclone Fani.&lt;/p&gt;

&lt;h2&gt;
  
  
  Benchmarks
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;Naive Baseline&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Intensity MAE (6 h)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;3.04 +/- 0.20 kt&lt;/td&gt;
&lt;td&gt;3.49 kt (persistence / last value)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Impact log-MAE&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;2.50 +/- 0.41&lt;/td&gt;
&lt;td&gt;2.69 (historical mean)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;For the intensity model, I used 5-fold cross-validation &lt;strong&gt;grouped by storm&lt;/strong&gt;, beating the persistence baseline across all 5 folds. For the impact model, I ran 5-fold CV with 20 repeats on the log scale.&lt;/p&gt;

&lt;h2&gt;
  
  
  Notes from working with 61 data points
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Complex models overfit fast on small disaster datasets.&lt;/strong&gt; When I first threw a RandomForest at the EM-DAT impact data, it performed &lt;em&gt;worse&lt;/em&gt; than simply predicting the historical mean. Stepping back to a 2-parameter log-linear model actually generalized. I even ended up fixing the population density coefficient at 1 (letting it fit freely gave about 1.08, so locking it simplified the model with zero performance penalty). The improvement over the baseline is still modest, so I make that explicit in the UI.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Random train/test splits cheat on track data.&lt;/strong&gt; Consecutive 6-hour fixes from the same cyclone look nearly identical. If you do a standard random split, adjacent points end up in both train and test sets, inflating your metrics. Grouping cross-validation folds strictly by storm ID is the only honest way to evaluate it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Don't hide wide error bars.&lt;/strong&gt; In the screenshot above, the top-ranked district's 10-90% range spans from roughly 17,000 to 2.2 million people. That looks massive, but that's the reality of training on 61 noisy historical events. Rather than pretending the point estimate is exact, the UI frames the output as a &lt;em&gt;relative ranking&lt;/em&gt; across districts and keeps the error band front and center.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Be transparent about data lag.&lt;/strong&gt; The upstream IBTrACS ACTIVE file only rebuilds about three times a week, meaning "live" data can lag by up to two days. The backend caches responses for 15 minutes, falls back to stale data if NOAA's upstream fails, and badges every fix in the UI with its exact age.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keep training and serving feature logic in one place.&lt;/strong&gt; Both the offline training scripts and the live API import from a single &lt;code&gt;features.py&lt;/code&gt; module so feature engineering can't silently drift between the two.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Current limitations
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No track or landfall forecasting yet:&lt;/strong&gt; Every district inside the impact radius currently receives the same predicted storm wind rather than a distance-decayed wind field.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Outdated census boundaries:&lt;/strong&gt; Relying on the 2011 Census means the population figures are 15 years old and several newly carved-out districts aren't represented.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Wind scale mismatch:&lt;/strong&gt; JTWC reports 1-minute sustained winds, whereas the India Meteorological Department (IMD) uses a 3-minute averaging period, making storm category labels approximate.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Not an official warning system:&lt;/strong&gt; This is an experimental decision-support project. IMD is the sole official authority for cyclone warnings in India.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Help wanted
&lt;/h2&gt;

&lt;p&gt;I have &lt;a href="https://github.com/Divyansh0208/Cyclone/issues" rel="noopener noreferrer"&gt;13 open issues on GitHub&lt;/a&gt;—several tagged &lt;code&gt;good first issue&lt;/code&gt;—covering a proper distance-decay wind field, better calibration for the 10-90% interval, post-2011 district boundaries, regional language support (Hindi, Odia, Telugu, Bengali), and an accessibility audit.&lt;/p&gt;

&lt;p&gt;If you work in disaster response, hydrometeorology, or geospatial ML, I’d really appreciate your critique on what would make this genuinely useful in the field.&lt;/p&gt;

</description>
      <category>python</category>
      <category>machinelearning</category>
      <category>opensource</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Sahayak — An AI Legal Assistant That Says "I Don't Know" When It Should</title>
      <dc:creator>Divyansh</dc:creator>
      <pubDate>Sun, 27 Sep 2026 19:54:40 +0000</pubDate>
      <link>https://dev.to/divyansh0208/sahayak-an-ai-legal-assistant-that-says-i-dont-know-when-it-should-42l2</link>
      <guid>https://dev.to/divyansh0208/sahayak-an-ai-legal-assistant-that-says-i-dont-know-when-it-should-42l2</guid>
      <description>&lt;p&gt;Legal help is expensive, jargon-heavy, and often out of reach for people who need it most. For PromptWars: Virtual (Exclusive Edition) — a hackathon by Hack2Skill — I built &lt;strong&gt;Sahayak&lt;/strong&gt;, a RAG-based legal assistant that answers questions and summarizes documents in plain language, grounded in real context instead of confident guesses.&lt;/p&gt;

&lt;p&gt;🔗 Live: &lt;a href="https://legal-ai-eight-liart.vercel.app/" rel="noopener noreferrer"&gt;https://legal-ai-eight-liart.vercel.app/&lt;/a&gt;&lt;br&gt;
🔗 Code: &lt;a href="https://github.com/Divyansh0208/Legal-AI" rel="noopener noreferrer"&gt;https://github.com/Divyansh0208/Legal-AI&lt;/a&gt; (MIT)&lt;/p&gt;
&lt;h2&gt;
  
  
  What it does
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Ask&lt;/strong&gt; — a tenancy, consumer-rights, or contract question. Answers are retrieved from indexed context, not hallucinated. No matching context? It says so, instead of guessing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Upload&lt;/strong&gt; — a PDF (scanned or native) → plain-language summary: what kind of document it is, what it obligates you to, what's worth double-checking.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Voice&lt;/strong&gt; — ask by speaking, transcribed via Whisper.
Every response is grounded in retrieved context and explicit about what it can't confirm — no confident guessing when the docs don't cover something.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Architecture
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User (voice/text/doc)
      │
      ▼
React + Vite Frontend
      │  REST
      ▼
FastAPI Backend
      ├─► Groq Whisper API      (speech-to-text)
      ├─► PyMuPDF + pytesseract (parsing / OCR)
      ├─► sentence-transformers (embeddings)
      ├─► ChromaDB              (vector retrieval)
      ├─► Groq LLM API          (answer generation)
      └─► SQLite                (app data)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The RAG hot path is intentionally thin — no framework ceremony for a single retrieval + one chat completion call:&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="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;answer_question&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;question&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;hits&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;retrieve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;question&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s"&gt;---&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;h&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;hits&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;hits&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;
    &lt;span class="n"&gt;completion&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;completions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GROQ_LLM_MODEL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;system&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;SYSTEM_PROMPT&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;RAG_ANSWER_TEMPLATE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;question&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;question&lt;/span&gt;&lt;span class="p"&gt;)},&lt;/span&gt;
        &lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="n"&gt;temperature&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;max_tokens&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;800&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;answer&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;completion&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;choices&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sources&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;source&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;source&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;distance&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;distance&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;h&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;hits&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The part I'm actually proud of: the guardrails
&lt;/h2&gt;

&lt;p&gt;Anyone can wire an LLM to a text box. The parts that took real effort:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Untrusted-context system prompt&lt;/strong&gt; — uploaded documents and retrieved chunks are treated as &lt;em&gt;data&lt;/em&gt;, never instructions. Prompt-injection resistant by design.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Real upload validation&lt;/strong&gt; — &lt;code&gt;python-magic&lt;/code&gt; sniffs actual file bytes; a relabeled &lt;code&gt;.exe&lt;/code&gt; doesn't sneak through as &lt;code&gt;.pdf&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rate limiting&lt;/strong&gt; — &lt;code&gt;slowapi&lt;/code&gt; on query, upload, and voice endpoints.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security headers&lt;/strong&gt; — CSP, X-Frame-Options, HSTS in production.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;5 CI workflows on every push&lt;/strong&gt; — lint, pytest, bandit (SAST), gitleaks (secrets), pip-audit (deps), axe-core (a11y) — auto-deploy to Render/Vercel on merge to &lt;code&gt;main&lt;/code&gt;.
None of that shows up in a demo GIF. All of it is why the demo GIF is trustworthy.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Stack
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;Tech&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Backend&lt;/td&gt;
&lt;td&gt;FastAPI&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LLM&lt;/td&gt;
&lt;td&gt;Groq (&lt;code&gt;openai/gpt-oss-120b&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Retrieval&lt;/td&gt;
&lt;td&gt;ChromaDB + sentence-transformers (&lt;code&gt;all-MiniLM-L6-v2&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Parsing&lt;/td&gt;
&lt;td&gt;PyMuPDF, pytesseract&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Speech&lt;/td&gt;
&lt;td&gt;Groq Whisper&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Frontend&lt;/td&gt;
&lt;td&gt;React + Vite, Tailwind&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DB&lt;/td&gt;
&lt;td&gt;SQLite&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Deploy&lt;/td&gt;
&lt;td&gt;Render + Vercel&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  What's next
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Multi-language support (deferred)&lt;/li&gt;
&lt;li&gt;Persisted chat sessions (currently in-memory)&lt;/li&gt;
&lt;li&gt;More jurisdiction-specific templates
If you've hit a real legal-access gap, or want to poke holes in the grounding/refusal behavior — feedback welcome. MIT-licensed, issues/PRs open.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>opensource</category>
      <category>rag</category>
      <category>showdev</category>
    </item>
    <item>
      <title>I Built a Prompt Forging Engine with NVIDIA Llama 3.1 70B for Google Prompt Wars 2026</title>
      <dc:creator>Divyansh</dc:creator>
      <pubDate>Wed, 24 Jun 2026 19:08:25 +0000</pubDate>
      <link>https://dev.to/divyansh0208/i-built-a-prompt-forging-engine-with-nvidia-llama-31-70b-for-google-prompt-wars-2026-3aa1</link>
      <guid>https://dev.to/divyansh0208/i-built-a-prompt-forging-engine-with-nvidia-llama-31-70b-for-google-prompt-wars-2026-3aa1</guid>
      <description>&lt;p&gt;What if you could take any weak, vague prompt and instantly transform it into an elite, production-ready one — with a power score, technique breakdown, and warrior rank?&lt;/p&gt;

&lt;p&gt;That's exactly what &lt;strong&gt;Prompt Forge&lt;/strong&gt; does.&lt;/p&gt;




&lt;h2&gt;
  
  
  🤔 The Problem
&lt;/h2&gt;

&lt;p&gt;Most people use AI wrong. They type something like:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Write me a blog post about productivity"&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And wonder why the output is generic. The secret isn't the model — it's the prompt. A better prompt unlocks dramatically better results.&lt;/p&gt;

&lt;p&gt;But most people don't know &lt;em&gt;how&lt;/em&gt; to write better prompts. That's the gap Prompt Forge fills.&lt;/p&gt;




&lt;h2&gt;
  
  
  ⚔️ What is Prompt Forge?
&lt;/h2&gt;

&lt;p&gt;Prompt Forge is a single-page web app that transforms weak prompts into elite, production-ready prompts using &lt;strong&gt;NVIDIA Llama 3.1 70B&lt;/strong&gt; via the NVIDIA NIM API.&lt;/p&gt;

&lt;p&gt;You paste a rough idea → hit &lt;strong&gt;Forge It&lt;/strong&gt; → get back:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A rewritten prompt (3–8× more detailed)&lt;/li&gt;
&lt;li&gt;Which of 8 prompt engineering techniques were applied&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;Power Level score&lt;/strong&gt; from 1–10&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;Warrior Rank&lt;/strong&gt; (Recruit → Bronze → Silver → Gold → Legendary)&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;Warrior Analysis&lt;/strong&gt; explaining every upgrade made
🔗 &lt;strong&gt;&lt;a href="https://prompt-forge-o970.onrender.com" rel="noopener noreferrer"&gt;Try it live →&lt;/a&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  ✨ Features
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Prompt Forging Engine&lt;/strong&gt; — rewrites weak prompts using Llama 3.1 70B Instruct&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;8 Technique Tags&lt;/strong&gt; — Context · Specificity · Constraints · Output Format · Persona · Examples · Chain-of-Thought · Negatives&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Power Level Meter&lt;/strong&gt; — 10-bar visual indicator (1–10)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Warrior Rank System&lt;/strong&gt; — gamified ranking based on power level&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Warrior Analysis Panel&lt;/strong&gt; — explains every improvement made&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Typewriter Animation&lt;/strong&gt; — forged prompt streams in dramatically&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;One-click Copy&lt;/strong&gt; — instantly copy the result&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keyboard Shortcut&lt;/strong&gt; — &lt;code&gt;Ctrl+Enter&lt;/code&gt; / &lt;code&gt;Cmd+Enter&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  - &lt;strong&gt;Responsive&lt;/strong&gt; — works on mobile and tablet
&lt;/h2&gt;

&lt;h2&gt;
  
  
  🛠️ Tech Stack
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;Technology&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Frontend&lt;/td&gt;
&lt;td&gt;Vanilla HTML / CSS / JavaScript&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Backend&lt;/td&gt;
&lt;td&gt;Python + Flask&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AI Model&lt;/td&gt;
&lt;td&gt;NVIDIA Llama 3.1 70B Instruct&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;API&lt;/td&gt;
&lt;td&gt;NVIDIA NIM API&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hosting&lt;/td&gt;
&lt;td&gt;Render&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Deliberately kept the stack simple — no React, no heavy frameworks. Just a single HTML file on the frontend and a lightweight Flask proxy on the backend.&lt;/p&gt;




&lt;h2&gt;
  
  
  🧠 How It Works
&lt;/h2&gt;

&lt;p&gt;The architecture is straightforward:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User Input → Flask Server → NVIDIA NIM API → Llama 3.1 70B → Structured JSON → UI
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;User enters a weak prompt&lt;/li&gt;
&lt;li&gt;Frontend sends &lt;code&gt;POST /api/forge&lt;/code&gt; to Flask&lt;/li&gt;
&lt;li&gt;Flask forwards to NVIDIA NIM API with a system prompt instructing Llama to act as a master prompt engineer&lt;/li&gt;
&lt;li&gt;Model returns structured JSON:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"forged_prompt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"techniques_used"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Context"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Specificity"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Persona"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"power_level"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"analysis"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Added role context, specified output format..."&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  5. Frontend renders with typewriter animation, technique tags, power meter, and warrior rank
&lt;/h2&gt;

&lt;h2&gt;
  
  
  📡 The Key: System Prompt Engineering
&lt;/h2&gt;

&lt;p&gt;The magic is in the system prompt sent to Llama 3.1 70B. It instructs the model to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Act as a master prompt engineer&lt;/li&gt;
&lt;li&gt;Identify weaknesses in the input prompt&lt;/li&gt;
&lt;li&gt;Apply specific prompt engineering techniques&lt;/li&gt;
&lt;li&gt;Return a structured JSON response with the forged prompt, techniques used, power level, and analysis
The model essentially uses prompt engineering &lt;em&gt;to improve prompts&lt;/em&gt; — meta, but incredibly effective.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🎖️ Warrior Rank System
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Power Level&lt;/th&gt;
&lt;th&gt;Rank&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1–2&lt;/td&gt;
&lt;td&gt;🪖 RECRUIT&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3–4&lt;/td&gt;
&lt;td&gt;🥉 BRONZE&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5–6&lt;/td&gt;
&lt;td&gt;🥈 SILVER&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7–8&lt;/td&gt;
&lt;td&gt;🥇 GOLD&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;9–10&lt;/td&gt;
&lt;td&gt;🏆 LEGENDARY&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  🚀 Run It Yourself
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/Divyansh0208/Prompt_Forge.git
&lt;span class="nb"&gt;cd &lt;/span&gt;Prompt_Forge
pip &lt;span class="nb"&gt;install &lt;/span&gt;flask python-dotenv requests
&lt;span class="nb"&gt;cp&lt;/span&gt; .env.example .env
&lt;span class="c"&gt;# Add your NVIDIA API key to .env&lt;/span&gt;
python server.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Get a free NVIDIA API key at &lt;a href="https://build.nvidia.com" rel="noopener noreferrer"&gt;build.nvidia.com&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  🔗 Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;🌐 &lt;strong&gt;Live Demo:&lt;/strong&gt; &lt;a href="https://prompt-forge-o970.onrender.com" rel="noopener noreferrer"&gt;prompt-forge-o970.onrender.com&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  - 💻 &lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/Divyansh0208/Prompt_Forge" rel="noopener noreferrer"&gt;github.com/Divyansh0208/Prompt_Forge&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Built for &lt;strong&gt;Google Prompt Wars 2026&lt;/strong&gt;. Try forging your weakest prompt and see what rank you earn ⚔️&lt;/p&gt;

</description>
      <category>promptengineering</category>
      <category>nvidia</category>
      <category>python</category>
      <category>webdev</category>
    </item>
    <item>
      <title>How I Built a Carbon Footprint Tracker with Django + NVIDIA NIM</title>
      <dc:creator>Divyansh</dc:creator>
      <pubDate>Wed, 24 Jun 2026 16:01:00 +0000</pubDate>
      <link>https://dev.to/divyansh0208/how-i-built-a-carbon-footprint-tracker-with-django-nvidia-nim-4jp3</link>
      <guid>https://dev.to/divyansh0208/how-i-built-a-carbon-footprint-tracker-with-django-nvidia-nim-4jp3</guid>
      <description>&lt;p&gt;I wanted to build something that mattered. Climate change is one of those topics where awareness is the first barrier — most people don't know their actual footprint. So I built &lt;strong&gt;Carbon.Ledger&lt;/strong&gt;: a full-stack web app that lets you log activities, see your CO₂ impact, and get AI-powered tips to cut back.&lt;/p&gt;

&lt;p&gt;Here's how I built it, what went wrong, and the one engineering decision I'm most proud of.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Stack
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Backend:&lt;/strong&gt; Django 5&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Frontend:&lt;/strong&gt; Tailwind CSS + htmx + Chart.js&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LLM:&lt;/strong&gt; NVIDIA NIM — &lt;code&gt;mistralai/mistral-large-3-675b-instruct-2512&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Database:&lt;/strong&gt; SQLite (dev) / PostgreSQL (prod)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cache:&lt;/strong&gt; Redis (prod)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  - &lt;strong&gt;Deployed on:&lt;/strong&gt; Render.com
&lt;/h2&gt;

&lt;h2&gt;
  
  
  The Three Pillars
&lt;/h2&gt;

&lt;p&gt;The app is built around one simple loop:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Understand → Track → Reduce&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Understand
&lt;/h3&gt;

&lt;p&gt;Users can read lesson articles, browse a glossary (CO₂e, Scope 1/2/3, Net Zero), and ask free-text climate questions via an LLM-powered Q&amp;amp;A — rate limited to 10 questions/day.&lt;/p&gt;

&lt;h3&gt;
  
  
  Track
&lt;/h3&gt;

&lt;p&gt;An activity logging form lets users log transport, energy, food, and goods. CO₂ is auto-computed on save using emission factors stored in the database — no LLM involved here. The dashboard shows a 30-day breakdown with a Chart.js donut chart and progress against a monthly goal.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reduce
&lt;/h3&gt;

&lt;p&gt;Rule-based insights compare the user's totals against national average benchmarks. The top emission category drives filtered recommendations. And once per day, the LLM generates a personalized tip — cached 24h per user.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Hardest Part: LLM Hallucination Guard
&lt;/h2&gt;

&lt;p&gt;This is the part I'm most proud of.&lt;/p&gt;

&lt;p&gt;LLMs make up numbers. That's a real problem when you're talking about emissions data — if the model says "beef produces 5 kg CO₂ per kg" when the real figure is 27, that's actively harmful misinformation.&lt;/p&gt;

&lt;p&gt;My solution in &lt;code&gt;services/llm.py&lt;/code&gt;:&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="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_contains_hallucinated_numbers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    Extract all numbers from the LLM response.
    Check every one of them exists in the context we provided.
    If not → hallucination detected.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;
    &lt;span class="n"&gt;response_numbers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findall&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;\d+\.?\d*&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;context_numbers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findall&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;\d+\.?\d*&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;response_numbers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;issubset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context_numbers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;ol&gt;
&lt;li&gt;Call LLM with context (user's actual data)&lt;/li&gt;
&lt;li&gt;Extract all numbers from the response&lt;/li&gt;
&lt;li&gt;Check every number exists in the context&lt;/li&gt;
&lt;li&gt;If not → retry with explicit "do not use numbers" instruction&lt;/li&gt;
&lt;li&gt;If still fails → return a safe static fallback message
On top of that: &lt;code&gt;timeout=10.0&lt;/code&gt; and &lt;code&gt;max_retries=1&lt;/code&gt; are explicitly set to prevent hung Gunicorn worker threads on slow LLM responses.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Key principle: the LLM is never used for calculations. All math is Python/DB.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  htmx for the Category Dropdown
&lt;/h2&gt;

&lt;p&gt;The activity log form has a dependent dropdown — selecting a category dynamically loads the relevant emission factors. I used htmx for this instead of writing custom JavaScript:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;select&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"category"&lt;/span&gt; 
        &lt;span class="na"&gt;hx-get=&lt;/span&gt;&lt;span class="s"&gt;"/factors/"&lt;/span&gt;
        &lt;span class="na"&gt;hx-target=&lt;/span&gt;&lt;span class="s"&gt;"#factor-select"&lt;/span&gt;
        &lt;span class="na"&gt;hx-trigger=&lt;/span&gt;&lt;span class="s"&gt;"change"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One attribute. No JS file. The server returns a partial HTML snippet with the filtered factors. This is exactly what htmx is built for.&lt;/p&gt;




&lt;h2&gt;
  
  
  Deployment on Render
&lt;/h2&gt;

&lt;p&gt;Render makes Django deployment straightforward. A few Django-specific things worth noting:&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="c1"&gt;# settings.py
&lt;/span&gt;&lt;span class="n"&gt;SECURE_PROXY_SSL_HEADER&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;HTTP_X_FORWARDED_PROTO&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;https&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;SECURE_SSL_REDIRECT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;DEBUG&lt;/span&gt;
&lt;span class="n"&gt;SESSION_COOKIE_SECURE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;DEBUG&lt;/span&gt;
&lt;span class="n"&gt;CSRF_COOKIE_SECURE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;DEBUG&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Render terminates TLS at the proxy level, so &lt;code&gt;SECURE_PROXY_SSL_HEADER&lt;/code&gt; is essential — without it Django won't recognize HTTPS requests and will redirect loop forever.&lt;/p&gt;

&lt;p&gt;Also: randomize your admin URL via env var:&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;ADMIN_URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ADMIN_URL&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;admin/&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Stops bots from hammering the default &lt;code&gt;/admin/&lt;/code&gt; endpoint.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I'd Do Differently
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Async LLM calls.&lt;/strong&gt; Right now the LLM calls are synchronous — a slow NVIDIA NIM response blocks a Gunicorn worker. The fix is Celery + Redis for background tasks. I skipped it for the MVP but it's the first thing I'd add.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Audited emission factors.&lt;/strong&gt; The 22 factors I seeded are approximations from public sources, not peer-reviewed figures. For a production app, you'd want to integrate a verified dataset like the UK Government GHG conversion factors.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Email verification.&lt;/strong&gt; Currently anyone can sign up with any email. Not a problem for an MVP, but a real gap.&lt;/p&gt;




&lt;h2&gt;
  
  
  Results
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;146 tests, 99% coverage&lt;/li&gt;
&lt;li&gt;Deployed and live&lt;/li&gt;
&lt;li&gt;Built in under a week&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;🔗 &lt;strong&gt;Live demo:&lt;/strong&gt; &lt;a href="https://carbon-footprint-awareness-4qpf.onrender.com" rel="noopener noreferrer"&gt;carbon-footprint-awareness-4qpf.onrender.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;🐙 &lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/Divyansh0208/Carbon-Footprint-Awareness" rel="noopener noreferrer"&gt;github.com/Divyansh0208/Carbon-Footprint-Awareness&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;If you're building something with Django + LLMs, the hallucination guard pattern is worth stealing. And if you have thoughts on better emission factor datasets, I'd love to hear them in the comments.&lt;/p&gt;

</description>
      <category>django</category>
      <category>python</category>
      <category>llm</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
