""" Curator pass — one LLM call per saved page. Classifies a page as a "lesson" (French class notes/vocab/grammar) or a "resource" (links, apps, book recommendations) and extracts a friendly title, a one-line summary, and — for resource pages — structured links/books for the Resources tab. """ import logging import llm import prompts logger = logging.getLogger(__name__) def _fallback(raw_text: str) -> dict: first_line = raw_text.strip().split("\n", 1)[0].lstrip("#").strip() return { "title": (first_line or "Untitled Lesson")[:80], "summary": "", "page_type": "lesson", "links": [], "books": [], } def curate_page(raw_text: str) -> dict: """Return {title, summary, page_type, links, books} for a page's raw text.""" fallback = _fallback(raw_text) result = llm.chat_json(prompts.CURATOR_SYSTEM, raw_text[:2000], fallback=fallback) title = (result.get("title") or fallback["title"]).strip()[:80] summary = (result.get("summary") or "").strip() page_type = result.get("page_type") if result.get("page_type") in ("lesson", "resource") else "lesson" links, books = [], [] if page_type == "resource": for link in result.get("links") or []: if isinstance(link, dict) and link.get("url"): links.append({ "label": str(link.get("label") or link["url"])[:120], "url": str(link["url"]), }) for book in result.get("books") or []: if isinstance(book, dict) and book.get("title"): books.append({ "title": str(book.get("title"))[:200], "author": str(book.get("author") or "")[:120], "note": str(book.get("note") or "")[:200], }) return { "title": title, "summary": summary, "page_type": page_type, "links": links, "books": books, }