meshai/meshai/notifications/summarizer.py
K7ZVX 3bf5e3dfbc feat(notifications): alert routing with channels, rules, and delivery
- Notification pipeline: categories -> rules -> channels
- Channels: mesh broadcast, mesh DM, email (SMTP), webhook (generic)
- Per-rule severity threshold and category filtering
- Quiet hours with emergency override
- LLM summarization for mesh delivery over 200 chars only
- !subscribe shows available categories, easy mesh subscription
- Dashboard notification rules API endpoints
- Extensible channel system for future transports (Winlink, JS8Call)
- config.yaml notification section with examples

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-05-13 03:51:37 +00:00

64 lines
1.9 KiB
Python

"""Message summarizer for mesh delivery."""
import logging
from typing import Optional, TYPE_CHECKING
if TYPE_CHECKING:
from ..backends import LLMBackend
logger = logging.getLogger(__name__)
class MessageSummarizer:
"""Summarizes long messages for mesh delivery.
Only used when:
- Delivering to mesh channels (broadcast or DM)
- Message exceeds max_chars (default 200)
- LLM backend is available
Email and webhook channels receive full messages.
"""
def __init__(self, llm_backend: Optional["LLMBackend"] = None):
self._llm = llm_backend
async def summarize(self, message: str, max_chars: int = 195) -> str:
"""Summarize a message to fit within max_chars.
Args:
message: Original message text
max_chars: Maximum characters for summary
Returns:
Summarized message, or truncated original if LLM unavailable
"""
if len(message) <= max_chars:
return message
if not self._llm:
return message[:max_chars - 3] + "..."
prompt = (
"Summarize this alert in under %d characters. "
"Keep severity, location, and key facts. No preamble, just the summary:\n\n%s"
% (max_chars, message)
)
try:
# Use the LLM to generate a summary
response = await self._llm.generate(
prompt,
system_prompt="You are a concise alert summarizer. Output only the summary, no explanation.",
max_tokens=100,
)
summary = response.strip()
# Ensure it fits
if len(summary) <= max_chars:
return summary
return summary[:max_chars - 3] + "..."
except Exception as e:
logger.debug("LLM summarization failed: %s", e)
return message[:max_chars - 3] + "..."