|
|
@@ -33,6 +33,7 @@ import logging
|
|
|
import mimetypes
|
|
|
import os
|
|
|
from datetime import datetime, timezone
|
|
|
+from enum import StrEnum
|
|
|
from typing import Any, Dict, List, Literal, Optional, Tuple, cast
|
|
|
from urllib.parse import urlsplit
|
|
|
|
|
|
@@ -96,6 +97,59 @@ except ImportError: # pragma: no cover - loaded as a top-level module (tests)
|
|
|
)
|
|
|
|
|
|
|
|
|
+# --------------------------------------------------------------------------- #
|
|
|
+# Chat types
|
|
|
+# --------------------------------------------------------------------------- #
|
|
|
+
|
|
|
+class HermesChatType(StrEnum):
|
|
|
+ """The ``chat_type`` vocabulary the Hermes gateway understands.
|
|
|
+
|
|
|
+ Declared in ``gateway/session.py:161`` as ``"dm", "group", "channel",
|
|
|
+ "thread"`` and consumed as a bare string all over the gateway:
|
|
|
+ ``SessionSource.description`` (session.py:239) and the PII-redacting
|
|
|
+ description in ``build_session_context_prompt`` (session.py:537) both
|
|
|
+ branch on these exact values and fall back to a nameless generic case for
|
|
|
+ anything else, and ``build_session_key`` puts the value straight into the
|
|
|
+ session key. Passing a chattolib ``RoomKind`` (``"ROOM_KIND_CHANNEL"``)
|
|
|
+ therefore does not fail loudly — it just quietly degrades what the agent is
|
|
|
+ told about where it is.
|
|
|
+
|
|
|
+ A StrEnum so it stays a drop-in ``str`` at every one of those call sites.
|
|
|
+ """
|
|
|
+
|
|
|
+ DM = "dm"
|
|
|
+ GROUP = "group"
|
|
|
+ CHANNEL = "channel"
|
|
|
+ # Emitted by adapters whose thread events are their own chat type (Slack,
|
|
|
+ # Discord). We don't: a Chatto thread keeps its room's chat_type and is
|
|
|
+ # identified by ``thread_id`` on the source instead. Listed for the record,
|
|
|
+ # because build_session_key rewrites the slot to "thread" itself
|
|
|
+ # (session.py:1190).
|
|
|
+ THREAD = "thread"
|
|
|
+
|
|
|
+
|
|
|
+# Chatto only distinguishes DMs from channels. UNSPECIFIED means the server
|
|
|
+# sent a kind this vendored chattolib doesn't know: map it to the generic
|
|
|
+# multi-user bucket rather than guessing "channel", and never to "dm" — that
|
|
|
+# value drives session isolation (is_shared_multi_user_session, session.py:1063)
|
|
|
+# and would silently turn a room into a private conversation.
|
|
|
+_ROOM_KIND_TO_CHAT_TYPE: Dict[RoomKind, HermesChatType] = {
|
|
|
+ RoomKind.DM: HermesChatType.DM,
|
|
|
+ RoomKind.CHANNEL: HermesChatType.CHANNEL,
|
|
|
+ RoomKind.UNSPECIFIED: HermesChatType.GROUP,
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+def chat_type_for_room_kind(kind: Optional[RoomKind]) -> HermesChatType:
|
|
|
+ """Map a chattolib RoomKind onto the gateway's chat_type vocabulary.
|
|
|
+
|
|
|
+ An unknown or missing kind becomes ``GROUP`` — see ``_ROOM_KIND_TO_CHAT_TYPE``.
|
|
|
+ """
|
|
|
+ if kind is None:
|
|
|
+ return HermesChatType.GROUP
|
|
|
+ return _ROOM_KIND_TO_CHAT_TYPE.get(kind, HermesChatType.GROUP)
|
|
|
+
|
|
|
+
|
|
|
# --------------------------------------------------------------------------- #
|
|
|
# Adapter
|
|
|
# --------------------------------------------------------------------------- #
|
|
|
@@ -573,7 +627,7 @@ class ChattoAdapter(BasePlatformAdapter):
|
|
|
source = self.build_source(
|
|
|
chat_id=payload.room_id,
|
|
|
chat_name=self._room_names.get(message.room_id),
|
|
|
- chat_type="dm" if room_kind == RoomKind.DM else room_kind or RoomKind.UNSPECIFIED, # Only "dm" seems to be a reserved keyword from Base adapter class.,
|
|
|
+ chat_type=chat_type_for_room_kind(room_kind),
|
|
|
user_id=message.actor_id,
|
|
|
user_name=user.login, # use login, because display_name is changeable by anyone.
|
|
|
thread_id=thread_id,
|
|
|
@@ -1158,10 +1212,9 @@ class ChattoAdapter(BasePlatformAdapter):
|
|
|
"""
|
|
|
name = self._room_names.get(chat_id, chat_id)
|
|
|
kind = self._room_kinds.get(chat_id)
|
|
|
- chat_type = "dm" if kind == RoomKind.DM else "group"
|
|
|
return {
|
|
|
"name": name,
|
|
|
- "type": chat_type,
|
|
|
+ "type": chat_type_for_room_kind(kind).value,
|
|
|
}
|
|
|
|
|
|
# ------------------------------------------------------------------ #
|