mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-30 19:09:28 +00:00
refactor(gateway): promote compile_mention_patterns to helpers
This commit is contained in:
parent
21d1d08a2f
commit
58400a6793
5 changed files with 132 additions and 105 deletions
|
|
@ -32,7 +32,7 @@ from gateway.platforms.base import (
|
|||
cache_document_from_bytes,
|
||||
)
|
||||
from .media_cache import ext_for_mime
|
||||
from gateway.platforms.helpers import strip_markdown
|
||||
from gateway.platforms.helpers import compile_mention_patterns, strip_markdown
|
||||
|
||||
# Historical BlueBubbles mime→ext maps, preserved verbatim as overrides for
|
||||
# the shared dispatch in gateway.platforms.media_cache. Both maps are
|
||||
|
|
@ -194,34 +194,12 @@ class BlueBubblesAdapter(BasePlatformAdapter):
|
|||
``raw`` is a list (from config or env JSON), a string (raw env var:
|
||||
JSON list, or comma/newline-separated), or None (use Hermes defaults).
|
||||
"""
|
||||
if raw is None:
|
||||
patterns = list(DEFAULT_MENTION_PATTERNS)
|
||||
elif isinstance(raw, str):
|
||||
text = raw.strip()
|
||||
try:
|
||||
loaded = json.loads(text) if text else []
|
||||
except Exception:
|
||||
loaded = None
|
||||
patterns = loaded if isinstance(loaded, list) else [
|
||||
part.strip()
|
||||
for line in text.splitlines()
|
||||
for part in line.split(",")
|
||||
]
|
||||
elif isinstance(raw, list):
|
||||
patterns = raw
|
||||
else:
|
||||
patterns = [raw]
|
||||
|
||||
compiled: List["re.Pattern"] = []
|
||||
for pattern in patterns:
|
||||
text = str(pattern).strip()
|
||||
if not text:
|
||||
continue
|
||||
try:
|
||||
compiled.append(re.compile(text, re.IGNORECASE))
|
||||
except re.error as exc:
|
||||
logger.warning("[bluebubbles] Invalid mention pattern %r: %s", text, exc)
|
||||
return compiled
|
||||
return compile_mention_patterns(
|
||||
raw,
|
||||
log_prefix="bluebubbles",
|
||||
defaults=DEFAULT_MENTION_PATTERNS,
|
||||
logger_=logger,
|
||||
)
|
||||
|
||||
def _message_matches_mention_patterns(self, text: str) -> bool:
|
||||
if not text or not self._mention_patterns:
|
||||
|
|
|
|||
|
|
@ -417,3 +417,105 @@ def convert_table_to_bullets(text: str) -> str:
|
|||
i += 1
|
||||
|
||||
return '\n'.join(out)
|
||||
|
||||
|
||||
# ─── Mention-pattern compilation ─────────────────────────────────────────────
|
||||
|
||||
|
||||
def compile_mention_patterns(
|
||||
raw,
|
||||
*,
|
||||
log_prefix: str,
|
||||
platform_label: str | None = None,
|
||||
display_label: str | None = None,
|
||||
defaults: 'list[str] | None' = None,
|
||||
logger_: 'logging.Logger | None' = None,
|
||||
) -> 'list[re.Pattern]':
|
||||
"""Compile regex wake-word/mention patterns from config or env values.
|
||||
|
||||
Two adapter families share this logic:
|
||||
|
||||
* **Config-style** (dingtalk, telegram): pass ``platform_label`` (e.g.
|
||||
``"dingtalk"``). ``raw`` is the value from ``config.extra`` after env
|
||||
fallback parsing; must be a list or string, anything else logs a warning
|
||||
and yields ``[]``. Non-string entries are skipped. A summary info log is
|
||||
emitted when patterns load.
|
||||
* **Wakeword-style** (photon, bluebubbles): pass ``defaults``. ``raw`` may
|
||||
be None (use defaults), a string (JSON list or comma/newline separated),
|
||||
a list, or a scalar (wrapped in a list). Entries are coerced via
|
||||
``str()``.
|
||||
|
||||
``log_prefix`` is interpolated into every log message so per-adapter log
|
||||
output stays byte-identical to the historical inline implementations.
|
||||
"""
|
||||
log = logger_ or logger
|
||||
|
||||
if platform_label is not None:
|
||||
# Config-style (dingtalk/telegram) semantics.
|
||||
display = display_label or platform_label
|
||||
patterns = raw
|
||||
if patterns is None:
|
||||
return []
|
||||
if isinstance(patterns, str):
|
||||
patterns = [patterns]
|
||||
if not isinstance(patterns, list):
|
||||
log.warning(
|
||||
"[%s] %s mention_patterns must be a list or string; got %s",
|
||||
log_prefix,
|
||||
platform_label,
|
||||
type(patterns).__name__,
|
||||
)
|
||||
return []
|
||||
|
||||
compiled: list[re.Pattern] = []
|
||||
for pattern in patterns:
|
||||
if not isinstance(pattern, str) or not pattern.strip():
|
||||
continue
|
||||
try:
|
||||
compiled.append(re.compile(pattern, re.IGNORECASE))
|
||||
except re.error as exc:
|
||||
log.warning(
|
||||
"[%s] Invalid %s mention pattern %r: %s",
|
||||
log_prefix,
|
||||
display,
|
||||
pattern,
|
||||
exc,
|
||||
)
|
||||
if compiled:
|
||||
log.info(
|
||||
"[%s] Loaded %d %s mention pattern(s)",
|
||||
log_prefix,
|
||||
len(compiled),
|
||||
display,
|
||||
)
|
||||
return compiled
|
||||
|
||||
# Wakeword-style (photon/bluebubbles) semantics.
|
||||
if raw is None:
|
||||
patterns = list(defaults or [])
|
||||
elif isinstance(raw, str):
|
||||
text = raw.strip()
|
||||
try:
|
||||
loaded = json.loads(text) if text else []
|
||||
except Exception:
|
||||
loaded = None
|
||||
patterns = loaded if isinstance(loaded, list) else [
|
||||
part.strip()
|
||||
for line in text.splitlines()
|
||||
for part in line.split(",")
|
||||
]
|
||||
elif isinstance(raw, list):
|
||||
patterns = raw
|
||||
else:
|
||||
patterns = [raw]
|
||||
|
||||
compiled = []
|
||||
for pattern in patterns:
|
||||
text = str(pattern).strip()
|
||||
if not text:
|
||||
continue
|
||||
try:
|
||||
compiled.append(re.compile(text, re.IGNORECASE))
|
||||
except re.error as exc:
|
||||
log.warning("[%s] Invalid mention pattern %r: %s", log_prefix, text, exc)
|
||||
return compiled
|
||||
|
|
|
|||
|
|
@ -95,7 +95,7 @@ except Exception:
|
|||
tea_util_models = None
|
||||
|
||||
from gateway.config import Platform, PlatformConfig
|
||||
from gateway.platforms.helpers import MessageDeduplicator
|
||||
from gateway.platforms.helpers import MessageDeduplicator, compile_mention_patterns
|
||||
from gateway.platforms.base import (
|
||||
BasePlatformAdapter,
|
||||
MessageEvent,
|
||||
|
|
@ -458,29 +458,13 @@ class DingTalkAdapter(BasePlatformAdapter):
|
|||
loaded = [part.strip() for part in raw.split(",") if part.strip()]
|
||||
patterns = loaded
|
||||
|
||||
if patterns is None:
|
||||
return []
|
||||
if isinstance(patterns, str):
|
||||
patterns = [patterns]
|
||||
if not isinstance(patterns, list):
|
||||
logger.warning(
|
||||
"[%s] dingtalk mention_patterns must be a list or string; got %s",
|
||||
self.name,
|
||||
type(patterns).__name__,
|
||||
)
|
||||
return []
|
||||
|
||||
compiled: List[re.Pattern] = []
|
||||
for pattern in patterns:
|
||||
if not isinstance(pattern, str) or not pattern.strip():
|
||||
continue
|
||||
try:
|
||||
compiled.append(re.compile(pattern, re.IGNORECASE))
|
||||
except re.error as exc:
|
||||
logger.warning("[%s] Invalid DingTalk mention pattern %r: %s", self.name, pattern, exc)
|
||||
if compiled:
|
||||
logger.info("[%s] Loaded %d DingTalk mention pattern(s)", self.name, len(compiled))
|
||||
return compiled
|
||||
return compile_mention_patterns(
|
||||
patterns,
|
||||
log_prefix=self.name,
|
||||
platform_label="dingtalk",
|
||||
display_label="DingTalk",
|
||||
logger_=logger,
|
||||
)
|
||||
|
||||
def _load_allowed_users(self) -> Set[str]:
|
||||
"""Load allowed-users list from config.extra or env var.
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ from gateway.platforms.base import (
|
|||
ProcessingOutcome,
|
||||
SendResult,
|
||||
)
|
||||
from gateway.platforms.helpers import strip_markdown
|
||||
from gateway.platforms.helpers import compile_mention_patterns, strip_markdown
|
||||
|
||||
from .auth import load_project_credentials
|
||||
|
||||
|
|
@ -823,34 +823,12 @@ class PhotonAdapter(BasePlatformAdapter):
|
|||
Mirrors the BlueBubbles implementation so both iMessage channels
|
||||
accept the same configuration shapes.
|
||||
"""
|
||||
if raw is None:
|
||||
patterns = list(_DEFAULT_MENTION_PATTERNS)
|
||||
elif isinstance(raw, str):
|
||||
text = raw.strip()
|
||||
try:
|
||||
loaded = json.loads(text) if text else []
|
||||
except Exception:
|
||||
loaded = None
|
||||
patterns = loaded if isinstance(loaded, list) else [
|
||||
part.strip()
|
||||
for line in text.splitlines()
|
||||
for part in line.split(",")
|
||||
]
|
||||
elif isinstance(raw, list):
|
||||
patterns = raw
|
||||
else:
|
||||
patterns = [raw]
|
||||
|
||||
compiled: "list[re.Pattern]" = []
|
||||
for pattern in patterns:
|
||||
text = str(pattern).strip()
|
||||
if not text:
|
||||
continue
|
||||
try:
|
||||
compiled.append(re.compile(text, re.IGNORECASE))
|
||||
except re.error as exc:
|
||||
logger.warning("[photon] Invalid mention pattern %r: %s", text, exc)
|
||||
return compiled
|
||||
return compile_mention_patterns(
|
||||
raw,
|
||||
log_prefix="photon",
|
||||
defaults=_DEFAULT_MENTION_PATTERNS,
|
||||
logger_=logger,
|
||||
)
|
||||
|
||||
def _message_matches_mention_patterns(self, text: str) -> bool:
|
||||
if not text or not self._mention_patterns:
|
||||
|
|
|
|||
|
|
@ -490,6 +490,7 @@ def _separate_chunk_indicator_from_fence(text: str) -> str:
|
|||
|
||||
from gateway.platforms.helpers import (
|
||||
TABLE_SEPARATOR_RE as _TABLE_SEPARATOR_RE,
|
||||
compile_mention_patterns,
|
||||
convert_table_to_bullets as _wrap_markdown_tables,
|
||||
)
|
||||
|
||||
|
|
@ -7821,29 +7822,13 @@ class TelegramAdapter(BasePlatformAdapter):
|
|||
loaded = [part.strip() for part in raw.split(",") if part.strip()]
|
||||
patterns = loaded
|
||||
|
||||
if patterns is None:
|
||||
return []
|
||||
if isinstance(patterns, str):
|
||||
patterns = [patterns]
|
||||
if not isinstance(patterns, list):
|
||||
logger.warning(
|
||||
"[%s] telegram mention_patterns must be a list or string; got %s",
|
||||
self.name,
|
||||
type(patterns).__name__,
|
||||
)
|
||||
return []
|
||||
|
||||
compiled: List[re.Pattern] = []
|
||||
for pattern in patterns:
|
||||
if not isinstance(pattern, str) or not pattern.strip():
|
||||
continue
|
||||
try:
|
||||
compiled.append(re.compile(pattern, re.IGNORECASE))
|
||||
except re.error as exc:
|
||||
logger.warning("[%s] Invalid Telegram mention pattern %r: %s", self.name, pattern, exc)
|
||||
if compiled:
|
||||
logger.info("[%s] Loaded %d Telegram mention pattern(s)", self.name, len(compiled))
|
||||
return compiled
|
||||
return compile_mention_patterns(
|
||||
patterns,
|
||||
log_prefix=self.name,
|
||||
platform_label="telegram",
|
||||
display_label="Telegram",
|
||||
logger_=logger,
|
||||
)
|
||||
|
||||
def _is_group_chat(self, message: Message) -> bool:
|
||||
chat = getattr(message, "chat", None)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue