Share pages: lazy, cached, our-own-words article summaries

The /a/<id> page now carries an original short summary so it stands on its own,
without republishing the publisher's article:
- summarize.py: transient SSRF-guarded fetch of the article text → local LLM
  writes a 2-4 sentence ORIGINAL summary (our words). Cached in article_summaries
  forever; we store only our summary, never the body. Generated lazily (only for
  shared/viewed articles), de-duped so concurrent hits don't double-generate.
- /a serves cached-or-pending; when pending it shows a calm "summary on its way,
  read at {source}" note and self-polls /api/summary/<id>, swapping the summary
  in the moment it's ready (never blocks the page on the batch-tier LLM).
- Share menu warms generation on open so recipients usually get the rich version.
- Container reaches the arbiter at arbiter:8080 over caddy_web (LLM env added to
  the API container). 124 tests pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
jay
2026-06-03 18:08:40 +00:00
parent 3d9900cdfc
commit 1d71575982
6 changed files with 256 additions and 13 deletions
+9
View File
@@ -196,6 +196,15 @@ CREATE TABLE IF NOT EXISTS user_prefs (
prefs_json TEXT NOT NULL DEFAULT '{}',
updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
);
-- Our OWN short summary of an article (generated on demand, cached forever).
-- We store only our derived summary text — never the publisher's article body.
CREATE TABLE IF NOT EXISTS article_summaries (
article_id INTEGER PRIMARY KEY REFERENCES articles(id) ON DELETE CASCADE,
summary TEXT NOT NULL,
model TEXT,
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
);
"""