LoveJesus's picture
Deploy Topical Bible Search Gradio Space
6b9f67d verified
# For God so loved the world that he gave his only begotten Son,
# that whoever believes in him should not perish but have eternal life. - John 3:16
"""
Gradio Space for Topical Bible Passage Search.
Enter a topic or question, get the most relevant Bible verses ranked by semantic similarity.
"""
import csv
import io
import os
from pathlib import Path
import gradio as gr
import numpy as np
import torch
from sentence_transformers import SentenceTransformer
from sentence_transformers.util import cos_sim
MODEL_ID_CHIRHO = "LoveJesus/biblical-topical-search-chirho"
FALLBACK_MODEL_CHIRHO = "sentence-transformers/all-MiniLM-L6-v2"
KJV_URL_CHIRHO = "https://raw.githubusercontent.com/scrollmapper/bible_databases/master/formats/csv/KJV.csv"
model_chirho = None
bible_verses_chirho = None
bible_embeddings_chirho = None
def load_kjv_bible_chirho() -> list[dict]:
"""Download and parse KJV Bible into list of {ref, text} dicts."""
import urllib.request
print("Downloading KJV Bible for verse lookup...")
response_chirho = urllib.request.urlopen(KJV_URL_CHIRHO)
content_chirho = response_chirho.read().decode("utf-8")
verses_chirho = []
reader_chirho = csv.reader(io.StringIO(content_chirho))
for row_chirho in reader_chirho:
if len(row_chirho) < 4:
continue
book_chirho = row_chirho[0].strip().strip('"')
chapter_chirho = row_chirho[1].strip()
verse_num_chirho = row_chirho[2].strip()
text_chirho = row_chirho[3].strip().strip('"')
# Skip header
if book_chirho == "Book" or not chapter_chirho.isdigit():
continue
if len(text_chirho) < 10:
continue
ref_chirho = f"{book_chirho} {chapter_chirho}:{verse_num_chirho}"
verses_chirho.append({
"ref_chirho": ref_chirho,
"text_chirho": text_chirho,
})
print(f" Loaded {len(verses_chirho)} verses")
return verses_chirho
def initialize_chirho():
"""Load model and Bible data on first use."""
global model_chirho, bible_verses_chirho, bible_embeddings_chirho
if model_chirho is not None and bible_embeddings_chirho is not None:
return
# Detect device
device_chirho = "cpu"
if torch.cuda.is_available():
device_chirho = "cuda"
elif hasattr(torch.backends, "mps") and torch.backends.mps.is_available():
device_chirho = "mps"
# Load model (try fine-tuned first, fall back to base)
if model_chirho is None:
try:
print(f"Loading model: {MODEL_ID_CHIRHO}")
model_chirho = SentenceTransformer(MODEL_ID_CHIRHO, device=device_chirho)
except Exception as error_chirho:
print(f"Fine-tuned model not available ({error_chirho}), using base model")
model_chirho = SentenceTransformer(FALLBACK_MODEL_CHIRHO, device=device_chirho)
# Load Bible
if bible_verses_chirho is None:
bible_verses_chirho = load_kjv_bible_chirho()
# Pre-compute embeddings for all Bible verses
if bible_embeddings_chirho is None:
print("Computing verse embeddings (this may take a minute)...")
verse_texts_chirho = [v_chirho["text_chirho"] for v_chirho in bible_verses_chirho]
bible_embeddings_chirho = model_chirho.encode(
verse_texts_chirho,
batch_size=256,
show_progress_bar=True,
convert_to_numpy=True,
)
print(f" Computed {len(bible_embeddings_chirho)} embeddings")
def search_bible_chirho(
query_chirho: str,
top_n_chirho: int = 10,
) -> str:
"""Search the Bible for verses matching the given topic/question."""
if not query_chirho or not query_chirho.strip():
return "Please enter a topic or question to search."
initialize_chirho()
# Encode query
query_embedding_chirho = model_chirho.encode(
[query_chirho], convert_to_numpy=True
)
# Compute similarities
similarities_chirho = cos_sim(query_embedding_chirho, bible_embeddings_chirho)[0]
similarities_np_chirho = similarities_chirho.cpu().numpy() if hasattr(similarities_chirho, "cpu") else np.array(similarities_chirho)
# Get top-N indices
top_indices_chirho = np.argsort(similarities_np_chirho)[::-1][:top_n_chirho]
# Format results
results_lines_chirho = []
results_lines_chirho.append(f"Top {top_n_chirho} results for: \"{query_chirho}\"\n")
results_lines_chirho.append("=" * 60)
for rank_chirho, idx_chirho in enumerate(top_indices_chirho):
verse_chirho = bible_verses_chirho[idx_chirho]
score_chirho = similarities_np_chirho[idx_chirho]
ref_chirho = verse_chirho["ref_chirho"]
text_chirho = verse_chirho["text_chirho"]
results_lines_chirho.append(
f"\n#{rank_chirho + 1} [{ref_chirho}] (similarity: {score_chirho:.4f})"
)
results_lines_chirho.append(f" {text_chirho}")
results_lines_chirho.append("\n" + "=" * 60)
results_lines_chirho.append(
"Model: all-MiniLM-L6-v2 fine-tuned on Nave's Topical Bible + TSK Cross-References"
)
return "\n".join(results_lines_chirho)
# ============================================================
# Gradio Interface
# ============================================================
EXAMPLES_CHIRHO = [
["What does the Bible say about anxiety?"],
["verses about love"],
["passages about forgiveness"],
["creation of the world"],
["prayer and intercession"],
["salvation through faith"],
["suffering and endurance"],
["wisdom and knowledge"],
["the Holy Spirit"],
["resurrection of Jesus"],
]
with gr.Blocks(
title="Topical Bible Search",
theme=gr.themes.Soft(),
) as demo_chirho:
gr.Markdown(
"""
# Topical Bible Search
*Find Bible verses by topic using semantic search*
Enter a topic, question, or phrase and discover the most relevant
Bible passages. Powered by a sentence transformer fine-tuned on
Nave's Topical Bible and TSK cross-references.
---
*For God so loved the world that he gave his only begotten Son,
that whoever believes in him should not perish but have eternal life.* -- John 3:16
"""
)
with gr.Row():
with gr.Column(scale=3):
query_input_chirho = gr.Textbox(
label="Search Topic or Question",
placeholder="e.g., 'What does the Bible say about anxiety?'",
lines=2,
)
with gr.Column(scale=1):
top_n_input_chirho = gr.Slider(
minimum=5,
maximum=25,
value=10,
step=1,
label="Number of Results",
)
search_btn_chirho = gr.Button("Search", variant="primary", size="lg")
results_output_chirho = gr.Textbox(
label="Search Results",
lines=25,
max_lines=50,
)
search_btn_chirho.click(
search_bible_chirho,
inputs=[query_input_chirho, top_n_input_chirho],
outputs=results_output_chirho,
)
# Also trigger on Enter key
query_input_chirho.submit(
search_bible_chirho,
inputs=[query_input_chirho, top_n_input_chirho],
outputs=results_output_chirho,
)
gr.Examples(
examples=EXAMPLES_CHIRHO,
inputs=[query_input_chirho],
label="Try these examples:",
)
with gr.Accordion("About this model", open=False):
gr.Markdown(
"""
## Model Details
- **Base**: sentence-transformers/all-MiniLM-L6-v2 (22M params)
- **Training**: MultipleNegativesRankingLoss on ~170K topical pairs
- **Data Sources**:
- Nave's Topical Bible (public domain, 1896) -- topic-to-verse mappings
- Treasury of Scripture Knowledge cross-references (OpenBible.info)
- **Embedding dim**: 384
- **Task**: Semantic topical Bible search
## How it works
The model was fine-tuned on pairs of (topic/question, Bible verse) and
(verse, related verse). Given a natural language query about a biblical
topic, it finds the most semantically relevant passages from the entire
KJV Bible (31,000+ verses).
Unlike keyword search, this model understands meaning:
- "anxiety" matches "casting all your care upon him" (1 Peter 5:7)
- "forgiveness" matches "cleanse us from all unrighteousness" (1 John 1:9)
- "creation" matches "In the beginning God created..." (Genesis 1:1)
## Bible Text
All verse text is from the **King James Version** (public domain).
---
*For God so loved the world that he gave his only begotten Son,
that whoever believes in him should not perish but have eternal life.* -- John 3:16
"""
)
demo_chirho.launch()