"""Whole-page text for the agent's context (parsed approach). The agent (models/minicpm_agent) is a text model, so "the page the mechanic is viewing" is given to it as text: a page's parsed elements rendered in reading order, with Picture/Table elements replaced by their MiniCPM descriptions (the same descriptions the chunker splices inline, but here we keep the WHOLE page, not just the chunks that matched a query). Source is the parsed store's parsed.json — [{"page": n, "elements": [{"class", "bbox", "text", "description"?}]}]. Pure functions, no model/GPU dependency. """ from __future__ import annotations from core.chunking import FIGURE_CLASS, SKIP_CLASSES, TABLE_CLASS def index_pages(parsed_pages: list[dict]) -> dict[int, list[dict]]: """{page_number: elements} so a turn reads parsed.json once and looks up any page it shows without re-scanning.""" return {pg["page"]: pg.get("elements", []) for pg in parsed_pages} def page_to_text(elements: list[dict]) -> str: """A page's elements as one text block in reading order. Figures become "[Figure: ]" and tables "[Table: ] ", so the agent reads what's on the page (and can name a figure/table to circle) without seeing the image. Headers/footers are dropped.""" lines: list[str] = [] for el in elements: cls = el.get("class", "") if cls in SKIP_CLASSES: continue text = (el.get("text") or "").strip() if cls == FIGURE_CLASS: desc = (el.get("description") or "").strip() if desc: lines.append(f"[Figure: {desc}]") elif cls == TABLE_CLASS: desc = (el.get("description") or "").strip() parts = [] if desc: parts.append(f"[Table: {desc}]") if text: parts.append(text) if parts: lines.append("\n".join(parts)) elif text: lines.append(text) return "\n".join(lines).strip()