"""Knowledge document parsing for supported upload formats.""" from __future__ import annotations from pathlib import Path from xml.etree import ElementTree from zipfile import BadZipFile, ZipFile from pypdf import PdfReader SUPPORTED_EXTENSIONS = {".txt", ".md", ".docx", ".pdf"} _WORD_NS = "{http://schemas.openxmlformats.org/wordprocessingml/2006/main}" def parse_document(path: str | Path) -> str: """Return document text for a supported knowledge-base upload.""" document_path = Path(path) suffix = document_path.suffix.lower() if suffix not in SUPPORTED_EXTENSIONS: raise ValueError(f"Unsupported document type: {suffix or ''}") if not document_path.is_file(): raise FileNotFoundError(document_path) if suffix in {".txt", ".md"}: return document_path.read_text(encoding="utf-8-sig") if suffix == ".pdf": return _parse_pdf(document_path) return _parse_docx(document_path) def _parse_docx(path: Path) -> str: try: with ZipFile(path) as archive: xml = archive.read("word/document.xml") root = ElementTree.fromstring(xml) except (BadZipFile, KeyError, ElementTree.ParseError) as exc: raise ValueError(f"Invalid DOCX document: {path}") from exc paragraphs = [] for paragraph in root.iter(f"{_WORD_NS}p"): text = "".join(node.text or "" for node in paragraph.iter(f"{_WORD_NS}t")) if text: paragraphs.append(text) return "\n".join(paragraphs) def _parse_pdf(path: Path) -> str: try: reader = PdfReader(str(path)) return "\n".join(page.extract_text() or "" for page in reader.pages).strip() except Exception as exc: # pypdf raises format-specific exceptions raise ValueError(f"Invalid PDF document: {path}") from exc