<?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: Nora Aguirre</title>
    <description>The latest articles on DEV Community by Nora Aguirre (@noraprogramadora).</description>
    <link>https://dev.to/noraprogramadora</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%2F4187500%2Fb00aa8c3-4a7c-44c3-8444-fcac2d75578d.png</url>
      <title>DEV Community: Nora Aguirre</title>
      <link>https://dev.to/noraprogramadora</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/noraprogramadora"/>
    <language>en</language>
    <item>
      <title>Ofrenda local: a Día de Muertos remembrance writer that runs offline with Gemma 3</title>
      <dc:creator>Nora Aguirre</dc:creator>
      <pubDate>Sat, 31 Oct 2026 21:08:58 +0000</pubDate>
      <link>https://dev.to/noraprogramadora/ofrenda-local-a-dia-de-muertos-remembrance-writer-that-runs-offline-with-gemma-3-3d07</link>
      <guid>https://dev.to/noraprogramadora/ofrenda-local-a-dia-de-muertos-remembrance-writer-that-runs-offline-with-gemma-3-3d07</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/hacktoberfest-week4-2026-10-26"&gt;Hacktoberfest Open-Source AI Challenge Week 4: 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;Every year on November 1 and 2, families across Mexico set a table for people who are no longer at it. There are candles, marigolds, papel picado, their favorite food and their favorite songs. The &lt;em&gt;ofrenda&lt;/em&gt; is not a decoration. It is a way of saying that someone is still part of the family.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ofrenda local&lt;/strong&gt; is a small tool that helps you start that table. You tell it the name of someone you miss, what they were to you, their favorite food, a song they loved, a memory, and the tone you want (tender, cheerful or solemn). You can add a photo if you like. A local language model then writes, in Mexican Spanish:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a short &lt;strong&gt;remembrance&lt;/strong&gt; addressed to them,&lt;/li&gt;
&lt;li&gt;a &lt;strong&gt;calaverita literaria&lt;/strong&gt;, the affectionate, playful verse that is part of the tradition,&lt;/li&gt;
&lt;li&gt;a one-line &lt;strong&gt;cartelito&lt;/strong&gt; for a papel picado or a card,&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;items for the ofrenda&lt;/strong&gt; taken only from what you told it,&lt;/li&gt;
&lt;li&gt;and, if you added a photo, a plain description of what is visible in it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It also lists the traditional elements of an ofrenda with their meaning. You can edit everything, copy it, or print a sheet to put on the altar.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How I read the theme.&lt;/strong&gt; For me, a "ghost in the machine" is the trace of a person that stays in the things they leave behind: a recipe, a song, a way of saying something. I wanted a machine that helps you hold on to that trace without taking it anywhere. &lt;/p&gt;

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

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/DrP-kSJcx5c" 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/nora-programadora" rel="noopener noreferrer"&gt;
        nora-programadora
      &lt;/a&gt; / &lt;a href="https://github.com/nora-programadora/ofrenda-local" rel="noopener noreferrer"&gt;
        ofrenda-local
      &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;Ofrenda local&lt;/h1&gt;
&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;Tell it about someone you miss and get a short remembrance, a &lt;em&gt;calaverita&lt;/em&gt;, a checklist for their &lt;em&gt;ofrenda&lt;/em&gt; and an image of how the altar could look. The AI runs on your own computer, with no internet: your stories and photos never leave your machine.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Built for the &lt;strong&gt;Hacktoberfest Open-Source AI Challenge, Week 4: Ghost in the Machine&lt;/strong&gt; (Día de Muertos).&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Post on DEV: [[TODO: add link once published]]&lt;/li&gt;
&lt;li&gt;Demo video: &lt;a href="https://youtu.be/DrP-kSJcx5c" rel="nofollow noopener noreferrer"&gt;VIDEO&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/nora-programadora/ofrenda-local/docs/ofrenda.svg"&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%2Fnora-programadora%2Fofrenda-local%2FHEAD%2Fdocs%2Fofrenda.svg" alt="Ofrenda local screenshot"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;What it does&lt;/h2&gt;
&lt;/div&gt;
&lt;ol&gt;
&lt;li&gt;You write a few things about a loved one: name, what they were to you, favorite food, a song, a memory, and the tone you want (tender, cheerful or solemn). A photo is optional.&lt;/li&gt;
&lt;li&gt;A local Gemma 3 model writes, in Mexican Spanish
&lt;ul&gt;
&lt;li&gt;a short &lt;strong&gt;remembrance&lt;/strong&gt; addressed to them,&lt;/li&gt;
&lt;li&gt;a &lt;strong&gt;calaverita literaria&lt;/strong&gt; (affectionate, never cruel),&lt;/li&gt;
&lt;li&gt;a one-line &lt;strong&gt;cartelito&lt;/strong&gt; for a papel picado or a card,&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;personal items&lt;/strong&gt; for the ofrenda taken only…&lt;/li&gt;
&lt;/ul&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/nora-programadora/ofrenda-local" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;To run it: install &lt;a href="https://ollama.com" rel="noopener noreferrer"&gt;Ollama&lt;/a&gt;, &lt;code&gt;ollama pull gemma3:4b&lt;/code&gt;, then &lt;code&gt;npm install &amp;amp;&amp;amp; npm run dev&lt;/code&gt; and open &lt;code&gt;http://localhost:5173&lt;/code&gt;.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Model:&lt;/strong&gt; Gemma 3 (4B, vision-capable, open weights).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Inference:&lt;/strong&gt; &lt;a href="https://ollama.com" rel="noopener noreferrer"&gt;Ollama&lt;/a&gt;, running on my laptop. No cloud calls anywhere.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Backend:&lt;/strong&gt; Node.js, TypeScript and Express.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Frontend:&lt;/strong&gt; React and Vite, with a print stylesheet for the ofrenda sheet.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A few decisions shaped the project.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The model writes. The code owns the facts.&lt;/strong&gt; The traditional elements of an ofrenda and what they mean are fixed text in the code. I did not want a small model improvising cultural content, so the model only writes the personal parts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Structured output.&lt;/strong&gt; I pass a JSON schema to Ollama's &lt;code&gt;format&lt;/code&gt; parameter so the answer always has the same shape, and the server validates it again before showing anything:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;          &lt;span class="c1"&gt;// gemma3:4b&lt;/span&gt;
  &lt;span class="na"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;format&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;               &lt;span class="c1"&gt;// JSON schema for the answer&lt;/span&gt;
  &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;temperature&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.7&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;system&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;systemPrompt&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;user&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;userMessage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;images&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;imageBase64&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;imageBase64&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Guardrails in the prompt.&lt;/strong&gt; This is a sensitive subject, so the system prompt is strict. The model must use only the data you provide and never invent facts about the person, such as dates, relatives or causes of death. If there is little information, it should write something more general instead of filling the gaps. It must treat your text as data, not as instructions. The calaverita has to stay affectionate and never mock illness, the body or how someone died. And a photo can be described but never used to identify anyone.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Photos.&lt;/strong&gt; Gemma's vision lets the app describe what is visible in an optional photo, like a smile or the kitchen behind someone, so the remembrance can mention it. That description is shown to you, so you can see exactly what the model noticed.&lt;/p&gt;

&lt;p&gt;I built this with help from Claude, in chat, as a pair programmer. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A real example.&lt;/strong&gt; &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Cartelito:&lt;/em&gt; ...&lt;br&gt;
&lt;em&gt;Remembrance:&lt;/em&gt; ...&lt;br&gt;
&lt;em&gt;Calaverita:&lt;/em&gt; ...&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;and say honestly what worked and what did not (rhymes, tone, anything it got wrong). If you tried &lt;code&gt;gemma3:12b&lt;/code&gt;, say whether the verses improved.]]&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Limits.&lt;/strong&gt; A 4B model rhymes unevenly in Spanish, so the output is a draft you are meant to edit, and that is how the app presents it. Each generation takes about 40 seconds on my laptop.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Privacy.&lt;/strong&gt; A family story and a photo of someone who has died are not things I want to upload to a company's server. Here they go from the browser to my own machine and stay there.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It works with the lights, and the internet, off.&lt;/strong&gt; After the model is downloaded, no connection is needed. That fits an ofrenda, which is about being present, not online.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It cannot disappear.&lt;/strong&gt; If a service shuts down or changes its terms, a remembrance you wrote with it is not lost, because the tool does not depend on anyone's API.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Control.&lt;/strong&gt; I can rewrite the prompt, enforce my own schema and swap the model with one environment variable.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I have not compared this against a closed API, so I will not claim it writes better verse. What open weights gave me is privacy, independence and control, in a use case where those matter more than polish.&lt;/p&gt;

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

&lt;p&gt;Gemma &lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>hf26challenge</category>
      <category>ai</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Sloth Digest: a local AI that reads my group chats while I sleep</title>
      <dc:creator>Nora Aguirre</dc:creator>
      <pubDate>Sat, 24 Oct 2026 20:01:25 +0000</pubDate>
      <link>https://dev.to/noraprogramadora/sloth-digest-a-local-ai-that-reads-my-group-chats-while-i-sleep-50ca</link>
      <guid>https://dev.to/noraprogramadora/sloth-digest-a-local-ai-that-reads-my-group-chats-while-i-sleep-50ca</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/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;Group chats pile up overnight. Reading 200 messages to find the two that matter ("can you send me the pricing doc by Wednesday?") is work nobody wants to do.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sloth Digest&lt;/strong&gt; does that reading for me. I drop my exported WhatsApp chats into a folder, and every morning a local model has already written one Markdown file with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Needs you:&lt;/strong&gt; only the tasks assigned to me, with their deadlines.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Per chat:&lt;/strong&gt; a short summary and the key dates.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Pizza orders, coffee tips and football talk are ignored. I set it up once with Windows Task Scheduler, and after that I do nothing. I just open the file.&lt;/p&gt;

&lt;p&gt;It is for anyone who wants to stay on top of busy group chats without scrolling through them, and who doesn't want to upload those chats to someone else's server to do it.&lt;/p&gt;

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

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

&lt;p&gt;All the chats in the video are invented samples. Here is what the digest looks like for the sample chat:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# Morning digest, 2026-10-23&lt;/span&gt;

&lt;span class="gu"&gt;## Needs you&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Send the updated pricing doc, due: Wednesday at noon. Asked by Maya in chat-team
&lt;span class="p"&gt;-&lt;/span&gt; Review slides, due: Tomorrow afternoon. Asked by Priya in chat-team

&lt;span class="gu"&gt;## chat-team&lt;/span&gt;
The team needs to finalize the client demo, with Sam responsible for sending the updated
pricing document by Wednesday at noon... The demo time has been moved to Thursday at 4:30 PM.
&lt;span class="p"&gt;
-&lt;/span&gt; Client Demo: Thursday at 4:30 PM
&lt;/code&gt;&lt;/pre&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/nora-programadora" rel="noopener noreferrer"&gt;
        nora-programadora
      &lt;/a&gt; / &lt;a href="https://github.com/nora-programadora/sloth-digest" rel="noopener noreferrer"&gt;
        sloth-digest
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Local AI that reads your exported chats overnight and leaves a short morning digest. Runs offline with Ollama. + Gemma
    &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;sloth-digest 🦥&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;A local AI that reads your exported group chats while you sleep and leaves a short morning digest. No cloud, no API key, no cost per message.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/f8df3091bbe1149f398a5369b2c39e896766f9f6efba3477c63e9b4aa940ef14/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d4d49542d677265656e"&gt;&lt;img src="https://camo.githubusercontent.com/f8df3091bbe1149f398a5369b2c39e896766f9f6efba3477c63e9b4aa940ef14/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d4d49542d677265656e" alt="License: MIT"&gt;&lt;/a&gt;
&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/721738b75b531b2182db206f48ee1995a8538bb9eed24a53322a34d5aa556ee5/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6e6f64652d32302532422d627269676874677265656e"&gt;&lt;img src="https://camo.githubusercontent.com/721738b75b531b2182db206f48ee1995a8538bb9eed24a53322a34d5aa556ee5/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6e6f64652d32302532422d627269676874677265656e" alt="Node"&gt;&lt;/a&gt;
&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/f84cd5e753ca35a1a6942b16ae0f4b3ade9973d104d1caa7add7b1ad56d46d6d/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6d6f64656c2d47656d6d6125323033253230283442292d626c7565"&gt;&lt;img src="https://camo.githubusercontent.com/f84cd5e753ca35a1a6942b16ae0f4b3ade9973d104d1caa7add7b1ad56d46d6d/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6d6f64656c2d47656d6d6125323033253230283442292d626c7565" alt="Model: Gemma 3"&gt;&lt;/a&gt;
&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/d4873f832b8cfcedce0fbb9dc320b3f6079a08cd5e6ad33bd1a07e15b1cc6c35/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f72756e74696d652d4f6c6c616d612d626c61636b"&gt;&lt;img src="https://camo.githubusercontent.com/d4873f832b8cfcedce0fbb9dc320b3f6079a08cd5e6ad33bd1a07e15b1cc6c35/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f72756e74696d652d4f6c6c616d612d626c61636b" alt="Runtime: Ollama"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Built for the &lt;a href="https://dev.to/challenges/hacktoberfest-week3-2026-10-19" rel="nofollow"&gt;Hacktoberfest Open-Source AI Challenge, Week 3: Slow and Steady&lt;/a&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;📝 Read the post on DEV: &lt;a href="https://dev.to/noraprogramadora/sloth-digest-a-local-ai-that-reads-my-group-chats-while-i-sleep-26o4-temp-slug-5874492?preview=67f67fc080ec1f72e7710bdadeaac8b98d31c5b4af07057cf388976028c3d7ebc1eeafdf37e871e7919a5eec9e3e84192c124cb8145c9ab07a1d585f" rel="nofollow"&gt;POST&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;🎥 Demo video: &lt;a href="https://youtu.be/4BhDWZK64_M" rel="nofollow noopener noreferrer"&gt;VIDEO DEMO&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;The problem&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;Group chats pile up overnight. Reading 200 messages to find the two that matter ("can you send me the pricing doc by Wednesday?") is work nobody wants to do.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;What it does&lt;/h2&gt;
&lt;/div&gt;
&lt;ol&gt;
&lt;li&gt;You drop exported WhatsApp chats (&lt;code&gt;.txt&lt;/code&gt;) into &lt;code&gt;inbox/&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;At a scheduled time (for example 6 am), a local model reads them.&lt;/li&gt;
&lt;li&gt;You wake up to one Markdown file with:
&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Needs you:&lt;/strong&gt; only the tasks assigned to you, with deadlines.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Per chat:&lt;/strong&gt; a two-sentence summary and the key dates.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Small talk (pizza, coffee, football) is ignored.&lt;/p&gt;
&lt;p&gt;Example output (from the…&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/nora-programadora/sloth-digest" 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;Stack:&lt;/strong&gt; Gemma 3 (4B, open-weight) running through Ollama, with Node.js and TypeScript. The model runs locally, and the scheduled run uses Windows Task Scheduler calling a script inside WSL.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;WhatsApp exports (.txt) → parser → Gemma 3 via Ollama (JSON Schema output)
→ code-side logic (assignee filter, dates) → daily Markdown digest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Parser.&lt;/strong&gt; It turns the export into clean messages: it skips system lines, keeps multi-line messages together and drops &lt;code&gt;&amp;lt;Media omitted&amp;gt;&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Structured output.&lt;/strong&gt; I call Ollama's &lt;code&gt;/api/chat&lt;/code&gt; with a JSON Schema in the &lt;code&gt;format&lt;/code&gt; field, &lt;code&gt;temperature: 0&lt;/code&gt; and an explicit &lt;code&gt;num_ctx&lt;/code&gt;, so the model always returns valid, predictable JSON.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The model extracts, the code decides.&lt;/strong&gt; The model lists every task in the chat with an &lt;code&gt;assignee&lt;/code&gt;. Plain code then keeps only the ones for me and works out which message each deadline came from.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scheduling.&lt;/strong&gt; A small shell script runs the digest, and a Windows scheduled task calls it through &lt;code&gt;wsl.exe&lt;/code&gt; every morning. Ollama runs on Windows and the project lives in WSL, so I used WSL's mirrored networking mode to let them talk over &lt;code&gt;localhost&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  What went wrong, and what I changed
&lt;/h3&gt;

&lt;p&gt;I tested on a synthetic chat with known answers: two tasks for me, one meeting time that changes, and lots of small talk. A 4B model made real mistakes, and fixing them taught me the most important lesson of the project.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Run&lt;/th&gt;
&lt;th&gt;Change&lt;/th&gt;
&lt;th&gt;What went wrong&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Baseline prompt&lt;/td&gt;
&lt;td&gt;A task Maya had promised to do was listed as mine; "tomorrow" left unresolved; the detail "include monthly and annual plans" got lost&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Asked the model to resolve dates&lt;/td&gt;
&lt;td&gt;Dates became absolute, but the model &lt;strong&gt;invented times&lt;/strong&gt; (4:00 PM, 9:00 AM) and picked the wrong day for "tomorrow"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Model extracts all tasks with an &lt;code&gt;assignee&lt;/code&gt;, code filters&lt;/td&gt;
&lt;td&gt;Assignment fixed, but dates still inconsistent&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;Model copies deadlines verbatim, no date arithmetic&lt;/td&gt;
&lt;td&gt;All 5 tasks correct, no invented times. Still missing "monthly and annual"&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;The lesson:&lt;/strong&gt; with a small model, don't ask for judgment or arithmetic that code can do. "Is this task for me?" and "what day is tomorrow?" are easy to verify in code and unreliable in a 4B model. Extraction stays with the model, and the rest moves to code.&lt;/p&gt;

&lt;h3&gt;
  
  
  Limitations
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Details spread across messages can be lost (the "monthly and annual" requirement never made it into the task).&lt;/li&gt;
&lt;li&gt;Long chats are not chunked yet, so very long ones may be cut off by the context window.&lt;/li&gt;
&lt;li&gt;Only WhatsApp &lt;code&gt;.txt&lt;/code&gt; exports in one locale format for now.&lt;/li&gt;
&lt;li&gt;Tested on one synthetic chat, not on a large or varied set.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;My chats stay on my machine.&lt;/strong&gt; A digest of private group chats is exactly the kind of data I don't want to send to a server I don't control.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No cost per message.&lt;/strong&gt; I can process every chat every day without watching a bill.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Swapping models is one variable.&lt;/strong&gt; &lt;code&gt;OLLAMA_MODEL=gemma3:12b npm run digest -- inbox&lt;/code&gt; changes the model without touching the code. When a 4B model fails at something, I can try another open model instead of waiting for a provider.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;I could see and fix the failures.&lt;/strong&gt; Because I control the prompt, the schema, the model and the code around it, I could trace each mistake and decide whether it belonged to the prompt or to code.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I did not compare it against a closed API, so I'm not claiming it is more accurate than one. What I can say is that this design is possible without my chats leaving my laptop.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Lazy Payoff
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Speed:&lt;/strong&gt; about 20 seconds for the 32-message sample chat on my laptop (Ryzen AI 9 HX 370, 63 GB RAM, integrated GPU only).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Effort:&lt;/strong&gt; - &lt;strong&gt;Effort:&lt;/strong&gt; for the 32-message sample chat, reading it by hand took me about 5 min and reading the digest about 30 seconds. The sample is small, so the saving is modest; the point is that a morning with 10 busy chats becomes one file. I haven't measured that with real chats.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zero effort after setup:&lt;/strong&gt; one scheduled task, then nothing to do.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>devchallenge</category>
      <category>pgaichallenge</category>
      <category>hf26challenge</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Snap a photo, get a listing: a local Gemma 3 listing generator for secondhand clothes</title>
      <dc:creator>Nora Aguirre</dc:creator>
      <pubDate>Mon, 19 Oct 2026 01:50:31 +0000</pubDate>
      <link>https://dev.to/noraprogramadora/snap-a-photo-get-a-listing-a-local-gemma-3-listing-generator-for-secondhand-clothes-c17</link>
      <guid>https://dev.to/noraprogramadora/snap-a-photo-get-a-listing-a-local-gemma-3-listing-generator-for-secondhand-clothes-c17</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;I'm building &lt;a href="https://manoenmano.mx" rel="noopener noreferrer"&gt;Mano en Mano&lt;/a&gt;, a circular-economy marketplace for secondhand clothing in Mexico. The part sellers hate most is not taking the photos. It's filling in the form afterwards: title, description, category, subcategory, style, gender, brand, materials, condition, color, size, defects. Twelve fields, for every single garment. For someone clearing out a closet, that is the difference between listing thirty pieces and listing three.&lt;/p&gt;

&lt;p&gt;So I built a listing generator. The seller uploads one to four photos of a garment and the app pre-fills the whole listing. The seller then reviews and edits everything before publishing. The model suggests, the seller decides.&lt;/p&gt;

&lt;p&gt;Who would pay for it? sellers who list a lot of pieces and want to stop retyping the same things&lt;/p&gt;

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

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/u4IuFxRxIdQ" 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/nora-programadora" rel="noopener noreferrer"&gt;
        nora-programadora
      &lt;/a&gt; / &lt;a href="https://github.com/nora-programadora/mano-en-mano-fichas-ia" rel="noopener noreferrer"&gt;
        mano-en-mano-fichas-ia
      &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;Mano en Mano · AI Listing Generator&lt;/h1&gt;
&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;Upload 1 to 4 photos of a secondhand garment and get an editable product listing with 12 fields, generated locally by an open-weight vision model.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a href="https://github.com/nora-programadora/mano-en-mano-fichas-ia/LICENSE" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/fdf2982b9f5d7489dcf44570e714e3a15fce6253e0cc6b5aa61a075aac2ff71b/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c6963656e73652d4d49542d79656c6c6f772e737667" alt="License: MIT"&gt;&lt;/a&gt;
&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/158ff9b1ff2873d298ba39b9946c7bd4e5a38951da2094074d74d3352f231dc5/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4e6f64652d32302532422d3333393933333f6c6f676f3d6e6f64652e6a73266c6f676f436f6c6f723d7768697465"&gt;&lt;img src="https://camo.githubusercontent.com/158ff9b1ff2873d298ba39b9946c7bd4e5a38951da2094074d74d3352f231dc5/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4e6f64652d32302532422d3333393933333f6c6f676f3d6e6f64652e6a73266c6f676f436f6c6f723d7768697465" alt="Node"&gt;&lt;/a&gt;
&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/3c0af6c551e289eab066ba690024e35842785f434713d91c8ec128cf4afdec73/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4d6f64656c2d47656d6d6125323033253230283442292d343238354634"&gt;&lt;img src="https://camo.githubusercontent.com/3c0af6c551e289eab066ba690024e35842785f434713d91c8ec128cf4afdec73/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4d6f64656c2d47656d6d6125323033253230283442292d343238354634" alt="Gemma 3"&gt;&lt;/a&gt;
&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/16e9eed0b787714e04c0325cd33f3bf16504a67810e6e7ed6db931c8b1379f29/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f52756e732532306f6e2d4f6c6c616d612d303030303030"&gt;&lt;img src="https://camo.githubusercontent.com/16e9eed0b787714e04c0325cd33f3bf16504a67810e6e7ed6db931c8b1379f29/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f52756e732532306f6e2d4f6c6c616d612d303030303030" alt="Ollama"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Built for the &lt;strong&gt;Hacktoberfest Open-Source AI Challenge, Week 2: Side Hustle&lt;/strong&gt; · Read the post on DEV: &lt;a href="https://dev.to/noraprogramadora/snap-a-photo-get-a-listing-a-local-gemma-3-listing-generator-for-secondhand-clothes-c17" rel="nofollow"&gt;POST_URL&lt;/a&gt; · Demo video: &lt;a href="https://www.youtube.com/watch?v=u4IuFxRxIdQ" rel="nofollow noopener noreferrer"&gt;VIDEO_URL&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/nora-programadora/mano-en-mano-fichas-ia/docs/ss.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%2Fnora-programadora%2Fmano-en-mano-fichas-ia%2FHEAD%2Fdocs%2Fss.png" alt="Pre-filled listing form"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;The problem&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;&lt;a href="https://manoenmano.mx" rel="nofollow noopener noreferrer"&gt;Mano en Mano&lt;/a&gt; is a circular-economy marketplace for secondhand clothing. Every seller has to write a listing for each garment: title, description, category, condition, size... For people selling dozens of items, that is slow, repetitive work, and it is one of the main reasons secondhand clothes never get listed.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;What it does&lt;/h2&gt;
&lt;/div&gt;
&lt;ol&gt;
&lt;li&gt;The seller uploads 1 to 4 photos of one garment.&lt;/li&gt;
&lt;li&gt;Gemma 3 (&lt;code&gt;gemma3:4b&lt;/code&gt;), running locally in Ollama, analyzes the images.&lt;/li&gt;
&lt;li&gt;The app returns a structured listing that &lt;strong&gt;pre-fills an editable form&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;The seller reviews it, fixes what is wrong…&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/nora-programadora/mano-en-mano-fichas-ia" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;The README has the steps to run it locally: install Ollama, pull &lt;code&gt;gemma3:4b&lt;/code&gt;, start the Express server and the Vite app.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;The open pieces&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Model:&lt;/strong&gt; Gemma 3 (4B, vision-capable), open weights.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Inference:&lt;/strong&gt; &lt;a href="https://ollama.com" rel="noopener noreferrer"&gt;Ollama&lt;/a&gt;, running locally on my laptop (an AMD Ryzen AI 9 HX 370 with 64 GB of RAM). No cloud calls anywhere.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Backend:&lt;/strong&gt; Node.js, TypeScript and Express. One endpoint, &lt;code&gt;POST /generate-listing&lt;/code&gt;, receives the photos and talks to Ollama.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Frontend:&lt;/strong&gt; React and Vite. An uploader, an editable pre-filled form, and a confirmation step.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Making the model answer in my marketplace's language&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The most useful thing I did was stop treating the model as a chatbot and treat it as a function that has to return a specific shape. Ollama accepts a JSON schema in its &lt;code&gt;format&lt;/code&gt; parameter and constrains the output to it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;          &lt;span class="c1"&gt;// gemma3:4b&lt;/span&gt;
  &lt;span class="na"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;format&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;               &lt;span class="c1"&gt;// schema.json&lt;/span&gt;
  &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;temperature&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;system&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;systemPrompt&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;user&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Genera la ficha de esta prenda.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;images&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The schema's closed lists are copied from Mano en Mano's own catalog: the same categories, subcategories, styles, genders, conditions, materials, colors and sizes the real form uses. That means the output can fill my form directly, with no translation layer. A &lt;code&gt;taxonomy.json&lt;/code&gt; next to it does two jobs the schema can't: it checks that the subcategory actually belongs to the category, and it maps the color name to the hex value the app stores.&lt;/p&gt;

&lt;p&gt;Every list also has an &lt;code&gt;unknown&lt;/code&gt; option. If the photo doesn't show a brand label or a size tag, the model is told to say &lt;code&gt;unknown&lt;/code&gt; instead of guessing. The server turns that into &lt;code&gt;null&lt;/code&gt;, and the form highlights those fields as "Completar" so the seller knows exactly what is left to fill.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What went wrong with my first prompt&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I ran my first prompt on 10 garment photos from my own closet and read the results field by field. It was fast to get wrong in specific, fixable ways:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Condition came back as "Con detalles/Usado" for &lt;strong&gt;all 10&lt;/strong&gt; garments, even when it reported no defects. My rule "when in doubt, pick the lowest" made the model always doubt.&lt;/li&gt;
&lt;li&gt;The defects field returned the literal string &lt;code&gt;unknown&lt;/code&gt; in 8 of 10 rows, because two of my rules contradicted each other.&lt;/li&gt;
&lt;li&gt;Two sporty Nike tops were labelled "Boho".&lt;/li&gt;
&lt;li&gt;A khaki pair of pants became "Jeans" and "Marrón" (brown instead of beige).&lt;/li&gt;
&lt;li&gt;A description of a pair of pants mentioned long sleeves.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I rewrote the rules instead of changing the model: condition is now tied to visible defects, defects are empty when none are visible, "Jeans" is only for denim, beige and khaki map to the same color, and a description may only mention parts that exist on that kind of garment. I re-ran the same photos: condition now varied across X of 10 garments, and 4 of the 5 errors above were gone.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Measuring the human in the loop&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The confirmation screen tells the seller how many of the 12 fields she corrected compared to what the model proposed. It's a small feature, but it gives me the number I care about: not how often the model is right on a test set, but how much work it actually saves a real person.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Speed and limits&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;On my laptop, each garment takes roughly 15 to 30 seconds (the first request is slower because it loads the model). That is fine for a seller doing a batch, and not fine for anything real-time. The 4B model also makes mistakes, which is why every field stays editable. Condition is the hardest field to judge from a single photo, so I treat it as a suggestion, never as a fact.&lt;/p&gt;

&lt;p&gt;I built this with help from Claude, in chat and in Claude Code, as a pair programmer. The product decisions, the catalog, the prompt rules and the testing are mine. &lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Privacy.&lt;/strong&gt; Sellers' photos go from the browser to my own server to a model running on the same machine. They never reach a third party. For a marketplace that handles people's belongings and homes in the background of their photos, that matters.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cost.&lt;/strong&gt; There are no per-request fees. Secondhand garments are low-value items, and a few cents of API cost per listing would eat into a business that works on small margins.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Control.&lt;/strong&gt; Because I run the model, I can enforce my own schema, rewrite the prompt as many times as I need, and swap Gemma for another open-weight model by changing one environment variable. Fine-tuning on real Mano en Mano listings is the obvious next step, and it is only possible because the weights are open.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No lock-in.&lt;/strong&gt; My pricing doesn't depend on someone else's pricing page.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I haven't benchmarked this against a closed API, so I won't claim it is more accurate. What it gave me is control, privacy and a cost structure that fits this specific product.&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; the whole project runs on Gemma 3 locally through Ollama.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;p&gt;Measurements in centimeters from a flat-lay photo with a reference marker, fine-tuning on real marketplace listings, and integrating the service into the Mano en Mano app.&lt;/p&gt;

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