Spaces:
Sleeping
Sleeping
| """ | |
| PDF file processor. | |
| Extracts text and tables page by page with pdfplumber. Each page becomes a | |
| '## Page N' markdown section so the chunker can split on page boundaries and | |
| citations reference page numbers. Tables are converted to markdown inline. | |
| The LLM summary (title, document_type, key_points, risks, entities, | |
| tables_found) is produced by a single Groq call after full extraction. | |
| """ | |
| import pdfplumber | |
| from app.processors.base import BaseProcessor | |
| class PDFProcessor(BaseProcessor): | |
| def extract(self) -> str: | |
| parts = [f"# {self.job.filename}"] | |
| with pdfplumber.open(self.job.file_path) as pdf: | |
| for i, page in enumerate(pdf.pages): | |
| parts.append(f"## Page {i + 1}") | |
| page_text = page.extract_text() or "" | |
| if page_text.strip(): | |
| parts.append(page_text.strip()) | |
| tables = page.extract_tables() | |
| for table in tables: | |
| if table: | |
| md_table = self._table_to_markdown(table) | |
| if md_table: | |
| parts.append(md_table) | |
| return "\n\n".join(parts) | |
| def summarise(self, text: str, db) -> dict: | |
| # Truncate for the summary LLM call only; full text goes to chunking | |
| summary_text = text[:12000] if len(text) > 12000 else text | |
| prompt = f"""You are a document analyst. Analyse the following document text and return ONLY valid JSON. | |
| No preamble, no markdown code blocks, just raw JSON. | |
| Return this exact structure: | |
| {{ | |
| "title": "document title or filename", | |
| "document_type": "report|contract|invoice|proposal|other", | |
| "summary": "2-3 sentence summary", | |
| "key_points": ["point 1", "point 2"], | |
| "entities": ["company names, person names, product names mentioned"] | |
| }} | |
| Document text: | |
| {summary_text} | |
| """ | |
| return self._call_gemini_json(prompt, db) | |