Hyperlocal travel discovery for time-constrained travelers.
"I was at a conference in Whitefield, Bangalore. It was 6 PM. I had the evening free but no idea what was nearby — Google Maps showed places 45 minutes away in traffic, TripAdvisor gave me a city-wide Top 10. I ended up staying at the hotel. I built DriftSpot so that never happens again."
Live Demo · API Docs · Product Docs
DriftSpot takes a single free-form query and returns 2–3 curated, opinionated place recommendations — filtered for where you actually are, what time it is, and how you feel right now.
"I'm done with my conference in Whitefield. It's 6 PM, I want to relax somewhere quiet nearby."
→ Windmills Craftworks Brewery · 12 min · "mellow craft beer evening"
→ Cubbon Park · 18 min · "quiet and green, good for a walk"
→ Koshy's · 20 min · "old-world Bangalore, filter coffee"
No search. No filters. No scrolling. Just go.
User Query (Natural Language)
│
▼
┌─────────────────────────────────────────┐
│ FastAPI Backend │
│ │
│ JWT Auth (Supabase) │
│ │ │
│ ▼ │
│ ┌─────────────────┐ │
│ │ ExtractIntent │ GPT-4.1-mini │
│ │ │ Expert + Few-shot│
│ └────────┬────────┘ prompting │
│ │ intent {mood, purpose ...} │
│ ▼ │
│ ┌─────────────────┐ │
│ │ DiscoverPlaces │ Google Places │
│ │ │ Nearby Search │
│ └────────┬────────┘ keyword-filtered │
│ │ candidate places[] │
│ ▼ │
│ ┌─────────────────┐ │
│ │ RankPlaces │ GPT-4.1-mini │
│ │ │ Strict rules, │
│ └────────┬────────┘ no forced fits │
│ │ recommendations[] │
└────────────┼────────────────────────────┘
│
▼
Supabase (PostgreSQL + RLS)
queries · saved_places · feedback
The pipeline is built with LangGraph — each stage is a discrete, independently testable node with typed state flowing between them.
Rather than chaining everything into a single monolithic prompt, the pipeline is broken into three nodes with a shared TravelState TypedDict. This makes each step independently debuggable, swappable, and testable — critical for a system where prompt changes in one step affect the entire output chain.
Instead of hardcoding a mood → place-type mapping, the LLM extracts the traveler's specific activity goal as a Google Places keyword:
"I want to smoke somewhere" → purpose: "hookah bar OR tobacco shop"
"eat biryani" → purpose: "biryani restaurant"
This keyword is passed directly to Google Places — so discovery is targeted before ranking, not after.
B2C auth via Google OAuth. The frontend only holds the user's JWT — no admin keys ever leave the backend. Row Level Security enforces per-user data isolation at the database layer:
create policy "Users access own data"
on saved_places for all using (auth.uid() = user_id);Even a compromised client cannot read or modify another user's data.
Every query, shortlist, and feedback action is stored with structured fields (not raw blobs), designed to be projected into a user–intent–place graph:
(User) -[:SEARCHED_IN]-> (Location)
(User) -[:SHORTLISTED {vibe, travel_time}]-> (Place)
(User) -[:LIKED / :DISLIKED]-> (Place)
This enables collaborative filtering and preference learning from Day 1 — without a painful schema migration later.
Two techniques in combination:
- Expert persona —
"You are a knowledgeable local travel guide..."activates domain-specific reasoning embedded in the model's weights - Few-shot examples — constrain output vocabulary to values the downstream system expects (
"relaxed"not"chill") - Strict rules layer — prevents creative reinterpretation (
"If someone asks to smoke, do NOT recommend salons")
| Layer | Choice | Why |
|---|---|---|
| Backend | FastAPI | Async-native, auto OpenAPI docs, Pydantic validation |
| AI Orchestration | LangGraph | Stateful multi-node pipelines; independently testable nodes |
| LLM | GPT-4.1-mini | Best quality/cost ratio for structured extraction + ranking |
| Place Data | Google Places API | Largest real-time coverage; open/closed status; keyword search |
| Auth + DB | Supabase | Google OAuth; RLS; PostgreSQL structured for graph-readiness |
| Frontend | React + Vite + Tailwind | Fast iteration; warm amber/orange design system |
| Hosting | Render (API) + Vercel (web) | Zero-config deploys from GitHub |
| Document | Description |
|---|---|
| PRD | Problem statement, target user, north star scenario |
| User Stories | Acceptance criteria for V1 and V2 features |
| Sprint Plan V1 | 5-day build plan for the core discovery loop |
| Sprint Plan V2 | Trust signals, cab booking, feedback loop |
| Architecture | ADRs — the why behind each technical decision |
| Startup Foundation | Market sizing, business model, GTM, competitive analysis |
Backend
cd apps/api
pip install -r requirements.txt
cp .env.example .env # fill in your keys
uvicorn main:app --app-dir src --reload
# API at http://localhost:8000 · Docs at http://localhost:8000/docsFrontend
cd apps/web
npm install
cp .env.example .env # fill in your keys
npm run dev
# App at http://localhost:5173Required environment variables
| Variable | Where to get it |
|---|---|
OPENAI_API_KEY |
platform.openai.com |
GOOGLE_PLACES_API_KEY |
Google Cloud Console → Maps → Places API |
SUPABASE_URL |
Supabase Dashboard → Project Settings → API |
SUPABASE_ANON_KEY |
Supabase Dashboard → Project Settings → API |
VITE_SUPABASE_URL |
Same as SUPABASE_URL |
VITE_SUPABASE_KEY |
Same as SUPABASE_ANON_KEY |
VITE_API_URL |
http://localhost:8000 (local) or your Render URL |
- Natural language intent extraction (mood, purpose, constraints)
- Hyperlocal discovery via Google Places with keyword filtering
- LLM-ranked recommendations with context-aware explanations
- Google OAuth + JWT auth
- Saved places (shortlist) persisted to Supabase
- Left panel showing shortlisted spots across sessions
- Trust signals per recommendation (safety, crowd, solo-friendliness)
- Cab booking deeplink (Uber/Ola) from recommendation card
- Thumbs up/down feedback loop for personalization
- Graph-based collaborative recommendations (Neo4j)
- GPS auto-location
Built by Aditya Dhir — Senior AI Engineer.
DriftSpot demonstrates the intersection of AI engineering (LangGraph, prompt engineering, LLM orchestration) and product thinking (PRD, sprint planning, competitive analysis, investor framing).