README.md (view raw)
1<h1 align="center">llm_aggregator</h1>
2
3<p align="center">
4 <img src="assets/logo.svg" alt="LLM Aggregator logo" width=500>
5 <br>
6 <strong>A CLI tool to aggregate RSS feeds and summarise them with LLMs</strong>
7</p>
8
9
10
11
12---
13
14## What is `llm_aggregator`?
15
16`llm_aggregator` is a command‑line utility that fetches articles from multiple
17RSS feeds, filters and processes the content, and sends it to an LLM through
18OpenAI-compatible API to generate a concise summary or analysis. It’s designed
19for keeping up with news and articles from your favourite sources without
20having to read dozens or hundreds of individual posts.
21
22## How do I use `llm_aggregator`?
23
24 llm_aggregator --feeds-file FEEDS-FILE --prompt PROMPT [OPTIONS]...
25
26By default, `llm_aggregator` reads a list of RSS feed URLs, fetches the
27articles, filters them by date and keywords, and sends a summary request to
28the LLM. The resulting output is printed to the terminal in your chosen format
29(text, markdown, or JSON).
30
31### Basic Options
32
33 -f, --feeds-file FILE Path to file containing RSS feed URLs (one per line)
34 --stdin Read a single RSS/Atom feed from stdin (combinable with -f)
35 -p, --prompt PROMPT User prompt for summarisation/analysis [required]
36 --api-key KEY API key (default: read from $LLM_AGGREGATOR_API_KEY)
37 -m, --model MODEL Model to use (default: deepseek-chat)
38 --base-url URL API base URL (default: https://api.deepseek.com)
39 --max-tokens N Maximum tokens in response (default: 4000)
40 -P, --plain Output only the raw LLM response without formatting or metadata
41 -o, --output FORMAT Output format: text, markdown, or json (default: text)
42 --output-file FILE Write output to FILE instead of STDOUT
43 -t, --tui Enable TUI interface with progress bar
44 -D, --dry-run Validate config, show article statistics, and exit without making LLM API calls
45 -v, --verbose Enable verbose logging
46 -h, --help Show this help message and exit
47 --version Show version information and exit
48
49### Filtering & Processing
50
51 -n, --max-articles-per-feed N Maximum articles to fetch from each feed (default: 10)
52 -d, --max-days-old N Only include articles from the last N days (0 for all) (default: 7)
53 --max-total-articles N Maximum total articles to process (default: 20)
54 -i, --include-keywords LIST Comma-separated list of keywords to include (case‑insensitive)
55 -e, --exclude-keywords LIST Comma-separated list of keywords to exclude (case‑insensitive)
56 --include-articles Include original articles in JSON output
57
58### LLM Configuration
59
60 --temperature VALUE Sampling temperature (0.0 to 1.0) (default: 0.7)
61 --system-prompt TEXT Custom system prompt for LLM
62
63### Examples
64
65```bash
66# Basic usage: summarise tech news from a list of feeds
67llm_aggregator -f feeds.txt -p "What are the latest AI-related trends in free software?"
68
69# Read RSS feed directly from stdin
70curl -s https://example.com/feed.xml | llm_aggregator --stdin -p "Summarise"
71
72# Combine feeds file with stdin feed
73llm_aggregator -f feeds.txt --stdin -p "Summarise all"
74
75# With TUI progress bar
76llm_aggregator -f feeds.txt -p "Summarise tech news" -t
77
78# Output to a JSON file with included articles
79llm_aggregator -f feeds.txt -p "Analyse AI developments" \
80 -o json --output-file analysis.json --include-articles
81
82
83# Filter by keywords (only include articles about Linux or open source)
84llm_aggregator -f feeds.txt -p "Linux news" \
85 -i linux,opensource -d 3
86
87# Use a custom model and higher token limit
88llm_aggregator -f feeds.txt -p "Code analysis" \
89 -m deepseek-reasoner --max-tokens 8000
90
91# Use a custom API endpoint (e.g., local Ollama)
92llm_aggregator -f feeds.txt -p "Summarise news" \
93 --base-url "http://localhost:11434/v1" -m llama3
94
95# Show version information
96llm_aggregator --version
97
98# Show help message
99llm_aggregator --help
100```
101
102## How does `llm_aggregator` work?
103
104`llm_aggregator` performs the following steps for each run:
105
1061. Parse command‑line arguments
1072. Read the feeds file: a plain text file containing one RSS/Atom feed URL per
108 line.
1093. **Fetch and parse feeds concurrently**: RSS, Atom, and JSON Feed formats are
110 supported. Feeds are fetched in parallel with rate limiting to maximise
111 throughput while avoiding server overload.
1124. **Extract article content**: for each feed entry, the tool extracts the
113 title, link, publication date, author, and description. If the feed provides
114 only a snippet, it can optionally fetch the full webpage using `goquery` to
115 extract the main content.
1165. **Filter and sort articles**: articles are filtered by age (configurable
117 with `--max-days-old`), optionally filtered by keywords (include/exclude),
118 and sorted by date, title, or source.
1196. Prepare the prompt with selected articles, formatted into a context
120 string that is sent to the LLM along with the user’s custom prompt.
1217. Call the OpenAI API via the `openai‑go` client.
1228. **Format and output the result**: the AI’s response is printed in the chosen
123 format (plain text, GitHub‑flavoured markdown, or JSON). If JSON output is
124 selected, the original articles can be included alongside the summary.
125
126
127
128When the `--tui` flag is used, the entire process is wrapped in a `bubbletea`
129TUI that shows a colourful progress bar, live article counters, and elapsed
130time. The TUI renders Markdown content from the LLM with proper styling for
131headers, bold, italic, code blocks, and lists. The TUI supports keyboard
132navigation (j/k, arrows, space, b, g/G) and mouse wheel scrolling for
133browsing long summaries.
134
135Feeds are fetched concurrently for optimal performance, with rate limiting to
136avoid overwhelming feed servers.
137
138## Configuration
139
140`llm_aggregator` supports multiple configuration sources with the following precedence order (highest to lowest):
141
1421. **Command‑line arguments** – Override everything
1432. **Environment variables** – Start with `LLM_AGGREGATOR_` prefix
1443. **Configuration file** – `~/.config/llm_aggregator/config.toml`
1454. **Built‑in defaults**
146
147### Configuration file
148
149Create a TOML file at `~/.config/llm_aggregator/config.toml` with the following structure:
150
151```toml
152# Feed aggregation options
153max_articles_per_feed = 10
154max_days_old = 7
155max_total_articles = 20
156
157# Content filtering (comma-separated keywords)
158# include_keywords = "linux,opensource"
159# exclude_keywords = "windows,microsoft"
160
161# LLM API options
162# api_key = "your_api_key_here" # Can also be set via LLM_AGGREGATOR_API_KEY env var
163# base_url = "https://api.deepseek.com" # Optional custom API endpoint
164model = "deepseek-chat"
165max_tokens = 4000
166temperature = 0.7
167
168# System prompt for LLM API
169system_prompt = """You are an expert analyst and summariser.
170You analyse content from multiple sources and provide
171concise, insightful summaries based on user requests.
172Focus on key points, trends, and important information."""
173
174# Output options
175output = "text" # Options: text, json, markdown
176# output_file = "" # Optional output file path
177include_articles = false
178```
179
180### Environment variables
181
182All configuration options can also be set via environment variables with the `LLM_AGGREGATOR_` prefix:
183
184| Variable | Description |
185|----------|-------------|
186| `LLM_AGGREGATOR_API_KEY` | LLM API key |
187| `LLM_AGGREGATOR_BASE_URL` | API base URL (default: "https://api.deepseek.com") |
188| `LLM_AGGREGATOR_MODEL` | Model name (default: "deepseek-chat") |
189| `LLM_AGGREGATOR_MAX_TOKENS` | Maximum tokens in response (default: 4000) |
190| `LLM_AGGREGATOR_TEMPERATURE` | Sampling temperature (default: 0.7) |
191| `LLM_AGGREGATOR_SYSTEM_PROMPT` | Custom system prompt |
192| `LLM_AGGREGATOR_MAX_ARTICLES_PER_FEED` | Maximum articles per feed (default: 10) |
193| `LLM_AGGREGATOR_MAX_DAYS_OLD` | Maximum article age in days (default: 7) |
194| `LLM_AGGREGATOR_MAX_TOTAL_ARTICLES` | Maximum total articles (default: 20) |
195| `LLM_AGGREGATOR_INCLUDE_KEYWORDS` | Comma‑separated include keywords |
196| `LLM_AGGREGATOR_EXCLUDE_KEYWORDS` | Comma‑separated exclude keywords |
197| `LLM_AGGREGATOR_OUTPUT` | Output format (default: "text") |
198| `LLM_AGGREGATOR_OUTPUT_FILE` | Output file path |
199| `LLM_AGGREGATOR_INCLUDE_ARTICLES` | Include articles in JSON output (true/false) |
200| `LLM_AGGREGATOR_PLAIN` | Plain output without metadata (true/false) |
201| `LLM_AGGREGATOR_STDIN` | Read RSS/Atom feed from stdin (true/false) |
202
203The API key can be provided via `--api‑key`, `LLM_AGGREGATOR_API_KEY` environment variable, or in the configuration file.
204
205## Example feeds file
206
207Create a file named `feeds.txt` with your favourite RSS feeds, one per line.
208For example:
209
210 https://news.ycombinator.com/rss
211 https://lwn.net/headlines/newrss
212 https://opensource.com/feed
213 https://www.phoronix.com/rss.php
214
215Then run:
216
217 llm_aggregator -f feeds.txt -p "Summarise the top tech stories"
218
219## Dependencies
220
221`llm_aggregator` is written in Go and uses the following libraries:
222
223| Library | Description |
224|---------|-------------|
225| [`gofeed`](https://github.com/mmcdole/gofeed) | Robust RSS/Atom/JSON feed parser |
226| [`openai‑go`](https://github.com/openai/openai-go) | Official OpenAI API library for Go |
227| [`bubbletea`](https://github.com/charmbracelet/bubbletea) | TUI framework for terminal applications |
228| [`lipgloss`](https://github.com/charmbracelet/lipgloss) | Library for styling terminal output |
229| [`glamour`](https://github.com/charmbracelet/glamour) | Markdown rendering for terminal (used in TUI mode) |
230| [`go‑arg`](https://github.com/alexflint/go-arg) | Struct‑based argument parsing |
231| [`tiktoken-go`](https://github.com/pkoukk/tiktoken-go) | OpenAI's tiktoken BPE tokeniser |
232| [`viper`](https://github.com/spf13/viper) | Configuration management |
233| [`goquery`](https://github.com/PuerkitoBio/goquery) | jQuery‑like HTML scraping |
234
235## How do I build `llm_aggregator`?
236
237`llm_aggregator` can be built with a standard Go toolchain:
238
239 go build ./cmd/llm_aggregator.go
240
241For information about the project's test suite, see
242[docs/TESTING.md](docs/TESTING.md).
243
244## Licence
245
246This project is licensed under [European Union Public Licence
2471.2](https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12).