Files
Asish 73fb62c120 [LLM Classification] Do not HTML-encode re-used tags again (#573)
* [LLM Classification] Do not HTML-encode re-used tags again

The tags of an entry are stored HTML-encoded: `applyClassification()`
encodes the tags returned by the LLM with `htmlspecialchars()`, like
SimplePie does for the categories of the feeds.

When an already classified entry is updated without any change to its
prompt, the extension re-uses the stored tags instead of calling the
LLM again, but passed them as-is to `applyClassification()`, which
encoded them a second time. So a tag such as `R&D` became `R&D` in
the database after classification, then `R&D` after the next
such update, and so on. When a list of allowed tags is configured, the
encoded tags did not match the list anymore (`R&D` is not `R&D`),
and were silently removed from the entry.

Decode the stored tags before re-using them, so that they go through
the same encoding and allow-list check as the tags returned by the LLM.
Decoding first also lets tags match a configured prefix containing such
characters.

Bump the extension version to 0.3.2.

* [LLM Classification] Decode single quotes in re-used tags too
2026-11-01 20:23:47 +01:00
..

FreshRSS - LLM Classification extension

This FreshRSS extension automatically tags incoming RSS articles by calling an OpenAI-compatible LLM API. It classifies articles on insertion using a customizable prompt, and applies tags based on the LLM response.

Features

  • Compatible with any OpenAI-compatible API (OpenAI, Ollama, LocalAI, etc.)
  • Customizable user prompt with template placeholders for article data
  • Tag prefixing (e.g. llm/)
  • Entry filtering via FreshRSS Boolean search syntax
  • Response caching to avoid redundant API calls
  • Content truncation to control token usage
  • Skips re-classifying updated articles without any prompt-relevant change, avoiding redundant LLM API calls

Requirements

  • FreshRSS 1.28.2+
  • An OpenAI-compatible LLM API endpoint

Installation

To use it, ensure that the ./xExtension-LlmClassification/ directory can be found under FreshRSS ./extensions/ directory, and enable it on the extension panel in FreshRSS.

Configuration

API Configuration

Setting Default Description
API URL (empty) OpenAI-compatible API base URL (e.g. https://api.openai.com/v1)
API Key (empty) Bearer token for API authentication (optional for local APIs)
Model gpt-4o-mini Model name
Timeout 30 HTTP request timeout in seconds (1–300)

Prompts

The system prompt is read-only and constrains the LLM to return a JSON object with the structure {"tags": ["tag1", "tag2"]}.

The user prompt is an editable template. The following placeholders are replaced with article data before each API call:

Placeholder Value
{title} Article title
{content} Article content (HTML stripped, truncated if needed)
{author} Article author(s)
{url} Article link
{feed_url} Feed URL
{feed_name} Feed name
{date} Article date
{tags} Existing article tags

Max content length (default: 4000 characters): Maximum number of characters for the {content} placeholder. Set to 0 for unlimited.

Tag Classification

Setting Default Description
Enable tag classification Off Master toggle for the extension
Tag prefix (empty) Prefix prepended to each LLM-generated tag (e.g. llm/)
Allowed tags (empty) Whitelist of accepted tags (one per line). If set, only these tags are kept from the LLM response. Empty = all tags allowed

Conditions for tagging

Search filters (one per line): Only entries matching at least one filter are classified. Uses FreshRSS Boolean search syntax. Leave empty to classify all entries.

How it works

  1. A new article arrives in FreshRSS
  2. The extension checks if tag classification is enabled and the article matches the configured search filters
  3. The user prompt is built by replacing placeholders with article data
  4. For updates of previously-classified articles, the new prompt is compared against the stored hash; if unchanged, the previous tags are restored and no API call is made
  5. Otherwise, the LLM API is called with the system prompt and the user prompt
  6. The returned tags are validated (prefix prepended, whitelist enforced) and applied to the article
  7. The prompt hash is stored along the entry