Ibrahim Shahid
← Projects

Meridian

Personal project · 2025 – 2026

Meridian transforms 75,173 real Yelp places into a searchable travel atlas. It combines semantic and keyword retrieval, behavioral recommendations, and LLM-generated itineraries in a production-deployed, observable platform.

75,173

live places indexed

143%

Yelp NDCG@10 improvement

10,000

retrieval evaluation queries

4

retrieval modes

6

containerized application images

154

automated tests

100%

OpenAPI-derived frontend typing

Problem

Travel discovery tools usually force a trade-off: keyword search misses intent, pure semantic search ignores hard constraints like price and distance, and itinerary generators treat destinations as interchangeable tokens instead of ranked, explainable recommendations grounded in real place data.

Meridian is more than a travel UI: it is a measured retrieval system, recommendation engine, LLM application, and production infrastructure project presented as one cohesive product — 75,173 real Yelp places, four retrieval modes, personalized ranking, and Gemini-powered itineraries deployed on Kubernetes with full observability.

Architecture

Retrieval and recommendation pipelinelive
Client

Next.js frontend

RSC · celestial UI · ⌘K command palette

Better Auth

traveler profiles · account history

natural-language query + filters

Query understanding

rewrite · multi-query · constraints · HyDE

hybrid retrieval

PostgreSQL + pgvector

dense embeddings · full-text search · RRF fusion

ONNX cross-encoder

reranking · circuit breaker

behavioral signals + preference vectors

Redis personalization

For You feed · explainable recs

Gemini itineraries

latency/cost budgets · deterministic fallbacks

OpenTelemetry · Prometheus · Grafana

k3s deployment

6 images · KEDA autoscaling · isolated workers

Technical Decisions

Hybrid search with reciprocal-rank fusion

Dense embeddings, PostgreSQL full-text search, reciprocal-rank fusion, and ONNX cross-encoder reranking run as a single retrieval stack — semantic recall for paraphrased intent, keyword precision for named places and categories, and a learned reranker to order the fused candidate set.

Natural-language discovery with hard filters

Queries accept natural language while enforcing location, category, price, rating, and distance constraints — so users can describe what they want without sacrificing the structured filters that make results actionable.

Redis-cached preference vectors

Personalized ranking uses Redis-cached preference vectors and behavioral signals so repeat visitors see results tuned to their history without recomputing embeddings on every request.

A behavioral “For You” feed

The feed synthesizes searches, listing views, itinerary activity, and recommendation clicks into a single surface — not a static popularity list, but a live reflection of how someone actually explores.

Explainable recommendations

Each suggestion identifies which previously viewed place influenced it, so recommendations are auditable instead of opaque — users can see why a place surfaced, not just that it did.

Gemini itineraries from ranked destinations

Day-by-day itinerary generation uses the highest-ranked destinations from the retrieval pipeline as input, keeping LLM output grounded in places the system has already validated and scored.

Explicit LLM latency and cost budgets

LLM requests carry explicit latency and cost budgets, with deterministic itinerary fallbacks when generation fails — the product degrades gracefully instead of hanging or burning quota.

Four query-understanding strategies

Query rewriting, multi-query retrieval, constraint extraction, and HyDE run as complementary strategies so vague, constraint-heavy, and exploratory queries each get an appropriate interpretation path.

Faceted browse and similar-place discovery

Faceted browsing, embedding-based similar places, account history, and live system-health pages round out the product surface beyond search — discovery works whether you know what you want or are still exploring.

Better Auth traveler profiles

Better Auth accounts connect to traveler profiles so personalization, history, and recommendations persist across sessions without bolting identity on as an afterthought.

Global ⌘K command palette

A global Ctrl/⌘ + K command palette provides debounced live search and full keyboard navigation — fast retrieval for power users without sacrificing the visual browse experience.

Challenges

Measuring retrieval quality at scale

Improved Yelp NDCG@10 from 0.079 to 0.191 on a fixed 10,000-query evaluation — a 143% gain that validated each retrieval mode and fusion strategy against a reproducible benchmark instead of subjective spot checks.

Contract safety between FastAPI and Next.js

TypeScript clients are generated directly from FastAPI's OpenAPI schemas, and CI rejects backend/frontend contract drift — payload shapes stay synchronized across the stack without manual type maintenance.

Circuit breakers on critical dependencies

Circuit breakers protect reranking, LLM generation, and ingestion dependencies so a slow reranker or unavailable model doesn't cascade into full request failures — each dependency fails independently with a defined fallback.

Queue depth autoscaling with KEDA

Redis queues with retry policies, dead-letter handling, and queue-depth autoscaling via KEDA keep ingestion and background work responsive under load without over-provisioning idle workers.

Distributed tracing across services

OpenTelemetry and Jaeger instrument traces across Next.js, FastAPI, and the reranker so latency spikes can be attributed to a specific hop instead of guessed from aggregate response times.

Production metrics and dashboards

Prometheus metrics and Grafana dashboards cover latency, throughput, fallbacks, and queue health — the operational view matches the retrieval benchmark rigor applied during development.

Six-image Kubernetes deployment

Six application images ship through GitHub Actions, GHCR, Helm, and k3s with automatic HTTPS — internal workers and the unauthenticated reranker stay isolated from public ingress.

Celestial-cartography interface with RSC

A custom celestial-cartography interface built with React Server Components and minimal client-side state — deterministic, category-colored constellation artwork uniquely generated for every listing, without shipping a heavy client bundle.

Shareable URL-based filters

Filters serialize into shareable URLs without a client state library — deep links work on first load, which matters for a discovery product where people share places and searches.

Responsive and accessible by default

Layouts span mobile, tablet, and desktop with reduced-motion support, semantic meters, live regions, keyboard navigation, and accessible dialog behavior — accessibility is part of the design system, not a post-launch patch.

Graceful degradation everywhere

Loading, empty, degraded-service, and cold-start states are handled throughout the application — the UI communicates system health honestly instead of showing broken shells during partial outages.

Stack

Next.jsReactTypeScriptPythonFastAPIPostgreSQLpgvectorRedisGeminiONNXOpenTelemetryPrometheusGrafanaKEDAHelmGitHub ActionsGitHub Container RegistryKubernetes