Spaces:
Running
Running
Download scripts/ingest_book.py from Success-dev/venthrive-api: direct link, hf CLI and curl.
- Browser
- Download file 6.48 kB
-
https://huggingface.co/spaces/Success-dev/venthrive-api/resolve/main/scripts/ingest_book.py
- Command line
-
hf download hf://spaces/Success-dev/venthrive-api/scripts/ingest_book.py
-
curl -L -o ingest_book.py https://huggingface.co/spaces/Success-dev/venthrive-api/resolve/main/scripts/ingest_book.py
6.48 kB
| """ | |
| "Built on Rock" — Pinecone Ingestion Script | |
| ────────────────────────────────────────────────────────────────────────────── | |
| Run this ONCE after setting up your Pinecone index to populate it with | |
| content from "Built on Rock" by Iyanuoluwa Joseph Akinnusi. | |
| Prerequisites: | |
| 1. Create a Pinecone index named 'venthrive-bff' with: | |
| - Dimensions: 1024 | |
| - Metric: Cosine | |
| - Cloud: AWS us-east-1 (Serverless, free tier) | |
| 2. Set PINECONE_API_KEY in backend/.env | |
| 3. Ensure the PDF is in the APEX folder (path configured below) | |
| Usage (from the VenThrive/backend/ directory): | |
| python scripts/ingest_book.py | |
| No local ML library needed — embeddings are generated by Pinecone's | |
| hosted multilingual-e5-large model (1024 dimensions). | |
| """ | |
| import os | |
| import sys | |
| import time | |
| # Load backend env | |
| sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) | |
| from dotenv import load_dotenv | |
| load_dotenv(os.path.join(os.path.dirname(os.path.dirname(__file__)), ".env")) | |
| from pinecone import Pinecone | |
| import pypdf | |
| # ─── Config ─────────────────────────────────────────────────────────────────── | |
| # Absolute path — the PDF lives in the sibling APEX project folder | |
| _BASE = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))) | |
| PDF_PATH = os.path.join(_BASE, "APEX", "Built On Rock by Iyanuoluwa Joseph Akinnusi-1.pdf") | |
| CHUNK_WORDS = 400 | |
| OVERLAP_WORDS = 50 | |
| BATCH_SIZE = 90 # Pinecone Inference allows up to 96 inputs per request | |
| EMBED_MODEL = "multilingual-e5-large" | |
| def extract_pdf_text(path: str) -> str: | |
| if not os.path.exists(path): | |
| raise FileNotFoundError( | |
| f"\n[ERR] PDF not found: {path}\n" | |
| "Ensure 'Built On Rock by Iyanuoluwa Joseph Akinnusi-1.pdf' " | |
| "is in the APEX folder." | |
| ) | |
| reader = pypdf.PdfReader(path) | |
| return "\n".join(page.extract_text() or "" for page in reader.pages) | |
| def chunk_text(text: str, chunk_words: int, overlap_words: int) -> list[dict]: | |
| words = text.split() | |
| chunks = [] | |
| i = 0 | |
| while i < len(words): | |
| chunk_text_str = " ".join(words[i : i + chunk_words]) | |
| chunks.append({ | |
| "id": f"chunk_{len(chunks):04d}", | |
| "text": chunk_text_str, | |
| "meta": { | |
| "source": "built_on_rock", | |
| "chunk_id": len(chunks), | |
| "word_start": i, | |
| "word_end": min(i + chunk_words, len(words)), | |
| }, | |
| }) | |
| i += chunk_words - overlap_words | |
| return chunks | |
| def main(): | |
| api_key = os.environ.get("PINECONE_API_KEY", "") | |
| index_name = os.environ.get("PINECONE_INDEX_NAME", "venthrive-bff") | |
| if not api_key: | |
| print("[ERR] PINECONE_API_KEY not set in backend/.env") | |
| return | |
| # ── Extract text ────────────────────────────────────────────────────────── | |
| print(f"[PDF] Reading: {os.path.basename(PDF_PATH)}") | |
| text = extract_pdf_text(PDF_PATH) | |
| words = len(text.split()) | |
| print(f" {words:,} words extracted from PDF") | |
| # ── Chunk ───────────────────────────────────────────────────────────────── | |
| chunks = chunk_text(text, CHUNK_WORDS, OVERLAP_WORDS) | |
| print(f" {len(chunks)} chunks ({CHUNK_WORDS} words each, {OVERLAP_WORDS} overlap)") | |
| # ── Connect to Pinecone ─────────────────────────────────────────────────── | |
| print(f"\n[PIN] Connecting to Pinecone index '{index_name}'...") | |
| pc = Pinecone(api_key=api_key) | |
| index = pc.Index(index_name) | |
| stats = index.describe_index_stats() | |
| existing = stats.get("total_vector_count", 0) | |
| print(f" Existing vectors: {existing}") | |
| if existing > 0: | |
| print(f" Index already has {existing} vectors.") | |
| ans = input(" Clear and re-ingest? (y/N): ").strip().lower() | |
| if ans == 'y': | |
| index.delete(delete_all=True) | |
| print(" ✓ Cleared") | |
| else: | |
| print(" Skipping ingestion.") | |
| return | |
| # ── Embed + upsert in batches ───────────────────────────────────────────── | |
| print(f"\n[AI] Embedding + uploading {len(chunks)} chunks via Pinecone Inference...") | |
| print(" (Model: multilingual-e5-large, 1024 dims — no local ML needed)") | |
| for i in range(0, len(chunks), BATCH_SIZE): | |
| batch = chunks[i : i + BATCH_SIZE] | |
| texts = [c["text"] for c in batch] | |
| # Generate embeddings via Pinecone Inference API | |
| embedding_result = pc.inference.embed( | |
| model=EMBED_MODEL, | |
| inputs=texts, | |
| parameters={"input_type": "passage", "truncate": "END"}, | |
| ) | |
| embeddings = [e.values for e in embedding_result] | |
| # Upsert vectors | |
| vectors = [ | |
| { | |
| "id": chunk["id"], | |
| "values": embeddings[j], | |
| "metadata": {**chunk["meta"], "text": chunk["text"]}, | |
| } | |
| for j, chunk in enumerate(batch) | |
| ] | |
| index.upsert(vectors=vectors) | |
| done = min(i + BATCH_SIZE, len(chunks)) | |
| print(f" {done}/{len(chunks)} chunks uploaded") | |
| # Small pause to stay within Pinecone rate limits | |
| if done < len(chunks): | |
| time.sleep(0.5) | |
| # ── Done ────────────────────────────────────────────────────────────────── | |
| final = index.describe_index_stats() | |
| total = final.get("total_vector_count", "?") | |
| print(f"\n[OK] Ingestion complete! Index '{index_name}' now has {total} vectors.") | |
| print(" VenThrive AI will now draw from 'Built on Rock' for every relevant query.") | |
| if __name__ == "__main__": | |
| main() | |