Spaces:
Sleeping
Sleeping
Create combined_pipeline.py
Browse files- combined_pipeline.py +70 -0
combined_pipeline.py
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# combined_pipeline.py
|
| 2 |
+
import os, subprocess
|
| 3 |
+
from sentence_transformers import CrossEncoder
|
| 4 |
+
import fitz
|
| 5 |
+
import pandas as pd
|
| 6 |
+
from langchain_community.embeddings import HuggingFaceEmbeddings
|
| 7 |
+
from langchain.text_splitter import RecursiveCharacterTextSplitter
|
| 8 |
+
from langchain_community.vectorstores import Chroma
|
| 9 |
+
from langchain.docstore.document import Document
|
| 10 |
+
|
| 11 |
+
def extract_styled_blocks(pdf_path):
|
| 12 |
+
doc = fitz.open(pdf_path)
|
| 13 |
+
pages = []
|
| 14 |
+
for page_num, page in enumerate(doc):
|
| 15 |
+
blocks = page.get_text("dict")["blocks"]
|
| 16 |
+
for block in blocks:
|
| 17 |
+
if "lines" in block:
|
| 18 |
+
for line in block["lines"]:
|
| 19 |
+
for span in line["spans"]:
|
| 20 |
+
pages.append({
|
| 21 |
+
"text": span["text"].strip(),
|
| 22 |
+
"is_bold": "Bold" in span["font"],
|
| 23 |
+
"is_italic": "Italic" in span["font"],
|
| 24 |
+
"page": page_num + 1
|
| 25 |
+
})
|
| 26 |
+
return pages
|
| 27 |
+
|
| 28 |
+
def parse_pdf_with_headings(pdf_path):
|
| 29 |
+
spans = extract_styled_blocks(pdf_path)
|
| 30 |
+
docs, cur_h, cur_s = [], "No heading", None
|
| 31 |
+
for span in spans:
|
| 32 |
+
t = span["text"]
|
| 33 |
+
if not t:
|
| 34 |
+
continue
|
| 35 |
+
if span["is_bold"] and len(t) > 3:
|
| 36 |
+
cur_h, cur_s = t, None
|
| 37 |
+
elif span["is_italic"] and len(t) > 3:
|
| 38 |
+
cur_s = t
|
| 39 |
+
docs.append(Document(page_content=t, metadata={"page": span["page"], "heading": cur_h, "subheading": cur_s}))
|
| 40 |
+
return docs
|
| 41 |
+
|
| 42 |
+
def run_rag(pdf_path, df_items):
|
| 43 |
+
raw = parse_pdf_with_headings(pdf_path)
|
| 44 |
+
splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=100)
|
| 45 |
+
docs = splitter.split_documents(raw)
|
| 46 |
+
|
| 47 |
+
embedder = HuggingFaceEmbeddings(model_name="nomic-ai/nomic-embed-text-v1", model_kwargs={"trust_remote_code": True})
|
| 48 |
+
vectordb = Chroma.from_documents(documents=docs, embedding=embedder, persist_directory="/tmp/chroma_db")
|
| 49 |
+
|
| 50 |
+
reranker = CrossEncoder("cross-encoder/ms-marco-MiniLM-L-6-v2")
|
| 51 |
+
results = []
|
| 52 |
+
for idx, row in df_items.iterrows():
|
| 53 |
+
query = row["item"]
|
| 54 |
+
tops = vectordb.similarity_search_with_score(query, k=10)
|
| 55 |
+
inps = [[query, d.page_content] for d, _ in tops]
|
| 56 |
+
scores = reranker.predict(inps)
|
| 57 |
+
ranked = sorted(zip(scores, tops), key=lambda x: x[0], reverse=True)[:3]
|
| 58 |
+
pages = [d.metadata["page"] for _, (d, _) in ranked]
|
| 59 |
+
headings = [d.metadata["heading"] for _, (d, _) in ranked]
|
| 60 |
+
subs = [d.metadata.get("subheading") or "" for _, (d, _) in ranked]
|
| 61 |
+
results.append({
|
| 62 |
+
"item_title": row["item_title"],
|
| 63 |
+
**{f"sim_{i+1}": pages[i] for i in range(3)},
|
| 64 |
+
**{f"heading_{i+1}": headings[i] for i in range(3)},
|
| 65 |
+
**{f"subheading_{i+1}": subs[i] for i in range(3)},
|
| 66 |
+
**{f"rerank_score_{i+1}": round(ranked[i][0], 2) for i in range(3)}
|
| 67 |
+
})
|
| 68 |
+
|
| 69 |
+
vectordb.delete_collection()
|
| 70 |
+
return pd.DataFrame(results)
|