Download full text of articles #23
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Problem
Article content comes from whatever the RSS feed includes, which is often only a summary — the reader rarely shows the complete story.
Goal
Findings
media_extractor._fetch_and_extract_full_articlealready fetches full article HTML for image extraction — that fetch seam is reusable for text.articlesalready store a content column; fetch politeness matters (per-origin limits; skip when the feed already delivered full content).Plan of attack
Design note (added during planning, from discussion with Marvin8)
There are two ways to source the full text, and the choice should be made — deliberately — when this issue is implemented:
htmx consideration: variant 2 is exactly the interaction style htmx is built for (fragment swap, hx-indicator spinner). If we choose lazy loading, implementing this issue is the natural moment to evaluate and decide whether to introduce htmx to yunjin — vendored locally (offline/LAN-safe, like the bundled Atkinson font), real
<form>elements kept as the no-JS fallback, and the AGENTS.md no-JavaScript rule amended deliberately to permit it. If we choose ingest-time fetching, htmx stays off the table for now.Either way the decision and its rationale get recorded in the findings comment when work starts.
Some of the article full texts will probably be in HTML format. This issue should also determine if we can convert the downloaded full text to markdown and display that.
Decisions (from the discussion)
output_format="markdown"(~4 KB markdown from 348 KB HTML). One dependency covers extraction and HTML→markdown. Weight measured: 17 packages / 59 MB site-packages (lxml 12 MB is the biggest; no compilers, all wheels).full_textwhen present.Design
articles.full_text(TEXT, NULL) +articles.full_text_fetched_at. NULL text = no clean extraction or pre-migration article; teaser behaviour unchanged.fetch_feed, after_process_feed_entries, for each newly created article: GET the article URL (existing browser-like UA, timeout), skip non-HTML content types, extract markdown with trafilatura, store.full_text_fetched_atrecords the attempt; failures leavefull_textNULL and are not retried.llm_aggregator._build_article_entryandaggregate_summarizersource fromfull_textfirst (aggregator cap raised to 4000 chars/article for prompt economy — a constant, easy to tune), falling back to feed content.Branch
feat/issue-23-full-textCommits
:heavy_plus_sign: Add trafilatura, mistune and nh3:sparkles: Store article full text during feed ingestion:sparkles: Feed article full text to LLM aggregation and summaries:sparkles: Render stored full text on the article pagePR (after CI-green locally)
Follow-up decision: grouping input reverted to teasers
An A/B comparison (identical 75-article set, fresh
refresh()runs differing only in whetherfull_textfeeds the prompt) showed full-text prompts make grouping slightly more fragmented: 4 co-grouped pairs split (Victorian-election pair; a solar/consumer-energy policy trio) vs 1 merge (two distinct energy podcasts), with no visible grouping gain. Decision: the aggregation prompt reverts to feed teasers; full text stays in the summarizer teasers and article display, where it clearly wins. Implemented in the issue-34 branch (stacked on PR #32).Implemented in PR #32 (merged as
dbf5046): full text fetched and stored as markdown at ingest (trafilatura, SSRF-guarded, images kept);articles.full_text+full_text_fetched_atvia alembic; article page renders it (mistune + nh3 sanitized); summarizer teasers sourced from it. Follow-ups: grouping prompt reverted to teasers after the A/B (see comment above), tracked with the refresh-robustness fix in PR #35.