Files
brain-of-reese/.agent/phases/todo/30_document_summaries/03_summarizer_module.md
T

2.8 KiB
Raw Blame History

Task 03 — app/rag/summarizer.py (prompt + lite call + pointer)

Phase: 30_document_summaries · Source: TODO.md:3 — "we need a small model to analyze non markdown documents and provide a textual summary of those documents with a pointer back to the source" Story: .agent/user_stories/document-summaries.md

Objective

Create the summarizer module: build the lite prompt for one document, call the model (task 01), validate the output, and return the summary text with a code-deterministic pointer line back to the source.

Work

  1. app/config.py — add summary_max_chars: int = 12_000 (env BOR_SUMMARY_MAX_CHARS): the cap on document content sent to the lite model in one call.
  2. app/rag/summarizer.py (new) —
    • SUMMARY_MODE = "SUMMARY_MODE" — marker constant the E2E mock keys on in the system prompt (same convention as DEFLECT_MODE).
    • build_summary_prompt(source: str, path: str, content: str, max_chars: int | None = None) -> tuple[str, str] → (system, user):
      • system: SUMMARY_MODE + instruction — "Write a 3–6 sentence plain-text summary of this document in natural language. Cover what it configures/defines and its most important values. Do not use markdown. Do not invent anything that is not in the document."
      • user: the document content, capped at max_chars (default get_settings().summary_max_chars); on overflow cut at the cap and append the shared TRUNCATION_MARKER (imported from app.rag.retriever).
    • async def generate_summary(llm, *, source: str, path: str, content: str) -> str — calls llm.chat([{"role":"system",…},{"role":"user",…}], model=llm.settings.llm_summary_model); validates non-empty after trim (else raise LLMError — the client already does this, but re-assert defensively); appends the deterministic pointer line: f"\nSource: {source}/{path}" (the pointer is never model-generated).
  3. tests/unit/test_summarizer.py (new) — fake LLM object (duck-typed chat + settings):
    • prompt: system contains SUMMARY_MODE; user == full content when under cap; user truncated + TRUNCATION_MARKER when over cap (custom and default cap).
    • generation: returned text = model text + pointer line Source: <source>/<path>; whitespace model text → LLMError; LLMError from the client propagates.
  • ASSUMPTION: the pointer is the literal line Source: <source>/<path> appended by code (the TODO's "pointer back to the source"); the model is told what to summarize but not to write the pointer.

Testing & Quality

  • Unit: the tests in Work step 3.
  • Coverage: >90% on app/rag/summarizer.py.

Completion Criteria

  • generate_summary returns a non-empty summary ending in the deterministic pointer line; all unit tests green.
  • uv run ruff check . && uv run pyright clean.
  • No app endpoint change yet (pipeline integration is task 05).