A Next.js web app that records Punjabi Gurbani audio, transcribes it with OpenAI Whisper, embeds the transcript with a multilingual embedding model, and searches stored Gurbani verses in PostgreSQL using pgvector.
-
Install dependencies:
npm install
-
Start the self-hosted embedding service (same
multilingual-e5-largemodel as production; no re-seeding of existing vectors):docker compose up -d embedding
First start downloads the model (~1–2 GB) and can take a few minutes.
-
Create
.env.localfrom.env.exampleand fill in:DATABASE_URL=postgresql://... OPENAI_API_KEY=sk-... EMBEDDING_SERVICE_URL=http://localhost:8100
HF_API_KEYis optional whenEMBEDDING_SERVICE_URLis set. Live search and seeding both use the local service, which produces the same E5query:/passage:vectors already stored in Postgres.Hugging Face fallback only: if you omit
EMBEDDING_SERVICE_URL, setHF_API_KEYfrom huggingface.co/settings/tokens (fine-grained token with Make calls to Inference Providers). -
Enable the database schema:
npm run db:schema
-
Seed Gurbani verses from ShabadOS SQLite or JSON data:
SHABADOS_SQLITE_PATH=./data/shabados.sqlite npm run db:seed
-
Start the app:
npm run dev
For production, npm run start runs startup checks first. It validates required
environment variables, applies scripts/schema.sql, creates the vector
extension and indexes, acquires a Postgres advisory lock, seeds the database if
verses is empty, then starts Next.js.
The app includes safeguards for Gurudwara live use:
- Microphone recordings stop automatically at 45 seconds.
- Audio uploads are capped at 12 MB.
- Search text is limited to one short Gurbani line.
- Whisper and embedding calls use timeouts.
- Embedding requests retry on rate limits and temporary provider failures.
- Public API responses avoid leaking provider/API-key details.
- PostgreSQL pool size and query timeouts are bounded for Railway.
- Startup uses a database lock so multiple instances do not seed at the same time.
The seed script supports:
SHABADOS_SQLITE_PATH: path to a ShabadOS SQLite database.SHABADOS_JSON_PATH: path to a JSON array of verse/line objects.SHABADOS_DOWNLOAD_URL: optional URL to download a SQLite database intodata/shabados.sqlite. If omitted, startup uses the official stable ShabadOS SQLite release:https://github.com/shabados/database/releases/download/4.8.7/database.sqlite.SHABADOS_LINES_QUERY: optional SQL query override for SQLite ingestion.SEED_LIMIT: optional limit for testing a small subset first.
This app is ready for Railway with railway.toml.
Recommended (two services):
- Web service: this repo’s root
Dockerfile— runs Next.js and the E5 embedding sidecar on127.0.0.1:8100(no separate embedding deploy, noEMBEDDING_SERVICE_URLneeded). - PostgreSQL with
pgvector(e.g.pgvector/pgvector:pg16).
Optional third service: deploy embedding-service/ separately only if you want embeddings on another container. Then on the web service:
START_EMBEDDING_SIDECAR=0
EMBEDDING_SERVICE_URL=https://your-embedding-service.up.railway.appUse a full URL with https://. Do not point EMBEDDING_SERVICE_URL at the Next.js app — that causes 404 on /embed.
On the web service:
DATABASE_URL=${{Postgres.DATABASE_URL}}
OPENAI_API_KEY=sk-your-openai-key
EMBEDDING_MODEL=intfloat/multilingual-e5-large
PG_POOL_MAX=5For the all-in-one Dockerfile, omit EMBEDDING_SERVICE_URL (or set EMBEDDING_SERVICE_URL=sidecar). Do not set it to the web app’s public domain.
HF_API_KEY is not required when the embedding sidecar or EMBEDDING_SERVICE_URL is used. Existing verse embeddings stay valid — no re-seed.
Hugging Face fallback (optional): unset EMBEDDING_SERVICE_URL, set START_EMBEDDING_SIDECAR=0, and set HF_API_KEY.
You can omit SHABADOS_DOWNLOAD_URL to use the default official stable ShabadOS SQLite release:
https://github.com/shabados/database/releases/download/4.8.7/database.sqliteOptional seed variables:
SHABADOS_SQLITE_PATH=./data/shabados.sqlite
SHABADOS_JSON_PATH=./data/sggs-lines.json
SEED_LIMIT=100Railway will use:
npm install && npm run build
npm run startOn startup, the app automatically:
- Validates
DATABASE_URL,OPENAI_API_KEY, and eitherEMBEDDING_SERVICE_URLorHF_API_KEY. - Runs the PostgreSQL schema from
scripts/schema.sql. - Creates
CREATE EXTENSION IF NOT EXISTS vector. - Creates the
versestable and HNSW vector index. - Acquires a Postgres advisory lock to prevent duplicate startup seeding.
- Checks
SELECT COUNT(*) FROM verses. - Seeds Gurbani data only when the table is empty, using your configured seed source or the default official ShabadOS SQLite URL.
- Starts Next.js only after the database is ready.
The health check is:
/api/healthFor a first test, set SEED_LIMIT so Railway only embeds a small set:
SEED_LIMIT=100When the small test works, remove SEED_LIMIT, clear/recreate the verses
table, and redeploy to seed the full Gurbani dataset.