From 2961a6729f36dd7acc62859f38a4a71a5b450162 Mon Sep 17 00:00:00 2001 From: NopeCharon <115794254+NopeCharon@users.noreply.github.com> Date: Sun, 1 Nov 2026 23:26:07 +0530 Subject: [PATCH 1/2] [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&amp;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. --- xExtension-LlmClassification/extension.php | 6 +++++- xExtension-LlmClassification/metadata.json | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/xExtension-LlmClassification/extension.php b/xExtension-LlmClassification/extension.php index 17c69cc3..79347e89 100644 --- a/xExtension-LlmClassification/extension.php +++ b/xExtension-LlmClassification/extension.php @@ -439,7 +439,11 @@ public function classifyEntry(FreshRSS_Entry $entry): FreshRSS_Entry { if ($existingPromptHash === $incomingPromptHash) { // Re-use existing tags matching the prefix $prefix = $this->getUserConfigurationString('tag_prefix') ?? ''; - $existingTags = $existingEntry->tags(); + // Decode the stored tags before re-adding them (will be HTML-encoded again in applyClassification) + $existingTags = array_map( + static fn(string $tag) => htmlspecialchars_decode($tag, ENT_COMPAT), + $existingEntry->tags() + ); if ($prefix !== '') { $existingTags = array_values(array_filter( $existingTags, diff --git a/xExtension-LlmClassification/metadata.json b/xExtension-LlmClassification/metadata.json index 9e48eedf..239c5d61 100644 --- a/xExtension-LlmClassification/metadata.json +++ b/xExtension-LlmClassification/metadata.json @@ -2,7 +2,7 @@ "name": "LLM Classification", "author": "Alkarex", "description": "Tag incoming articles by calling an OpenAI-compatible LLM API", - "version": "0.3.1", + "version": "0.3.2", "entrypoint": "LlmClassification", "type": "user", "compatibility": "1.28.2" From cc80ec0e1caba8ff66249ab61c2fce06e7dec928 Mon Sep 17 00:00:00 2001 From: NopeCharon <115794254+NopeCharon@users.noreply.github.com> Date: Mon, 2 Nov 2026 00:51:08 +0530 Subject: [PATCH 2/2] [LLM Classification] Decode single quotes in re-used tags too --- xExtension-LlmClassification/extension.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xExtension-LlmClassification/extension.php b/xExtension-LlmClassification/extension.php index 79347e89..2edeb925 100644 --- a/xExtension-LlmClassification/extension.php +++ b/xExtension-LlmClassification/extension.php @@ -441,7 +441,7 @@ public function classifyEntry(FreshRSS_Entry $entry): FreshRSS_Entry { $prefix = $this->getUserConfigurationString('tag_prefix') ?? ''; // Decode the stored tags before re-adding them (will be HTML-encoded again in applyClassification) $existingTags = array_map( - static fn(string $tag) => htmlspecialchars_decode($tag, ENT_COMPAT), + static fn(string $tag) => htmlspecialchars_decode($tag, ENT_QUOTES), $existingEntry->tags() ); if ($prefix !== '') {