Visual RAG system for Indian urban planning regulations
Phase 0 Status (2026-06-19): The April hybrid architecture described below (ColQwen2.5 + GTE + BM25 + Qdrant + RRF + Gemini rerank) is not the current live v1 path. It is stale and not used for v1. The real v1 runtime is the Tomoro Modal in-memory MaxSim retriever. See
docs/tomoro_v1_runtime.mdfor the honest current state. Do not overclaim readiness.
A production-grade multimodal retrieval system that indexes planning documents (NBC, URDPFI, SWM Rules) as page images, embeds them with ColQwen2.5 visual encoders, and answers questions via Gemini 2.5 Flash with precise page citations.
Ask questions like:
The system:
Current v1 path: See
docs/tomoro_v1_runtime.md. The detailed hybrid below is the April prototype (not live for v1). v1 is Tomoro 4B visual MaxSim (in-memory on Modal) over the 738-row legacy tensor. No Qdrant/hybrid for v1.
vidore/colqwen2.5-v0.2 β 128-dim multi-vector per patch (ColPali-style late interaction)lightonai/GTE-ModernColBERT-v1 β 768-dim multi-vector per token (ModernBERT late interaction)pages_visual β ColQwen2.5 multi-vector with MAX_SIM comparator + INT8 scalar quantizationpages_text β GTE-ModernColBERT multi-vector (text + BM25 sparse)Query β 3-channel parallel search (visual/text/sparse BM25)
β RRF k=60 fusion (top-20 per channel β top-20 fused)
β Gemini 2.5 Flash VLM rerank (top-20 β top-5)
[k] citation markers/v1/query β streaming query endpoint/v1/healthz β health check/metrics β Prometheus metricsweb/ directory)[k] to view the source page)qdrant_latency_seconds, gemini_cost_usd_total, faithfulness_p50)infra/grafana/)eval/smoke.jsonl β 25 hand-curated questions (CI gate on every PR)eval/regression.jsonl β 106 questions including adversarial probes| Stat | Value |
|---|---|
| Documents indexed | 8 |
| Total pages | 743 PNG renders |
| Corpus ready to scale | No β v1 uses legacy 738-row Tomoro tensor (Phase 0 recovery); April pipeline not ready for visual retrieval |
uv sync
Copy .env.example to .env and fill in required values:
# Google Gemini API (required)
GEMINI_API_KEY=your_gemini_api_key_here
# Qdrant vector database
QDRANT_URL=http://localhost:6333
QDRANT_API_KEY=your_qdrant_api_key_here # optional for local
# Langfuse tracing (optional)
LANGFUSE_PUBLIC_KEY=your_langfuse_public_key
LANGFUSE_SECRET_KEY=your_langfuse_secret_key
LANGFUSE_HOST=https://cloud.langfuse.com
See .env.example for the full template.
# Ingest a single PDF
python -m urban_rag.cli ingest ./path/to/document.pdf
# Ingest all PDFs in a directory
python -m urban_rag.cli ingest ./pdfs/
# Corpus management
python -m urban_rag.cli corpus list
python -m urban_rag.cli corpus stats
# Streaming query via CLI
python -m urban_rag.cli query "What is FSI for residential zones?"
# Retrieve-only mode (no generation)
python -m urban_rag.cli query "parking requirements" --retrieve-only
# Control top-k candidates
python -m urban_rag.cli query "open space standards" --top-k 10
# Start the FastAPI gateway (port 3100)
uvicorn urban_rag.api.main:app --host 0.0.0.0 --port 3100
# Start the embed service (port 3102)
uvicorn urban_rag.embed.serve:app --host 0.0.0.0 --port 3102
# Start Qdrant (Docker)
docker run -d --name urban-rag-qdrant -p 3103:6333 qdrant/qdrant
# Start the Next.js web UI (port 3101)
cd web && npm run dev -- --port 3101
# Smoke eval (25 questions, CI gate)
python -m src.eval run --dataset smoke
# Regression eval (106 questions, weekly)
python -m src.eval run --dataset regression
# Run tests
pytest tests/unit/ -v
pytest tests/integration/ -v
ruff check src/urban_rag/
ruff format src/urban_rag/
pyright src/urban_rag/
urban-planning-rag/
βββ src/urban_rag/
β βββ api/ # FastAPI gateway (main.py, /v1/query streaming endpoint)
β βββ embed/ # ColQwen2.5 + GTE-ModernColBERT encoder loaders
β β βββ colqwen.py # Visual embedding model
β β βββ text_encoder.py # Text embedding model
β β βββ serve.py # Embed service (uvicorn, port 3102)
β βββ index/ # Qdrant batch indexers (visual, text, BM25 sparse)
β β βββ batch.py # Visual index (ColQwen2.5 β Qdrant pages_visual)
β β βββ text_index.py # Text index (GTE-ModernColBERT β Qdrant pages_text)
β β βββ sparse.py # Sparse BM25 indexer
β βββ retrieve/ # Query execution
β β βββ visual.py # Visual channel (Qdrant ANN + MaxSim)
β β βββ text.py # Text channel (GTE-ModernColBERT + MaxSim)
β β βββ sparse.py # Sparse BM25 channel (Qdrant native sparse)
β β βββ rerank.py # Gemini 2.5 Flash VLM cross-encoder rerank
β β βββ orchestrator.py # 3-channel RRF fusion + orchestrates retrieval
β βββ generate/ # Gemini streaming generation
β β βββ gemini.py # Streaming client with SSE parsing
β β βββ orchestrator.py # Grounded generation with citations
β β βββ prompts.py # Prompt templates (fast/deep modes)
β βββ ingest/ # PDF parse + render ingest pipeline
β β βββ load.py # PDF validation and hashing
β β βββ parse.py # Docling/Marker markdown extraction
β β βββ classify.py # Per-page DPI classifier (text vs visual)
β β βββ render.py # PDF β PNG at adaptive DPI (100/250)
β β βββ chunk.py # Text chunking with overlap
β β βββ sections.py # Section boundary detection
β βββ eval/ # RAGAS metrics + smoke/regression CI gates
β β βββ metrics/
β β βββ ragas_wrapper.py # Pinned judge model, reproducible scores
β βββ telemetry/ # Observability
β β βββ tracing.py # Langfuse OTel spans
β β βββ metrics.py # Prometheus gauges and histograms
β βββ cli/ # Typer CLI commands (ingest, corpus, query)
β βββ common/ # Settings, types, logging, errors
βββ web/ # Next.js 14 App Router frontend
β βββ app/
β β βββ page.tsx # Main query UI
β βββ lib/
β βββ api.ts # SSE streaming client
βββ eval/
β βββ smoke.jsonl # 25 CI gate questions
β βββ regression.jsonl # 106 regression questions
βββ infra/
β βββ lightning/ # Lightning AI GPU deployment scripts
β βββ grafana/ # Dashboard configs
βββ services.yaml # Service commands manifest (single source of truth)
The following are not yet deployed β pending infrastructure credentials:
infra/lightning/deploy-embed.shQDRANT_URL and QDRANT_API_KEY in .envweb/VERCEL_DEPLOY.mdPlanning documents contain tables, diagrams, flowcharts, and color-coded maps. Traditional OCR destroys spatial layout and visual context. This system embeds entire page images as multi-vector representations, preserving all visual information for retrieval.
@software{choudhary2026urbanrag,
author = {Choudhary, Tanmay},
title = {Urban Planning RAG: Visual Retrieval for Indian Planning Documents},
year = {2026},
url = {https://github.com/tanmayxchoudhary/urban-planning-rag}
}
79 commits
Python
92.7%
TypeScript
6.5%
Visual RAG system for Indian urban planning regulations
Phase 0 Status (2026-06-19): The April hybrid architecture described below (ColQwen2.5 + GTE + BM25 + Qdrant + RRF + Gemini rerank) is not the current live v1 path. It is stale and not used for v1. The real v1 runtime is the Tomoro Modal in-memory MaxSim retriever. See
docs/tomoro_v1_runtime.mdfor the honest current state. Do not overclaim readiness.
A production-grade multimodal retrieval system that indexes planning documents (NBC, URDPFI, SWM Rules) as page images, embeds them with ColQwen2.5 visual encoders, and answers questions via Gemini 2.5 Flash with precise page citations.
Ask questions like:
The system:
Current v1 path: See
docs/tomoro_v1_runtime.md. The detailed hybrid below is the April prototype (not live for v1). v1 is Tomoro 4B visual MaxSim (in-memory on Modal) over the 738-row legacy tensor. No Qdrant/hybrid for v1.
vidore/colqwen2.5-v0.2 β 128-dim multi-vector per patch (ColPali-style late interaction)lightonai/GTE-ModernColBERT-v1 β 768-dim multi-vector per token (ModernBERT late interaction)pages_visual β ColQwen2.5 multi-vector with MAX_SIM comparator + INT8 scalar quantizationpages_text β GTE-ModernColBERT multi-vector (text + BM25 sparse)Query β 3-channel parallel search (visual/text/sparse BM25)
β RRF k=60 fusion (top-20 per channel β top-20 fused)
β Gemini 2.5 Flash VLM rerank (top-20 β top-5)
[k] citation markers/v1/query β streaming query endpoint/v1/healthz β health check/metrics β Prometheus metricsweb/ directory)[k] to view the source page)qdrant_latency_seconds, gemini_cost_usd_total, faithfulness_p50)infra/grafana/)eval/smoke.jsonl β 25 hand-curated questions (CI gate on every PR)eval/regression.jsonl β 106 questions including adversarial probes| Stat | Value |
|---|---|
| Documents indexed | 8 |
| Total pages | 743 PNG renders |
| Corpus ready to scale | No β v1 uses legacy 738-row Tomoro tensor (Phase 0 recovery); April pipeline not ready for visual retrieval |
uv sync
Copy .env.example to .env and fill in required values:
# Google Gemini API (required)
GEMINI_API_KEY=your_gemini_api_key_here
# Qdrant vector database
QDRANT_URL=http://localhost:6333
QDRANT_API_KEY=your_qdrant_api_key_here # optional for local
# Langfuse tracing (optional)
LANGFUSE_PUBLIC_KEY=your_langfuse_public_key
LANGFUSE_SECRET_KEY=your_langfuse_secret_key
LANGFUSE_HOST=https://cloud.langfuse.com
See .env.example for the full template.
# Ingest a single PDF
python -m urban_rag.cli ingest ./path/to/document.pdf
# Ingest all PDFs in a directory
python -m urban_rag.cli ingest ./pdfs/
# Corpus management
python -m urban_rag.cli corpus list
python -m urban_rag.cli corpus stats
# Streaming query via CLI
python -m urban_rag.cli query "What is FSI for residential zones?"
# Retrieve-only mode (no generation)
python -m urban_rag.cli query "parking requirements" --retrieve-only
# Control top-k candidates
python -m urban_rag.cli query "open space standards" --top-k 10
# Start the FastAPI gateway (port 3100)
uvicorn urban_rag.api.main:app --host 0.0.0.0 --port 3100
# Start the embed service (port 3102)
uvicorn urban_rag.embed.serve:app --host 0.0.0.0 --port 3102
# Start Qdrant (Docker)
docker run -d --name urban-rag-qdrant -p 3103:6333 qdrant/qdrant
# Start the Next.js web UI (port 3101)
cd web && npm run dev -- --port 3101
# Smoke eval (25 questions, CI gate)
python -m src.eval run --dataset smoke
# Regression eval (106 questions, weekly)
python -m src.eval run --dataset regression
# Run tests
pytest tests/unit/ -v
pytest tests/integration/ -v
ruff check src/urban_rag/
ruff format src/urban_rag/
pyright src/urban_rag/
urban-planning-rag/
βββ src/urban_rag/
β βββ api/ # FastAPI gateway (main.py, /v1/query streaming endpoint)
β βββ embed/ # ColQwen2.5 + GTE-ModernColBERT encoder loaders
β β βββ colqwen.py # Visual embedding model
β β βββ text_encoder.py # Text embedding model
β β βββ serve.py # Embed service (uvicorn, port 3102)
β βββ index/ # Qdrant batch indexers (visual, text, BM25 sparse)
β β βββ batch.py # Visual index (ColQwen2.5 β Qdrant pages_visual)
β β βββ text_index.py # Text index (GTE-ModernColBERT β Qdrant pages_text)
β β βββ sparse.py # Sparse BM25 indexer
β βββ retrieve/ # Query execution
β β βββ visual.py # Visual channel (Qdrant ANN + MaxSim)
β β βββ text.py # Text channel (GTE-ModernColBERT + MaxSim)
β β βββ sparse.py # Sparse BM25 channel (Qdrant native sparse)
β β βββ rerank.py # Gemini 2.5 Flash VLM cross-encoder rerank
β β βββ orchestrator.py # 3-channel RRF fusion + orchestrates retrieval
β βββ generate/ # Gemini streaming generation
β β βββ gemini.py # Streaming client with SSE parsing
β β βββ orchestrator.py # Grounded generation with citations
β β βββ prompts.py # Prompt templates (fast/deep modes)
β βββ ingest/ # PDF parse + render ingest pipeline
β β βββ load.py # PDF validation and hashing
β β βββ parse.py # Docling/Marker markdown extraction
β β βββ classify.py # Per-page DPI classifier (text vs visual)
β β βββ render.py # PDF β PNG at adaptive DPI (100/250)
β β βββ chunk.py # Text chunking with overlap
β β βββ sections.py # Section boundary detection
β βββ eval/ # RAGAS metrics + smoke/regression CI gates
β β βββ metrics/
β β βββ ragas_wrapper.py # Pinned judge model, reproducible scores
β βββ telemetry/ # Observability
β β βββ tracing.py # Langfuse OTel spans
β β βββ metrics.py # Prometheus gauges and histograms
β βββ cli/ # Typer CLI commands (ingest, corpus, query)
β βββ common/ # Settings, types, logging, errors
βββ web/ # Next.js 14 App Router frontend
β βββ app/
β β βββ page.tsx # Main query UI
β βββ lib/
β βββ api.ts # SSE streaming client
βββ eval/
β βββ smoke.jsonl # 25 CI gate questions
β βββ regression.jsonl # 106 regression questions
βββ infra/
β βββ lightning/ # Lightning AI GPU deployment scripts
β βββ grafana/ # Dashboard configs
βββ services.yaml # Service commands manifest (single source of truth)
The following are not yet deployed β pending infrastructure credentials:
infra/lightning/deploy-embed.shQDRANT_URL and QDRANT_API_KEY in .envweb/VERCEL_DEPLOY.mdPlanning documents contain tables, diagrams, flowcharts, and color-coded maps. Traditional OCR destroys spatial layout and visual context. This system embeds entire page images as multi-vector representations, preserving all visual information for retrieval.
@software{choudhary2026urbanrag,
author = {Choudhary, Tanmay},
title = {Urban Planning RAG: Visual Retrieval for Indian Planning Documents},
year = {2026},
url = {https://github.com/tanmayxchoudhary/urban-planning-rag}
}
79 commits
Python
92.7%
TypeScript
6.5%