DEV Community

Gabriel Almeida
Gabriel Almeida

Posted on

TouchGrass API: outdoor missions from local Gemma 3 and the weather

Hacktoberfest Open-Source AI Challenge Week 1: Touch Grass Submission 🌿

This is a submission for the Hacktoberfest Open-Source AI Challenge Week 1: Touch Grass

What I Built

TouchGrass API is a small backend that answers one question: "I have 20 free minutes. What should I do outside?"

You send it a city and the minutes you have. It looks up the current weather, hands that to a local open-weight model, and returns one short, concrete outdoor mission that fits both the sky and your schedule. Drizzle gets a "stand under an awning and listen to the rain" mission. A clear afternoon gets a walk with a destination. Do the mission, check in, and the API tracks your streak of consecutive days.

It is three endpoints on purpose, so the screen stays the shortest part of the experience: ask, read one paragraph, go outside.

  • POST /mission takes { city, minutes } and returns a mission.
  • POST /checkin marks a mission as done. It is idempotent.
  • GET /streak returns { current, longest, completedToday }.

It is built for anyone who wants a nudge off the screen without installing another app that wants their data. Because it is an API, it can sit behind a CLI, a shortcut, a widget or a bot.

Demo

This is a real response from my machine, with the model running locally:

curl -X POST localhost:3000/mission \
  -H 'content-type: application/json' \
  -d '{"city":"Curitiba","minutes":20}'
Enter fullscreen mode Exit fullscreen mode
{
  "id": 1,
  "title": "Observação da Garoa",
  "description": "Saia e observe a garoa que cobre Curitiba. Concentre-se nos sons da chuva, nas gotas que escorrem pelas folhas e na sensação do ar úmido no rosto. [...]",
  "minutes": 20,
  "weather": {
    "city": "Curitiba",
    "temperatureC": 14.4,
    "precipitationMm": 0.2,
    "windKmh": 10.8,
    "isDay": true,
    "condition": "garoa"
  }
}
Enter fullscreen mode Exit fullscreen mode

Curitiba was drizzling at 14 °C when I called it, and the model proposed a rain-listening mission instead of a jog. That is the point: the weather changes the mission.

Code

🌱 TouchGrass API

Fecha o notebook. Abre a porta. Vai pegar sol.

API em Node/TypeScript que olha o clima da sua cidade e pede a um modelo de IA open-weight rodando na sua máquina uma missão curta ao ar livre, do tamanho do tempo que você tem. Feita para o desafio Touch Grass do Hacktoberfest 2026 Open-Source AI Challenge (DEV).

Como funciona

POST /mission {city, minutes}
   │
   ├─► Open-Meteo (geocoding + clima atual, sem chave de API)
   └─► Gemma 3 4B via Ollama (local) ─► missão em JSON validado
         │
         └─► SQLite (histórico de missões)
Endpoint Status O que faz
POST /mission ✅ Gera uma missão para a cidade e o tempo livre informados
POST /checkin ✅ Marca a missão como cumprida ({"missionId": 1}); idempotente
GET /streak ✅ Dias seguidos com missões cumpridas
GET /health ✅ Health check

Como rodar

Requisitos: Node 20+ e Ollama.

ollama pull
…
Enter fullscreen mode Exit fullscreen mode

How I Built It

The open-weight model. Missions come from Gemma 3 4B served by Ollama, running on my own machine. It is about 3.3 GB and fast enough on a regular laptop.

Structured output instead of prompt-and-pray. A 4B model is happy to ramble. I pass a JSON schema (generated from a zod schema) in Ollama's format field, so the model has to answer { "title", "description" }. The reply is then parsed with the same zod schema, and anything invalid becomes a clean 503 rather than garbage in the database. The system prompt asks for pt-BR, a mission that fits the time including the walk back, no equipment, and a safe alternative when there is rain, a storm, extreme cold or darkness.

Weather without an API key. Open-Meteo handles geocoding and current conditions, so there is nothing to sign up for. I map the WMO weather code to a readable condition, and the model sees temperature, feels-like, rain, wind and day or night.

Streaks. Missions live in SQLite (better-sqlite3). Check-ins store the local day, and the streak is computed from distinct completed days. It stays alive if your last completed day was yesterday, so you have until the end of today to keep it, and it resets after a skipped day while preserving your longest run.

Testable by design. The weather provider, the mission generator and the clock are injected into the Fastify app. The 12 tests run with no network and no Ollama, and the clock injection let me test consecutive days and a month rollover without waiting.

Stack: Node, TypeScript, Fastify, zod, SQLite, Ollama, Gemma 3 4B, Open-Meteo.

Why Does Open Innovation Matter?

  • Privacy. The model runs on your machine. Where you live and how often you go outside is not sent to an AI provider. The only outbound request is the weather lookup, which carries a city name.
  • Offline-capable. Inference needs no internet. Only the forecast does.
  • Zero cost. No API key, quota or per-token bill, so a habit app can call the model every day for free.
  • Swappable. Open weights mean one environment variable (OLLAMA_MODEL) swaps the model. A closed API can change or retire a model under you.

I haven't tried the closed-API version of this, so I won't claim it is worse. For a tool about personal routine, keeping the data local was the deciding factor.

Testing It

I ran the full flow against live weather and the local model. In Curitiba (heavy drizzle, 14 °C, 15 minutes) it proposed a rain-watching mission. In Lisbon at night (20 °C, 45 minutes) it switched to a night walk by the river instead of a daytime route, so the weather and the time of day do change the output. A check-in moved the streak to 1, and repeating the same check-in changed nothing. An unknown city returned a clean 404.

The honest limitation: with a 4B model the missions lean toward "observe and reflect", and the minutes barely change their scope. The next thing I'd try is a richer prompt (varied mission types per duration) or a bigger Gemma through the same OLLAMA_MODEL switch.

What's Next

This is the backend only. Next I want a tiny client (CLI or phone shortcut) and per-user time zones. Right now days follow the server's time zone, which fits the local, single-user setup.

Prize Categories

Best Use of Gemma: Gemma 3 4B runs locally through Ollama and generates every mission.

Top comments (0)