fix(article-summary): only engage FSM when user explicitly asks for summary/audio
Some checks failed
Build & Deploy / test (push) Successful in 9s
Tests / test (push) Successful in 9s
Build & Deploy / build-and-deploy (push) Failing after 5s

Previously any chat message containing an article URL triggered the
Blinkist FSM: Firecrawl extraction + LLM topic detection + 3-option
menu. Pasting a link as conversational context spammed the menu.

Now _check_for_url additionally requires an intent keyword (summary,
zusammenfassung, audio, mp3, blinkist, tldr, lies das, fasse zusammen,
discuss/diskutieren, etc.) before engaging. Without intent the URL
falls through to the normal AI handler.

Also bind-mount article_summary/ so future fixes survive container
recreate (matches the pattern used for bot.py/voice.py/agent.py).
This commit is contained in:
Christian Gick
2026-04-18 06:30:42 +00:00
parent 7087fbf733
commit 8b7cf46312
2 changed files with 20 additions and 1 deletions

View File

@@ -28,6 +28,16 @@ _DISCUSS_KW = {"discuss", "diskutieren", "besprechen", "reden", "talk", "chat"}
_TEXT_KW = {"text", "zusammenfassung", "summary", "lesen", "read", "schriftlich", "written"}
_AUDIO_KW = {"audio", "mp3", "anhören", "vorlesen", "hören", "listen", "blinkist", "abspielen", "podcast"}
# Words that signal the user actually wants the article-summary FSM to engage.
# Without one of these, a pasted URL is left alone (chat-as-usual).
# Union of discuss/text/audio keywords + explicit summary asks.
_INTENT_KW = (
_DISCUSS_KW | _TEXT_KW | _AUDIO_KW |
{"tldr", "tl;dr", "fasse zusammen", "fass zusammen", "zusammenfassen",
"summarise", "summarize", "worum geht", "was steht", "what does it say",
"kannst du das lesen", "lies das", "lies mir", "read this", "read it"}
)
# Simple German detection: common words that appear frequently in German text
_DE_INDICATORS = {"der", "die", "das", "und", "ist", "ein", "eine", "für", "mit", "auf", "den", "dem", "sich", "nicht", "von", "wird", "auch", "nach", "wie", "aber"}
@@ -160,13 +170,21 @@ class ArticleSummaryHandler:
async def _check_for_url(
self, room_id: str, sender: str, body: str
) -> str | None:
"""Check if message contains an article URL."""
"""Check if message contains an article URL AND explicit summary intent."""
urls = URL_PATTERN.findall(body)
# Filter to article-like URLs
article_urls = [u for u in urls if is_article_url(u)]
if not article_urls:
return None
# Only engage the FSM if the user explicitly asked for a summary /
# discussion / audio. Otherwise a pasted URL is just context for normal
# chat and we shouldn't burn a Firecrawl + LLM topic-detection call,
# nor interrupt with the 3-option menu.
body_lower = body.lower()
if not any(kw in body_lower for kw in _INTENT_KW):
return None
url = article_urls[0]
session = self.sessions.get(sender, room_id)

View File

@@ -36,6 +36,7 @@ services:
- ./e2ee_patch.py:/app/e2ee_patch.py:ro
- ./cross_signing.py:/app/cross_signing.py:ro
- ./device_trust.py:/app/device_trust.py:ro
- ./article_summary:/app/article_summary:ro
depends_on:
memory-service:
condition: service_healthy