<?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: Preksha barjatya</title>
    <description>The latest articles on DEV Community by Preksha barjatya (@preksha_barjatya).</description>
    <link>https://dev.to/preksha_barjatya</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%2F3011718%2Fbbea0e56-6d9f-4a6a-9efd-070e7ad5292c.png</url>
      <title>DEV Community: Preksha barjatya</title>
      <link>https://dev.to/preksha_barjatya</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/preksha_barjatya"/>
    <language>en</language>
    <item>
      <title>SlothWeaver: a local summarizer that naps when your laptop does (and picks up where it left off) 🦥</title>
      <dc:creator>Preksha barjatya</dc:creator>
      <pubDate>Thu, 22 Oct 2026 09:33:05 +0000</pubDate>
      <link>https://dev.to/preksha_barjatya/slothweaver-a-local-summarizer-that-naps-when-your-laptop-does-and-picks-up-where-it-left-off-220</link>
      <guid>https://dev.to/preksha_barjatya/slothweaver-a-local-summarizer-that-naps-when-your-laptop-does-and-picks-up-where-it-left-off-220</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/events/challenges/hacktoberfest-week3-2026-10-19"&gt;Hacktoberfest Open-Source AI Challenge: Week 3 — Slow and Steady&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;SlothWeaver&lt;/strong&gt; is a desktop app for the most sloth-like workflow I know: pick a file, walk away, come back to a summary.&lt;/p&gt;

&lt;p&gt;You choose a &lt;code&gt;.txt&lt;/code&gt; or &lt;code&gt;.md&lt;/code&gt; file. A local &lt;strong&gt;Gemma 2 (9B)&lt;/strong&gt; model running in &lt;strong&gt;Ollama&lt;/strong&gt; reads it and writes a short summary plus three tags to &lt;code&gt;&amp;lt;file&amp;gt;.summary.md&lt;/code&gt;. Nothing leaves your machine.&lt;/p&gt;

&lt;p&gt;It is deliberately slow and steady:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;It doesn't care if your laptop falls asleep.&lt;/strong&gt; In durable mode, each summary runs as a &lt;strong&gt;Temporal&lt;/strong&gt; workflow. If Ollama is down, the model crashes, or the lid closes mid-run, Temporal retries with backoff (5s, 10s, 20s… capped at 5 minutes) and resumes. You never re-click anything.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It never destroys your work.&lt;/strong&gt; If a summary file already exists, the old one is backed up to &lt;code&gt;.backup&lt;/code&gt;, then &lt;code&gt;.backup.1&lt;/code&gt;, &lt;code&gt;.backup.2&lt;/code&gt;… and the new one is written atomically. A crash mid-write can't leave a half-written file.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It's lazy about failure too.&lt;/strong&gt; If the model rambles instead of returning JSON, that's caught and reported (to Sentry, if you set &lt;code&gt;SENTRY_DSN&lt;/code&gt;) instead of saving garbage.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Here's a real run against the project's own README, using gemma2:9b locally:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"completed"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"summary"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"SlothWeaver is a tool for generating durable document summaries using local AI models. It leverages Ollama for processing, Temporal for persistence, and Tauri for a desktop application..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"tags"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"AI"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Summarization"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Local Development"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;👉 &lt;a href="https://github.com/Prekshabarjatya/slothweaver" rel="noopener noreferrer"&gt;https://github.com/Prekshabarjatya/slothweaver&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;Four small layers, each built test-first:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;th&gt;Key test&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;summarizer.py&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Calls Ollama with &lt;code&gt;format: json&lt;/code&gt;, strips markdown fences, validates there's a &lt;code&gt;summary&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Garbage, fenced, and keyless model output all raise &lt;code&gt;LLMError&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;file_ops.py&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Backup-then-atomic-write&lt;/td&gt;
&lt;td&gt;A second save never clobbers the first backup&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;workflows.py&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Temporal workflow + heartbeating activity&lt;/td&gt;
&lt;td&gt;A flaky activity fails twice, then succeeds, on Temporal's time-skipping test server&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tauri (&lt;code&gt;src-tauri/&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Rust command that shells out to the Python CLI&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The retry test was my favourite. Temporal's &lt;code&gt;WorkflowEnvironment.start_time_skipping()&lt;/code&gt; fast-forwards the backoff timers, so a test that would "wait" 15 seconds for retries runs in about one:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@activity.defn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;process_file_activity&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;flaky&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file_path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;attempts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;attempts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;RuntimeError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;laptop went to sleep&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;completed&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;file&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;file_path&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One gotcha worth sharing: my first version was an &lt;code&gt;async&lt;/code&gt; activity that called a blocking &lt;code&gt;requests.post&lt;/code&gt; to Ollama. That stalls the worker's event loop for the whole generation. The fix was to make the activity a plain &lt;code&gt;def&lt;/code&gt; and give the worker a small &lt;code&gt;ThreadPoolExecutor&lt;/code&gt;. It's two threads, because the GPU is the bottleneck, not the CPU.&lt;/p&gt;

&lt;p&gt;The LLM is mocked in unit tests so they run in about a second. A separate &lt;code&gt;pytest -m integration&lt;/code&gt; hits real Ollama, and it passes in about 38s on my machine. CI runs the unit tests on every push.&lt;/p&gt;

&lt;p&gt;I built this pairing with Claude Code (Claude Opus 5.5).&lt;/p&gt;

&lt;h2&gt;
  
  
  Why open innovation matters here
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Private by default.&lt;/strong&gt; Notes, journals and work docs are exactly the files you &lt;em&gt;don't&lt;/em&gt; want to upload. An open-weight model on localhost means they never leave the laptop.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Free to be slow.&lt;/strong&gt; There's no per-token bill, so retrying five times after a sleep costs nothing. That makes durable retries a sensible default rather than a luxury.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Swappable.&lt;/strong&gt; The model is one env var (&lt;code&gt;SLOTH_MODEL&lt;/code&gt;). When a better open model ships, I change one line.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Open plumbing.&lt;/strong&gt; Temporal, Tauri, and Ollama are all open source, so every layer is inspectable and replaceable.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of Gemma:&lt;/strong&gt; Gemma 2 9B is the only model in the loop, running locally through Ollama.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of Temporal:&lt;/strong&gt; durable workflow with heartbeats, an exponential-backoff retry policy, and time-skipping tests that prove the retries work.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What I'd do next
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Watch a folder and summarize new files overnight. That's the ultimate sloth mode.&lt;/li&gt;
&lt;li&gt;Bundle Python as a Tauri sidecar so it ships as a single &lt;code&gt;.app&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>devchallenge</category>
      <category>hf26challenge</category>
      <category>temporal</category>
      <category>ai</category>
    </item>
    <item>
      <title>Sentinel: Test Your AI Visibility Fixes Before You Ship Them</title>
      <dc:creator>Preksha barjatya</dc:creator>
      <pubDate>Sun, 18 Oct 2026 12:44:14 +0000</pubDate>
      <link>https://dev.to/preksha_barjatya/sentinel-test-your-ai-visibility-fixes-before-you-ship-them-41d9</link>
      <guid>https://dev.to/preksha_barjatya/sentinel-test-your-ai-visibility-fixes-before-you-ship-them-41d9</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/hacktoberfest-week2-2026-10-12"&gt;Hacktoberfest Open-Source AI Challenge Week 2: Side Hustle&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Sentinel&lt;/strong&gt; is a weekly "AI visibility" audit for small brands. Shoppers in India now ask an AI assistant "best baby wipes for sensitive skin" instead of scrolling Google. If the assistant never names your brand, you lose that sale and never find out.&lt;/p&gt;

&lt;p&gt;Sentinel asks open-weight models the questions a brand's buyers ask, measures how often the brand is named versus its competitors (share of voice), checks whether AI crawlers can read the site, audits the page for SEO and answer-engine readiness, and writes a prioritized fix list.&lt;/p&gt;

&lt;p&gt;New this week: a &lt;strong&gt;digital twin&lt;/strong&gt;. It takes the client's real homepage, builds a copy with the recommended fixes applied, and puts each version into the &lt;em&gt;real&lt;/em&gt; Google results for the same question (via SerpApi). Then it asks the model the same questions against both and compares. The client sees what the fixes are likely to change &lt;strong&gt;before&lt;/strong&gt; touching their website.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Who pays:&lt;/strong&gt; small D2C brands and the freelance SEO consultants who serve them. A one-time audit plus fix list, then a monthly re-check. The paid AI-visibility tools in this space cost hundreds of dollars a month, priced for enterprises, not a small Indian hygiene brand.&lt;/p&gt;

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

&lt;p&gt;First real brand: &lt;strong&gt;an Indian D2C hygiene brand&lt;/strong&gt; (I'll call it Brand X) selling baby wipes, face wipes, tissues, hand wash and home cleaning products.&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%2Fziqhez90bo9w97u51f35.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%2Fziqhez90bo9w97u51f35.png" alt="Sentinel report for Brand X" width="800" height="1091"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Baseline (8 buyer questions, 16 AI answers, open model with no page given):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Share of voice: &lt;strong&gt;0%&lt;/strong&gt;. Brand X was never named.&lt;/li&gt;
&lt;li&gt;AI crawlers (OAI-SearchBot, ClaudeBot, PerplexityBot, ...): all allowed, so the gap is content, not access.&lt;/li&gt;
&lt;li&gt;31 fixes found, for example: no FAQ section or FAQPage schema, no question-style headings, a 114-character title, 34 of 103 images without alt text.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Digital twin (homepage inserted at rank 3 in real Google results):&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Buyer question&lt;/th&gt;
&lt;th&gt;Original page&lt;/th&gt;
&lt;th&gt;Fixed page&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;best natural baby wipes in India&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;best baby wipes for sensitive skin in India&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;best refreshing face wipes in India&lt;/td&gt;
&lt;td&gt;50%&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;best toilet paper and tissue brands in India&lt;/td&gt;
&lt;td&gt;50%&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;best kitchen tissue roll brand India&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;best liquid hand wash brand in India&lt;/td&gt;
&lt;td&gt;50%&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;eco-friendly bamboo hygiene products India&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;best floor cleaner brand in India&lt;/td&gt;
&lt;td&gt;0%&lt;/td&gt;
&lt;td&gt;50%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;All 8 questions&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;68.8%&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;93.8%&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Answers that named a competitor but not Brand X fell from &lt;strong&gt;25.0%&lt;/strong&gt; to &lt;strong&gt;6.2%&lt;/strong&gt;. The fixed page wins on exactly the questions where the original had no direct answer: face wipes, toilet paper, hand wash and floor cleaner.&lt;/p&gt;

&lt;p&gt;Read this as a direction, not a forecast. The page is placed inside the results, so even the original scores far above the 0% baseline; the gap between the two columns is what matters. It's 2 samples per question on one open model (16 answers per side, 0 failed).&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/Prekshabarjatya" rel="noopener noreferrer"&gt;
        Prekshabarjatya
      &lt;/a&gt; / &lt;a href="https://github.com/Prekshabarjatya/sentinel-ai-visibility" rel="noopener noreferrer"&gt;
        sentinel-ai-visibility
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Open-model AI visibility audit for small brands, with a digital twin that tests fixes before you ship them
    &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;Sentinel AI: Phase 1 engine&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;Weekly AI-citation tracker per BRD v1.1. Python 3.10+, only dependency: &lt;code&gt;requests&lt;/code&gt;.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Run&lt;/h2&gt;
&lt;/div&gt;
&lt;div class="snippet-clipboard-content notranslate position-relative overflow-auto"&gt;&lt;pre class="notranslate"&gt;&lt;code&gt;pip install requests
python tests/test_engine.py                                   # 8 tests, no keys needed
python -m sentinel.run clients/acme.json --mock               # full cycle with fake engines
export OPENAI_API_KEY=... ANTHROPIC_API_KEY=... GEMINI_API_KEY=... PERPLEXITY_API_KEY=...
python -m sentinel.run clients/acme.json --samples 3          # real engines
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Output per client in &lt;code&gt;data/&amp;lt;domain&amp;gt;/&lt;/code&gt;: &lt;code&gt;latest_report.md&lt;/code&gt;, &lt;code&gt;history.json&lt;/code&gt;, &lt;code&gt;audit.jsonl&lt;/code&gt;, &lt;code&gt;review_queue.json&lt;/code&gt;.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Optimization (needs internet; add &lt;code&gt;--optimize&lt;/code&gt;)&lt;/h2&gt;

&lt;/div&gt;
&lt;div class="snippet-clipboard-content notranslate position-relative overflow-auto"&gt;&lt;pre class="notranslate"&gt;&lt;code&gt;python -m sentinel.run clients/acme.json --samples 3 --optimize
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Adds to the report and writes &lt;code&gt;data/&amp;lt;domain&amp;gt;/fixes.json&lt;/code&gt;:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Crawler access&lt;/strong&gt; (&lt;code&gt;crawlers.py&lt;/code&gt;): reads robots.txt; flags blocked OAI-SearchBot, ClaudeBot, PerplexityBot, etc. as &lt;strong&gt;P0&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Page gap analysis&lt;/strong&gt; (&lt;code&gt;pagegap.py&lt;/code&gt;): for each losing prompt (SoV under 50%), fetches your page and the competitor pages engines cited, and lists what they have that you lack (direct answer, FAQ schema, Organization schema, table, question headings, depth). &lt;strong&gt;P1&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fix list&lt;/strong&gt; (…&lt;/li&gt;
&lt;/ol&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/Prekshabarjatya/sentinel-ai-visibility" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;Two commands: &lt;code&gt;sentinel.run&lt;/code&gt; for the weekly audit (with &lt;code&gt;--optimize --seo&lt;/code&gt;) and &lt;code&gt;sentinel.twin&lt;/code&gt; for the digital twin.&lt;/p&gt;

&lt;p&gt;A new client is one JSON file (brand, domain, competitors, prompts) plus a &lt;code&gt;_twin_fixes.json&lt;/code&gt; file. No code changes.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Python + &lt;code&gt;requests&lt;/code&gt; only.&lt;/strong&gt; 28 tests run without any API keys, using a mock engine.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Open-weight models:&lt;/strong&gt; OpenAI's open-weight &lt;code&gt;gpt-oss-120b&lt;/code&gt; on Groq, plus OpenRouter's free router, which picks an open model that's available. Every engine is an OpenAI-compatible endpoint, so swapping models is one environment variable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SerpApi&lt;/strong&gt; fetches the real Google India results for each buyer question. They're cached, so every AI run reuses the same results and doesn't spend extra searches.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The twin's fairness rules:&lt;/strong&gt; both versions sit at the same rank among the same real results, and the model is told to use a result only if it actually helps. Fixes may only use facts already on the client's site. No invented "dermatologist-tested" claims.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Everything is a recommendation.&lt;/strong&gt; Sentinel never edits the client's site. Fixes have ids, and when one is applied, later weekly runs report the before and after.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What went wrong along the way (and is now handled): free model slugs get retired without warning (switched to OpenRouter's &lt;code&gt;openrouter/free&lt;/code&gt; router), free tiers rate-limit hard (paced requests, a configurable delay, failed runs counted rather than hidden), and SerpApi's &lt;code&gt;num&lt;/code&gt; parameter returned unrelated results (dropped it and pinned &lt;code&gt;google.co.in&lt;/code&gt;).&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The unit economics only work this way.&lt;/strong&gt; An audit is roughly 100 model calls. On free open-weight endpoints that costs nothing, so I can sell an audit to a small brand at a price it can pay.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No lock-in.&lt;/strong&gt; In one afternoon two "free" models disappeared. Because every engine is an OpenAI-compatible endpoint serving open weights, the fix was changing a string, not rewriting the product.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Can run on the client's own machine.&lt;/strong&gt; The same code can point at a locally hosted open model, so an agency can audit a client's unreleased pages without sending them to a third party.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Honest limit:&lt;/strong&gt; open models aren't ChatGPT or Gemini. Sentinel treats them as a stand-in for consumer AI search, and the report says so on page one.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  My First User
&lt;/h2&gt;

&lt;p&gt;I ran it on a real Indian hygiene brand I know well (anonymized here). The first useful feedback came immediately: my first config tracked diapers and sanitary pads, but the brand doesn't sell those yet. I rebuilt the questions around what it actually sells, which is the kind of correction a real user gives that no test catches.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prior art on DEV
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://dev.to/odeeb/measure-ai-share-of-voice-in-python-chatgpt-gemini-perplexity-12bc"&gt;Measure AI Share of Voice in Python&lt;/a&gt; by odeeb, on counting mentions while ignoring "I'm not familiar with X" answers.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.to/benardkemp/measuring-ai-visibility-a-practical-framework-for-reliable-results-28ek"&gt;Measuring AI Visibility: A Practical Framework for Reliable Results&lt;/a&gt; by benardkemp, on why repeated samples matter.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Sentinel adds the step those stop short of: testing the fix before shipping it.&lt;/p&gt;

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

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

</description>
      <category>devchallenge</category>
      <category>hf26challenge</category>
      <category>ai</category>
      <category>seo</category>
    </item>
    <item>
      <title>Dharohar: an open-source AI audio guide that makes you put your phone in your pocket 🌿</title>
      <dc:creator>Preksha barjatya</dc:creator>
      <pubDate>Tue, 06 Oct 2026 10:11:58 +0000</pubDate>
      <link>https://dev.to/preksha_barjatya/dharohar-an-open-source-ai-audio-guide-that-makes-you-put-your-phone-in-your-pocket-1451</link>
      <guid>https://dev.to/preksha_barjatya/dharohar-an-open-source-ai-audio-guide-that-makes-you-put-your-phone-in-your-pocket-1451</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/hacktoberfest-week1-2026-10-05"&gt;Hacktoberfest Open-Source AI Challenge Week 1: Touch Grass&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;I live in Indore. Ten minutes from my house is Rajwada, a seven-storey palace the Holkars started building in 1747. I have walked past it hundreds of times, usually looking at my phone.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Dharohar&lt;/strong&gt; (धरोहर, &lt;em&gt;heritage&lt;/em&gt;) is an audio guide for India's old city lanes that tries to get you to stop doing that. You tap &lt;strong&gt;Walk where I am&lt;/strong&gt;, or pick a heritage city. It finds the heritage sites around you, shows you the route, writes a short spoken guide for each stop, voices it, and then asks you to put your phone in your pocket. The screen goes black. You walk. When GPS says you have reached the next stop, the guide starts talking, and when it finishes it tells you where to go next.&lt;/p&gt;

&lt;p&gt;The measure of success is how little you look at it.&lt;/p&gt;

&lt;p&gt;It's for anyone in a heritage town who wants the story without the screen: residents who never learned the history of the street they live on, and visitors who don't want to follow a blue dot for an hour.&lt;/p&gt;

&lt;p&gt;What keeps you off the screen:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Black, not locked.&lt;/strong&gt; Phones pause GPS and web audio when the screen locks. So Dharohar keeps the screen awake with the Wake Lock API and paints it pure black. On an OLED phone, black pixels are switched off: almost no battery, and nothing to look at.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GPS does the tapping.&lt;/strong&gt; Each stop has a geofence. Walk into it, feel a short vibration, hear the story. A stop never repeats. Then you hear "Next: Gopal Mandir, about 140 metres north-east", and at the end, that the walk is complete.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dead zones are fine.&lt;/strong&gt; Every stop is written and voiced before you reach it, while you still have signal. A service worker caches the app and every MP3, so a temple courtyard with no network doesn't stop the walk. If GPS is weak, an "I'm here" button plays the stop by hand.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The guide is forbidden from mentioning screens.&lt;/strong&gt; No "on your map", no "tap here". Only things you can see with your own eyes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Your language.&lt;/strong&gt; English plus nine Indian languages, from Hindi and Marathi to Tamil and Malayalam.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;&lt;strong&gt;Live:&lt;/strong&gt; &lt;a href="https://dharohar-agent.onrender.com" rel="noopener noreferrer"&gt;https://dharohar-agent.onrender.com&lt;/a&gt;. Open it on your phone, allow location, tap &lt;strong&gt;Walk where I am&lt;/strong&gt;, or pick a city under &lt;strong&gt;Heritage cities&lt;/strong&gt;. Indore and Ujjain are instant: their walks ship with the audio already made. Anywhere else, the first stop is ready in about 15 seconds and the rest are written while you walk. (It runs on Render's free tier, so the very first visit after a quiet spell takes a minute to wake up.)&lt;br&gt;
&lt;strong&gt;Video:&lt;/strong&gt; &lt;a href="https://youtu.be/wnoLYbbGi38?si=egkM_3cZ_uh14CGK" rel="noopener noreferrer"&gt;https://youtu.be/wnoLYbbGi38?si=egkM_3cZ_uh14CGK&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%2F0j29hwartbpqsxmypa4s.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%2F0j29hwartbpqsxmypa4s.png" alt="Dharohar's home screen, with Walk where I am, search and a list of heritage cities, next to the route screen for Old Indore showing four ready stops joined by a dotted trail" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The whole interface is black and white on purpose: one button to start, a list of cities, and the route. Everything else stays out of the way, because the point is to look up.&lt;/p&gt;

&lt;p&gt;In the video I'm standing at Holkar Stadium in Indore, a cricket ground with no heritage of its own. Dharohar looks wider, finds Gajanan Maharaj Temple, Indore Museum, Rajwada, Lalbagh Palace and Kanch Mandir, and orders them into a walk. Then: countdown, black screen, the guide begins. When I reach Rajwada, the phone vibrates and Rajwada's story plays.&lt;/p&gt;

&lt;p&gt;You can also type any place, or pick one of 18 heritage cities, from Delhi and Agra to Hampi and Madurai. Gwalior Fort gives Chaturbhuj Temple, Gujari Mahal, Sasbahu Temple and the Siddhachal caves, all within 800 metres. Mandu gives Jama Masjid, Lohani Caves, Hindola Mahal and Nilkanth temple.&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/Prekshabarjatya" rel="noopener noreferrer"&gt;
        Prekshabarjatya
      &lt;/a&gt; / &lt;a href="https://github.com/Prekshabarjatya/dharohar-agent" rel="noopener noreferrer"&gt;
        dharohar-agent
      &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;Dharohar agent&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;Audio-first heritage walking tours for Indian cities. A Mastra agent fetches live facts with SerpApi and Gemma writes a short spoken script with physical, real-world cues only. No screen talk: put the phone in your pocket and walk.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;POST /script {"monument": "Rajwada, Indore"}&lt;/code&gt; → &lt;code&gt;{"script": "...", "audioUrl": "/audio/rajwada-indore_&amp;lt;ts&amp;gt;.mp3", "duration": 32}&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;GET /audio/&amp;lt;file&amp;gt;.mp3&lt;/code&gt; serves the spoken guide (ElevenLabs &lt;code&gt;eleven_multilingual_v2&lt;/code&gt;, so Hindi works too).&lt;/li&gt;
&lt;li&gt;Audio is cached per monument, so a second visitor costs no ElevenLabs credits. If ElevenLabs fails, the response has &lt;code&gt;audioUrl: null&lt;/code&gt; and a &lt;code&gt;warning&lt;/code&gt;, and the script is still returned.&lt;/li&gt;
&lt;li&gt;Model: Gemma via Google AI Studio (&lt;code&gt;gemma-4-26b-a4b-it&lt;/code&gt;) when &lt;code&gt;GOOGLE_GENERATIVE_AI_API_KEY&lt;/code&gt; is set, else Groq when &lt;code&gt;GROQ_API_KEY&lt;/code&gt; is set, else local Ollama (&lt;code&gt;gemma3:4b&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Evals (&lt;code&gt;npm test&lt;/code&gt;, ElevenLabs mocked, no credits used) check audio, fallback and caching, and fail if a script runs over 100 words, mentions screens/apps/maps, or skips the…&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/Prekshabarjatya/dharohar-agent" 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;Each stop goes through a small pipeline, held together by a &lt;strong&gt;Mastra&lt;/strong&gt; agent with two tools:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;SerpApi&lt;/strong&gt; searches the web for the monument, so the guide starts from real facts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gemma&lt;/strong&gt; writes a script of under 100 words: physical directions only, and only facts from the search.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ElevenLabs&lt;/strong&gt; voices it, with four voices to choose from. &lt;code&gt;eleven_multilingual_v2&lt;/code&gt; covers English, Hindi and Tamil; Marathi, Gujarati, Bengali, Punjabi, Telugu, Kannada and Malayalam need &lt;code&gt;eleven_v3&lt;/code&gt;, which I found by testing every language against the API.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Then the walk page takes over: a route screen that shows every stop and its live status, geofences, wake lock, next-stop directions and the offline cache.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Gemma, two ways.&lt;/strong&gt; On my laptop Gemma 3 4B runs locally through &lt;strong&gt;Ollama&lt;/strong&gt;, with no API at all. In production the same code calls &lt;code&gt;gemma-4-26b-a4b-it&lt;/code&gt; on Google AI Studio. One environment variable switches between them, and Groq works too. A Gemma 4 script reads noticeably better than the 4B one. Kanch Mandir's script, for example, names Seth Hukumchand, gives the year it was built (1903) and points out the glass murals.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why not let the model call the tools?&lt;/strong&gt; Small Gemma models don't do native tool calling reliably. So the pipeline calls the tools in a fixed order instead of hoping the model remembers to search. That's simpler, and it never skips the facts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Evals as the rules.&lt;/strong&gt; The "touch grass" rules are tests, not hopes. &lt;code&gt;npm test&lt;/code&gt; runs 13 evals:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;every script is under 100 words, opens with &lt;em&gt;"Namaste. Put your phone in your pocket"&lt;/em&gt;, and never says screen, app, map, click, tap or scroll (whole words, so "approach" passes)&lt;/li&gt;
&lt;li&gt;a real MP3 is written for each stop&lt;/li&gt;
&lt;li&gt;if ElevenLabs fails, the walk still works: the script comes back with a warning and the phone's own voice reads it&lt;/li&gt;
&lt;li&gt;the same stop twice costs one ElevenLabs call, and switching voice reuses the script&lt;/li&gt;
&lt;li&gt;Hindi comes back in Devanagari, and Marathi is voiced with the right model and language code&lt;/li&gt;
&lt;li&gt;geofences fire inside the radius, not 500 m away, and never replay a stop&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;GitHub Actions runs the fast evals on every push.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The eval that lied to me.&lt;/strong&gt; My first version passed every check while Gemma described "a carved wooden balcony" and "a stone lion" at Rajwada. Neither exists. It had copied the example sentence from its own instructions. Format checks can't catch invented facts. What fixed it was grounding every script in search results, plus a rule to mention only features named in those facts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The minute that wasn't the model's fault.&lt;/strong&gt; A new stop took 60 to 100 seconds, and real people trying it said it felt stuck. I timed each step: the search took 1.4 seconds and the voice about 6. The rest was Gemma 4 &lt;em&gt;thinking&lt;/em&gt; before every 80-word script. Setting its thinking level to minimal took the writing step from over a minute to about 6 seconds, with no drop in quality I could hear. A whole stop now takes about 13 seconds. Two more changes made the wait disappear: the walk starts as soon as stop 1 is ready, and the ready-made walks ship with their audio, so they're instant.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Caching.&lt;/strong&gt; Each stop's script is cached, and its audio is cached per voice. The second person to walk past Rajwada gets it in under a second, and costs no model call and no ElevenLabs credits.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Agent tracing.&lt;/strong&gt; The pipeline is instrumented for Sentry: each tour is one agent run, with separate spans for the search, the Gemma call (model, tokens, latency) and the voice. Each response also returns its own timing, which is how I found the thinking problem.&lt;/p&gt;

&lt;p&gt;I built this with an AI coding agent (Claude Code). I set the idea, the rules and the evals. The agent wrote most of the code, ran the tests and fixed what failed, and I reviewed every change. The session is below.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Local history belongs to locals.&lt;/strong&gt; A temple trust, a heritage walk group or a college club can run Dharohar on its own hardware. I ran the whole thing on my laptop with Gemma through Ollama, with no API key for the model at all.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Swapping models is one line.&lt;/strong&gt; This happened during the build: the Gemma version I first deployed was retired on one provider. Because the model is open-weight and the code doesn't depend on one vendor, I listed what was available and switched to Gemma 4 in one line. With a closed API, that's a rewrite or a shutdown.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Open data, open code.&lt;/strong&gt; Walks come from Wikipedia's open geodata. The guide is grounded in public search results. The code is MIT-licensed, so anyone can add a walk for their own town.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What's next:&lt;/strong&gt; a "local story" box at each stop, so people who live there can add the oral history that never reached Wikipedia, read aloud as part of the walk. That only works if the community can see and change how the guide works. That's what open is for.&lt;/p&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;Building Dharohar: audio-first heritage walks with Gemma, Mastra, SerpApi and ElevenLabs&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;Concept: "Dharohar" – Audio-First Heritage Walking Tour Generator. A tool that generates hyper-local, self-guided walking tours of Indian neighborhoods, stepwells, temples, or colonial heritage sites. The user inputs their location, and …&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;Got the Dharohar concept. What do you want me to do with it? 1. Build a working MVP … 2. Write the pitch or submission … 3. Check it against a real event.&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;1&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-tools"&gt;tools&lt;/span&gt;
                    &lt;span class="tool-input-preview"&gt;Bash, Edit, Write, in-app browser&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;Bash, Edit, Write, in-app browser&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 tool calls&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;I built the Dharohar MVP as one HTML file. The Rajwada, Indore demo made a 3-stop tour: Rajwada, Lalbagh Palace, Indore Museum, each with Wikipedia history and walking directions.&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;use this key for elevenlabs - &lt;span class="agent-session-redacted"&gt;[REDACTED]&lt;/span&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-tools"&gt;tools&lt;/span&gt;
                    &lt;span class="tool-input-preview"&gt;Bash, Edit, Write&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;Bash, Edit, Write&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;12 tool calls&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;Dharohar is live on Cloudflare Pages. I didn't type your key in anywhere: the key belongs in a server-side secret, never in the page. A /api/tts function now proxies ElevenLabs.&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;[Hacktoberfest prize categories pasted]&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;Dharohar fits several of these tracks without a rebuild: ElevenLabs, Gemma, Render/DigitalOcean, MongoDB, Entire. Skip the forced fits.&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;mkdir dharohar-agent &amp;amp;&amp;amp; npm install @mastra/core @mastra/evals ollama serpapi dotenv …&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;Those commands start a separate Node backend with a Mastra agent. Before running them, three things will break as written: Cloudflare can't run Ollama, the package names are wrong, and two packages are extra.&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;SERPAPI_KEY=&lt;a href="Day%201%20plan:%20Mastra%20+%20SerpApi%20+%20Gemma%20+%20eval%20tests"&gt;REDACTED&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-tools"&gt;tools&lt;/span&gt;
                    &lt;span class="tool-input-preview"&gt;Bash, Write&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;Bash, Write&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;11 tool calls&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;Day 1 is done and committed. Both evals pass. Changes from the plan: Mastra 1.74 API, gemma3:4b instead of gemma2:2b, the search runs before Gemma because small Gemma can't call tools, and whole-word banned-word checks so 'approach' doesn't fail. One real problem: with no facts, Gemma invented a 'carved wooden balcony' copied from its own instructions — the evals still passed.&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;serpapi api key - &lt;span class="agent-session-redacted"&gt;[REDACTED]&lt;/span&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-tools"&gt;tools&lt;/span&gt;
                    &lt;span class="tool-input-preview"&gt;Bash&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;Bash&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;2 tool calls&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;SerpApi is working: the script now has real history (Malhar Rao Holkar, 1747). Removed the example from the rules so Gemma stops copying it.&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;Day 2 prompt: ElevenLabs TTS, audio caching, expanded eval tests&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-tools"&gt;tools&lt;/span&gt;
                    &lt;span class="tool-input-preview"&gt;Bash, Write&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;Bash, Write&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;8 tool calls&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;Day 2 is done. All evals pass, and a real Rajwada run produced a 32-second spoken MP3. Used fetch instead of the deprecated SDK, eleven_multilingual_v2 instead of the retired monolingual model, and a fake ElevenLabs in tests so they cost no credits.&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;Day 3 prompt: Next.js black-screen UI and geolocation triggers&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-tools"&gt;tools&lt;/span&gt;
                    &lt;span class="tool-input-preview"&gt;Bash, Write, in-app browser&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;Bash, Write, in-app browser&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;27 tool calls&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;Did Day 3 without Next.js: one static page served by the existing agent server. Phones stop GPS and audio when the screen locks, so the black screen uses Wake Lock to stay on but black. GPS geofences play each stop on arrival; double-tap is the no-GPS 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;we need to match and built everything in the claims&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-tools"&gt;tools&lt;/span&gt;
                    &lt;span class="tool-input-preview"&gt;Bash, Write, Chrome DevTools&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;Bash, Write, Chrome DevTools&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;17 tool calls&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;Built and verified: offline audio via a service worker (checked in real Chrome), Sentry agent tracing spans, GitHub Actions evals, DigitalOcean spec. Removed claims that can't be made true: 'data never leaves our infrastructure', '$0 per tour', 'built with Copilot without touching the keyboard', 'fine-tuned'.&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;Render (free) + Groq + ElevenLabs + SerpApi free tiers&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;Switched the post to Render. Flagged claims in the draft paragraph that weren't true or measured yet: Next.js, 'under 2 seconds', 'zero latency', and Gemma 2 on Groq.&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;I have deployed&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-tools"&gt;tools&lt;/span&gt;
                    &lt;span class="tool-input-preview"&gt;Bash&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;Bash&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;20+ tool calls&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;Debugged the live deploy from outside: added a short error reason to the 502, found gemma-3-27b-it retired on AI Studio, listed available models, switched to gemma-4-26b-a4b-it, and found the ElevenLabs env var held the key ID instead of the key. SerpApi takes 1.4 s; nearly all latency is Gemma on the free tier, so scripts and audio are cached per stop.&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;corrected elevenlabs key — but only the demo walks work&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-tools"&gt;tools&lt;/span&gt;
                    &lt;span class="tool-input-preview"&gt;Bash, Chrome DevTools&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;Bash, Chrome DevTools&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;16 tool calls&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;Live audio works. Added walks for any place or your current location, built from nearby Wikipedia heritage pages; widens to 10 km for spread-out sites. Gwalior Fort gives 5 stops; Mandu gives Jama Masjid, Lohani Caves, Hindola Mahal and Nilkanth temple.&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;
        26 of 32 messages
          · some content was hidden by the author
    &lt;/span&gt;
  &lt;/div&gt;
&lt;/div&gt;


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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of Gemma&lt;/strong&gt;: Gemma writes every grounded, length-capped script (Gemma 4 in production, Gemma 3 locally through Ollama), and tuning its thinking level cut a stop from over a minute to seconds.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of Render&lt;/strong&gt;: the agent and the walk UI run as one free Render web service, deployed from a &lt;code&gt;render.yaml&lt;/code&gt; blueprint.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of ElevenLabs&lt;/strong&gt;: the guide's voice in ten languages across two models, four voices, audio cached per stop.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of SerpApi&lt;/strong&gt;: live facts for every stop before Gemma writes a word.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of Mastra&lt;/strong&gt;: the agent, its rules and its tools.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of GitHub Copilot&lt;/strong&gt;: GitHub Actions runs the evals on every push.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>hf26challenge</category>
      <category>devchallenge</category>
      <category>ai</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Dawai Saathi: a medicine reminder my grandmother can use without typing or speaking</title>
      <dc:creator>Preksha barjatya</dc:creator>
      <pubDate>Sun, 04 Oct 2026 17:59:07 +0000</pubDate>
      <link>https://dev.to/preksha_barjatya/dawai-saathi-a-medicine-reminder-my-grandmother-can-use-without-typing-or-speaking-916</link>
      <guid>https://dev.to/preksha_barjatya/dawai-saathi-a-medicine-reminder-my-grandmother-can-use-without-typing-or-speaking-916</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;&lt;strong&gt;Dawai Saathi&lt;/strong&gt; ("medicine companion") is a medicine reminder for my grandmother. She takes several tablets a day, finds speaking difficult, and reads Hindi and Marwari rather than English.&lt;/p&gt;

&lt;p&gt;Someone in the family photographs her prescription or types it in. A Gemma model running on the family's own computer reads it into a medicine list. A person always confirms that list before anything is saved. The app flags known risky pairs (for example aspirin with ibuprofen) as "ask the doctor or pharmacist", and it never gives dose advice.&lt;/p&gt;

&lt;p&gt;Her screen is deliberately simple: one medicine at a time in large Hindi type, one green button, and a voice that reads the reminder aloud. She needs no typing and no speaking, only taps. The family gets a separate page with the history and warnings.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Video (47s, with sound): &lt;a href="https://youtube.com/shorts/asac9HyuAN0?feature=share" rel="noopener noreferrer"&gt;https://youtube.com/shorts/asac9HyuAN0?feature=share&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Live app: &lt;a href="https://dawai-saathi.onrender.com" rel="noopener noreferrer"&gt;https://dawai-saathi.onrender.com&lt;/a&gt; (free tier, the first load after a quiet spell takes about a minute; it uses a simple stand-in reader and the browser's Hindi voice, and nothing is stored permanently)&lt;/li&gt;
&lt;/ul&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/Prekshabarjatya" rel="noopener noreferrer"&gt;
        Prekshabarjatya
      &lt;/a&gt; / &lt;a href="https://github.com/Prekshabarjatya/dawai-saathi" rel="noopener noreferrer"&gt;
        dawai-saathi
      &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&lt;/strong&gt; (via Ollama) reads the prescription photo into structured JSON, with a fixed schema so the output can be validated. It only reads; it never advises.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Interaction check&lt;/strong&gt; is a small fixed table, not the model. A language model should not decide what is medically safe. The table needs a pharmacist's review before real use.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Temporal&lt;/strong&gt; runs each dose as a durable workflow: remind, wait for a tap, repeat up to 3 times, then record a missed dose and alert the family. Failed steps retry instead of losing a dose.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Voice&lt;/strong&gt;: the Hindi reminder is spoken by the browser or, if a key is set, by ElevenLabs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Render&lt;/strong&gt; hosts the web app.&lt;/li&gt;
&lt;li&gt;The interface is plain HTML and CSS with system Devanagari fonts, high contrast and dark mode, so it works on an old phone.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;A prescription is some of the most private data a family has. With an open-weight model running on the family's own computer, the photo never has to be sent to someone else's server. I can also change the prompt, the language and the safety rules, and swap models, without asking anyone. A closed API would have meant uploading my grandmother's medicines to a third party and hoping the terms never change.&lt;/p&gt;

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

&lt;p&gt;Best Use of Render (the app is deployed there).&lt;/p&gt;

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