<?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: Himani Sahu</title>
    <description>The latest articles on DEV Community by Himani Sahu (@himani_sahu_de9e1ed9b002d).</description>
    <link>https://dev.to/himani_sahu_de9e1ed9b002d</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%2F4091606%2F94fe9bb6-06a6-48bb-93c0-a54204e70197.jpg</url>
      <title>DEV Community: Himani Sahu</title>
      <link>https://dev.to/himani_sahu_de9e1ed9b002d</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/himani_sahu_de9e1ed9b002d"/>
    <language>en</language>
    <item>
      <title>GrassHopper</title>
      <dc:creator>Himani Sahu</dc:creator>
      <pubDate>Mon, 12 Oct 2026 06:49:44 +0000</pubDate>
      <link>https://dev.to/himani_sahu_de9e1ed9b002d/grasshopper-2ld5</link>
      <guid>https://dev.to/himani_sahu_de9e1ed9b002d/grasshopper-2ld5</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;Grasshopper turns a walk into a tiny side quest. Tap one button and a local Gemma model writes five things to find nearby, from easy ("a bicycle") to hard ("finding a rickshaw with grafiti").&lt;br&gt;
Tell it where you are and the quests get local: in Jhansi, mine included a decorated&lt;br&gt;
auto-rickshaw and a hand-painted shop sign.&lt;/p&gt;

&lt;p&gt;Then the phone goes back in your pocket. When you spot something, snap it, and an&lt;br&gt;
open-source vision model checks the photo right on your phone. No upload, no signal needed.&lt;br&gt;
The screen is just a checklist; the walk is the point.&lt;/p&gt;

&lt;p&gt;It's for anyone who opens their phone "for a second" on a walk and spent the whole walk glued to their phones. It makes every day walks around your city more enjoyable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Taking it outside:&lt;/strong&gt;&lt;br&gt;
Ok that was from claude, i actually tried it, out in my walk, it worked pretty well, it was fun, to actually click photos of things i can actually find in my locality.&lt;/p&gt;

&lt;p&gt;The finding a guy in a lungi, cycling around, was a quest i really enjoyed lol i went out with my sister, and we laughed so much finding A photo like that.&lt;/p&gt;

&lt;p&gt;Yes, but its not perfect, CLIP got some wrong,, like one time it could really tell what the image uploaded represents, but that was only once.&lt;/p&gt;
&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
      &lt;div class="c-embed__body flex items-center justify-between"&gt;
        &lt;a href="https://grasshopper-ea0p.onrender.com" rel="noopener noreferrer" class="c-link fw-bold flex items-center"&gt;
          &lt;span class="mr-2"&gt;grasshopper-ea0p.onrender.com&lt;/span&gt;
          

        &lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&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%2Fbdfmavko60kql08mmbxf.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%2Fbdfmavko60kql08mmbxf.png" alt=" " width="799" height="425"&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%2Fb5h3p02h7j3qc2q2c6od.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%2Fb5h3p02h7j3qc2q2c6od.png" alt=" " width="800" height="423"&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%2Fsn8hzcf014grbyls97mf.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%2Fsn8hzcf014grbyls97mf.png" alt=" " width="717" height="1600"&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%2Fr55zrnz0kxlvqfcvtwyd.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%2Fr55zrnz0kxlvqfcvtwyd.png" alt=" " width="717" height="1600"&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%2F9mgtgplcgqhecpy19h19.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%2F9mgtgplcgqhecpy19h19.png" alt=" " width="717" height="1600"&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/Siddhi-sahu" rel="noopener noreferrer"&gt;
        Siddhi-sahu
      &lt;/a&gt; / &lt;a href="https://github.com/Siddhi-sahu/Grasshopper" rel="noopener noreferrer"&gt;
        Grasshopper
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;


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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Gemma 3 (4B), running locally with Ollama.&lt;/strong&gt; It writes the quests. An Express +
TypeScript server asks for exactly five, constrained to a JSON schema, so the output is
always valid. Gemma tended to miscount the easy/medium/hard split, so the app assigns
difficulty by position and lets Gemma focus on what to find.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CLIP (&lt;code&gt;clip-vit-base-patch32&lt;/code&gt;) via Transformers.js in a Web Worker.&lt;/strong&gt; It checks photos
in the browser. Each photo is compared against the quest, the other quests, and decoys like
"a phone screen" or "the inside of a room", so a picture of your ceiling doesn't count.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;IndexedDB&lt;/strong&gt; keeps quests and photos on the device.&lt;/li&gt;
&lt;li&gt;When CLIP isn't convinced, it says what it thinks it sees ("looks more like a cat to me")
and you can count it anyway. The AI suggests; the human decides.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Your photos stay yours.&lt;/strong&gt; A walk produces photos of your street, your neighbourhood,
maybe your home. With an open model running in the browser, none of them get uploaded.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It works with no signal.&lt;/strong&gt; The photo checker downloads once and is cached. Out on a trail
or in a dead zone, it still works. A closed vision API would fail exactly when you're
outside.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It costs nothing to run.&lt;/strong&gt; Both models run on my own hardware: no API key, no per-photo
bill, no rate limit.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;I could change the model's behaviour.&lt;/strong&gt; I could see exactly how Gemma's output was
shaped, constrain it to a schema, and swap hosts without rewriting the app.&lt;/li&gt;
&lt;/ul&gt;

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


&lt;div class="ltag-agent-session"&gt;
  &lt;div class="agent-session-header"&gt;
    
    &lt;span class="agent-session-tool-icon-badge" title="Claude Code"&gt;
&lt;/span&gt;
    &lt;span class="agent-session-title"&gt;Planning Side Quest Walk for Hacktoberfest "Touch Grass"&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;All I need to know about this challenge (Hacktoberfest Open-Source AI Challenge: Week 1, theme 'Touch Grass'), and ideas on what should I build? Like someone built this - &lt;a href="https://mymeowfolio.vercel.app" rel="noopener noreferrer"&gt;https://mymeowfolio.vercel.app&lt;/a&gt;; &lt;a href="https://github.com/ThunderKhan/meowfolio" rel="noopener noreferrer"&gt;https://github.com/ThunderKhan/meowfolio&lt;/a&gt;&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-webfetch"&gt;WebFetch&lt;/span&gt;
                    &lt;span class="tool-input-preview"&gt;https://github.com/ThunderKhan/meowfolio&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"&gt;&lt;code&gt;https://github.com/ThunderKhan/meowfolio&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;Meowfolio: private street-cat scrapbook. YOLOS-tiny detection + DINOv2-small embeddings via Transformers.js in the browser, IndexedDB storage, static Vercel hosting, no backend. Auto identity matching disabled after false positives; humans confirm identity.&lt;/code&gt;&lt;/pre&gt;
                    &lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
                &lt;div&gt;
                  &lt;div class="agent-session-text agent-session-text-collapse"&gt;
                    &lt;p&gt;Summary: build a new project with open-source AI at its core that gets people outside. Due Oct 11. Writing quality is weighted most. Explain why open matters (offline, private, free, swappable). Partner categories: Gemma, Render, ElevenLabs, Entire, etc.&lt;/p&gt;

&lt;p&gt;What Meowfolio got right: fully in-browser local models, a clear privacy story, strong visual identity, honesty about limits.&lt;/p&gt;

&lt;p&gt;Ideas: 1) Side Quest Walk: Gemma writes 5 small things to find on a walk, a local CLIP model verifies your photos. 2) Proof of Grass: a screen-time blocker that unlocks on a verified outdoor photo. 3) Leaf Ledger. 4) Trail Ears (BirdNET offline). 5) Run Club Hype Coach (Gemma + ElevenLabs). 6) Cloud Atlas Journal.&lt;/p&gt;

&lt;p&gt;Recommendation: Side Quest Walk. Spend half the time on the build and half on the write-up, and test it outside.&lt;/p&gt;

                  &lt;/div&gt;
                  Show more
                &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;I like Side Quest Walk. Later: use the user's location so Gemma suggests local things to find, easy to hard (e.g. in Jhansi, India: find a rickshaw). Save this session to DEV. I'll configure Entire myself, so tell me how. Initialize the project in ~/projects/hackathons after we pick a name; suggest some. Stack: TypeScript, Node, Express. Suggest the tech we need.&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;Saving this session to DEV, then suggesting names, a tech stack, and Entire setup steps.&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;
        4 of 4 messages
    &lt;/span&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;br&gt;
&lt;div class="ltag-agent-session"&gt;
  &lt;div class="agent-session-header"&gt;
    
    &lt;span class="agent-session-tool-icon-badge" title="Claude Code"&gt;
&lt;/span&gt;
    &lt;span class="agent-session-title"&gt;Building Grasshopper: local Gemma quests + on-device CLIP photo checks&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;I like the name Grasshopper, I've initialized it. What do you mean by 'plan with Gemma, check photos offline'? I like the stack. Running locally is the first priority. Frontend inspired by meowfolio: green, cutesy, Y2K. Web and phone. Does the Gemma host matter for deployment?&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;ollama list&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;$ ollama list&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;gemma3:4b (3.3 GB) already pulled&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;Scaffolding an npm workspace: server/ (Express 5 + TypeScript) and web/ (Vite + vanilla TypeScript).&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-write"&gt;Write&lt;/span&gt;
                    &lt;span class="tool-input-preview"&gt;server/src/gemma.ts, quests.ts, index.ts&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"&gt;&lt;code&gt;server/src/gemma.ts, quests.ts, index.ts&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;GemmaClient interface with an Ollama implementation; POST /api/quests asks gemma3:4b for 5 quests using a zod schema converted to JSON Schema and passed as Ollama's structured-output format.&lt;/code&gt;&lt;/pre&gt;
                    &lt;/div&gt;
                &lt;/div&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;curl -X POST localhost:3001/api/quests -d '{"place":"Jhansi, India"}'&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 -X POST localhost:3001/api/quests -d '{"place":"Jhansi, India"}'&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;5 quests in ~13s, including 'a yellow rickshaw reflected in water' (easy). Too contrived for 'easy'.&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;Tightening the prompt: one thing per quest, no reflections or combinations, and difficulty comes from rarity. The 'visual' field must be 2-5 words for the image classifier.&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;retest Jhansi + no place&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;$ retest Jhansi + no place&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;Jhansi: a decorated auto-rickshaw, a pile of ripe mangoes, a hand-painted shop sign... Generic run came back with a 1/2/2 split instead of 2/2/1.&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;Gemma orders quests well but miscounts the split, so I assign difficulty by position: easy, easy, medium, medium, hard.&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-write"&gt;Write&lt;/span&gt;
                    &lt;span class="tool-input-preview"&gt;web/src/clip.worker.ts, verify.ts, store.ts, main.ts, style.css&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"&gt;&lt;code&gt;web/src/clip.worker.ts, verify.ts, store.ts, main.ts, style.css&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;Transformers.js CLIP (Xenova/clip-vit-base-patch32) zero-shot in a Web Worker. Candidate labels = target + the other quests + decoys like 'a phone or computer screen' and 'the inside of a room'. Pass if it's the top label or scores &amp;gt;= 0.3. Photos are downscaled to 1024px and stored in IndexedDB. Green Y2K UI with pixel-font title bars. If CLIP isn't convinced, the walker can choose 'count it anyway'.&lt;/code&gt;&lt;/pre&gt;
                    &lt;/div&gt;
                &lt;/div&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;vite build&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;$ vite build&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;built; clip worker 546 kB + onnxruntime wasm&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;Plan with Gemma, check offline: quest generation needs the server once, but CLIP runs in the phone's browser and is cached after the first download. Photo checks work with no signal, and photos never leave the device. The Gemma host mostly doesn't matter; it sits behind an interface so it can be swapped.&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;node test.mjs (same CLIP model + pass logic)&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;$ node test.mjs (same CLIP model + pass logic)&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;cats/a cat: pass 0.897 | cats/a bicycle: fail, saw 'a cat' | tiger/a tiger: pass 0.999 | tiger/a patch of moss: fail, saw 'a tiger'&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;4/4 correct on sample images. Real street photos still need testing outside.&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;The title wraps weirdly: 'Grasshoppe' then 'r'. Put Grass on one line and hopper on the next.&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-edit"&gt;Edit&lt;/span&gt;
                    &lt;span class="tool-input-preview"&gt;web/src/main.ts + style.css&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 typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;web&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ts&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;css&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 typescript"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;h1&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;span&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Grass&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/span&amp;gt;&amp;lt;span&amp;gt;hopper&amp;lt;/&lt;/span&gt;&lt;span class="nx"&gt;span&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h1&amp;gt; with each span on its own line&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&gt;

  &lt;div class="agent-session-footer"&gt;
    &lt;span class="agent-session-meta"&gt;
        4 of 4 messages
    &lt;/span&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;br&gt;
&lt;div class="ltag-agent-session"&gt;
  &lt;div class="agent-session-header"&gt;
    
    &lt;span class="agent-session-tool-icon-badge" title="Claude Code"&gt;
&lt;/span&gt;
    &lt;span class="agent-session-title"&gt;Grasshopper part 3: location, hosted Gemma 4, and shipping to Render&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;Do auto-detect location and hosted Gemma; I have an API key. I'll deploy both backend and frontend to Render.&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;list Gemini API models containing 'gemma'&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;$ list Gemini API models containing 'gemma'&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;models/gemma-4-26b-a4b-it, models/gemma-4-31b-it&lt;/code&gt;&lt;/pre&gt;
                    &lt;/div&gt;
                &lt;/div&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-write"&gt;Write&lt;/span&gt;
                    &lt;span class="tool-input-preview"&gt;server/src/gemma.ts (geminiGemma), server/src/place.ts, web 📍 button&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 typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;gemma&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ts &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;geminiGemma&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;place&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;web&lt;/span&gt; &lt;span class="err"&gt;📍&lt;/span&gt; &lt;span class="nx"&gt;button&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 typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;GEMMA_PROVIDER&lt;/span&gt; &lt;span class="nx"&gt;switches&lt;/span&gt; &lt;span class="nx"&gt;between&lt;/span&gt; &lt;span class="nx"&gt;local&lt;/span&gt; &lt;span class="nx"&gt;Ollama&lt;/span&gt; &lt;span class="nx"&gt;and&lt;/span&gt; &lt;span class="nx"&gt;Google&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;hosted&lt;/span&gt; &lt;span class="nx"&gt;Gemma&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="nx"&gt;GET&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;place&lt;/span&gt; &lt;span class="nx"&gt;rounds&lt;/span&gt; &lt;span class="nx"&gt;coordinates&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="nx"&gt;km&lt;/span&gt; &lt;span class="nx"&gt;and&lt;/span&gt; &lt;span class="nx"&gt;reverse&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;geocodes&lt;/span&gt; &lt;span class="kd"&gt;with&lt;/span&gt; &lt;span class="nx"&gt;OpenStreetMap&lt;/span&gt; &lt;span class="nx"&gt;Nominatim&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;only&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;city&lt;/span&gt; &lt;span class="nx"&gt;goes&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="nx"&gt;Gemma&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="nx"&gt;Express&lt;/span&gt; &lt;span class="nx"&gt;also&lt;/span&gt; &lt;span class="nx"&gt;serves&lt;/span&gt; &lt;span class="nx"&gt;web&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;dist&lt;/span&gt; &lt;span class="nx"&gt;so&lt;/span&gt; &lt;span class="nx"&gt;Render&lt;/span&gt; &lt;span class="nx"&gt;runs&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="nx"&gt;single&lt;/span&gt; &lt;span class="nx"&gt;service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
                    &lt;/div&gt;
                &lt;/div&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;curl /api/place?lat=25.45&amp;amp;lon=78.57&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/place?lat=25.45&amp;amp;lon=78.57&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 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;"place"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Jhansi, Uttar Pradesh, India"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
                    &lt;/div&gt;
                &lt;/div&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;POST /api/quests via hosted gemma-4-26b-a4b-it&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;$ POST /api/quests via hosted gemma-4-26b-a4b-it&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;First run: rejected because Gemma 4 wrote titles longer than the 80-char limit. Later runs: Gemini 500 'Internal error' and 90s timeouts.&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;Debugging hosted Gemma 4: it emits 'thought' parts before answering. thinkingBudget: 0 isn't supported, and thinkingLevel returns 400/500. A plain JSON-mode request with no schema answers in ~18s. Fixes: trim long strings instead of rejecting them, move the JSON schema and system prompt into the user message, filter out thought parts, and retry once.&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;POST /api/quests (Jhansi) after fixes&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;$ POST /api/quests (Jhansi) after fixes&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;200 in 45s (attempt 1 hit a 500; the retry succeeded): yellow auto rickshaw, chai stall, marigold garland, colorful sari, carved stone pillar&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;Deployed at &lt;a href="https://grasshopper-ea0p.onrender.com" rel="noopener noreferrer"&gt;https://grasshopper-ea0p.onrender.com&lt;/a&gt;. Looks like it's not working.&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;probe Render: /, assets, /api/health, /api/place, /api/quests&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;$ probe Render: /, assets, /api/health, /api/place, /api/quests&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;page 200, assets 200, health {provider: gemini}, place 'Jhansi, Uttar Pradesh, India', quests 502 after 46s&lt;/code&gt;&lt;/pre&gt;
                    &lt;/div&gt;
                &lt;/div&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;run generateQuests against both hosted models side by side&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;$ run generateQuests against both hosted models side by side&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;gemma-4-31b-it: Gemini 500 after 19s | gemma-4-26b-a4b-it: timeout after 90s&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;Hosted Gemma is failing on Google's side, minutes before the deadline. I added a hand-written starter pack, labelled in the UI as not AI-written, so a walk is never blocked. Each Gemma try is capped at 30s.&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;server with a bogus key → POST /api/quests&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;$ server with a bogus key → POST /api/quests&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;source: starter, 5 quests returned instantly&lt;/code&gt;&lt;/pre&gt;
                    &lt;/div&gt;
                &lt;/div&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;
        4 of 4 messages
    &lt;/span&gt;
  &lt;/div&gt;
&lt;/div&gt;


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

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

</description>
      <category>devchallenge</category>
      <category>hf26challenge</category>
    </item>
    <item>
      <title>Reme (Remember Me)</title>
      <dc:creator>Himani Sahu</dc:creator>
      <pubDate>Mon, 05 Oct 2026 06:22:51 +0000</pubDate>
      <link>https://dev.to/himani_sahu_de9e1ed9b002d/reme-remember-me-791</link>
      <guid>https://dev.to/himani_sahu_de9e1ed9b002d/reme-remember-me-791</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/hacktoberfest-weekend-2026-10-01"&gt;Hacktoberfest Weekend Challenge: Build for a Friend&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;When I read the prompt about building something for a loved one, I could think of one person, my mom. She runs a business, she's always busy and she skips her multivitamins, more than she takes it. I am not around always to remind her, and when I do I get the same reply, &lt;br&gt;
&lt;em&gt;"lelungi baad mein"&lt;/em&gt; (I'll take it later). Honestly, it annoys me sometimes.&lt;/p&gt;

&lt;p&gt;So I built &lt;strong&gt;Reme&lt;/strong&gt; (from &lt;em&gt;remember me&lt;/em&gt;). It's a small helper that reminds her on &lt;strong&gt;WhatsApp&lt;/strong&gt;, the only app she actually uses. It writes &lt;strong&gt;from my own number&lt;/strong&gt;, in the &lt;strong&gt;Hinglish&lt;/strong&gt; we text each other in, so to her it's just me checking in.&lt;/p&gt;

&lt;p&gt;This is what our chats looked like before Reme. I was the reminder system:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Me:&lt;/strong&gt; mummy khana done? &lt;em&gt;(Mummy, eaten?)&lt;/em&gt;&lt;br&gt;
&lt;strong&gt;Mummy:&lt;/strong&gt; Han krungi thodi der mein &lt;em&gt;(Yeah, I'll eat in a bit)&lt;/em&gt;&lt;br&gt;
...an hour later...&lt;br&gt;
&lt;strong&gt;Me:&lt;/strong&gt; done?&lt;br&gt;
&lt;strong&gt;Mummy:&lt;/strong&gt; Haa beta ho gya just abhi &lt;em&gt;(Yes dear, just did)&lt;/em&gt;&lt;br&gt;
&lt;strong&gt;Me:&lt;/strong&gt; Vitamins le liye? &lt;em&gt;(Took your vitamins?)&lt;/em&gt;&lt;br&gt;
&lt;strong&gt;Mummy:&lt;/strong&gt; Ha le liya &lt;em&gt;(Yes, took it)&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;What Reme does:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;At 11:45, after her breakfast (she eats around 11 to 11:30), it asks &lt;em&gt;"Mummy, nashta ho gaya?"&lt;/em&gt; (had breakfast?)&lt;/li&gt;
&lt;li&gt;When she says yes, it reminds her to take the vitamin.&lt;/li&gt;
&lt;li&gt;It understands &lt;em&gt;"10 min ruko"&lt;/em&gt; (wait 10 min), &lt;em&gt;"lelungi baad mein"&lt;/em&gt;(I will take it later) and &lt;em&gt;"aaj nahi lena, pet kharab hai"&lt;/em&gt; (not today, upset stomach) and waits or backs off.&lt;/li&gt;
&lt;li&gt;If she ignores three nudges, it stops nagging &lt;strong&gt;her&lt;/strong&gt; and messages &lt;strong&gt;me&lt;/strong&gt; instead: &lt;em&gt;"Ek call kar lo?"&lt;/em&gt; (maybe give her a call?)&lt;/li&gt;
&lt;li&gt;It &lt;strong&gt;stays quiet&lt;/strong&gt; on everything else. When she asks &lt;em&gt;"Ab ghar kab aaogi?"&lt;/em&gt; (when are you coming home?), that's a conversation for me, not a bot.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

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

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


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/Siddhi-sahu" rel="noopener noreferrer"&gt;
        Siddhi-sahu
      &lt;/a&gt; / &lt;a href="https://github.com/Siddhi-sahu/reme" rel="noopener noreferrer"&gt;
        reme
      &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;Reme&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;&lt;em&gt;Remember me.&lt;/em&gt; A WhatsApp nudge that reminds my mummy to take her daily multivitamin after breakfast, in Hinglish, from my own number.&lt;/p&gt;
&lt;p&gt;It understands her replies ("Haan", "Ha leli", "10 min ruko", "aaj nahi lena") with &lt;strong&gt;Gemma 3 4B running locally through Ollama&lt;/strong&gt;, so her messages never leave my laptop.&lt;/p&gt;
&lt;div class="snippet-clipboard-content notranslate position-relative overflow-auto"&gt;&lt;pre class="notranslate"&gt;&lt;code&gt;15:20  Reme → mummy: Mummy, lunch ho gaya? 🍽️
15:22  mummy: Haan                     [gemma: meal_done]
15:23  Reme → mummy: Mummy, vitamin le lijiye 💊
15:23  mummy: Ok                       [ignored: just an acknowledgement]
15:23  mummy: Ha leli                  [gemma: vitamin_taken]
15:23  Reme → mummy: Shabaash mummy! 💊❤️
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;em&gt;(the first real run, 4 Oct 2026)&lt;/em&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;How it works&lt;/h2&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;One vitamin a day, after breakfast&lt;/strong&gt; (she eats around 11 to 11:30). Once it's taken, Reme goes quiet for the day.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;She messages first&lt;/strong&gt; ("nashta ho gaya"): Reme reminds her 15 minutes later.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;She doesn't&lt;/strong&gt;: at 11:45 Reme asks "nashta ho gaya?", then nudges…&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/Siddhi-sahu/reme" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


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

&lt;p&gt;The open Stack:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Gemma 3 4B&lt;/strong&gt; (Google's open-weight model) running locally with &lt;strong&gt;Ollama&lt;/strong&gt; on my MacBook Air. About 2 seconds per message.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Baileys&lt;/strong&gt;, an open-source WhatsApp Web client (no browser needed), linked to my own WhatsApp like WhatsApp Web.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Node.js + TypeScript&lt;/strong&gt;, and &lt;strong&gt;SQLite&lt;/strong&gt; (built into Node) so reminders survive a restart.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Gemma has exactly one job: read her message and say what it means. Ollama's &lt;code&gt;format&lt;/code&gt; option takes a JSON schema, so Gemma &lt;em&gt;has&lt;/em&gt; to answer in this shape:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Intent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
 &lt;span class="na"&gt;intent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;enum&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;meal_done&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;vitamin_taken&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;snooze&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;skip&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;other&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
 &lt;span class="na"&gt;meal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;enum&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;breakfast&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;lunch&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dinner&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;unknown&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nf"&gt;nullable&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
 &lt;span class="na"&gt;snooze_minutes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;number&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;nullable&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
 &lt;span class="na"&gt;reply&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ollama&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
 &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;gemma3:4b&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="na"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
   &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;system&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;SYSTEM&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
   &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;user&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Your last message to her: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;lastSent&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;\nHer message: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
 &lt;span class="p"&gt;],&lt;/span&gt;
 &lt;span class="na"&gt;format&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toJSONSchema&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Intent&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
 &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;temperature&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="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Everything after that is ordinary code: when to remind, how many times, when to tell me. &lt;strong&gt;Every message she receives is written by me&lt;/strong&gt; in one file. I tried letting Gemma write the replies and got gems like &lt;em&gt;"Aaja mummy, tumhari dawa bhi le hi le"&lt;/em&gt; (roughly "come mummy, just take your medicine too"). When it's your mom on the other end, predictable beats clever.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Making a 4B model reliable&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I tested every change against real messages, most of them my mom's actual texts.&lt;/p&gt;

&lt;p&gt;Four bugs that taught me something:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;firstly, &lt;strong&gt;"Ok" counted as "vitamin taken".&lt;/strong&gt; even If the ok was out of context. remember would stop reminding. Rewording the prompt didn't fix it, so a few lines of code do: a bare ok/acha/👍 to a statement is never "taken".&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;My own question was ambiguous.&lt;/strong&gt; The check-in used to say &lt;em&gt;"lunch ho gaya? Ho gaya ho to vitamin le lijiye"&lt;/em&gt; (had lunch? if so, take the vitamin), so "haan" could mean either. I fixed the wording, not the model.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;"Our Lunch ho gya" at 2:35pm was classified as dinner.&lt;/strong&gt; Now the meal comes from the word she used, or else the clock. The model doesn't guess.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Fixing one thing broke another.&lt;/strong&gt; &lt;em&gt;"Han krungi thodi der mein"&lt;/em&gt; (yeah, I'll do it in a bit) was read as "done". When I explained future tense in the prompt, Gemma started calling a plain "haan" a snooze. Future tense in Hindi has a visible ending (&lt;em&gt;-ungi&lt;/em&gt;), so it became one line of code instead.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The lesson from all four: &lt;strong&gt;with a small model, keep the prompt for fuzzy language and move every predictable rule into code.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To test whole mornings without waiting for real ones, &lt;code&gt;pnpm simulate&lt;/code&gt; runs a fake clock through five scenarios (she replies, she says "lelungi baad mein", she says "not today", she ignores it) in seconds.&lt;/p&gt;

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

&lt;p&gt;Uhmm, so,&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;These are my mother's health details and her personal chats with her daughter. With Gemma running locally, no AI company ever sees them, so her messages never leave my laptop.&lt;/li&gt;
&lt;li&gt;Its free, and works.&lt;/li&gt;
&lt;/ul&gt;

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

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

&lt;p&gt;Best Use of Gemma&lt;br&gt;
Best Use of Entire&lt;/p&gt;

&lt;h2&gt;
  
  
  My mother's reply
&lt;/h2&gt;

&lt;p&gt;I told her that it wasn't me who texted her last day, she said,(rough translation) "I didnot know, I thought it was you. My daughter couldn't even text me herself huhu" lol.&lt;/p&gt;

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