<?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: Nuwahereza Peter </title>
    <description>The latest articles on DEV Community by Nuwahereza Peter  (@petercodes34).</description>
    <link>https://dev.to/petercodes34</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%2F1467819%2F436d164f-0db9-4c2f-8982-aa31af27bde4.JPG</url>
      <title>DEV Community: Nuwahereza Peter </title>
      <link>https://dev.to/petercodes34</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/petercodes34"/>
    <language>en</language>
    <item>
      <title>plesase check it out</title>
      <dc:creator>Nuwahereza Peter </dc:creator>
      <pubDate>Fri, 30 Oct 2026 11:38:58 +0000</pubDate>
      <link>https://dev.to/petercodes34/plesase-check-it-out-474a</link>
      <guid>https://dev.to/petercodes34/plesase-check-it-out-474a</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/petercodes34/ebyafaayo-keeping-my-grandparents-stories-alive-with-gemma-4-offline-published-false-tags-3m3j" class="crayons-story__hidden-navigation-link"&gt;Ebyafaayo: keeping my grandparents' stories alive with Gemma 4, offline&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
      &lt;a href="https://dev.to/petercodes34/ebyafaayo-keeping-my-grandparents-stories-alive-with-gemma-4-offline-published-false-tags-3m3j" class="crayons-article__context-note crayons-article__context-note__feed"&gt;&lt;p&gt;Hacktoberfest Open-Source AI Challenge: Ghost in the Machine Submission 👻🌼&lt;/p&gt;

&lt;/a&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/petercodes34" class="crayons-avatar  crayons-avatar--l  "&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%2Fuser%2Fprofile_image%2F1467819%2F436d164f-0db9-4c2f-8982-aa31af27bde4.JPG" alt="petercodes34 profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/petercodes34" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Nuwahereza Peter 
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Nuwahereza Peter 
                
                
              
              &lt;div id="story-author-preview-content-4947167" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/petercodes34" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&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%2Fuser%2Fprofile_image%2F1467819%2F436d164f-0db9-4c2f-8982-aa31af27bde4.JPG" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Nuwahereza Peter &lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/petercodes34/ebyafaayo-keeping-my-grandparents-stories-alive-with-gemma-4-offline-published-false-tags-3m3j" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Oct 30&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/petercodes34/ebyafaayo-keeping-my-grandparents-stories-alive-with-gemma-4-offline-published-false-tags-3m3j" id="article-link-4947167"&gt;
          Ebyafaayo: keeping my grandparents' stories alive with Gemma 4, offline
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/devchallenge"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;devchallenge&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/hf26challenge"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;hf26challenge&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/hacktoberfest"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;hacktoberfest&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/ai"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;ai&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/petercodes34/ebyafaayo-keeping-my-grandparents-stories-alive-with-gemma-4-offline-published-false-tags-3m3j" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;2&lt;span class="hidden s:inline"&gt;&amp;nbsp;reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/petercodes34/ebyafaayo-keeping-my-grandparents-stories-alive-with-gemma-4-offline-published-false-tags-3m3j#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              1&lt;span class="hidden s:inline"&gt;&amp;nbsp;comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            11 min read
          &lt;/small&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
      <category>ai</category>
      <category>gemma</category>
      <category>llm</category>
    </item>
    <item>
      <title>Ebyafaayo: keeping my grandparents' stories alive with Gemma 4, offline</title>
      <dc:creator>Nuwahereza Peter </dc:creator>
      <pubDate>Fri, 30 Oct 2026 11:33:07 +0000</pubDate>
      <link>https://dev.to/petercodes34/ebyafaayo-keeping-my-grandparents-stories-alive-with-gemma-4-offline-published-false-tags-3m3j</link>
      <guid>https://dev.to/petercodes34/ebyafaayo-keeping-my-grandparents-stories-alive-with-gemma-4-offline-published-false-tags-3m3j</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/devteam/join-the-hacktoberfest-open-source-ai-challenge-ghost-in-the-machine-2850-in-prizes-across-21-3cho"&gt;Hacktoberfest Open-Source AI Challenge: Ghost in the Machine&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;Ask a young person in Kampala today what their clan is, and many will pause. Ask about their totem, the taboos their family keeps, or the names of their great-grandparents, and the pause gets longer.&lt;/p&gt;

&lt;p&gt;I see it in my own generation. We grew up with phones, school and the city, and somewhere along the way we stopped sitting with our grandparents in the evening to listen. The knowledge did not disappear because it was unimportant. It disappeared because nobody wrote it down. In our culture, history lives in the voices of our jjajjas: who our people were, where they came from, which clan we belong to, what we must never eat, why a child is given a certain name, the proverbs that taught right from wrong.&lt;/p&gt;

&lt;p&gt;Every time an elder passes on, a whole library closes, and most of those libraries were never written down.&lt;/p&gt;

&lt;p&gt;This week's theme asks us to honour the past, in the spirit of Día de los Muertos. For my family, the past is not in a book. It is in jjajja's voice, and a voice is the first thing we lose. So my "ghost in the machine" is a gentle one: a machine that listens to our elders while they are still with us and keeps their words for the grandchildren who will never meet them.&lt;/p&gt;

&lt;p&gt;That is why I built &lt;strong&gt;Ebyafaayo&lt;/strong&gt;, which means "history" in Luganda. It lets a family sit with jjajja omusajja or jjajja omukazi, press record, and keep their stories in their own words, in Luganda, for the generations that come after us. Everything stays on the family's own laptop.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The family asks the elder for consent and records the story.&lt;/li&gt;
&lt;li&gt;An open Luganda speech model transcribes it.&lt;/li&gt;
&lt;li&gt;The family checks and corrects the transcript.&lt;/li&gt;
&lt;li&gt;Gemma 4, running locally, turns it into a memory: the story in Luganda and English, the people and places mentioned, the clan and totem if the elder named them, proverbs, and gentle questions to ask next time.&lt;/li&gt;
&lt;li&gt;The family can add old photos from the album. Gemma describes what it can see, never who anyone is, and suggests questions to ask about each photo.&lt;/li&gt;
&lt;li&gt;Each elder gets a remembrance page, and the family can print a memory book.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Gemma is only allowed to use what the elder actually said. Every name, place and relationship links back to the sentence it came from, and anything unclear stays marked as unclear. A family's history is not something an AI should guess.&lt;/p&gt;

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

&lt;p&gt;These screenshots use a short sample story written for testing, not a real family's story.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Consent comes first.&lt;/strong&gt; The consent text is stored with the recording.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fljntt4s94l90uqf2no7v.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%2Fljntt4s94l90uqf2no7v.png" alt="Consent and record screen" width="800" height="788"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The memory Gemma wrote.&lt;/strong&gt; Every person, place and time links to the sentence it came from. Clan and totem stay empty because the elder did not say them.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fp1rcvww3su2vxt3hai49.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%2Fp1rcvww3su2vxt3hai49.png" alt="Memory editor" width="800" height="1188"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The remembrance page&lt;/strong&gt;, with a timeline and questions to ask next time.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fe7sjqd2t5eds0ej86gl2.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%2Fe7sjqd2t5eds0ej86gl2.png" alt="Remembrance page" width="800" height="1038"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The memory book&lt;/strong&gt;, ready to save as a PDF.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fs54i2461ptcdwnbmq91j.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%2Fs54i2461ptcdwnbmq91j.png" alt="Memory book" width="800" height="925"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/Nuwahereza-eng" rel="noopener noreferrer"&gt;
        Nuwahereza-eng
      &lt;/a&gt; / &lt;a href="https://github.com/Nuwahereza-eng/Ebyafaayo" rel="noopener noreferrer"&gt;
        Ebyafaayo
      &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;Ebyafaayo&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;An offline family oral-history keeper for Luganda speakers.&lt;/strong&gt; Record an elder telling a story, get a transcript from an open Luganda speech model, correct it together, and let Gemma 4 (running on your own laptop) turn it into a family memory: the story in clean Luganda, an English translation, the people, places and times mentioned, and gentle questions to ask next time. Every story goes onto a remembrance page for that elder and into a printable memory book.&lt;/p&gt;
&lt;p&gt;Nothing leaves the computer. No cloud AI, no analytics, no telemetry.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Ebyafaayo&lt;/em&gt; is Luganda for history: the things that happened.&lt;/p&gt;
&lt;p&gt;Built for the DEV Hacktoberfest Open-Source AI Challenge, Week 4: "Ghost in the Machine".&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;The story behind it&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;Ask a young person in Kampala today what their clan is, and many will pause. Ask about their totem, the taboos their family keeps, or the names of their great-grandparents, and the pause…&lt;/p&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/Nuwahereza-eng/Ebyafaayo" 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;ul&gt;
&lt;li&gt;
&lt;strong&gt;Gemma 4 (&lt;code&gt;gemma4:e2b&lt;/code&gt;) through Ollama&lt;/strong&gt; does the thinking (more on that below). I chose E2B because my laptop has 8 GB of RAM and no GPU; the bigger Gemma 4 models do not fit.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sunbird AI's open speech model &lt;code&gt;asr-whisper-large-v3-salt&lt;/code&gt;&lt;/strong&gt; (MIT licence, adapted from Whisper Large v3 for Ugandan languages) transcribes Luganda. I convert it once to an 8-bit version for faster-whisper. It is the same Sunbird weights, but it needs about 1.5 GB of RAM instead of 6 GB.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Gemma audio test did not happen.&lt;/strong&gt; I planned to test whether Gemma 4 could transcribe Luganda directly, but I expected it to need the full model through &lt;code&gt;transformers&lt;/code&gt;, another ~10 GB download my laptop could not hold. So Sunbird does the listening and Gemma does the writing. (Late in the week I noticed my Ollama now lists audio among Gemma 4 E2B's abilities, so that test is next.)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Temporal&lt;/strong&gt;, running locally, makes every job durable. More on that below too.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;FastAPI and SQLite&lt;/strong&gt; keep everything on one laptop. &lt;strong&gt;React with MUI&lt;/strong&gt; for the interface.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  How Gemma writes a memory without making things up
&lt;/h3&gt;

&lt;p&gt;A family history is the worst place for an AI to be creative. A wrong clan or a wrong grandfather is not a typo; it gets passed down. So I set Gemma one rule: use only what the elder said. Three things make that rule hold on a 2-billion-parameter model.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Gemma must fill in a fixed form.&lt;/strong&gt; Ollama can force Gemma's answer to match a JSON schema while it writes. I build the schema from the same Pydantic models the app saves, so Gemma cannot leave out a field, invent a new one, or write an essay where a name should go:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;model&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;gemma4:e2b&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;messages&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;}],&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;format&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;      &lt;span class="c1"&gt;# JSON schema from Pydantic: every field required, every list and string capped
&lt;/span&gt;    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;think&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;options&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;temperature&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;num_ctx&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;8192&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;num_predict&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;2. Every fact carries its proof.&lt;/strong&gt; Each person, place, time, proverb, clan and totem must come with the exact sentence the elder said it in. Here is what Gemma returned for one sentence of my test story:&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;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Kizito"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"relationship_to_speaker"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"father"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"source_sentence"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Taata wange yali ayitibwa Kizito, era yali mulimi wa mmwanyi."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"confidence"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"clear"&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;p&gt;("My father was called Kizito, and he was a coffee farmer.") In the app, a small quote icon sits next to every item; tap it and the sentence appears.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. The app checks Gemma's work.&lt;/strong&gt; Gemma is good, but I do not take its word for it. After it answers, plain Python code:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;looks for every quoted sentence in the transcript, and swaps in the elder's exact words when Gemma's copy is slightly off;&lt;/li&gt;
&lt;li&gt;checks that every name and place really appears in the transcript, and marks the ones that don't;&lt;/li&gt;
&lt;li&gt;deletes any clan or totem the elder never said, and tells the family why.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On my test story, Gemma found all 4 people with the right relationships (the speaker, her father, her mother, her husband), all 3 places and both time periods. Every quote matched the transcript. Clan and totem came back empty, which is correct, because the story never names them. It took 170 seconds on a laptop CPU.&lt;/p&gt;

&lt;p&gt;Gemma also writes 3 to 5 gentle follow-up questions in Luganda and English, about things the elder mentioned but did not explain. They go on the elder's remembrance page, so the next visit to jjajja starts with a question.&lt;/p&gt;
&lt;h3&gt;
  
  
  Gemma reads the old photos too
&lt;/h3&gt;

&lt;p&gt;Every family has a box of photos nobody can name any more. Gemma 4 E2B can read images, so the family can add a photo from the album to an elder's page. Gemma describes what it can see and suggests questions to ask about it. The family writes who is in it, because Gemma never says who anyone is. As a safety net, the app removes any sentence of the description that contains the name of someone the family knows.&lt;/p&gt;

&lt;p&gt;Getting this right taught me how easily a small model can be led. My first photo prompt talked about a Ugandan family, gomesi and kanzu. Given a 1965 photo of an &lt;strong&gt;empty living room&lt;/strong&gt;, Gemma confidently described "at least four people", then "two individuals standing outdoors". Asked the same thing without any of that framing, it said: "There are no people visible in the photo." The fix was a short, neutral prompt. A small model can see what you tell it to expect, so the prompt should not tell it what to expect.&lt;/p&gt;

&lt;p&gt;Gemma's Luganda questions about photos looped ("nnyo nnyo nnyo…") or drifted into Kinyarwanda. So for photos, Gemma writes the questions in English, and the family writes them the way they would actually ask jjajja.&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%2Fy91dqfkmp2zuv6bd4sx5.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%2Fy91dqfkmp2zuv6bd4sx5.png" alt="Old photos: Gemma describes the empty room and the portrait without naming anyone" width="800" height="1182"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Test photos from Wikimedia Commons (public domain), not a family's.&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  When the laptop runs out of memory: Temporal
&lt;/h3&gt;

&lt;p&gt;My laptop has 8 GB of RAM. Gemma needs about 5 of them, and the out-of-memory killer stopped it again and again while I built this (once taking my code editor with it). For a family, that would mean a story half-written and lost, and starting again.&lt;/p&gt;

&lt;p&gt;So every slow job is now a &lt;a href="https://temporal.io" rel="noopener noreferrer"&gt;Temporal&lt;/a&gt; workflow, split into small steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A memory:&lt;/strong&gt; Gemma writes the story → Gemma finds the details → check everything against the transcript and save.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A transcript:&lt;/strong&gt; cut the audio into 30-second pieces → one step per piece → join and save.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A photo:&lt;/strong&gt; Gemma describes it → remove any names and save.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Temporal records each finished step. If the app dies, the steps that finished stay finished. When the app starts again, the worker inside it picks up the unfinished step. While a step runs, it sends Temporal a heartbeat every 20 seconds, so a dead app is noticed within a minute. A failed step (say, Ollama was killed) is tried up to five times over about four minutes, which gives Ollama time to restart. Then, if it still fails, the family sees a clear "try again".&lt;/p&gt;

&lt;p&gt;The Temporal server is the open-source dev server (&lt;code&gt;temporal server start-dev&lt;/code&gt;), running on the same laptop with its history in a local SQLite file. Nothing leaves the house.&lt;/p&gt;

&lt;p&gt;I tested it the hard way: I started a memory, waited for Gemma to finish step 1, then killed the app with &lt;code&gt;kill -9&lt;/code&gt; in the middle of step 2:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Time&lt;/th&gt;
&lt;th&gt;What happened&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;13:24:26&lt;/td&gt;
&lt;td&gt;Job starts. Step 1: Gemma writes the story.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;13:25:47&lt;/td&gt;
&lt;td&gt;Step 1 done (81 s) and recorded by Temporal. Step 2 starts.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;13:26:18&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;App killed&lt;/strong&gt; in the middle of step 2.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;13:26:29&lt;/td&gt;
&lt;td&gt;App restarted. Log: &lt;code&gt;Resuming 1 job(s) with Temporal: ebyafaayo-memory-3&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;13:27:03&lt;/td&gt;
&lt;td&gt;Step 2 starts again on the new worker. &lt;strong&gt;Step 1 is not repeated.&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;13:28:34&lt;/td&gt;
&lt;td&gt;Done. Same result as a run with no crash: 4 people, 3 places, 2 times, no invented clan.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Temporal's web UI shows it plainly: the "find the details" step on its &lt;strong&gt;second attempt&lt;/strong&gt;, and the first step untouched.&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%2F76r6y8nmdgbvrwaxgqm6.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%2F76r6y8nmdgbvrwaxgqm6.png" alt="Temporal event history for the memory job, showing gemma_find_details on attempt 2 after the crash" width="800" height="629"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;My first version of that test took 5 minutes to resume, because the heartbeat only came while Gemma was writing, so the timeout had to be long enough to cover Gemma loading. A separate heartbeat thread fixed it. There is also an automated test that starts a real local Temporal server, makes the "find the details" step crash once, and checks that Gemma's first answer is reused rather than asked for again.&lt;/p&gt;

&lt;p&gt;Without a Temporal server the app still works: the same steps run in a background thread, and an interrupted job is marked for the family to retry.&lt;/p&gt;

&lt;p&gt;Problems I hit along the way:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Out of memory.&lt;/strong&gt; The first time Gemma loaded, Linux's out-of-memory killer stopped it, and my editor went with it. Now Ollama runs in its own memory-capped service that restarts itself, and the app runs one heavy step at a time: Gemma is unloaded before transcription, and the speech model is dropped afterwards. Temporal (above) handles the crashes that still happen.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;One big prompt was too much for a small model.&lt;/strong&gt; Asked for everything at once, Gemma E2B sometimes returned every list empty. Splitting the work into two focused calls fixed it: one for the text and translation, one for people, places and times. After that it found every person with the right relationship (father, mother, husband), every place and every date in my test story.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A runaway loop.&lt;/strong&gt; Once, Gemma wrote the same Luganda word over and over for eight minutes. Now every field in the JSON schema has a maximum length and every list a maximum size, and Ollama enforces them while Gemma writes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gemma still makes up facts in translation.&lt;/strong&gt; In one test, "that is where I met my husband" came out as "my husband was killed". The app checks every quote and name against the transcript, and it drops any clan or totem the elder never said. But it cannot check a translation, so the family's review matters most there.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I wrote the build spec (the rules on consent, honesty and the stack) and built the app with an AI coding agent. Here is the session, trimmed to the real development work:&lt;/p&gt;


&lt;div class="ltag-agent-session"&gt;
  &lt;div class="agent-session-header"&gt;
    
    &lt;span class="agent-session-tool-icon-badge" title="Claude Code"&gt;
&lt;/span&gt;
    &lt;span class="agent-session-title"&gt;Built AI assistance&lt;/span&gt;
  &lt;/div&gt;

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

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



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

&lt;p&gt;Family stories are private. Many families would never upload a grandmother's voice to a company's server, and many Ugandan homes do not have steady internet. With open-weight models, Ebyafaayo runs fully offline on a laptop, costs nothing per story, and keeps every recording in the family's hands.&lt;/p&gt;

&lt;p&gt;Even the crash recovery is open: Temporal's server runs on the same laptop, so a durable job does not need a cloud service either.&lt;/p&gt;

&lt;p&gt;Open models also mean our languages can grow. Sunbird AI trained an open model for Luganda and other Ugandan languages, and anyone can build on it. Closed tools rarely serve languages like ours first. Open ones let us do it ourselves.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Best Use of Gemma.&lt;/strong&gt; Gemma 4 E2B is the part of Ebyafaayo that turns a transcript into a family memory. It runs locally through Ollama on an 8 GB laptop with no GPU and no internet. It writes the story in Luganda and English, finds the people, places and times, and gives a source sentence for every one. Its answers are held to a JSON schema and checked against what the elder actually said. It also reads old family photos, using Gemma 4's image input, and turns them into questions for the next visit to jjajja. I chose a small open model on purpose: it means a family can run this at home, for free, without sending a grandmother's voice anywhere.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best Use of Temporal.&lt;/strong&gt; On an 8 GB laptop, the out-of-memory killer is a regular visitor; it stopped Gemma (and my editor) several times while I built this. Every slow job in Ebyafaayo is now a Temporal workflow made of small steps, running on a local Temporal server. When the app is killed half way, the job picks up at the step it was on instead of starting again, and a failed step is retried until Ollama is back. Temporal is open source, and its server runs on the same laptop, so the stories still never leave the house.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best Use of Render.&lt;/strong&gt; Families run Ebyafaayo at home, but judges and curious readers should not have to install anything to try it. So the same app runs as a public demo on Render, with Render as its AI runtime: one Docker service (4 CPUs, 8 GB, no GPU) runs Ollama with Gemma 4 E2B on the CPU, a Temporal dev server, and the app, all talking over &lt;code&gt;127.0.0.1&lt;/code&gt; inside the container. A persistent disk keeps Gemma, the database and Temporal's history, so a job survives a redeploy just as it survives a crash at home. The whole setup is one &lt;a href="https://github.com/Nuwahereza-eng/ebyafaayo/blob/main/render.yaml" rel="noopener noreferrer"&gt;&lt;code&gt;render.yaml&lt;/code&gt;&lt;/a&gt; Blueprint. The demo holds only a sample story and public-domain test photos, and a banner asks visitors not to upload real family recordings: the private version is the one on your own laptop.&lt;/p&gt;

&lt;h2&gt;
  
  
  Credits
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Gemma 4 by Google DeepMind (open weights)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Sunbird/asr-whisper-large-v3-salt&lt;/code&gt; by Sunbird AI (MIT)&lt;/li&gt;
&lt;li&gt;Temporal (MIT), Ollama, faster-whisper, FastAPI, React, MUI&lt;/li&gt;
&lt;li&gt;Test photos: Wikimedia Commons, public domain&lt;/li&gt;
&lt;li&gt;Built with help from an AI coding agent (Claude Code); the session is embedded above, and a summary of the decisions is in the repo at &lt;code&gt;docs/agent-log.md&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

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