<?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: Madhav Gupta</title>
    <description>The latest articles on DEV Community by Madhav Gupta (@madhav_gupta_7caf016167d2).</description>
    <link>https://dev.to/madhav_gupta_7caf016167d2</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%2F3692952%2F2b495873-7222-4e32-9090-dae2fcb74fc9.jpg</url>
      <title>DEV Community: Madhav Gupta</title>
      <link>https://dev.to/madhav_gupta_7caf016167d2</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/madhav_gupta_7caf016167d2"/>
    <language>en</language>
    <item>
      <title>MandiLens: Open-Source AI That Tells a Farmer Which Mandi Actually Pays More</title>
      <dc:creator>Madhav Gupta</dc:creator>
      <pubDate>Thu, 15 Oct 2026 14:32:24 +0000</pubDate>
      <link>https://dev.to/madhav_gupta_7caf016167d2/mandilens-open-source-ai-that-tells-a-farmer-which-mandi-actually-pays-more-11d9</link>
      <guid>https://dev.to/madhav_gupta_7caf016167d2/mandilens-open-source-ai-that-tells-a-farmer-which-mandi-actually-pays-more-11d9</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;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%2F5helszjpp3951mqnbqqc.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%2F5helszjpp3951mqnbqqc.png" alt=" " width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;Every day, a small farmer in India has to answer one question: &lt;strong&gt;which mandi (market) should I take my crop to today?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Public price data exists. The government publishes it. But the published price is a &lt;em&gt;headline&lt;/em&gt;, not a payout. That higher price forty kilometres away can be wiped out by transport, loading, commission, and market charges. The farmer is left doing mental arithmetic with a number that was never the real number.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;MandiLens&lt;/strong&gt; turns that headline price into the number that actually matters: &lt;strong&gt;estimated net proceeds after your costs&lt;/strong&gt;. You enter a crop, a quantity, and your expenses; MandiLens ranks the candidate markets by what you'd actually take home, and shows exactly how it got there.&lt;/p&gt;

&lt;p&gt;It is built for the person who would pay for it: small and marginal farmers, and the traders and commission agents who advise them. The value is obvious in a single comparison — a ₹8/kg difference between two mandis on a tonne of maize is ₹8,000, and transport rarely eats all of it.&lt;/p&gt;

&lt;p&gt;Three things make MandiLens more than another price dashboard:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A deterministic decision engine.&lt;/strong&gt; Net proceeds are pure arithmetic: gross minus expenses, with correct unit conversion (kg, quintal, tonne) and percentage commissions. The same inputs always give the same outputs, and it is covered by unit tests. The AI is never allowed to overwrite these numbers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Provenance on every number.&lt;/strong&gt; Every price stores the source it came from and the &lt;em&gt;source-reported&lt;/em&gt; date — not the time we fetched it. Freshness is computed from when the market actually reported, never from when we cached. Demo data is badged as &lt;code&gt;DEMO DATA&lt;/code&gt; everywhere and is never silently mixed with real prices.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A grounded copilot that admits when it doesn't know.&lt;/strong&gt; Ask it "compare maize across Samastipur" or "what's the cheapest mandi for potatoes" and it calls real tools against the real database. Each answer carries a provenance banner, a visible tool trace, and explicit &lt;code&gt;grounded&lt;/code&gt; / &lt;code&gt;missingData&lt;/code&gt; flags. If there is no real data, it says so instead of inventing a number. In deterministic mode it states plainly that the answer was not generated by a model.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;&lt;strong&gt;Live app:&lt;/strong&gt; &lt;a href="https://mandilens.onrender.com" rel="noopener noreferrer"&gt;https://mandilens.onrender.com&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%2F8jnq8baspggiqh9be1ib.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%2F8jnq8baspggiqh9be1ib.png" alt=" " width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The Compare workbench ranks markets by net proceeds, not headline price.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;A quick tour:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open &lt;a href="https://mandilens.onrender.com/compare" rel="noopener noreferrer"&gt;Compare&lt;/a&gt;, type a crop and quantity, and add your transport and commission costs. MandiLens ranks markets by &lt;strong&gt;net proceeds&lt;/strong&gt;, not by headline price.&lt;/li&gt;
&lt;li&gt;Open &lt;a href="https://mandilens.onrender.com/copilot" rel="noopener noreferrer"&gt;Copilot&lt;/a&gt; and ask a plain-language question. Watch the tool trace show you which prices it actually read.&lt;/li&gt;
&lt;li&gt;Browse the &lt;a href="https://mandilens.onrender.com/commodities" rel="noopener noreferrer"&gt;commodities&lt;/a&gt; and &lt;a href="https://mandilens.onrender.com/markets" rel="noopener noreferrer"&gt;markets&lt;/a&gt; directories, then post a buy or sell offer in &lt;a href="https://mandilens.onrender.com/listings" rel="noopener noreferrer"&gt;Listings&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fux056ys7q9vl0a7qudua.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%2Fux056ys7q9vl0a7qudua.png" alt=" " width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Every copilot answer shows a provenance banner, a tool trace, and grounded / missingData flags.&lt;/em&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%2Fm6obb257myyg82olu5nt.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%2Fm6obb257myyg82olu5nt.png" alt=" " width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Post a buy or sell offer and get scored matches with reasons.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Demo accounts&lt;/strong&gt; (password &lt;code&gt;DemoPass123&lt;/code&gt; for all):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;farmer@demo.mandilens.test&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;trader@demo.mandilens.test&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;buyer@demo.mandilens.test&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The demo dataset is deterministic and clearly labelled: 8 Bihar APMC markets × 8 commodities × 45 days of prices, generated so anyone can reproduce exactly the same walkthrough.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Repository:&lt;/strong&gt; &lt;a href="https://github.com/madhavgupta07/mandilens" rel="noopener noreferrer"&gt;https://github.com/madhavgupta07/mandilens&lt;/a&gt; (MIT licensed)&lt;/p&gt;

&lt;p&gt;MandiLens is a real application, not a sketch: 15 MongoDB models with proper indexes, a two-phase CSV/JSON import pipeline, an admin console with ingestion and audit, first-party authentication, alerts, watchlists, saved comparisons, and a listings marketplace with matching and inquiries. There are &lt;strong&gt;73 passing tests&lt;/strong&gt; covering unit conversions, the opportunity engine, NLU, the importer, ingestion idempotency, and — importantly — the reliability of the assistant (that it does not fabricate data).&lt;/p&gt;

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

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgmm8dlvqxsxcxu7d6lrg.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%2Fgmm8dlvqxsxcxu7d6lrg.png" alt=" " width="800" height="540"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Client → API → deterministic engine + grounded copilot → MongoDB, with AGMARKNET ingestion.&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Next.js 16 (App Router) + React 19 + TypeScript&lt;/strong&gt;, styled with &lt;strong&gt;Tailwind CSS v4&lt;/strong&gt;, Radix primitives, and lucide-react.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MongoDB&lt;/strong&gt; (Atlas) with &lt;strong&gt;Mongoose&lt;/strong&gt; for storage, with a composite &lt;code&gt;dedupeKey&lt;/code&gt; so re-running ingestion is idempotent rather than duplicating rows.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Decimal.js&lt;/strong&gt; for every money and quantity calculation, because floating-point arithmetic has no place near someone's income.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zod&lt;/strong&gt; for validation at every API boundary, and &lt;strong&gt;Papaparse&lt;/strong&gt; for the CSV import path.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;vitest + mongodb-memory-server&lt;/strong&gt; so the whole suite runs without needing a live database.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Render&lt;/strong&gt; for deployment — the app and its environment are declared in a &lt;code&gt;render.yaml&lt;/code&gt; blueprint.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The AI layer is deliberately provider-agnostic. &lt;code&gt;AI_PROVIDER&lt;/code&gt; can be &lt;code&gt;ollama&lt;/code&gt; (fully local, open-weight), &lt;code&gt;openai_compatible&lt;/code&gt; (any compatible endpoint, including open-weight models like &lt;strong&gt;Gemma&lt;/strong&gt; served through an OpenAI-compatible API), or &lt;code&gt;deterministic&lt;/code&gt; — which needs &lt;strong&gt;no model key at all&lt;/strong&gt; and still runs the full grounded tool-calling pipeline. The data-ingestion adapter talks to &lt;strong&gt;AGMARKNET&lt;/strong&gt; (via data.gov.in) under the Government Open Data License – India.&lt;/p&gt;

&lt;p&gt;Two design decisions I'd defend in a code review:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The arithmetic is not the AI's job.&lt;/strong&gt; The comparison engine is a pure, testable function. The copilot's tools call it; the model never recomputes it. This is the difference between a decision tool and a confident guess.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Missing data is a first-class outcome.&lt;/strong&gt; An empty result set is reported as &lt;em&gt;"we have no recent price for this"&lt;/em&gt; — never as &lt;em&gt;"the market is closed"&lt;/em&gt;. Words like that change what a farmer does with their morning.&lt;/li&gt;
&lt;/ol&gt;

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

&lt;p&gt;Agricultural price intelligence is exactly the kind of tool where openness is not a philosophy but a requirement.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It has to be cheap enough to be free for the people who need it most.&lt;/strong&gt; A farmer comparing two mandis is not a customer who can absorb a per-query AI bill. MandiLens runs entirely without a model key in deterministic mode, and can run a fully local open-weight model through Ollama when a model is wanted. There is no vendor whose pricing decides whether a smallholder gets to use it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It has to be inspectable, because the stakes are someone's harvest.&lt;/strong&gt; Judging is done against government data under an open licence, and every number in the app traces back to its source and its source-reported date. When a number is wrong, anybody can follow it home. That audit trail is only possible because the whole stack — the price engine, the ingestion adapter, the copilot's tools — is open and readable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It has to be adaptable to a niche a general API handles poorly.&lt;/strong&gt; "Which mandi pays more for my maize after transport" is not a question a general-purpose assistant answers well, because the answer lives in a specific, messy, local dataset. Grounding an open model in that dataset — and letting that logic be forked, localized, and translated by anyone — is what makes it work at all.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;And it keeps the honest path the easy path.&lt;/strong&gt; Running in deterministic mode means MandiLens would rather say &lt;em&gt;"I don't have this price"&lt;/em&gt; than invent one. Open, inspectable code is what lets a judge, a contributor, or a farmer verify that claim instead of taking it on faith.&lt;/p&gt;

&lt;p&gt;Open-source AI makes a tool like this possible for a community that a closed product would never find worth serving.&lt;/p&gt;

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

&lt;p&gt;MandiLens was built in a human-in-the-loop workflow with an AI coding agent: the architecture decisions, the grounding rules, the data model, and the review were human; the agent did a lot of the implementation and the test scaffolding. The design constraints that shaped it — &lt;em&gt;the AI never overrides the arithmetic, missing data is never dressed up as certainty&lt;/em&gt; — were enforced by tests, not just intent. I can share an agent-session transcript to illustrate the process.&lt;/p&gt;

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

&lt;p&gt;My project is submitted for the &lt;strong&gt;Hacktoberfest Open-Source AI Challenge — Week 2: Side Hustle&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;It also genuinely uses partner technology, so I am entering it in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of MongoDB Atlas&lt;/strong&gt; — MongoDB is the system of record: 15 collections with compound and unique indexes, an idempotent ingestion pipeline keyed on a composite &lt;code&gt;dedupeKey&lt;/code&gt;, and provenance stored per price record.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of Render&lt;/strong&gt; — the live demo is deployed on Render as a web service, with the environment defined in a checked-in &lt;code&gt;render.yaml&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of Gemma&lt;/strong&gt; — the copilot supports open-weight &lt;strong&gt;Gemma&lt;/strong&gt; served over an OpenAI-compatible endpoint, and runs fully locally through Ollama; deterministic mode keeps it working even with no model key at all.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;MandiLens is open source under the MIT licence. Mandi price data is attributed to AGMARKNET, Directorate of Marketing &amp;amp; Inspection, via data.gov.in under the Government Open Data License – India. It is an independent project and is not affiliated with or endorsed by AGMARKNET or the Government of India.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>hf26challenge</category>
      <category>devchallenge</category>
      <category>opensource</category>
      <category>ai</category>
    </item>
    <item>
      <title>Trailside: an open-weight bird call identifier that talks back</title>
      <dc:creator>Madhav Gupta</dc:creator>
      <pubDate>Fri, 09 Oct 2026 08:59:28 +0000</pubDate>
      <link>https://dev.to/madhav_gupta_7caf016167d2/trailside-an-open-weight-bird-call-identifier-that-talks-back-21eb</link>
      <guid>https://dev.to/madhav_gupta_7caf016167d2/trailside-an-open-weight-bird-call-identifier-that-talks-back-21eb</guid>
      <description>&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Trailside&lt;/strong&gt; is a bird call identifier built to get you outside — and keep you outside. Open the PWA, tap record, stand still for 5–20 seconds, and it tells you what you heard &lt;strong&gt;out loud&lt;/strong&gt;: the species, the confidence, and a short spoken field note. No account, no app-store install, no staring at a screen mid-trudge.&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%2Fo18tomk0afzuugk3o9zl.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%2Fo18tomk0afzuugk3o9zl.png" alt=" " width="520" height="920"&gt;&lt;/a&gt;&lt;br&gt;
It's for day hikers, beginner birders, parents with curious kids, and anyone who walks past birds they'd like to &lt;em&gt;name&lt;/em&gt; without stopping to fiddle with a phone.&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%2Fujxj8gorj93wasmn7r3e.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%2Fujxj8gorj93wasmn7r3e.png" alt=" " width="520" height="920"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;Try it live: &lt;strong&gt;&lt;a href="https://trailside-0hlr.onrender.com" rel="noopener noreferrer"&gt;https://trailside-0hlr.onrender.com&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Verified end-to-end from the deploy: a 34-second American Robin recording returned &lt;strong&gt;American Robin @ 90%&lt;/strong&gt; (plus Song Sparrow and House Finch hints) with an ElevenLabs-narrated field note in ~15 seconds.&lt;/p&gt;
&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;

&lt;p&gt;Open source: &lt;strong&gt;&lt;a href="https://github.com/madhavgupta07/trailside" rel="noopener noreferrer"&gt;https://github.com/madhavgupta07/trailside&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Python/FastAPI backend, plain-JS dependency-free PWA frontend (no build step)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;onnxruntime&lt;/code&gt; CPU inference — no TensorFlow, no GPU needed&lt;/li&gt;
&lt;li&gt;Async job flow so a 30-second clip never trips a slow gateway timeout&lt;/li&gt;
&lt;li&gt;10 unit/integration tests, including a real-audio classification test (&lt;code&gt;american_robin.mp3&lt;/code&gt; → American Robin @ 90.1% locally)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  How I Built It
&lt;/h2&gt;

&lt;p&gt;The whole experience is built &lt;em&gt;around&lt;/em&gt; open-weight AI — the species engine is the core, and the narrator is a second open-weight model on top:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Piece&lt;/th&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;th&gt;License&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;BirdNET v3.0&lt;/strong&gt; (Cornell / TU Chemnitz, via &lt;code&gt;tphakala/BirdNET-v3.0-Models&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;11,560-species bioacoustics classifier; I use the 800-class &lt;code&gt;north-america-east&lt;/code&gt; regional ONNX slice (~150 MB)&lt;/td&gt;
&lt;td&gt;CC BY-SA 4.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Gemma 4&lt;/strong&gt; (Google)&lt;/td&gt;
&lt;td&gt;writes the 2–3 sentence spoken field note; served via an OpenAI-compatible endpoint (OpenRouter, free &lt;code&gt;google/gemma-4-26b-a4b-it:free&lt;/code&gt; by default)&lt;/td&gt;
&lt;td&gt;Gemma Terms (open weights)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ElevenLabs&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;narrates the note back so the result is &lt;em&gt;heard&lt;/em&gt;, not read&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Render&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Blueprint (&lt;code&gt;render.yaml&lt;/code&gt;) deploys FastAPI; &lt;code&gt;startCommand&lt;/code&gt; downloads and checksum-verifies the model at boot&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Pipeline: &lt;code&gt;mic → browser WAV encode (16-bit PCM) → FastAPI → BirdNET (32 kHz, 5 s windows, running max-aggregate) → top-5 species → Gemma field note → ElevenLabs MP3, played back in the page&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Resilience was a real constraint, not a checkbox: the free Gemma endpoint rate-limits hard under load, so narration retries with backoff and then degrades to a deterministic offline template note — the app &lt;strong&gt;never errors&lt;/strong&gt; because a model is busy. There's the same graceful no-voice fallback if no TTS key is set. The API surfaces &lt;em&gt;why&lt;/em&gt; (&lt;code&gt;note_error&lt;/code&gt;) instead of hiding it.&lt;/p&gt;
&lt;h2&gt;
  
  
  Why Does Open Innovation Matter?
&lt;/h2&gt;

&lt;p&gt;Trail use is exactly where closed APIs fail:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;It can work fully offline.&lt;/strong&gt; The identical code path runs on a laptop with llama.cpp and no internet at all. A network drop mid-hike doesn't produce an error — it produces a gentler fallback note.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Your recordings stay yours.&lt;/strong&gt; Audio goes to a server you own; you can run it yourself and take the network out entirely.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Swap anything at runtime.&lt;/strong&gt; Regional model, Gemma size, voice, provider — all env vars, zero rewrites. Fine-tune a model and point a variable at it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It costs nothing at the core.&lt;/strong&gt; Both core models are free weights; BirdNET inference is pure CPU on the cheapest deploy.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A closed API literally cannot offer a free, server-owned model that keeps working when the phone loses signal in the woods. That's the difference open weights made here.&lt;/p&gt;
&lt;h2&gt;
  
  
  My Agent Session
&lt;/h2&gt;

&lt;p&gt;The full build transcript, as it happened:&lt;/p&gt;


&lt;div class="ltag-agent-session"&gt;
  &lt;div class="agent-session-header"&gt;
    
    &lt;span class="agent-session-tool-icon-badge" title="Gemini CLI"&gt;
&lt;/span&gt;
    &lt;span class="agent-session-title"&gt;Session 2026-10-09 08:14&lt;/span&gt;
  &lt;/div&gt;

  &lt;div class="agent-session-scroll"&gt;

      &lt;div class="agent-session-message agent-session-user"&gt;
        &lt;div class="agent-session-role-badge agent-session-role-user"&gt;
          You
        &lt;/div&gt;
        &lt;div class="agent-session-content"&gt;
                &lt;div class="agent-session-text"&gt;
                  &lt;p&gt;Take a Touch Grass / open-source AI idea and build it: a bird call identifier that gets people outside, with open-weight models at its core, deployable on Render's free plan.&lt;/p&gt;

                &lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;

      &lt;div class="agent-session-message agent-session-assistant"&gt;
        &lt;div class="agent-session-role-badge agent-session-role-assistant"&gt;
          Agent
        &lt;/div&gt;
        &lt;div class="agent-session-content"&gt;
                &lt;div class="agent-session-text"&gt;
                  &lt;p&gt;Architecture: ref — browser mic -&amp;gt; client-side WAV encode (no ffmpeg) -&amp;gt; FastAPI -&amp;gt; BirdNET v3.0 ONNX (open weights, regional north-america-east, 800 classes) -&amp;gt; Gemma writes a spoken field note via OpenRouter -&amp;gt; ElevenLabs TTS. Scaffolding FastAPI app, classifier service, config, requirements.&lt;/p&gt;

                &lt;/div&gt;
              &lt;div class="agent-session-tool-call"&gt;
                
                  &lt;span class="agent-session-chevron"&gt;▸&lt;/span&gt;
                  &lt;span class="tool-name tool-name-writefile"&gt;write_file&lt;/span&gt;
                    &lt;span class="tool-input-preview"&gt;app/services/classifier.py (BirdNET ONNX loading: scipy resample_poly to 32kH...&lt;/span&gt;
                
                &lt;div class="agent-session-tool-detail"&gt;
                    &lt;div class="tool-section"&gt;
                      &lt;div class="tool-section-label"&gt;Input&lt;/div&gt;
                      &lt;pre class="agent-session-pre highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;classifier&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;py &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BirdNET&lt;/span&gt; &lt;span class="n"&gt;ONNX&lt;/span&gt; &lt;span class="n"&gt;loading&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;scipy&lt;/span&gt; &lt;span class="n"&gt;resample_poly&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="n"&gt;kHz&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="n"&gt;windowing&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;uncontrollable&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nb"&gt;max&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;aggregate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;low&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;memory&lt;/span&gt; &lt;span class="n"&gt;SessionOptions&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
                    &lt;/div&gt;
                    &lt;div class="tool-section"&gt;
                      &lt;div class="tool-section-label"&gt;Output&lt;/div&gt;
                      &lt;pre class="agent-session-pre highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;Classifier&lt;/span&gt; &lt;span class="n"&gt;service&lt;/span&gt; &lt;span class="n"&gt;written&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="n"&gt;auto&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;download&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;sha256&lt;/span&gt; &lt;span class="n"&gt;verify&lt;/span&gt; &lt;span class="n"&gt;script&lt;/span&gt; &lt;span class="n"&gt;added&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
                    &lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;

      &lt;div class="agent-session-message agent-session-user"&gt;
        &lt;div class="agent-session-role-badge agent-session-role-user"&gt;
          You
        &lt;/div&gt;
        &lt;div class="agent-session-content"&gt;
                &lt;div class="agent-session-text"&gt;
                  &lt;p&gt;Add tests and a real audio fixture, then download the model and verify classification locally.&lt;/p&gt;

                &lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;

      &lt;div class="agent-session-message agent-session-assistant"&gt;
        &lt;div class="agent-session-role-badge agent-session-role-assistant"&gt;
          Agent
        &lt;/div&gt;
        &lt;div class="agent-session-content"&gt;
                &lt;div class="agent-session-text"&gt;
                  &lt;p&gt;Downloaded the 149MB regional model, grabbed an American Robin recording from Wikimedia Commons as a fixture, wired narrator (Gemma) + TTS (ElevenLabs) with graceful fallbacks, and wrote API + service tests with mocked HTTP.&lt;/p&gt;

                &lt;/div&gt;
              &lt;div class="agent-session-tool-call"&gt;
                
                  &lt;span class="agent-session-chevron"&gt;▸&lt;/span&gt;
                  &lt;span class="tool-name tool-name-bash"&gt;bash&lt;/span&gt;
                    &lt;span class="tool-input-preview"&gt;python -m pytest tests/ -q&lt;/span&gt;
                
                &lt;div class="agent-session-tool-detail"&gt;
                    &lt;div class="tool-section"&gt;
                      &lt;div class="tool-section-label"&gt;Input&lt;/div&gt;
                      &lt;pre class="agent-session-pre agent-session-terminal"&gt;&lt;code&gt;$ python -m pytest tests/ -q&lt;/code&gt;&lt;/pre&gt;
                    &lt;/div&gt;
                    &lt;div class="tool-section"&gt;
                      &lt;div class="tool-section-label"&gt;Output&lt;/div&gt;
                      &lt;pre class="agent-session-pre"&gt;&lt;code&gt;10 passed — classifier returns American Robin @ 90.1% on the 34s fixture&lt;/code&gt;&lt;/pre&gt;
                    &lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
                &lt;div class="agent-session-text"&gt;
                  &lt;p&gt;Deployed via render.yaml Blueprint; set OpenRouter + ElevenLabs keys in Render env; health endpoint reports model/gemma/elevenlabs readiness.&lt;/p&gt;

                &lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;

      &lt;div class="agent-session-message agent-session-user"&gt;
        &lt;div class="agent-session-role-badge agent-session-role-user"&gt;
          You
        &lt;/div&gt;
        &lt;div class="agent-session-content"&gt;
                &lt;div class="agent-session-text"&gt;
                  &lt;p&gt;Gemma narration is silently falling back to the offline template on production. Find out why.&lt;/p&gt;

                &lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;

      &lt;div class="agent-session-message agent-session-assistant"&gt;
        &lt;div class="agent-session-role-badge agent-session-role-assistant"&gt;
          Agent
        &lt;/div&gt;
        &lt;div class="agent-session-content"&gt;
              &lt;div class="agent-session-tool-call"&gt;
                
                  &lt;span class="agent-session-chevron"&gt;▸&lt;/span&gt;
                  &lt;span class="tool-name tool-name-bash"&gt;bash&lt;/span&gt;
                    &lt;span class="tool-input-preview"&gt;curl /api/identify with american_robin.mp3; inspect note_error&lt;/span&gt;
                
                &lt;div class="agent-session-tool-detail"&gt;
                    &lt;div class="tool-section"&gt;
                      &lt;div class="tool-section-label"&gt;Input&lt;/div&gt;
                      &lt;pre class="agent-session-pre agent-session-terminal"&gt;&lt;code&gt;$ curl /api/identify with american_robin.mp3; inspect note_error&lt;/code&gt;&lt;/pre&gt;
                    &lt;/div&gt;
                    &lt;div class="tool-section"&gt;
                      &lt;div class="tool-section-label"&gt;Output&lt;/div&gt;
                      &lt;pre class="agent-session-pre"&gt;&lt;code&gt;note_error: 404 'No endpoints found for google/gemma-3n-e4b-it:free' — OpenRouter no longer serves gemma-3n&lt;/code&gt;&lt;/pre&gt;
                    &lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
                &lt;div class="agent-session-text"&gt;
                  &lt;p&gt;Swapped to google/gemma-4-26b-a4b-it:free (open-weight Gemma 4, actually serviced), updated docs and the DEV post, redeployed. Next prod run hit 429 rate limits — added 3 retries with backoff before the template fallback.&lt;/p&gt;

                &lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;

      &lt;div class="agent-session-message agent-session-user"&gt;
        &lt;div class="agent-session-role-badge agent-session-role-user"&gt;
          You
        &lt;/div&gt;
        &lt;div class="agent-session-content"&gt;
                &lt;div class="agent-session-text"&gt;
                  &lt;p&gt;Long clips time out / 502 on the 512MB free instance. Make the API fit the free tier.&lt;/p&gt;

                &lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;

      &lt;div class="agent-session-message agent-session-assistant"&gt;
        &lt;div class="agent-session-role-badge agent-session-role-assistant"&gt;
          Agent
        &lt;/div&gt;
        &lt;div class="agent-session-content"&gt;
                &lt;div class="agent-session-text"&gt;
                  &lt;p&gt;Diagnosed peak-memory &amp;amp; long-request issues: inference now runs windows one-at-a-time (batches stayed safe), threads=1, analysis windows capped; then made /api/identify fully async — POST returns a job_id in ~0ms and the page polls GET /api/jobs/{id}.&lt;/p&gt;

                &lt;/div&gt;
              &lt;div class="agent-session-tool-call"&gt;
                
                  &lt;span class="agent-session-chevron"&gt;▸&lt;/span&gt;
                  &lt;span class="tool-name tool-name-editfile"&gt;edit_file&lt;/span&gt;
                    &lt;span class="tool-input-preview"&gt;app/jobs.py (ThreadPoolExecutor job store) + frontend app.js polling loop&lt;/span&gt;
                
                &lt;div class="agent-session-tool-detail"&gt;
                    &lt;div class="tool-section"&gt;
                      &lt;div class="tool-section-label"&gt;Input&lt;/div&gt;
                      &lt;pre class="agent-session-pre highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;jobs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;py &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ThreadPoolExecutor&lt;/span&gt; &lt;span class="n"&gt;job&lt;/span&gt; &lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;frontend&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;js&lt;/span&gt; &lt;span class="n"&gt;polling&lt;/span&gt; &lt;span class="n"&gt;loop&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
                    &lt;/div&gt;
                    &lt;div class="tool-section"&gt;
                      &lt;div class="tool-section-label"&gt;Output&lt;/div&gt;
                      &lt;pre class="agent-session-pre highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;Verified&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;end&lt;/span&gt; &lt;span class="n"&gt;on&lt;/span&gt; &lt;span class="n"&gt;prod&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;34&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="n"&gt;clip&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;American&lt;/span&gt; &lt;span class="n"&gt;Robin&lt;/span&gt; &lt;span class="mi"&gt;90&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;ElevenLabs&lt;/span&gt; &lt;span class="n"&gt;narration&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;no&lt;/span&gt; &lt;span class="mi"&gt;502&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
                    &lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;

      &lt;div class="agent-session-message agent-session-user"&gt;
        &lt;div class="agent-session-role-badge agent-session-role-user"&gt;
          You
        &lt;/div&gt;
        &lt;div class="agent-session-content"&gt;
                &lt;div class="agent-session-text"&gt;
                  &lt;p&gt;Wrap up the submission: structure the DEV post per the challenge template, fill verified facts, and prep the agent session embed.&lt;/p&gt;

                &lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;

      &lt;div class="agent-session-message agent-session-assistant"&gt;
        &lt;div class="agent-session-role-badge agent-session-role-assistant"&gt;
          Agent
        &lt;/div&gt;
        &lt;div class="agent-session-content"&gt;
                &lt;div class="agent-session-text"&gt;
                  &lt;p&gt;Wrote DEV_SUBMISSION.md matching the official template (What I Built / Demo / Code / How I Built It / Why Open Innovation / My Agent Session / Prize Categories), filled in the live URL and verified results, and saved this session transcript for the {% agent_session %} embed.&lt;/p&gt;

                &lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;
  &lt;/div&gt;

  &lt;div class="agent-session-footer"&gt;
    &lt;span class="agent-session-meta"&gt;
        10 of 10 messages
    &lt;/span&gt;
  &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;(If the embed doesn't render on your view, the direct link is &lt;a href="https://dev.to/agent_sessions/session-2026-10-09-0814-tf0pln"&gt;https://dev.to/agent_sessions/session-2026-10-09-0814-tf0pln&lt;/a&gt;)&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Best Use of Gemma (open-weight Gemma 4 writing the field note)&lt;/li&gt;
&lt;li&gt;Best Use of ElevenLabs (spoken narration keeps your eyes on the trees)&lt;/li&gt;
&lt;li&gt;Deploy/Run on Render (Blueprint deploy; the model is downloaded + checksummed at boot)&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Built for Round 1 of the Hacktoberfest 2026 "Touch Grass" challenge. Open weights for the win — now go outside and listen.&lt;/em&gt;&lt;/p&gt;

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