2.8 KiB
2.8 KiB
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
app/config.py— addsummary_max_chars: int = 12_000(envBOR_SUMMARY_MAX_CHARS): the cap on document content sent to the lite model in one call.app/rag/summarizer.py(new) —SUMMARY_MODE = "SUMMARY_MODE"— marker constant the E2E mock keys on in the system prompt (same convention asDEFLECT_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 sharedTRUNCATION_MARKER(imported fromapp.rag.retriever).
- system:
async def generate_summary(llm, *, source: str, path: str, content: str) -> str— callsllm.chat([{"role":"system",…},{"role":"user",…}], model=llm.settings.llm_summary_model); validates non-empty after trim (else raiseLLMError— the client already does this, but re-assert defensively); appends the deterministic pointer line:f"\nSource: {source}/{path}"(the pointer is never model-generated).
tests/unit/test_summarizer.py(new) — fake LLM object (duck-typedchat+settings):- prompt: system contains
SUMMARY_MODE; user == full content when under cap; user truncated +TRUNCATION_MARKERwhen over cap (custom and default cap). - generation: returned text = model text + pointer line
Source: <source>/<path>; whitespace model text →LLMError;LLMErrorfrom the client propagates.
- prompt: system contains
- 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_summaryreturns a non-empty summary ending in the deterministic pointer line; all unit tests green.uv run ruff check . && uv run pyrightclean.- No app endpoint change yet (pipeline integration is task 05).