DEV Community

Nora Aguirre
Nora Aguirre

Posted on

Snap a photo, get a listing: a local Gemma 3 listing generator for secondhand clothes

Hacktoberfest Open-Source AI Challenge Week 2: Side Hustle Submission đź’¸

This is a submission for the Hacktoberfest Open-Source AI Challenge Week 2: Side Hustle

What I Built

I'm building Mano en Mano, 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.

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.

Who would pay for it? sellers who list a lot of pieces and want to stop retyping the same things

Demo

.

Code

Mano en Mano · AI Listing Generator

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.

License: MIT Node Gemma 3 Ollama

Built for the Hacktoberfest Open-Source AI Challenge, Week 2: Side Hustle · Read the post on DEV: POST_URL · Demo video: VIDEO_URL

Pre-filled listing form


The problem

Mano en Mano 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.

What it does

  1. The seller uploads 1 to 4 photos of one garment.
  2. Gemma 3 (gemma3:4b), running locally in Ollama, analyzes the images.
  3. The app returns a structured listing that pre-fills an editable form.
  4. The seller reviews it, fixes what is wrong…

The README has the steps to run it locally: install Ollama, pull gemma3:4b, start the Express server and the Vite app.

How I Built It

The open pieces

  • Model: Gemma 3 (4B, vision-capable), open weights.
  • Inference: Ollama, running locally on my laptop (an AMD Ryzen AI 9 HX 370 with 64 GB of RAM). No cloud calls anywhere.
  • Backend: Node.js, TypeScript and Express. One endpoint, POST /generate-listing, receives the photos and talks to Ollama.
  • Frontend: React and Vite. An uploader, an editable pre-filled form, and a confirmation step.

Making the model answer in my marketplace's language

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 format parameter and constrains the output to it:

body: JSON.stringify({
  model: config.model,          // gemma3:4b
  stream: false,
  format: schema,               // schema.json
  options: { temperature: 0 },
  messages: [
    { role: "system", content: systemPrompt },
    { role: "user", content: "Genera la ficha de esta prenda.", images },
  ],
})
Enter fullscreen mode Exit fullscreen mode

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 taxonomy.json 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.

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

What went wrong with my first prompt

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:

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

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.

Measuring the human in the loop

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.

Speed and limits

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.

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.

Why Does Open Innovation Matter?

  • Privacy. 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.
  • Cost. 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.
  • Control. 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.
  • No lock-in. My pricing doesn't depend on someone else's pricing page.

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.

Prize Categories

  • Best Use of Gemma: the whole project runs on Gemma 3 locally through Ollama.

What's Next

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.

Top comments (0)