from __future__ import annotations import logging from yuxi.channel.domain.model.binding.channel_binding import ChannelBinding from yuxi.channel.domain.model.session.channel_session import ChannelSession from yuxi.channel.domain.repository.binding_repository import BindingRepositoryPort from yuxi.channel.domain.repository.session_repository import SessionRepositoryPort logger = logging.getLogger(__name__) class SessionResolver: def __init__( self, session_repo: SessionRepositoryPort, binding_repo: BindingRepositoryPort, *, default_agent_config_id: int = 1, session_key_strategy: str = "auto", ) -> None: self._session_repo = session_repo self._binding_repo = binding_repo self._default_agent_config_id = default_agent_config_id self._session_key_strategy = session_key_strategy async def resolve(self, payload: dict) -> ChannelSession | None: channel_type = payload["channel_type"] sender_id = payload.get("sender_id", "") metadata = payload.get("metadata", {}) agent_config_id = payload.get("agent_config_id") if not agent_config_id: agent_config_id = await self._resolve_agent_config(channel_type, metadata) binding = await self._binding_repo.find_binding( channel_type=channel_type, account_id=metadata.get("account_id", ""), group_id=metadata.get("group_id", ""), ) session_key = self._resolve_session_key(payload, channel_type, sender_id, binding) try: session = await self._session_repo.get_or_create( channel_type=channel_type, account_id=session_key, agent_config_id=agent_config_id, ) return session except Exception: logger.exception( "session creation failed: %s [%s]", payload.get("message_id", ""), payload.get("trace_id", ""), ) return None async def _resolve_agent_config(self, channel_type: str, metadata: dict) -> int: binding = await self._binding_repo.find_binding( channel_type=channel_type, account_id=metadata.get("account_id", ""), group_id=metadata.get("group_id", ""), ) return binding.agent_config_id if binding else self._default_agent_config_id def _resolve_session_key( self, payload: dict, channel_type: str, sender_id: str, binding: ChannelBinding | None ) -> str: metadata = payload.get("metadata", {}) if binding: strategy = binding.resolve_session_key_strategy() else: strategy = metadata.get("session_key_strategy", self._session_key_strategy) if strategy == "main": return sender_id elif strategy == "channel_group": group_id = metadata.get("group_id", "") if group_id: return f"{sender_id}:{channel_type}:{group_id}" return sender_id elif strategy == "custom": prefix = metadata.get("session_key_prefix", "") custom_key = metadata.get("session_key", "") if custom_key and prefix: return f"{prefix}:{custom_key}" elif custom_key: return custom_key return sender_id else: is_group = metadata.get("is_group", False) if is_group: group_id = metadata.get("group_id", "") return f"{sender_id}:{channel_type}:{group_id}" if group_id else sender_id return sender_id