"""Participant Registry — Andockpunkt für Plugins als Teilnehmer.""" from __future__ import annotations import logging from abc import ABC, abstractmethod from typing import Any logger = logging.getLogger(__name__) class ParticipantHandler(ABC): """Interface that plugins implement to act as a conversation participant.""" @abstractmethod async def on_message_received( self, conversation_id: Any, message: dict[str, Any], conversation: dict[str, Any], mentions: list[str], context: dict[str, Any], ) -> list[dict[str, Any]] | None: """Called when a new message arrives in a conversation this participant is part of. Args: conversation_id: UUID of the conversation. message: The new message dict. conversation: Full conversation dict with participants. mentions: Parsed @mention participant types. context: Tenant, user, and other context. Returns: Optional list of new message dicts (e.g. AI response). For passive readers (system) → return None. For reactive participants (ai) → return [message_dict, ...]. """ pass @abstractmethod def get_participant_info(self) -> dict[str, Any]: """Return metadata: display_name, avatar_url, capabilities, description.""" pass class ParticipantRegistry: """Global registry for plugin participants.""" def __init__(self) -> None: self._handlers: dict[str, ParticipantHandler] = {} def register(self, participant_type: str, handler: ParticipantHandler) -> None: """Register a plugin as a participant type.""" self._handlers[participant_type] = handler logger.info(f"Participant registered: {participant_type}") def unregister(self, participant_type: str) -> None: """Unregister a participant type.""" handler = self._handlers.pop(participant_type, None) if handler: logger.info(f"Participant unregistered: {participant_type}") def get_handler(self, participant_type: str) -> ParticipantHandler | None: """Get the handler for a participant type.""" return self._handlers.get(participant_type) def list_types(self) -> list[str]: """List all registered participant types.""" return list(self._handlers.keys()) def get_all_info(self) -> dict[str, dict[str, Any]]: """Get info for all registered participants.""" return {ptype: handler.get_participant_info() for ptype, handler in self._handlers.items()} # Global instance _registry = ParticipantRegistry() def get_participant_registry() -> ParticipantRegistry: """Get the global participant registry.""" return _registry def reset_participant_registry_for_testing() -> ParticipantRegistry: """Create a fresh registry for testing.""" global _registry _registry = ParticipantRegistry() return _registry