A conversational AI that helps people find spiritual encouragement and relevant scripture based on their life situations. Built with a modular architecture that supports multiple LLM backends.
- AI-Powered Conversations: Natural dialogue grounded in Biblical text
- Semantic Scripture Search: Find relevant verses based on meaning, not just keywords
- Multilingual Interface: Available in English, Italian, and German with automatic browser language detection
- Configurable LLM Backend: Start with Ollama (local), switch to Claude, OpenRouter, or OpenAI later
- REST API: Ready for mobile app development
- Modern Web Interface: Clean, responsive chat UI
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Frontend (Next.js) β
ββββββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββ
β
ββββββββββββββββββββββββββββΌβββββββββββββββββββββββββββββββββββ
β Backend API (FastAPI) β
β βββββββββββββββ βββββββββββββββ ββββββββββββββββββββββ β
β β LLM Providerβ β Scripture β β Embedding β β
β β (Ollama/ β β Search β β Provider β β
β β Claude/ β β Service β β β β
β β OpenRouter)β β β β β β
β βββββββββββββββ βββββββββββββββ ββββββββββββββββββββββ β
βββββββββ¬ββββββββββββββββββ¬ββββββββββββββββββ¬ββββββββββββββββββ
β β β
βββββββββΌββββββββ βββββββββΌββββββββ βββββββββΌββββββββ
β Ollama β β PostgreSQL β β pgvector β
β (Local LLM) β β (Bible Data) β β (Embeddings) β
βββββββββββββββββ βββββββββββββββββ βββββββββββββββββ
Full guide with every run mode (local, side-by-side dev, local against the production DB + LLMs): docs/LOCAL_DEVELOPMENT.md
- Docker & Docker Compose (v2,
docker compose) - 8GB+ GPU (recommended) or CPU with 16GB+ RAM for Ollama
make docker-up # CPU (make docker-up-gpu for NVIDIA GPU)On first run this automatically:
- creates
.env.localfrom the committed.env.local.exampletemplate, - pulls the Ollama models (
mistral:7b,mxbai-embed-large, 5β10 min), - loads the Bible and generates embeddings via the one-shot
db-initcontainer (docker compose logs -f db-initto follow progress).
docker compose logs -f # watch all services
make docker-down # stop- Web App: http://localhost:3000
- API Docs: http://localhost:8000/docs
- Health Check: http://localhost:8000/health/live
cp .env.production.example .env.production # fill in secrets
make az-pg-add-ip # allow your IP on the Azure PG firewall
make docker-up-local-prod # local containers -> prod DB + OpenRouter/Azure OpenAISee docs/LOCAL_DEVELOPMENT.md for details, the ACR-backend variant, and troubleshooting.
vox-quieta/
βββ docker-compose.yml # Container orchestration
βββ api/ # FastAPI backend
β βββ main.py # Application entry point
β βββ config.py # Configuration settings
β βββ providers/ # LLM provider abstraction
β β βββ base.py # Provider interface
β β βββ ollama.py # Ollama implementation
β β βββ claude.py # Claude implementation
β β βββ openrouter.py # OpenRouter implementation
β β βββ factory.py # Provider factory
β βββ scripture/ # Bible data layer
β β βββ models.py # Database models
β β βββ database.py # DB connection
β β βββ repository.py # Data queries
β β βββ search.py # Semantic search
β βββ chat/ # Chat logic
β β βββ service.py # Chat orchestration
β β βββ prompts.py # System prompts
β βββ routes/ # API endpoints
βββ frontend/ # Next.js web app
β βββ src/
β βββ app/ # Pages
β βββ components/ # UI components
β βββ lib/ # API client
βββ scripts/ # Utility scripts
β βββ load_bible.py # Load Bible data
β βββ create_embeddings.py # Generate vectors
βββ data/ # Local data storage
| Variable | Default | Description |
|---|---|---|
LLM_PROVIDER |
ollama |
LLM backend: ollama, claude, openrouter, openai |
LLM_MODEL |
llama3:8b (compose default: mistral:7b) |
Model name |
OLLAMA_HOST |
http://localhost:11434 |
Ollama server URL |
EMBEDDING_MODEL |
mxbai-embed-large |
Embedding model (multilingual, 1024 dims) |
DATABASE_URL |
postgresql://... |
PostgreSQL connection |
ANTHROPIC_API_KEY |
- | For Claude provider |
OPENAI_API_KEY |
- | For OpenAI provider and OpenAI Moderation (content safety keyword_only/hybrid) |
OPENROUTER_API_KEY |
- | For OpenRouter provider |
These are inlined into the Next.js bundle at build time (e.g.
docker build --build-arg ... or via the CI env).
| Variable | Default | Description |
|---|---|---|
NEXT_PUBLIC_API_URL |
http://localhost:8000 |
Backend API URL. |
NEXT_PUBLIC_TURNSTILE_SITE_KEY |
(unset) | Cloudflare Turnstile site key. See note below. |
NEXT_PUBLIC_CITATION_SPANS_ENABLED |
false |
Render verse links from the server's citations spans (BITB-109) instead of the client-side regex. See note below. |
About NEXT_PUBLIC_TURNSTILE_SITE_KEY: When set to a real site key,
the frontend skips the runtime GET /config round-trip and starts
loading the Turnstile widget on first paint β closing the brief window
in which a fast first message could race past Turnstile and get bounced
with 403 TURNSTILE_REQUIRED. The site key is public by design and
visible to every browser that loads the widget; only the backend
secret key (used to call Cloudflare's siteverify) must stay
private. Leave the variable unset (or empty) to fall back to the
runtime /config path. To disable Turnstile entirely, set
TURNSTILE_ENABLED=false on the backend β /config will then report
that to the frontend, which will not gate any requests.
About NEXT_PUBLIC_CITATION_SPANS_ENABLED: a build-time kill switch
for BITB-109. The backend already sends the citations field on every
completion event unconditionally, so this is purely a client-side
toggle β no backend coordination needed. Off (the default) or unset, the
web client ignores citations and always linkifies verse references with
its own regex, exactly as before. Set to true to render links from the
server's offsets instead, falling back to the regex per-message wherever
citations doesn't (validly) cover the text β see
frontend/src/lib/citationSpans.ts.
# In docker-compose.yml or .env
LLM_PROVIDER=claude
LLM_MODEL=claude-sonnet-4-20250514
ANTHROPIC_API_KEY=your-api-key-hereOpenRouter provides access to various LLMs including free models. Get your API key at openrouter.ai/keys.
# In docker-compose.yml or .env
LLM_PROVIDER=openrouter
LLM_MODEL=meta-llama/llama-3.3-70b-instruct # or google/gemma-4-31b-it
OPENROUTER_API_KEY=sk-or-v1-...
EMBEDDING_PROVIDER=ollama # OpenRouter doesn't support embeddingsNote: OpenRouter doesn't support embedding generation, so keep EMBEDDING_PROVIDER=ollama for semantic search to work.
The semantic search feature requires embeddings to be generated for all Bible verses. Currently, only Ollama supports embedding generation in this project. This means:
- OpenRouter requires Ollama - Even when using OpenRouter for chat, you still need Ollama running somewhere for embeddings
- Not fully serverless - Free hosting services (Railway free tier, Render free tier) typically lack resources to run Ollama
- OpenAI embeddings - Not yet implemented (would enable fully serverless deployment but costs money)
Best for: Production deployment with moderate budget
Requirements:
- Paid hosting service with GPU or 16GB+ RAM (Railway Pro, Render standard, AWS EC2, etc.)
- Separate Ollama instance running 24/7 for embeddings
Setup:
# Deploy API to serverless platform (Railway, Render, etc.)
LLM_PROVIDER=openrouter
OPENROUTER_API_KEY=sk-or-v1-...
EMBEDDING_PROVIDER=ollama
OLLAMA_HOST=https://your-ollama-instance.com # Hosted Ollama endpoint
# Separate Ollama deployment (Railway Pro, EC2, etc.)
# Must run: mxbai-embed-large model (multilingual, 1024 dimensions)Pros: Free LLM calls, fast response times Cons: Still requires hosting Ollama (~$10-20/month minimum)
Best for: Fully static deployment, lowest ongoing cost
Requirements:
- One-time embedding generation (run locally or on temporary cloud instance)
- Database with pre-generated embeddings
- OpenRouter for LLM only
Setup:
-
Generate embeddings locally using Ollama:
# Run once locally or on temp cloud instance docker compose up -d python scripts/load_bible.py python scripts/create_embeddings.py # Takes 30-60 minutes
-
Export database with embeddings:
pg_dump bibledb > bible_with_embeddings.sql -
Deploy to cloud database (Neon, Supabase, etc.) and API platform:
# Import embeddings to cloud database psql $DATABASE_URL < bible_with_embeddings.sql # Deploy API with OpenRouter LLM_PROVIDER=openrouter OPENROUTER_API_KEY=sk-or-v1-... EMBEDDING_PROVIDER=ollama # Keep this for code compatibility OLLAMA_HOST=http://localhost:11434 # Won't be used for new embeddings
Pros: No ongoing Ollama hosting costs, fully serverless API Cons: Complex setup, can't generate new embeddings without Ollama, requires re-deployment for Bible data updates
Best for: Local development, self-hosting, privacy-focused deployments
Requirements:
- Server/computer with GPU or 16GB+ RAM
- Docker support
Setup:
# Use docker-compose.yml as-is
docker compose up -d
# All services run locally
LLM_PROVIDER=ollama # or openrouter if you prefer
EMBEDDING_PROVIDER=ollamaPros: Full control, privacy, no API costs, can regenerate embeddings anytime Cons: Requires adequate hardware, higher resource usage
- Development: Option C (full local Ollama)
- Production (budget): Option B (pre-generated embeddings + OpenRouter)
- Production (best UX): Option A (hosted Ollama + OpenRouter) or full Ollama on adequate hardware
The frontend supports multiple languages using next-intl.
Supported Languages:
| Code | Language | URL |
|---|---|---|
en |
English (default) | /en |
it |
Italian | /it |
de |
German | /de |
How it works:
- All routes are locale-prefixed (e.g.,
/en/,/it/,/de/) - Visiting
/automatically redirects to the best locale based on your browser's language settings - Users can switch languages at any time using the language selector in the header
- Translation files are in
frontend/messages/(one JSON file per locale)
-
Copy
frontend/messages/en.jsontofrontend/messages/{locale}.jsonand translate all values -
Add the locale to
frontend/src/i18n/routing.ts:locales: ["en", "it", "de", "fr"], // add new locale
-
Add the locale label to
frontend/src/components/LanguageSwitcher.tsx -
Add
hreflanginfrontend/src/app/[locale]/layout.tsxgenerateMetadata() -
Run
npx vitest runβ tests automatically verify key consistency across all locales
Send a message and receive a Bible-grounded response.
{
"message": "I'm feeling anxious about my future",
"conversation_history": [],
"include_search": true
}Stream a response in real-time (Server-Sent Events).
Semantic search for relevant verses.
Get a specific verse.
Get all verses in a chapter.
These endpoints are operational diagnostics and are not part of the public API schema.
They require the X-Monitor-Probe-Secret header.
Returns per-translation verse and embedding counts plus unusable language mappings
(unusable_languages) for supported UI languages whose translation data has zero
verses or zero embeddings.
# Terminal 1: Start PostgreSQL and Ollama
ollama serve
# Terminal 2: Start API
cd api
pip install -r requirements.txt
uvicorn main:app --reload
# Terminal 3: Start Frontend
cd frontend
npm install
npm run devcd api
pytest- Mobile app (React Native)
- User accounts and saved conversations
- Reading plans integration
- Audio Bible support
- Multiple Bible translations
- Multilingual UI (English, Italian, German)
- Community features (shared verses)
- Refactor SQLAlchemy models to use
Mapped[]type annotations (see docs/TECHNICAL_DEBT.md) - Add Vitest for frontend unit tests (186 tests across 17 suites)
- Add Playwright/Cypress for E2E tests
- Add code coverage reporting
- Mock Ollama in tests for faster execution
Additional documentation is available in the docs/ directory:
- Local Development - Every local run mode (local stack, dev stack, local β prod DB/LLMs)
- Architecture - System architecture and design patterns
- Testing - Testing strategy and guidelines
- Deployment - Deployment options and infrastructure
- How to Enable Content Safety - Step-by-step guide to enable multi-language content safety filter (deployed 2026-03-04, currently disabled)
- How to Read Chat Stage Timings - Find the
per-stage latency breakdown (
chat_stage_timingslog +chat.stage.duration_msmetric) in container logs and Application Insights - GitHub Actions Security - CI/CD security best practices
- Technical Debt - Known issues and improvement roadmap
- Troubleshooting - Common issues and solutions
MIT License - see LICENSE file for details.
This project is licensed under the MIT License, which means you're free to use, modify, and distribute this software. Bible text uses the KJV (public domain).
Contributions welcome! Please read our contributing guidelines first.
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
- Keep commits atomic and well-described
- Follow the existing code style
- Add tests for new features
- Update documentation as needed
- Files to exclude from commits are listed in
.gitignore
The following files and directories are excluded from version control (see .gitignore):
- Environment variables (
.envfiles) - Python virtual environments (
.venv,venv/) - Node modules (
node_modules/) - Build outputs (
dist/,build/,.next/) - Database files (
*.db,*.sqlite) - IDE settings (
.vscode/,.idea/) - Logs and cache files
- OS-specific files (
.DS_Store,Thumbs.db)
"Your word is a lamp for my feet, a light on my path." - Psalm 119:105