DEV Community

Mahasvan Mohan
Mahasvan Mohan

Posted on Fully Autonomous

Hacktoberfest Week 1 - Anti-Router

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

touch-grass is the Anti-Router: an offline-first wanderlust agent that plans exploratory pedestrian dérives — scavenger-hunt walks optimized for discovery, not speed.

Instead of telling you "turn left in 100 feet," it generates navigational riddles: "Face north. Somewhere north-west of here waits a fountain — follow the quietest path you can find, and let me know when you arrive." You have to actually look at the world — buildings, trees, water, art — to orient yourself. The phone stays in your pocket.

It's for anyone who walks the same three blocks every day and wants their own neighborhood to feel foreign again.

Demo

$ uv run touch-grass derive "Carmel-by-the-Sea, California" --minutes 45
Dérive: 2 waypoints, ~2.70 mi (budget 2.25 mi).
  - a tree @ (36.54186, -121.92063)
  - a tree @ (36.54569, -121.92933)

$ uv run touch-grass track   # feed "lat,lon" GPS fixes on stdin
You found a tree. Face north. Somewhere north-west of here waits a tree —
turn toward the north-west, follow the quietest path you can find...
Enter fullscreen mode Exit fullscreen mode

On macOS, track also speaks each riddle aloud via say. A fully offline touch-grass demo simulates the whole loop — graph, routing, geofences, riddles — on a synthetic city.

Code

touch-grass — the Anti-Router

Offline-first wanderlust agent: plans exploratory pedestrian dérives (scavenger-hunt walks) from cached OpenStreetMap data, then narrates navigational riddles instead of turn-by-turn directions.

Setup

uv sync                    # core deps (osmnx only — stays light)
Enter fullscreen mode Exit fullscreen mode

For LLM riddles, run Ollama separately and set TOUCH_GRASS_MODEL (e.g. qwen3:4b). The daemon holds the weights; this package only talks HTTP to localhost — no heavy deps, no model in RAM.

Usage

uv run touch-grass demo                       # offline sim, synthetic city
uv run touch-grass derive "Portland, Oregon" --minutes 45
Enter fullscreen mode Exit fullscreen mode

derive downloads the walk graph + POIs once, caches them under ~/.cache/touch-grass/, then works fully offline.

How it works

Module Role
spatial.py Cached OSMnx walk graph + POI tag extraction; radius from time budget
router.py Anti-cost routing: penalizes arterials, rewards footways/alleys + jitter
persona.py Turns bearings/POIs into riddles; template by default, GGUF LLM via TOUCH_GRASS_MODEL
state.py Geofenced waypoint state machine —
…
  • spatial.py — caches a pedestrian-only OSMnx walk graph + POI tags (historic=*, natural=*, leisure=park...) locally before you go offline
  • router.py — the Anti-Router: Dijkstra where arterials cost up to 50× and footways/alleys are discounted, plus seeded jitter so no two dérives repeat
  • persona.py — turns bearings and POI metadata into riddles via local Ollama (stdlib urllib, zero SDK), with a template fallback when no model is running
  • state.py — a 15 m geofence state machine: the LLM only wakes when you physically arrive

How I Built It

Everything runs at the edge:

  • Open-weight LLM: any Ollama-pulled model (e.g. qwen3:4b) served by a local Ollama daemon — TOUCH_GRASS_MODEL selects it, keep_alive: 5m drops weights between waypoints to save battery
  • Open data: OpenStreetMap via OSMnx — network_type="walk" graphs cached as GraphML, POIs pickled; after one fetch the app never touches the network
  • No agent framework: LangGraph/Smolagents were in the spec, but a 4-module pipeline didn't need them — plain functions won

Built with uv, testable headless (uv run pytest — no network, no model required).

Why Does Open Innovation Matter?

The entire premise — data sovereignty for where your feet go — only works because the stack is open. Google Maps can't power an app whose selling point is that no one logs your route. OSM gave me the graph, open-weight models gave me the voice, and Ollama gave me inference that works in airplane mode. A closed API would have made this project self-defeating: the agent's value is that it knows where you walked and tells no one.

My Agent Session

Built with Devin (SWE-2). Notable: the agent hit two real bugs on first live run — geopandas demanding pyarrow for a parquet cache (fixed by switching to stdlib pickle), and ox.nearest_nodes requiring scikit-learn on unprojected graphs (replaced with a 3-line equirectangular scan). Both fixes made the dependency tree lighter — staying honest to "offline-first" meant fewer deps, not more.

Prize Categories

  • Best use of open-weight models / local inference
  • Best offline-first / privacy-preserving project

Top comments (0)