Files
leocrm/app/plugins/builtins/kommunikation/participant_registry.py
T

90 lines
2.9 KiB
Python
Raw Normal View History

"""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