| |
| """Ingest research papers from /root/backend/data/papers/ into RAG forensic_reports.""" |
|
|
| import hashlib |
| import json |
| import os |
|
|
| import httpx |
|
|
| PAPERS_DIR = "/root/backend/data/papers" |
| METADATA_FILE = f"{PAPERS_DIR}/papers_metadata.json" |
| RAG_API = "http://localhost:8000/api/v1/rag/ingest" |
| CHUNK_SIZE = 2500 |
| CHUNK_OVERLAP = 300 |
|
|
|
|
| def main(): |
| with open(METADATA_FILE) as f: |
| metadata = json.load(f) |
|
|
| pdfs = sorted([f for f in os.listdir(PAPERS_DIR) if f.endswith(".pdf")]) |
| print(f"Found {len(pdfs)} PDFs, {len(metadata)} metadata entries") |
|
|
| ingested = 0 |
| chunks_total = 0 |
| errors = 0 |
|
|
| for pdf_file in pdfs: |
| arxiv_id = pdf_file.replace(".pdf", "") |
| meta = metadata.get(arxiv_id, {}) |
|
|
| |
| try: |
| import fitz |
|
|
| doc = fitz.open(os.path.join(PAPERS_DIR, pdf_file)) |
| text = "" |
| for page in doc: |
| text += page.get_text() + "\n" |
| doc.close() |
| except Exception as e: |
| print(f" SKIP {arxiv_id}: extract failed: {e}") |
| errors += 1 |
| continue |
|
|
| if len(text.strip()) < 200: |
| print(f" SKIP {arxiv_id}: too short ({len(text)} chars)") |
| errors += 1 |
| continue |
|
|
| |
| chunks = [] |
| start = 0 |
| while start < len(text): |
| end = min(start + CHUNK_SIZE, len(text)) |
| chunk_text = text[start:end].strip() |
| if len(chunk_text) > 100: |
| chunks.append(chunk_text) |
| start += CHUNK_SIZE - CHUNK_OVERLAP |
|
|
| |
| title = meta.get("title", arxiv_id) |
| attack_types = meta.get("attack_types", []) |
| authors_list = meta.get("authors", []) |
| collection = meta.get("collection", "forensic_reports") |
|
|
| success = 0 |
| for i, chunk in enumerate(chunks): |
| doc_id = hashlib.sha256(f"{arxiv_id}:chunk:{i}".encode()).hexdigest()[:16] |
| payload = { |
| "collection": collection, |
| "content": chunk, |
| "metadata": { |
| "arxiv_id": arxiv_id, |
| "title": title, |
| "authors": ", ".join(authors_list[:3]) if isinstance(authors_list, list) else str(authors_list), |
| "attack_types": ", ".join(attack_types) if isinstance(attack_types, list) else str(attack_types), |
| "chunk_index": i, |
| "total_chunks": len(chunks), |
| "source": "research_paper", |
| "doc_id": doc_id, |
| }, |
| } |
| try: |
| resp = httpx.post(RAG_API, json=payload, timeout=30) |
| if resp.status_code == 200: |
| success += 1 |
| else: |
| if errors < 5: |
| print(f" ERR {arxiv_id} chunk {i}: {resp.status_code} {resp.text[:100]}") |
| errors += 1 |
| except Exception as e: |
| if errors < 5: |
| print(f" ERR {arxiv_id} chunk {i}: {e}") |
| errors += 1 |
|
|
| chunks_total += success |
| ingested += 1 |
| if arxiv_id not in metadata: |
| metadata[arxiv_id] = {} |
| metadata[arxiv_id]["ingested"] = True |
| metadata[arxiv_id]["chunk_count"] = success |
| print(f" {arxiv_id}: {len(chunks)} chunks, {success} ingested — {title[:50]}") |
|
|
| |
| with open(METADATA_FILE, "w") as f: |
| json.dump(metadata, f, indent=2) |
|
|
| print(f"\nDONE: {ingested} papers, {chunks_total} total chunks, {errors} errors") |
|
|
|
|
| if __name__ == "__main__": |
| main() |
|
|