From a6543c1dc5740d9634cca72660af4ed87da9889f Mon Sep 17 00:00:00 2001 From: Bryan Van Deusen Date: Wed, 8 Apr 2026 22:07:24 -0400 Subject: [PATCH] feat(briefing): cluster news by topic for thematic synthesis instead of flat headlines --- .../services/briefing_pipeline.py | 39 ++++++++++++++----- 1 file changed, 30 insertions(+), 9 deletions(-) diff --git a/src/fabledassistant/services/briefing_pipeline.py b/src/fabledassistant/services/briefing_pipeline.py index 65b20d2..b5900fa 100644 --- a/src/fabledassistant/services/briefing_pipeline.py +++ b/src/fabledassistant/services/briefing_pipeline.py @@ -450,17 +450,38 @@ def _unified_user_prompt(internal_data: dict, external_data: dict, slot: str, te lines.append(" (Identify free blocks between events for focused work.)") lines.append("") - # News highlights (top 3 with excerpts — right panel shows full list) + # News — clustered by topic for thematic synthesis rss = external_data.get("rss_items") or [] if rss: - lines.append("NEWS HIGHLIGHTS (weave 1-2 into your briefing naturally; the full list is shown separately):") - for item in rss[:3]: - source = item.get("feed_title") or item.get("source") or "News" - title = item.get("title", "") - excerpt = (item.get("content") or item.get("snippet") or "")[:500].strip() - lines.append(f" [{source}] {title}") - if excerpt: - lines.append(f" {excerpt}") + # Group articles by their primary topic + clusters: dict[str, list[dict]] = {} + uncategorized: list[dict] = [] + for item in rss: + topics = item.get("topics") or [] + if topics: + primary = topics[0] + clusters.setdefault(primary, []).append(item) + else: + uncategorized.append(item) + + # Sort clusters by size (most articles = most active theme) + sorted_clusters = sorted(clusters.items(), key=lambda x: len(x[1]), reverse=True) + + lines.append("NEWS THEMES (synthesize 1-2 themes into your briefing; mention article count per theme):") + for topic, items in sorted_clusters[:4]: + count = len(items) + titles = [i.get("title", "") for i in items[:3]] + sources = list({i.get("feed_title") or i.get("source") or "News" for i in items}) + lines.append(f" [{topic.title()}] ({count} article{'s' if count != 1 else ''} from {', '.join(sources[:2])})") + for t in titles: + lines.append(f" • {t}") + # Include one excerpt for the top cluster + if items == sorted_clusters[0][1]: + excerpt = (items[0].get("content") or items[0].get("snippet") or "")[:400].strip() + if excerpt: + lines.append(f" > {excerpt}") + if uncategorized: + lines.append(f" [Other] ({len(uncategorized)} uncategorized article{'s' if len(uncategorized) != 1 else ''})") lines.append("") return "\n".join(lines)