🏆 Why I Built MatchDay v2 for H0: Hack the Zero Stack
I already built MatchDay once.
For the Hugging Face × Gradio Build Small hackathon, I built a focused World Cup trip planner for Vancouver. One city. One stadium. One agent. It worked.
But the real World Cup is not one city.
It is 16 host cities across Canada, the United States, and Mexico. 104 matches. Millions of fans traveling from everywhere.
So for this hackathon, I rebuilt MatchDay from the ground up.
Same mission. Bigger scale. Better infrastructure.
That is what this submission is.
🔁 What Changed
The Hugging Face version was Vancouver-only. Nemotron on Modal. Gradio for hosting. No real database.
The H0 version is:
- All 16 host cities: Vancouver, Seattle, San Francisco, Los Angeles, Guadalajara, Mexico City, Monterrey, Houston, Dallas, Atlanta, Miami, Kansas City, Boston, New York, Philadelphia, Toronto.
- Every fixture. Every stadium.
- AWS DynamoDB as the database.
- Vercel for the frontend.
- A smarter multi-turn agent that owns the conversation.
AWS DynamoDB uses a single-table design:
TRIPmetadataPACKAGErankingsTRANSPORTflightsSTAYhotelsDAYitinerariesEXPactivitiesEVENTprogress events
On-demand capacity. Zero cost when idle. Real production infrastructure, not a JSON file.
The frontend is now a real web app: Next.js scaffolded with v0, dark mode, SSE progress streaming, assistant bubbles, and progressive trip preview during generation.
The agent got smarter. It supports multi-turn conversation. The LLM owns every message — it decides whether to chat, clarify, or build. Follow-ups like “make it cheaper” or “closer to the stadium” preserve context and refine the same trip.
Istanbul intent capture now works from messy natural language.
The architecture stayed the same:
Brain + Hands
- 🧠 Nemotron-30B on Modal A100 — reasons, decides, explains.
- ✋ Python — fetches, scores, validates, renders.
That split is still the whole point.
🗄️ The Database Decision
DynamoDB was not the obvious choice.
Aurora PostgreSQL and Aurora DSQL were also options. But MatchDay's access pattern is simple:
- one trip per query
- events sorted by timestamp
- packages sorted by rank
- user trip history through GSIs
A single-table DynamoDB design maps cleanly to that.
The trip entity model breaks down into:
| Entity | Purpose |
|---|---|
TRIP |
status, title, total price, match, venue |
PACKAGE |
label, score, provenance |
TRANSPORT |
origin, destination, price, booking link |
STAY |
hotel name, rating, distance, price, booking link |
DAY |
date, weather, itinerary title |
EXP |
POIs, category, location |
EVENT |
typed progress events for SSE |
All in one table. Sort keys for ordering. GSIs for My Trips by user.
No SQL. No migrations. No connection pooling.
Just works.
And when nobody is using it, it costs nothing.
That matters for a hackathon project that might sit idle between demos.
🤖 What the Agent Actually Does Now
The old version was a single-turn trip builder. You gave it a match, it gave you packages.
The new version is a conversation.
User: "hi"
MatchDay: [varied, warm greeting — different every time]
User: "tell me about the atmosphere in Houston for the World Cup"
MatchDay: [real answer about NRG Stadium, the city, what to expect]
User: "Plan a cheaper trip from Istanbul for Argentina vs Cabo Verde on July 4"
MatchDay: [builds 3 ranked packages from Istanbul to the right host city]
The LLM decides the path:
- chat answer
- clarifying question
- trip build
It chooses.
No regex pre-parser owns the routing.
That is the biggest architectural change from v1.
🐛 What Broke
The Istanbul Bug
For the longest time, the deterministic intent parser had no entry for Istanbul. So “from Istanbul” would fail to resolve an origin airport, and the user would get:
“Tell me where you're flying from.”
That is exactly the kind of bug that kills a demo.
The fix was expanding the airport alias map from ~20 entries to 45+ covering every host city and major international hub.
But the deeper fix was architectural: the LLM now handles intent extraction natively. The deterministic parser is only the fallback.
The Greeting Loop
At one point, “hi,” “hello,” and “hey” all returned the exact same canned text. Every time.
The app read as broken/static — the opposite of conversational.
The fix: removed the greeting pre-gate entirely. Every non-trip message now goes to the LLM with a character prompt.
If the LLM is cold, an 8-second timeout triggers a varied fallback with 6 rotating greeting variants. Once warm, every reply is a real LLM response.
Cold Start
The Nemotron model takes 90–150 seconds to load on Modal's A100.
The first message of a session always hits this.
So I built a layered approach:
- conversational messages use an 8-second timeout with instant fallback
- trip requests use a 280-second timeout with a cold-start retry loop
The system degrades gracefully instead of hanging.
🌍 Why This Matters
World Cup 2026 is real.
The matches are scheduled. The host cities are confirmed. Millions of fans will travel.
And planning a trip across 16 cities in 3 countries with volatile pricing is genuinely hard.
MatchDay is not trying to be a generic travel chatbot.
It is one focused product slice:
plan one World Cup match-day trip really well.
With:
- real fixture data
- live flight and hotel prices
- honest provenance labels
- ranked, comparable packages
- safe search links
No fake bookings. No invented prices. No hidden fallbacks.
Just a focused agent that helps one fan make one decision.
🧠 What I Learned
DynamoDB single-table design is underrated.
Once you stop thinking in rows and columns and start thinking in access patterns, it clicks.
The LLM must own the conversation. Any pre-gate, regex parser, or keyword ladder between the user and the model will eventually fail on a real input.
The model is the gate. Everything else is the fallback.
Cold start is the #1 UX risk for GPU-hosted agents.
You need both a fast fallback and a warmup strategy. Neither alone is enough.
Vercel + v0 + DynamoDB + Modal is a shockingly productive stack.
Four platforms. Zero infrastructure headaches.
Write code, push, deploy.
🚀 What Is Next
SMS and WhatsApp channels for fans at stadiums without reliable data.
Persistent user preferences across sessions.
Real-time price alerts.
Voice input for hands-free planning.
But for now:
one fan, one match, one trip, 16 cities, real data, honest labels, ranked packages.
That is MatchDay v2.
Built With
- aws-dynamodb
- css
- modal
- nemotron-30b
- next.js
- open-meteo
- openstreetmap
- pydantic
- python
- serpapi
- sglang
- tailwind
- typescript
- v0
- vercel
Log in or sign up for Devpost to join the conversation.