Download src/ingestion.py from thieuluan/hackathon: direct link, hf CLI and curl.
- Browser
- Download file 3.02 kB
-
https://huggingface.co/spaces/thieuluan/hackathon/resolve/main/src/ingestion.py
- Command line
-
hf download hf://spaces/thieuluan/hackathon/src/ingestion.py
-
curl -L -o ingestion.py https://huggingface.co/spaces/thieuluan/hackathon/resolve/main/src/ingestion.py
3.02 kB
| import json | |
| import os | |
| import re | |
| from dataclasses import dataclass, asdict | |
| from typing import List, Optional, Tuple | |
| from pypdf import PdfReader | |
| import pdfplumber | |
| class Chunk: | |
| content: str | |
| source: str | |
| page: Optional[int] = None | |
| chapter: Optional[str] = None | |
| def normalize_text(text: str) -> str: | |
| # Remove control characters, normalize whitespace | |
| text = text.encode("utf-8", errors="ignore").decode("utf-8", errors="ignore") | |
| text = re.sub(r"[\x00-\x08\x0B\x0C\x0E-\x1F]", " ", text) | |
| text = re.sub(r"\s+", " ", text).strip() | |
| return text | |
| def extract_text_pypdf(file_path: str) -> List[Tuple[int, str]]: | |
| pages: List[Tuple[int, str]] = [] | |
| reader = PdfReader(file_path) | |
| for i, page in enumerate(reader.pages, start=1): | |
| text = page.extract_text() or "" | |
| pages.append((i, text)) | |
| return pages | |
| def extract_text_pdfplumber(file_path: str) -> List[Tuple[int, str]]: | |
| pages: List[Tuple[int, str]] = [] | |
| with pdfplumber.open(file_path) as pdf: | |
| for i, page in enumerate(pdf.pages, start=1): | |
| text = page.extract_text() or "" | |
| pages.append((i, text)) | |
| return pages | |
| def chunk_text(text: str, chunk_size: int = 4000, overlap: int = 400) -> List[str]: | |
| # Simple recursive-like character splitter | |
| if chunk_size <= 0: | |
| return [text] | |
| chunks: List[str] = [] | |
| start = 0 | |
| n = len(text) | |
| while start < n: | |
| end = min(start + chunk_size, n) | |
| chunk = text[start:end] | |
| chunks.append(chunk) | |
| if end == n: | |
| break | |
| start = max(end - overlap, start + 1) | |
| return chunks | |
| def make_chunks(pages: List[Tuple[int, str]], source: str, chunk_size: int, overlap: int) -> List[Chunk]: | |
| all_text = [] | |
| for page_num, raw in pages: | |
| norm = normalize_text(raw) | |
| if norm: | |
| all_text.append((page_num, norm)) | |
| merged = "\n".join(t for _, t in all_text) | |
| pieces = chunk_text(merged, chunk_size=chunk_size, overlap=overlap) | |
| chunks = [Chunk(content=p, source=source) for p in pieces if p.strip()] | |
| return chunks | |
| def persist_chunks(session_id: str, chunks: List[Chunk]) -> str: | |
| base = os.path.join(".tmp", "ingestion") | |
| os.makedirs(base, exist_ok=True) | |
| path = os.path.join(base, f"{session_id}.json") | |
| with open(path, "w", encoding="utf-8") as f: | |
| json.dump([asdict(c) for c in chunks], f, ensure_ascii=False, indent=2) | |
| return path | |
| def ingest_pdf( | |
| file_path: str, | |
| session_id: str, | |
| parser: str = "pypdf", | |
| chunk_size: int = 4000, | |
| overlap: int = 400, | |
| ) -> Tuple[List[Chunk], str]: | |
| if parser == "pypdf": | |
| pages = extract_text_pypdf(file_path) | |
| elif parser == "pdfplumber": | |
| pages = extract_text_pdfplumber(file_path) | |
| else: | |
| raise ValueError("Unknown parser: " + parser) | |
| chunks = make_chunks(pages, source=os.path.basename(file_path), chunk_size=chunk_size, overlap=overlap) | |
| out = persist_chunks(session_id, chunks) | |
| return chunks, out | |