Summary briefing layer: Today pre-summarized, /a is the canonical read
Make summaries the core reading experience (summary-first, source-forward): - Cycle pre-warms summaries for Today's 7 (idempotent → only new ones hit the LLM). - /api/brief items carry their cached summary; Today cards (hero + tiles) show it inline, so Today reads as a calm briefing. - Card title/image now open the /a summary page (the canonical artifact), with a visible "Full story" link straight to the source on every card (the escape hatch). - /a gains related-grouping chips + a Copy-link/share control. - Tighten the summary prompt: original, factual, no quotations / no close paraphrase. Long tail stays lazy+cached. No article bodies stored. 129 tests pass. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
+4
-1
@@ -243,11 +243,13 @@ class Article(BaseModel):
|
||||
rank: int | None = None # position within a brief, when applicable
|
||||
paywalled: bool = False
|
||||
tags: list[str] = []
|
||||
summary: str | None = None # our own cached summary (present on the brief)
|
||||
|
||||
@classmethod
|
||||
def from_row(cls, row: dict) -> "Article":
|
||||
raw_tags = row.get("tags")
|
||||
return cls(
|
||||
summary=row.get("summary"),
|
||||
id=row["id"],
|
||||
title=row["title"],
|
||||
description=row.get("description"),
|
||||
@@ -693,7 +695,8 @@ def create_app() -> FastAPI:
|
||||
with get_conn() as conn:
|
||||
row = conn.execute(
|
||||
"SELECT a.id, a.title, a.description, a.image_url, a.canonical_url, "
|
||||
"a.duplicate_of, src.name AS source_name, s.reason_text, s.accepted "
|
||||
"a.duplicate_of, src.name AS source_name, s.reason_text, s.accepted, "
|
||||
"(SELECT group_concat(t.tag) FROM article_tags t WHERE t.article_id = a.id) AS tags "
|
||||
"FROM articles a JOIN sources src ON src.id = a.source_id "
|
||||
"LEFT JOIN article_scores s ON s.article_id = a.id WHERE a.id = ?",
|
||||
(aid,),
|
||||
|
||||
Reference in New Issue
Block a user