extract_mcp / extractor.py
SEUyishu's picture
Upload 15 files
4e8d71a verified
Raw
History Blame Contribute Delete
3.65 kB
"""Local PDF extraction: text, images, and query-aware snippets."""
import os
import re
from pathlib import Path
from typing import Dict, List, Optional
import fitz # PyMuPDF
from config import CFG
from retrieval import build_snippet, retrieve_relevant_passages
def select_relevant_snippet(
text: str,
query: str,
window: Optional[int] = None,
) -> str:
window = window or CFG.max_chars_per_paper
if not text:
return ""
if not query or len(text) <= window:
return text[:window]
passages = retrieve_relevant_passages(text, query, top_k=CFG.retrieval_top_k)
snippet = build_snippet(passages, max_chars=window)
return snippet or text[:window]
def extract_pdf(
pdf_path: str,
query: str = "",
*,
analyze_images: Optional[bool] = None,
image_query: str = "",
) -> Dict:
"""Extract text, selected images, and a query-aware snippet from a PDF."""
doc = fitz.open(pdf_path)
text_parts: List[str] = []
images: List[Dict] = []
img_dir = os.path.join(CFG.cache_dir, "images", Path(pdf_path).stem)
if CFG.extract_images:
Path(img_dir).mkdir(parents=True, exist_ok=True)
img_count = 0
try:
for page_index, page in enumerate(doc):
text_parts.append(page.get_text("text") or "")
if not CFG.extract_images or img_count >= CFG.max_images_per_paper:
continue
for image_index, image in enumerate(page.get_images(full=True)):
if img_count >= CFG.max_images_per_paper:
break
try:
pix = fitz.Pixmap(doc, image[0])
if pix.width < 80 or pix.height < 80:
pix = None
continue
if pix.n - pix.alpha > 3:
pix = fitz.Pixmap(fitz.csRGB, pix)
out_path = os.path.join(
img_dir,
f"p{page_index}_{image_index}.png",
)
pix.save(out_path)
images.append(
{
"page": page_index,
"path": out_path,
"w": pix.width,
"h": pix.height,
}
)
img_count += 1
pix = None
except Exception as exc: # noqa: BLE001 - skip one bad image
print(f"[extractor] img skip p{page_index} #{image_index}: {exc}")
finally:
doc.close()
full_text = "\n".join(text_parts)
retrieval_passages = (
retrieve_relevant_passages(full_text, query, top_k=CFG.retrieval_top_k)
if query
else []
)
snippet = (
build_snippet(retrieval_passages, max_chars=CFG.max_chars_per_paper)
if retrieval_passages
else select_relevant_snippet(full_text, query)
)
do_analyze_images = CFG.analyze_images_default if analyze_images is None else analyze_images
image_analyses = []
if do_analyze_images and images:
from image_understanding import analyze_images as _analyze_images
image_analyses = _analyze_images(images, prompt=image_query or query)
return {
"ok": True,
"pdf_path": pdf_path,
"full_text": full_text,
"full_text_len": len(full_text),
"snippet": snippet,
"retrieval_method": CFG.retrieval_method,
"retrieval_passages": retrieval_passages,
"images": images,
"image_analyses": image_analyses,
}