<?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: Jayana  Gunaweera</title>
    <description>The latest articles on DEV Community by Jayana  Gunaweera (@jayanagunaweera01).</description>
    <link>https://dev.to/jayanagunaweera01</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%2F1147001%2F9fb7b59e-b99a-4acb-832b-925b61bc3e84.jpeg</url>
      <title>DEV Community: Jayana  Gunaweera</title>
      <link>https://dev.to/jayanagunaweera01</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jayanagunaweera01"/>
    <language>en</language>
    <item>
      <title>Cleanup Walk: an Android app that turns screen time into a neighborhood cleanup.</title>
      <dc:creator>Jayana  Gunaweera</dc:creator>
      <pubDate>Sat, 10 Oct 2026 16:52:39 +0000</pubDate>
      <link>https://dev.to/jayanagunaweera01/cleanup-walk-is-an-android-app-that-turns-screen-time-into-a-neighborhood-cleanup-3j59</link>
      <guid>https://dev.to/jayanagunaweera01/cleanup-walk-is-an-android-app-that-turns-screen-time-into-a-neighborhood-cleanup-3j59</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;You tap &lt;strong&gt;"30-minute cleanup"&lt;/strong&gt; (or 15, 45, 60), and the app plans a walking loop that routes you past known problem spots nearby. You pick up litter, snap photos of what you find, and at the end you get a summary like:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You cleaned 1.8 km of your neighborhood and removed 14 pieces of litter.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The app's job isn't to keep you scrolling. It exists to get you out the door and hand you something useful to do once you're there.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Spots problems with the camera.&lt;/strong&gt; Take a photo and an on-device model finds litter, overflowing bins, damaged public infrastructure, and fallen branches, with a count. You confirm or correct with one tap.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Plans a time-boxed route.&lt;/strong&gt; Spots with damaged infrastructure and overflowing bins are weighted highest. Any spare time becomes exploration stops in a new direction, so you find new problems.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Guides the walk.&lt;/strong&gt; It shows time left, distance walked, litter removed, and the direction to the next stop. A big &lt;strong&gt;+1 litter&lt;/strong&gt; button counts everything you pick up along the way.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Remembers.&lt;/strong&gt; Open issues are saved so future walks route past them, and lifetime totals show on the home screen.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Who it's for:&lt;/strong&gt; anyone who wants a reason to walk and leave the block a bit better than they found it. That includes people who care about their neighborhood but never know where to start.&lt;/p&gt;

&lt;p&gt;It works fully offline. There's no account, server, API key, or Google Play Services dependency, and photos never leave the phone.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Home&lt;/th&gt;
&lt;th&gt;Walk in progress&lt;/th&gt;
&lt;th&gt;AI detection&lt;/th&gt;
&lt;th&gt;Summary&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2FJayanaGunaweera01%2Fclean-up-walk%2Fmain%2Fdocs%2Fscreenshots%2F01-home.png" width="720" height="1620"&gt;&lt;/td&gt;
&lt;td&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2FJayanaGunaweera01%2Fclean-up-walk%2Fmain%2Fdocs%2Fscreenshots%2F03-walk-in-progress.png" width="720" height="1620"&gt;&lt;/td&gt;
&lt;td&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2FJayanaGunaweera01%2Fclean-up-walk%2Fmain%2Fdocs%2Fscreenshots%2F06-ai-detected.png" width="720" height="1620"&gt;&lt;/td&gt;
&lt;td&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2FJayanaGunaweera01%2Fclean-up-walk%2Fmain%2Fdocs%2Fscreenshots%2F08-walk-summary.png" width="720" height="1620"&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&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/JayanaGunaweera01" rel="noopener noreferrer"&gt;
        JayanaGunaweera01
      &lt;/a&gt; / &lt;a href="https://github.com/JayanaGunaweera01/clean-up-walk" rel="noopener noreferrer"&gt;
        clean-up-walk
      &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;Cleanup Walk&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;An Android app that turns screen time into a neighborhood cleanup.&lt;/p&gt;
&lt;p&gt;Pick &lt;strong&gt;"30-minute cleanup"&lt;/strong&gt;, follow a walking loop that prioritizes places with visible problems, pick up litter or report what you find, and at the end:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;You cleaned 1.8 km of your neighborhood and removed 14 pieces of litter.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The app's job isn't to entertain you — it converts screen interaction into physical action.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Screenshots&lt;/h2&gt;
&lt;/div&gt;
&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;1 · Home&lt;/th&gt;
&lt;th&gt;2 · Choose a duration&lt;/th&gt;
&lt;th&gt;3 · Walk in progress&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a rel="noopener noreferrer" href="https://github.com/JayanaGunaweera01/clean-up-walk/docs/screenshots/01-home.png"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2FJayanaGunaweera01%2Fclean-up-walk%2FHEAD%2Fdocs%2Fscreenshots%2F01-home.png" width="250" alt="Home screen"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a rel="noopener noreferrer" href="https://github.com/JayanaGunaweera01/clean-up-walk/docs/screenshots/02-choose-duration.png"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2FJayanaGunaweera01%2Fclean-up-walk%2FHEAD%2Fdocs%2Fscreenshots%2F02-choose-duration.png" width="250" alt="Choose how long to clean"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a rel="noopener noreferrer" href="https://github.com/JayanaGunaweera01/clean-up-walk/docs/screenshots/03-walk-in-progress.png"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2FJayanaGunaweera01%2Fclean-up-walk%2FHEAD%2Fdocs%2Fscreenshots%2F03-walk-in-progress.png" width="250" alt="Walk screen with timer, distance, litter count and direction to the next stop"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Start a walk or scan an issue; open issues listed below.&lt;/td&gt;
&lt;td&gt;15, 30, 45 or 60 minutes. The app gets your location and plans the loop.&lt;/td&gt;
&lt;td&gt;Time left, distance, litter count, and an arrow to the next problem spot.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;4 · At a problem spot&lt;/th&gt;
&lt;th&gt;5 · Scan&lt;/th&gt;
&lt;th&gt;6 · AI suggestion&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a rel="noopener noreferrer" href="https://github.com/JayanaGunaweera01/clean-up-walk/docs/screenshots/04-arrived-litter-count.png"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2FJayanaGunaweera01%2Fclean-up-walk%2FHEAD%2Fdocs%2Fscreenshots%2F04-arrived-litter-count.png" width="250" alt="Arrived at a litter spot, choosing how many pieces were picked up"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a rel="noopener noreferrer" href="https://github.com/JayanaGunaweera01/clean-up-walk/docs/screenshots/05-scan-camera.png"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2FJayanaGunaweera01%2Fclean-up-walk%2FHEAD%2Fdocs%2Fscreenshots%2F05-scan-camera.png" width="250" alt="Camera photo of litter"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a rel="noopener noreferrer" href="https://github.com/JayanaGunaweera01/clean-up-walk/docs/screenshots/06-ai-detected.png"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2FJayanaGunaweera01%2Fclean-up-walk%2FHEAD%2Fdocs%2Fscreenshots%2F06-ai-detected.png" width="250" alt="Detected litter x3, 91% sure"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;"Done here" asks how many pieces you picked up.&lt;/td&gt;
&lt;td&gt;Take a photo; the on-device model finds the&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;…&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/JayanaGunaweera01/clean-up-walk" 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;&lt;strong&gt;Open-source AI: Gemma 3n E2B.&lt;/strong&gt; It's Google's open-weight multimodal model, running entirely on the phone through &lt;a href="https://github.com/google-ai-edge/LiteRT-LM" rel="noopener noreferrer"&gt;LiteRT-LM&lt;/a&gt; (Apache 2.0). Each scan sends the camera photo plus a fixed prompt, and Gemma replies in a strict, parseable format:&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="err"&gt;litter:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;overflowing_bin:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;damaged_infrastructure:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;fallen_branch:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;main:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;litter&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;confidence:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;high&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A few design decisions I'm happy with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Forgiving parsing.&lt;/strong&gt; Small models don't always follow the format perfectly. &lt;code&gt;GemmaScan.kt&lt;/code&gt; holds the prompt and a parser that copes with markdown, odd casing, and missing lines, all covered by unit tests.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deterministic output.&lt;/strong&gt; Sampling is set to &lt;code&gt;topK = 1, temperature = 0.1&lt;/code&gt;, and every scan uses a fresh conversation so photos don't influence each other.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Model stays loaded.&lt;/strong&gt; The first scan loads the engine (a few seconds) and it's kept for the session. The vision encoder runs on the GPU when available and falls back to the CPU.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Swappable model.&lt;/strong&gt; The &lt;code&gt;.litertlm&lt;/code&gt; file is imported at runtime (&lt;strong&gt;⋮ → Import Gemma model&lt;/strong&gt;), so you can try Gemma 3n E4B or a fine-tune without rebuilding.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI suggests, human confirms.&lt;/strong&gt; The model's answer comes first, and one tap confirms or corrects it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The route planner&lt;/strong&gt; is plain Kotlin, not AI. Choosing spots within a time limit is an &lt;em&gt;orienteering problem&lt;/em&gt;, so the planner:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Scores each issue: &lt;code&gt;priority × (1 + ln(count)) × freshness&lt;/code&gt;, where freshness halves every 14 days.&lt;/li&gt;
&lt;li&gt;Keeps only issues reachable and returnable within the budget.&lt;/li&gt;
&lt;li&gt;Greedily inserts the best score-per-extra-second issue, improving the loop with 2-opt after each insertion.&lt;/li&gt;
&lt;li&gt;Fills leftover time with an octagonal exploration loop heading away from visited issues.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Stack:&lt;/strong&gt; Kotlin, Jetpack Compose, Room, platform &lt;code&gt;LocationManager&lt;/code&gt;, and LiteRT-LM. About 3,000 lines of Kotlin.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tests
&lt;/h3&gt;

&lt;p&gt;The core logic is pure Kotlin, so it's covered by unit tests that run in CI on every push to &lt;code&gt;main&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Route planner&lt;/strong&gt;: includes a randomized test that the estimated time never exceeds the time you chose&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gemma reply parser&lt;/strong&gt;: markdown, odd casing, and missing lines&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Walk manager and distance tracking&lt;/strong&gt;: walks survive the app being closed&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Domain model&lt;/strong&gt;: scoring, freshness, and issue handling&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://github.com/JayanaGunaweera01/clean-up-walk/actions/runs/37635833010" rel="noopener noreferrer"&gt;Passing CI run&lt;/a&gt;: &lt;code&gt;./gradlew testDebugUnitTest assembleDebug&lt;/code&gt;, which also builds the debug APK.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./gradlew testDebugUnitTest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;A cleanup app is only useful if people actually trust it with their camera and location, and that's where open weights changed the design.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Privacy by architecture.&lt;/strong&gt; Because Gemma 3n runs on the device, photos are analyzed locally and deleted right after. There's no server to leak from and no upload policy to trust.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Works where the problems are.&lt;/strong&gt; Litter and broken bins are often in places with poor signal. A closed vision API would have made the core feature useless exactly where it's needed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No keys, no bills, no accounts.&lt;/strong&gt; A free community tool can't carry per-photo API costs, and every user would need an account or key. With open weights the marginal cost of a scan is zero.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Swappable and improvable.&lt;/strong&gt; The model is just a file. Anyone can drop in a bigger Gemma, or a version fine-tuned on their own city's litter and signage, with no rebuild.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;Gemma (Gemma 3n E2B running on-device via LiteRT-LM)&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>devchallenge</category>
      <category>hf26challenge</category>
      <category>gemma</category>
    </item>
    <item>
      <title>Thaththa's Memory Box - a personal AI agent that isn't allowed to make things up</title>
      <dc:creator>Jayana  Gunaweera</dc:creator>
      <pubDate>Sun, 04 Oct 2026 15:37:29 +0000</pubDate>
      <link>https://dev.to/jayanagunaweera01/thaththas-memory-box-a-personal-ai-agent-that-isnt-allowed-to-make-things-up-82d</link>
      <guid>https://dev.to/jayanagunaweera01/thaththas-memory-box-a-personal-ai-agent-that-isnt-allowed-to-make-things-up-82d</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;"Thaththa", in Sinhala language, is my father. In his cupboard there is a box of things he has kept for about 30 years: certificates,awards, medals, newspaper articles, old photographs and coins. Every one of them has a story and almost none of those stories are written down anywhere. Thaththa is the only one who can tell them.&lt;/p&gt;

&lt;p&gt;So I built &lt;strong&gt;Thaththa's Memory Box&lt;/strong&gt;, a personal AI agent that runs on a laptop at home. You photograph a keepsake and the agent fills in a catalogue card for it: what it is, every word written on it and careful guesses about when, where, who and why. Then it asks questions and Thaththa answers them. His answers become part of the record and the agent reads every later keepsake knowing what he said.&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%2Fzmq7hacykl9mu671n90c.jpg" 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%2Fzmq7hacykl9mu671n90c.jpg" alt=" " width="800" height="694"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  The moment the app exists for
&lt;/h3&gt;

&lt;p&gt;One of the first things we photographed was an old photo of Thaththa, sitting at a desk in uniform. The agent described what it could see, noting the emblem visible on his shirt and that the photo appears to be an older,possibly faded print. Then, instead of inventing his life story, it asked:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Does this photo show Thaththa at a particular time in his life?&lt;/em&gt;&lt;br&gt;
&lt;em&gt;Is the emblem on his shirt related to his work or a group he belonged to?&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Thaththa answered:"Yes. This photo was taken during my work at Samanthurei, Sri Lanka".&lt;/p&gt;

&lt;p&gt;The details turned blue, with "from Thaththa" next to them. The agent's earlier guesses moved into the card's history. And every keepsake photographed after that one was read knowing what Thaththa had said.&lt;/p&gt;

&lt;h2&gt;
  
  
  The rule: it is not allowed to invent a memory
&lt;/h2&gt;

&lt;p&gt;An AI that confidently writes "your 1998 school sports meet" when the medal says no such thing is not building a memory collection. It is writing fiction about someone's life and handing it to their grandchildren. So the whole design rests on one rule and the rule is enforced in code, not in the prompt.&lt;/p&gt;

&lt;p&gt;Every detail on a card carries where it came from, shown in three inks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Blue italic: the family told it.&lt;/strong&gt; Nothing the model does later can overwrite it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Typewriter: it is written on the object.&lt;/strong&gt; The model has to transcribe every word it can see, exactly. A date or place only counts as "written on it" if the code finds it in that transcription. If Gemma says "I read 1998" but the transcription of the medal is &lt;code&gt;INTER-SCHOOL ATHLETICS 400 M&lt;/code&gt;, then 1998 stays a guess.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Grey with a question mark: a guess.&lt;/strong&gt; The story may only use it with "appears to" and the card turns the
biggest gaps into questions for the family.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;grounded&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;visible_text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Is `value` actually written on the object? Years must all appear in the transcription;
    anything else must appear in it as a whole phrase.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The model proposes. Plain Python decides what counts as known. When the family corrects something, the old value is never deleted; it moves into the history with who changed it and when. And if the agent can't work out what someone meant, their words are still saved on the card exactly as they said them. Nothing a person tells it is thrown away.&lt;/p&gt;
&lt;h2&gt;
  
  
  Demo Video
&lt;/h2&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/TQnalzTmMiE" 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/JayanaGunaweera01" rel="noopener noreferrer"&gt;
        JayanaGunaweera01
      &lt;/a&gt; / &lt;a href="https://github.com/JayanaGunaweera01/memory-box-agent" rel="noopener noreferrer"&gt;
        memory-box-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;Thaththa's Memory Box&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Your personal AI agent for family memories, running at home on Gemma 4.&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Built for my father, with love
&lt;a href="https://youtu.be/TQnalzTmMiE" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/f62d34a10207504991660366f2cb0d91c5122eac7a1ce68bc42f3885437dd56f/68747470733a2f2f696d672e796f75747562652e636f6d2f76692f54516e616c7a546d4d69452f687164656661756c742e6a7067" alt="Watch the demo"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;▶ &lt;strong&gt;Demo video:&lt;/strong&gt; &lt;a href="https://youtu.be/TQnalzTmMiE" rel="nofollow noopener noreferrer"&gt;https://youtu.be/TQnalzTmMiE&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/JayanaGunaweera01/memory-box-agent/docs/screenshots/keepsake.jpg"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2FJayanaGunaweera01%2Fmemory-box-agent%2FHEAD%2Fdocs%2Fscreenshots%2Fkeepsake.jpg" alt="A keepsake's catalogue card"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thaththa has a box of things he has kept for about 30 years: certificates, awards, medals, newspaper articles
old photographs and coins. Only he knows the stories behind them.&lt;/p&gt;
&lt;p&gt;Thaththa's Memory Box is a personal AI agent that runs on a laptop at home. Photograph a keepsake, and the agent:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;reads it&lt;/strong&gt; with Gemma 4: what it is, every word written on it, and careful guesses about when, where, who and why;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;asks&lt;/strong&gt; the family the questions that would fill the biggest gaps;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;remembers&lt;/strong&gt; what they answer, and reads every later keepsake knowing it.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Nothing leaves the house: no account, no API key, no cloud. The whole archive is one folder you can copy to a USB stick.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;The rule it is built on:&lt;/h2&gt;…&lt;/div&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/JayanaGunaweera01/memory-box-agent" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;Python, FastAPI and htmx, with Gemma 4 served locally by Ollama and 28 tests. The interesting part is &lt;code&gt;src/memorybox/memory.py&lt;/code&gt;: the provenance rules, in plain Python, that sit between the model and the card.&lt;/p&gt;

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

&lt;p&gt;I built it over the challenge weekend with Claude as a pair programmer, then tested it on Thaththa's real memory box.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Gemma 4 does three jobs&lt;/strong&gt;, all through Ollama on Azure Vivobook, 16GB RAM&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Looking&lt;/strong&gt; (vision). The photo, plus everything the family has said so far, goes in. Out come the object's kind, an exact transcription of any writing, a guess with a stated reason for date, place, people and occasion and one to three questions for the family.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Writing&lt;/strong&gt; (text). Two to four sentences for the card, given every detail labelled &lt;code&gt;[family]&lt;/code&gt;, &lt;code&gt;[read]&lt;/code&gt;,&lt;code&gt;[seen]&lt;/code&gt; or &lt;code&gt;[guess]&lt;/code&gt;, with rules about how each may be used.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Understanding&lt;/strong&gt; (text). Thaththa's free-text answer becomes structured updates ("date → [year]") plus anything worth remembering beyond this object. Those become memory notes and every later photo is read against them.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Three things that mattered when running a small model locally:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Ollama's native API with a JSON Schema in &lt;code&gt;format&lt;/code&gt;.&lt;/strong&gt; Ollama constrains decoding to the schema, so a small model's reply always parses. Asking for JSON in the prompt is fine for a big hosted model; on a laptop, enforcing it is the difference between working and not.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Setting &lt;code&gt;num_ctx&lt;/code&gt; explicitly.&lt;/strong&gt; Ollama's default context window silently drops the start of a long prompt and the start is where the instructions are. As the memory notes grow, the agent passes only the notes that share words with the object being read.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Thinking off (&lt;code&gt;think: false&lt;/code&gt;).&lt;/strong&gt; On a laptop the reasoning is most of the wait. Each keepsake took about 3 minutes on my laptop.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;One keepsake at a time.&lt;/strong&gt; A background worker reads the box in order, so the family can add twenty photos and keep browsing while it works. You can add the back of a photo, or the next page of a letter and every side is&lt;br&gt;
read together. After a restart it picks up where it left off.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why open innovation matters for this
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;These are the most private things a family owns.&lt;/strong&gt; Thaththa's certificates, his awards, photos of him at work.I would not upload his box to a company to be described and nobody should have to. Here the photos go from a phone to a laptop in the same house and the model that reads them is a file on that laptop. Turn off the Wi-Fi and everything still works.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A memory archive has to outlive the service it was made with.&lt;/strong&gt; An app built on an API key stops working when the key, the price or the company changes. This archive is a folder: a readable JSON file and the photos.&lt;br&gt;
"Take a copy" downloads it as one zip, with a memory book that opens in any browser. The model is open weights, so in ten years it can be run again, or swapped for a better one by editing one line.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I could put the rules around the model, not inside it.&lt;/strong&gt; Because the whole stack is open, the "never invent a memory" rule is ordinary, testable code that sits between the model and the card. With a closed API I could only have asked nicely in the prompt.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It costs nothing per photo, so the whole box gets done.&lt;/strong&gt; Not just the ten best things, but the coins and the newspaper cuttings too.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where a closed model was better&lt;/strong&gt; Hosted models are faster and generally better with faded handwriting and small print; Gemma took about 3 minutes per keepsake on my laptop and on Thaththa's uniform photo it asked what the emblem was instead of recognising it. I chose local anyway: his photos and certificates stay in the house and for a memory archive a good question beats a confident guess, because Thaththa's answer is the real record.&lt;/p&gt;

&lt;h2&gt;
  
  
  Handing it over
&lt;/h2&gt;

&lt;p&gt;I sat down with Thaththa at home with the laptop and his box. The first thing he chose was an old photo of himself, sitting at a desk in uniform.&lt;/p&gt;

&lt;p&gt;The agent described what it could see, the emblem on his shirt and the faded print and then, instead of guessing his story, it asked: "Does this photo show Thaththa at a particular time in his life?"&lt;/p&gt;

&lt;p&gt;He answered it: yes and he told it when the photo was taken. His words appeared on the card in blue, marked "from Thaththa" and the agent's guesses moved into the history. Every keepsake after that was read knowing what he had said.&lt;/p&gt;

&lt;p&gt;That was the moment I knew the idea worked. The agent didn't tell him about his own life. It asked and he was the one who remembered.&lt;/p&gt;

&lt;p&gt;What didn't work: while testing, a new keepsake showed the picture from an earlier one. Gemma had read the right photo, but the browser was reusing a cached image because keepsake numbers repeat when you start a new box. I changed the caching so the browser always checks for the latest photo.&lt;/p&gt;

&lt;p&gt;What's next: the box isn't finished and that's the point. There are still certificates, medals, newspaper cuttings and coins to go through, one evening at a time, with Thaththa answering the questions only he can. When we're done, "Take a copy" will turn it into a memory book the whole family can keep, on a USB stick, with no app and no internet needed.&lt;/p&gt;

&lt;p&gt;Thirty years of keepsakes sat in a box because nobody had time to ask him about them. Now something does ask and it waits for his answer.&lt;/p&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 4 (E4B, with E2B as a fallback) runs locally through Ollama and does all three
jobs: it reads keepsake photos with vision, transcribing printed text and handwriting exactly; it writes
provenance-constrained stories; and it turns the family's free-text answers into structured updates. It uses
schema-constrained output with thinking off and switching to the 26B model is one line in &lt;code&gt;config/models.toml&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>devchallenge</category>
      <category>hf26challenge</category>
      <category>gemma</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
