bgp-rag-groq / ingest.py
VP21's picture
BGP RAG cloud chatbot β€” initial commit
491f25a
Raw
History Blame Contribute Delete
4.79 kB
"""
ingest.py β€” Build FAISS vector index from BGP documentation.
Run this ONCE locally before deploying to HuggingFace Spaces:
python ingest.py
Output: faiss_index/index.faiss + faiss_index/index.pkl
Commit both files to your repo β€” the Space loads them at startup.
WHY PRE-BUILD?
- Building the index requires downloading ~90MB sentence-transformers model
and embedding every chunk. On HF free CPU tier this takes 3-5 minutes
and risks OOM errors. Pre-building means the Space starts in ~3 seconds.
- Pattern: compute expensive artifacts locally, ship the result.
"""
import os
from pathlib import Path
from langchain_community.document_loaders import TextLoader, DirectoryLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_huggingface import HuggingFaceEmbeddings
from langchain_community.vectorstores import FAISS
# ── Configuration ────────────────────────────────────────────────────────────
DATA_DIRS = ["data/docs", "data/telemetry"]
INDEX_DIR = "faiss_index"
EMBEDDING_MODEL = "sentence-transformers/all-MiniLM-L6-v2"
CHUNK_SIZE = 500
CHUNK_OVERLAP = 50
# ── Load documents ────────────────────────────────────────────────────────────
def load_documents(data_dirs: list[str]) -> list:
"""Load all .txt files from the given directories."""
all_docs = []
for data_dir in data_dirs:
if not os.path.exists(data_dir):
print(f" ⚠️ Directory not found, skipping: {data_dir}")
continue
loader = DirectoryLoader(
data_dir,
glob="**/*.txt",
loader_cls=TextLoader,
loader_kwargs={"encoding": "utf-8"},
show_progress=False,
)
docs = loader.load()
print(f" πŸ“‚ {data_dir}: {len(docs)} file(s) loaded")
all_docs.extend(docs)
return all_docs
# ── Main ──────────────────────────────────────────────────────────────────────
def main():
print("=" * 60)
print("BGP RAG β€” FAISS Index Builder")
print("=" * 60)
# 1. Load raw documents
print("\n[1/4] Loading documents...")
docs = load_documents(DATA_DIRS)
if not docs:
raise RuntimeError("No documents found. Check data/ directory.")
print(f" βœ… Total documents loaded: {len(docs)}")
# 2. Split into chunks
print(f"\n[2/4] Splitting into chunks (size={CHUNK_SIZE}, overlap={CHUNK_OVERLAP})...")
splitter = RecursiveCharacterTextSplitter(
chunk_size=CHUNK_SIZE,
chunk_overlap=CHUNK_OVERLAP,
length_function=len,
separators=["\n\n", "\n", " ", ""],
)
chunks = splitter.split_documents(docs)
print(f" βœ… Chunks created: {len(chunks)}")
print(f" πŸ“Š Avg chunk size: {sum(len(c.page_content) for c in chunks) // len(chunks)} chars")
# 3. Create embeddings
print(f"\n[3/4] Loading embedding model: {EMBEDDING_MODEL}")
print(" (First run downloads ~90MB model β€” subsequent runs use cache)")
embeddings = HuggingFaceEmbeddings(
model_name=EMBEDDING_MODEL,
model_kwargs={"device": "cpu"},
encode_kwargs={"normalize_embeddings": True},
)
print(" βœ… Embedding model loaded")
# 4. Build and save FAISS index
print(f"\n[4/4] Building FAISS index and saving to {INDEX_DIR}/...")
os.makedirs(INDEX_DIR, exist_ok=True)
vectorstore = FAISS.from_documents(chunks, embeddings)
vectorstore.save_local(INDEX_DIR)
# Verify files were written
index_file = Path(INDEX_DIR) / "index.faiss"
pkl_file = Path(INDEX_DIR) / "index.pkl"
if index_file.exists() and pkl_file.exists():
size_mb = (index_file.stat().st_size + pkl_file.stat().st_size) / (1024 * 1024)
print(f" βœ… Saved: {INDEX_DIR}/index.faiss")
print(f" βœ… Saved: {INDEX_DIR}/index.pkl")
print(f" πŸ“¦ Total index size: {size_mb:.2f} MB")
else:
raise RuntimeError("Index files not found after save β€” check permissions.")
print("\n" + "=" * 60)
print("βœ… FAISS index built successfully!")
print(f" Documents: {len(docs)}")
print(f" Chunks: {len(chunks)}")
print(f" Location: {INDEX_DIR}/")
print("\nNext steps:")
print(" 1. git add faiss_index/")
print(" 2. git commit -m 'Add pre-built FAISS index'")
print(" 3. Push to HuggingFace Space")
print("=" * 60)
if __name__ == "__main__":
main()