Spaces:
Build error
Build error
File size: 6,767 Bytes
0ee953b | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 | """
JAIM - Data Augmentation & Re-indexing Script
Generates 4 paraphrased symptom variants per entry using Groq (Llama 3.3 70B),
embeds everything with BAAI/bge-large-en-v1.5 (1024 dims),
and batch-upserts all vectors into Pinecone.
Run this ONCE before starting the app.
Requires: A Pinecone index with dimension=1024, metric=cosine.
"""
import os
import json
import time
import requests
from dotenv import load_dotenv
from pinecone import Pinecone
from sentence_transformers import SentenceTransformer
from embed_pdf import extract_entries_from_pdf
load_dotenv()
# βββ Config ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
PINECONE_API_KEY = os.getenv("PINECONE_API_KEY")
GROQ_API_KEY = os.getenv("GROQ_API_KEY")
INDEX_NAME = os.getenv("PINECONE_INDEX_NAME", "jaim3")
EMBEDDING_MODEL = "BAAI/bge-large-en-v1.5"
GROQ_MODEL = "llama-3.3-70b-versatile"
PDF_PATH = "db.pdf"
NUM_PARAPHRASES = 4
BATCH_SIZE = 50
# βββ Paraphrase Generation via Groq ββββββββββββββββββββββββββββββββββββββββββ
def generate_paraphrases(symptoms: str) -> list[str]:
"""Use Groq (Llama 3.3 70B) to generate paraphrased symptom variants."""
url = "https://api.groq.com/openai/v1/chat/completions"
headers = {
"Authorization": f"Bearer {GROQ_API_KEY}",
"Content-Type": "application/json",
}
prompt = f"""Generate exactly {NUM_PARAPHRASES} different paraphrased versions of the following plant symptom description.
Each paraphrase should:
- Use different vocabulary and sentence structure
- Preserve the exact medical/botanical meaning
- Sound like how a farmer or gardener would naturally describe the problem
Return ONLY a valid JSON array of {NUM_PARAPHRASES} strings. No extra text, no markdown.
Original symptoms: "{symptoms}"
"""
payload = {
"model": GROQ_MODEL,
"messages": [
{"role": "system", "content": "You paraphrase text. Return only valid JSON arrays."},
{"role": "user", "content": prompt},
],
"temperature": 0.8,
"max_tokens": 512,
}
for attempt in range(3):
resp = requests.post(url, json=payload, headers=headers, timeout=30)
if resp.status_code == 429:
wait = 2 ** (attempt + 1)
print(f" β³ Rate limited, waiting {wait}s...")
time.sleep(wait)
continue
resp.raise_for_status()
break
else:
raise RuntimeError("Groq rate limit exceeded after 3 retries")
content = resp.json()["choices"][0]["message"]["content"].strip()
# Handle potential markdown code-block wrapping
if content.startswith("```"):
content = content.split("\n", 1)[1].rsplit("```", 1)[0].strip()
paraphrases = json.loads(content)
return paraphrases[:NUM_PARAPHRASES]
# βββ Main Pipeline ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
def main():
print("π Loading BAAI/bge-large-en-v1.5 embedding model (1024 dims)...")
model = SentenceTransformer(EMBEDDING_MODEL)
print("π Extracting entries from PDF...")
entries = extract_entries_from_pdf(PDF_PATH)
print(f" Found {len(entries)} entries")
print("π² Connecting to Pinecone...")
pc = Pinecone(api_key=PINECONE_API_KEY)
index = pc.Index(INDEX_NAME)
all_vectors = []
for i, entry in enumerate(entries):
original_id = f"entry_{i}_chunk_0"
symptoms = entry["symptoms"]
print(f"\n{'β' * 60}")
print(f"π Entry {i} | Cause: {entry['cause_given']}")
print(f" Original: {symptoms[:80]}...")
# ββ Generate paraphrases via Groq βββββββββββββββββββββββββ
print(f" π€ Generating {NUM_PARAPHRASES} paraphrases...")
try:
paraphrases = generate_paraphrases(symptoms)
except Exception as e:
print(f" β οΈ Failed to generate paraphrases: {e}")
paraphrases = []
time.sleep(1) # Rate-limit buffer between Groq calls
# ββ Shared metadata (same for original + all variants) ββββ
base_metadata = {
"disorder": entry["disorder"],
"cause_given": entry["cause_given"],
"symptoms": entry["symptoms"],
"cause_elaborated": entry["cause_elaborated"],
"possible_causes": entry["possible_causes"],
"treatment_material": entry["treatment_material"],
}
# ββ Embed original symptoms ββββββββββββββββββββββββββββββ
# BGE spec: documents do NOT need a prefix
original_embedding = model.encode(symptoms).tolist()
all_vectors.append({
"id": original_id,
"values": original_embedding,
"metadata": {
**base_metadata,
"is_augmented": False,
"parent_id": original_id,
}
})
# ββ Embed paraphrased variants βββββββββββββββββββββββββββ
for j, para in enumerate(paraphrases):
aug_embedding = model.encode(para).tolist()
all_vectors.append({
"id": f"entry_{i}_aug_{j}",
"values": aug_embedding,
"metadata": {
**base_metadata,
"symptoms": para, # Store paraphrased version
"is_augmented": True,
"parent_id": original_id,
}
})
print(f" β
Variant {j + 1}: {para[:80]}...")
# ββ Batch upsert to Pinecone ββββββββββββββββββββββββββββββββββ
total = len(all_vectors)
print(f"\nπ€ Uploading {total} vectors to Pinecone in batches of {BATCH_SIZE}...")
for start in range(0, total, BATCH_SIZE):
batch = all_vectors[start : start + BATCH_SIZE]
index.upsert(vectors=batch)
print(f" Batch {start // BATCH_SIZE + 1}: {len(batch)} vectors uploaded")
print("\nβ
Augmentation & re-indexing complete!")
stats = index.describe_index_stats()
print(f"π Index stats: {stats}")
print(f" Expected: {len(entries)} originals + {len(entries) * NUM_PARAPHRASES} augmented = {total} total")
if __name__ == "__main__":
main()
|