Skip to content

hsalhab/biteweek

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

83 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

BiteWeek

A NYC Restaurant Week website that doesn't suck.

Python FastAPI Next.js PostgreSQL License


Tired of fighting the official NYC Restaurant Week website? So were we.

BiteWeek is a lightning-fast, beautifully designed alternative that makes discovering Restaurant Week deals actually enjoyable. Search by cuisine, neighborhood, price—or just ask AI what you're in the mood for.

ezgif-1c0e199d946dfcc1

Features

Multi-Select Filtering

Filter restaurants by cuisine, neighborhood, price tier ($30 / $45 / $60), meal period, Google rating, and review count—all with intuitive controls for precise filtering.

Google Maps Ratings

See real Google ratings and review counts for each restaurant. Filter by minimum rating (4.5+, 4.0+, 3.5+) or minimum number of reviews to find proven favorites.

Hierarchical Neighborhood Picker

StreetEasy-style neighborhood selector with boroughs, areas, and individual neighborhoods. Select "Upper Manhattan" to automatically include UES, UWS, Harlem, and more.

Smart Relevance Sorting

Results are sorted by relevance by default, factoring in:

  • Menu availability — restaurants with menus are prioritized
  • Reservation links — Resy/OpenTable integration
  • Popularity — Google review count (capped to avoid outlier dominance)
  • Rating — Higher-rated restaurants bubble up

AI-Powered Natural Language Search

Ask questions in plain English:

"pasta in lower manhattan" "cheap sushi in williamsburg" "vegetarian dinner in brooklyn"

BiteWeek uses LLM tool calling (Groq primary, OpenAI fallback) to extract structured filters from your query, then executes precise SQL + text search on menu content.

Menu Deep-Dive

We scrape and index actual menu content from each restaurant. Search for specific dishes like "lamb" or "risotto" and find restaurants that actually serve them.

Instant Reservations

One click takes you directly to each restaurant's booking page. No more hunting for links.


Architecture

┌─────────────────┐     ┌─────────────────┐     ┌─────────────────┐
│                 │     │                 │     │                 │
│   Next.js UI    │────▶│   FastAPI       │────▶│   PostgreSQL    │
│                 │     │                 │     │   (Supabase)    │
└─────────────────┘     └────────┬────────┘     └─────────────────┘
                                 │
                                 ▼
                        ┌─────────────────┐
                        │   Groq LLM      │
                        │ llama-3.3-70b   │
                        │                 │
                        │  ↓ fallback ↓   │
                        │                 │
                        │    OpenAI       │
                        │  gpt-4o-mini    │
                        └─────────────────┘

AI Search Flow

  1. User query → Groq LLM (or OpenAI fallback) with tool schema + available filter values
  2. LLM extracts structured filters: {near: "Tribeca", menu_search: ["pasta", "penne", ...]}
  3. Backend expands near to nearby neighborhoods using distance calculation
  4. SQL filtering on structured fields + text search on menu content
  5. LLM summarizes results for the user
  6. Return restaurants with explanation

Tech Stack

Layer Technology
Frontend Next.js 16, React 19, TypeScript, Tailwind CSS
Backend FastAPI, Python 3.13+, Pydantic
Database PostgreSQL (Supabase)
AI Groq API (primary), OpenAI (fallback)
Scraping Playwright, BeautifulSoup, PyMuPDF
Package Manager uv

Quick Start

Prerequisites

  • Python 3.13+
  • Node.js 18+
  • PostgreSQL 17+ (or Supabase)
  • uv
  • Groq API key (free at console.groq.com)

1. Install Dependencies

git clone https://github.com/yourusername/biteweek.git
cd biteweek

# Install Python dependencies
uv sync

# Install frontend dependencies
cd frontend && npm install && cd ..

2. Set Up Database

# Option A: Local PostgreSQL
brew install postgresql@17
brew services start postgresql@17
createdb biteweek
uv run migrate

# Option B: Supabase (recommended for production)
# Create a project at supabase.com and get your DATABASE_URL

3. Configure Environment

# .env
DATABASE_URL=postgresql://localhost/biteweek  # or Supabase URL
GROQ_API_KEY=gsk_...                          # from console.groq.com (primary)
OPENAI_API_KEY=sk-...                         # optional fallback for AI search
GOOGLE_PLACES_API_KEY=...                     # optional, for Google ratings

4. Populate Data

# Run the full automated pipeline
uv run pipeline configs/nyc-restaurant-week.yaml

The pipeline will automatically:

  • Scrape restaurant listings
  • Scrape menu content
  • Fetch Google ratings (if GOOGLE_PLACES_API_KEY is set)
  • Scrape reservation URLs from Resy/OpenTable
  • Clean and format menus using LLM (if OPENAI_API_KEY is set)
  • Build AI search cache

See PIPELINE.md for full pipeline documentation.

5. Run

# Terminal 1: Start API server
uv run api

# Terminal 2: Start frontend
cd frontend && npm run dev

Open http://localhost:3000 and start exploring.


CLI Reference

# Database
uv run migrate          # Run database migrations

# Data Pipeline
uv run pipeline configs/nyc-restaurant-week.yaml                 # Run full pipeline
uv run pipeline configs/nyc-restaurant-week.yaml --no-resume     # Force restart
uv run pipeline configs/nyc-restaurant-week.yaml --stages menu_cleaning  # Run specific stages

# API
uv run api              # Start FastAPI server (port 8000)

API Endpoints

Method Endpoint Description
GET /restaurants List restaurants with filters
GET /restaurants/{id} Get restaurant details
GET /filters Get available filter options
POST /ask Natural language search with AI

Filter Parameters (all support multiple values)

# Single filter
GET /restaurants?cuisine=Italian

# Multiple values (OR logic)
GET /restaurants?cuisine=Italian&cuisine=French&price=30&price=45

# Neighborhood filter
GET /restaurants?neighborhood=Soho&neighborhood=Tribeca&neighborhood=West%20Village

# Rating filters
GET /restaurants?min_rating=4.0&min_reviews=100

Natural Language Search

curl -X POST http://localhost:8000/ask \
  -H "Content-Type: application/json" \
  -d '{"query": "pasta in lower manhattan"}'

Response:

{
  "restaurants": [...],
  "reasoning": "I found 10 restaurants in Lower Manhattan with pasta on their menus, including A Pasta Bar in Soho and several Italian spots in Tribeca."
}

Database Schema

-- Restaurant data
restaurants (
    id TEXT PRIMARY KEY,
    name TEXT,
    cuisine JSONB,        -- ["Italian", "Mediterranean"]
    borough TEXT,
    neighborhood TEXT,
    price_tier INTEGER,   -- 30, 45, or 60
    meals JSONB,          -- ["lunch", "dinner"]
    dietary JSONB,        -- ["vegetarian", "gluten-free"]
    menu_url TEXT,
    reservation_url TEXT,
    address TEXT,
    description TEXT,
    google_rating NUMERIC(2,1),    -- 1.0 to 5.0
    google_review_count INTEGER,
    google_place_id TEXT
)

-- Raw menu content (for text search)
menu_items (
    restaurant_id TEXT REFERENCES restaurants(id),
    content TEXT
)

Project Structure

biteweek/
├── biteweek/              # Python backend
│   ├── api.py             # FastAPI application
│   ├── db.py              # PostgreSQL operations
│   ├── google_places.py   # Google Places API client
│   ├── migrate.py         # Database migration runner
│   ├── models.py          # Pydantic models
│   ├── nl_search.py       # LLM tool calling + search
│   ├── pipeline/          # Modular pipeline framework
│   │   ├── core.py        # Pipeline orchestrator
│   │   ├── stages.py      # Base stage classes
│   │   ├── state.py       # State tracking
│   │   └── config.py      # YAML config loader
│   ├── stages/            # Pipeline stage implementations
│   │   ├── scraping.py    # Restaurant & menu scraping
│   │   ├── enrichment.py  # Ratings & reservations
│   │   ├── cleaning.py    # LLM menu processing
│   │   └── caching.py     # AI search cache
│   ├── sources/           # Event-specific adapters
│   │   ├── base.py        # Base source interface
│   │   └── nyc_restaurant_week.py  # NYC implementation
│   └── utils/             # Reusable utilities
│       ├── web.py         # Playwright helpers
│       ├── pdf.py         # PDF extraction
│       ├── text.py        # Text cleaning
│       └── fuzzy.py       # Name matching
├── frontend/              # Next.js frontend
│   └── src/
│       ├── app/           # Pages
│       ├── components/    # React components (FilterSidebar, NeighborhoodPicker, etc.)
│       └── lib/           # API client, neighborhood hierarchy
├── configs/               # Pipeline configurations
│   └── nyc-restaurant-week.yaml
├── scripts/               # Pipeline runner
│   └── run_pipeline.py
├── migrations/            # SQL migration files
│   └── 001_initial_schema.sql
├── PIPELINE.md            # Pipeline documentation
└── pyproject.toml

Environment Variables

# .env
DATABASE_URL=postgresql://localhost/biteweek  # PostgreSQL connection
GROQ_API_KEY=gsk_...                          # Primary LLM for AI search
OPENAI_API_KEY=sk-...                         # Fallback LLM (optional)
GOOGLE_PLACES_API_KEY=...                     # Optional, for Google ratings

Contributing

Contributions welcome! Open a PR for bug fixes, features, or improvements.

# Backend development
uv run uvicorn biteweek.api:app --reload

# Frontend development
cd frontend && npm run dev

License

MIT


Stop fighting bad UX. Start discovering great food.

About

A website for NYC restaurant week that actually works

Resources

Contributing

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors