5.5 KiB
5.5 KiB
RSSPipe Variant Data & Extensible Processing Pipeline Architecture
1. Overview & Vision
RSSPipe is designed as an RSS-Driven Information Gateway. Subscriptions come in many formats (full-text blogs, brief news tickers, media podcasts, academic papers). Consequently, a one-size-fits-all processing approach fails:
- Some feeds require automatic full-text extraction via Readability.
- Some feeds require LLM keyword and topic tagging.
- Specific high-value posts (filtered by keywords or feeds) require AI summarization, translation, or Text-to-Speech (TTS) audio synthesis.
To support this cleanly without database corruption, performance degradation, or rigid schemas, RSSPipe adopts a Variant Data Architecture paired with a Reactive Chained Trigger Pipeline.
2. Core Data Model
2.1 Separation of Immutable Source vs. Enriched Variants
feed_entries(Immutable Source of Truth):- Stores the exact metadata and content parsed from the RSS XML feed.
- Core fields:
id,feed_id,guid,url,title,author,rss_content,published_at,is_read,is_starred,created_at. - Rule:
rss_contentis NEVER overwritten by scrapers, LLMs, or TTS engines.
entry_variants(Extensible Key-Value Store):-
Stores all downstream enrichments as strongly typed variant entries.
-
Schema:
CREATE TABLE IF NOT EXISTS entry_variants ( entry_id TEXT NOT NULL, variant_key TEXT NOT NULL, content_type TEXT NOT NULL, -- 'text', 'html', 'json', 'yaml', 'audio/mp3', 'binary' text_value TEXT, -- UTF-8 text, HTML, JSON array/object, YAML string blob_path TEXT, -- Relative filesystem path for large media (e.g. data/blobs/{entry_id}/{key}.mp3) metadata JSON DEFAULT '{}', -- Stats: model, tokens, duration_ms, byte_size, executed_at created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (entry_id, variant_key), FOREIGN KEY (entry_id) REFERENCES feed_entries(id) ON DELETE CASCADE ); CREATE INDEX IF NOT EXISTS idx_entry_variants_key ON entry_variants(variant_key);
-
3. Supported Variant Keys & Types
| Variant Key | Content Type | Storage Location | Description |
|---|---|---|---|
readability_full_text |
html |
text_value |
Sanitized readability full text extracted from the source web page. |
keywords |
json / yaml |
text_value |
List of extracted keywords (e.g., ["AI", "Go", "Compiler"]). |
tags |
json |
text_value |
Category and topic tags. |
ai_summary |
text / markdown |
text_value |
Bulleted or narrative AI executive summary. |
translation_{lang} |
html / text |
text_value |
Translated content (e.g. translation_zh). |
tts |
audio/mp3 |
blob_path |
Synthesized speech audio file of the article. |
podcast_tts |
audio/mp3 |
blob_path |
Multi-speaker podcast discussion audio file. |
ai_summary_tts |
audio/mp3 |
blob_path |
Audio narration of the AI summary. |
4. Large Binary Blob Strategy
Storing multi-megabyte audio files or binary blobs directly inside SQLite columns degrades query cache efficiency and slows down table scans.
- Text & JSON Variants are stored inline in
entry_variants.text_value. - Audio & Media Blobs are stored on disk under
./data/blobs/{entry_id}/{variant_key}.{ext}. entry_variants.blob_pathstores the relative path.- The API provides a streaming endpoint with HTTP Range support:
GET /api/entries/{id}/variants/{key}/stream.
5. Reactive Chained Trigger & Watch Pipeline
The pipeline operates on typed pub/sub events with depth guards to prevent infinite loops:
graph TD
A[Feed Ingestion / Crawler] -->|Event: on_post_created| Bus[Typed Event Bus]
Bus --> Rules[Rules Engine Evaluator]
Rules -->|Matches Rule 1: Scrape Tech| W1[Worker: Readability Scraper]
W1 -->|Writes variant: readability_full_text| DB[(entry_variants)]
W1 -->|Event: on_variant_updated| Bus
Rules -->|Matches Rule 2: Tag AI Keywords| W2[Worker: Keyword Extractor]
W2 -->|Writes variant: keywords| DB
W2 -->|Event: on_variant_updated| Bus
Bus -->|Matches Rule 3: Watch keywords contains 'AI'| W3[Worker: AI Summary & TTS]
W3 -->|Writes variant: ai_summary & tts blob| DB
5.1 Concrete Chained Pipeline Example
- Trigger 1: New post ingested from Hacker News -> Emits
on_post_created. - Rule 1 (Auto-Enrich Keywords):
- Condition:
feed_id == "hn-feed-id" - Action:
extract_keywords-> Savesentry_variants(key='keywords', text_value='["OpenAI", "LLM", "Agent"]'). - Emits:
on_variant_updated(key='keywords').
- Condition:
- Rule 2 (Watch & Synthesize Audio):
- Condition:
variant.keywords CONTAINS "OpenAI" - Action:
generate_tts-> Synthesizes MP3, saves to./data/blobs/..., saves varianttts. - Emits:
on_variant_updated(key='tts').
- Condition:
6. Reader UX & Content Switcher
In the 3-pane reader interface:
- Default View: Renders
rss_content(original source). - Enrichment Pills: If variants exist on the entry, dynamic tabs/pills appear in the reader header:
[📄 Original RSS][⚡ Readability Full-Text](ifreadability_full_textexists)[💡 AI Summary](ifai_summaryexists)[🎧 Listen Audio](ifttsorpodcast_ttsexists, plays with embedded HTML5 player)
- Switching variants never mutates the underlying article or overwrites database fields.