dashboard-nanobot/backend/services/topic_runtime/publisher.py

118 lines
3.7 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

import re
from typing import Any, Dict, List, Optional
_MARKDOWN_PREFIX_RE = re.compile(r"^\s{0,3}(?:[#>*-]+|\d+[.)])\s*")
_TABLE_LINE_RE = re.compile(r"^\s*\|.*\|\s*$")
_SEPARATOR_LINE_RE = re.compile(r"^\s*[-=:_`~]{3,}\s*$")
def _clean_topic_line(raw: Any) -> str:
text = str(raw or "").strip()
if not text:
return ""
if _SEPARATOR_LINE_RE.fullmatch(text):
return ""
if _TABLE_LINE_RE.fullmatch(text):
return ""
text = _MARKDOWN_PREFIX_RE.sub("", text).strip()
return text
def _clean_topic_lines(content: str) -> List[str]:
rows: List[str] = []
for line in str(content or "").splitlines():
cleaned = _clean_topic_line(line)
if cleaned:
rows.append(cleaned)
return rows
def _extract_highlights(content: str) -> List[str]:
rows: List[str] = []
for line in str(content or "").splitlines():
raw = str(line or "").strip()
if not raw:
continue
cleaned = _clean_topic_line(raw)
if not cleaned:
continue
if raw.lstrip().startswith(("-", "*")) or ":" in cleaned or "" in cleaned:
value = cleaned[:120]
if value not in rows:
rows.append(value)
if len(rows) >= 3:
break
return rows
def _unique_rows(rows: List[str]) -> List[str]:
deduped: List[str] = []
seen = set()
for row in rows:
value = str(row or "").strip()
if not value or value in seen:
continue
seen.add(value)
deduped.append(value)
return deduped
def _build_summary_card_view(title: str, content: str) -> Dict[str, Any]:
lines = _clean_topic_lines(content)
fallback_title = title or (lines[0] if lines else "")
summary_source = [line for line in lines if line != fallback_title]
narrative_lines = [
line for line in summary_source
if not line.startswith(("-", "*")) and ":" not in line and "" not in line
]
summary = " ".join((narrative_lines or summary_source)[:2]).strip()
if not summary and lines:
summary = lines[0]
summary = summary[:220].strip()
highlights = _unique_rows(_extract_highlights(content))[:3]
snippet_source = _unique_rows(
[line for line in summary_source if line != summary and line not in highlights]
)
snippet = " ".join(snippet_source[:2]).strip()[:180].strip()
return {
"type": "summary_card",
"title": fallback_title[:120],
"summary": summary,
"highlights": highlights,
"snippet": snippet,
}
def build_topic_publish_payload(bot_id: str, packet: Dict[str, Any], message_id: Optional[int]) -> Optional[Dict[str, Any]]:
packet_type = str(packet.get("type") or "").strip().upper()
is_progress = bool(packet.get("is_progress"))
is_tool_hint = bool(packet.get("is_tool_hint"))
if packet_type == "BUS_EVENT" and is_progress:
return None
if packet_type == "BUS_EVENT":
content = str(packet.get("content") or packet.get("text") or "").strip()
else:
content = str(packet.get("text") or "").strip()
if not content:
return None
lines = _clean_topic_lines(content)
title = (lines[0] if lines else content[:120]).strip()
if len(title) > 120:
title = f"{title[:117].rstrip()}..."
source_channel = str(packet.get("channel") or "dashboard").strip().lower() or "dashboard"
dedupe_key = f"{bot_id}:message:{message_id}" if message_id else ""
return {
"title": title,
"content": content,
"level": "info",
"source": source_channel,
"dedupe_key": dedupe_key,
"is_progress": is_progress,
"is_tool_hint": is_tool_hint,
"view": _build_summary_card_view(title, content),
}