repair-guy / pipelines /visual_ingest.py
airayven7's picture
Sync from GitHub b4f1871
2ad630b verified
Raw
History Blame Contribute Delete
2.26 kB
"""Visual ingest pipeline: PDF -> page images -> ColEmbed embeddings -> store.
Pages are embedded in chunks of EMBED_PAGES_PER_CALL so each GPU call stays
short; progress events are yielded between chunks so callers can stream them.
"""
from __future__ import annotations
import os
from collections.abc import Iterator
from core.constants import EMBED_PAGES_PER_CALL, RENDER_DPI
from core.pdf import page_count, render_pages
from core.store import slugify
from core.visual_store import VisualStore
from models.colembed import ColEmbed
class VisualIngestPipeline:
def __init__(self, embedder: ColEmbed, store: VisualStore):
self.embedder = embedder
self.store = store
def run(
self, pdf_path: str | None, doc_name: str = "", max_pages: int | None = None
) -> Iterator[tuple]:
"""Index one PDF. Generator yielding ("progress", pages_done, total)
after each embedded chunk, then ("done", doc summary dict) last.
Re-indexing a manual with the same name overwrites it."""
if not pdf_path:
raise ValueError("Please provide a PDF first.")
if not pdf_path.lower().endswith(".pdf"):
raise ValueError("Only PDFs can be indexed.")
name = doc_name.strip() or (
os.path.splitext(os.path.basename(pdf_path))[0].replace("_", " ")
)
doc_id = slugify(name)
total = page_count(pdf_path)
if max_pages and total > max_pages:
raise ValueError(
f"This PDF has {total} pages — over the {max_pages}-page cap."
)
writer = self.store.create(doc_id, name, pdf_path, RENDER_DPI, self.embedder.MODEL_ID)
try:
for start in range(1, total + 1, EMBED_PAGES_PER_CALL):
nums = list(range(start, min(start + EMBED_PAGES_PER_CALL, total + 1)))
images = render_pages(pdf_path, nums)
for num, emb in zip(nums, self.embedder.embed_pages(images)):
writer.add_page(num, emb)
yield ("progress", nums[-1], total)
writer.finalize()
except BaseException:
writer.abort()
raise
yield ("done", {"doc_id": doc_id, "name": name, "pages": total})