"""Participant Service - unified service for users and agents in chat rooms.""" import logging from typing import Dict, Any, Optional, AsyncGenerator, List from luxx.agents.base import BaseAgent from luxx.agents.registry import agent_registry from luxx.models.participant import Participant from luxx.services.room import chat_room_service logger = logging.getLogger(__name__) class ParticipantService: """Unified service for managing participants in chat rooms.""" def __init__(self): self._active_agents: Dict[str, BaseAgent] = {} self._active_users: Dict[int, Any] = {} def _cm(self): """Lazy import connection manager.""" from luxx.services.room_ws import connection_manager return connection_manager # ==================== Agent ==================== def register_agent(self, agent: BaseAgent) -> Participant: """Register an active agent in the participant service""" self._active_agents[agent.agent_id] = agent agent_registry.register(agent) return Participant.from_agent( agent.agent_id, agent.name, agent.role, agent.avatar ) def unregister_agent(self, agent_id: str) -> bool: if agent_id in self._active_agents: del self._active_agents[agent_id] agent_registry.unregister(agent_id) return True return False def get_agent_participant(self, agent_id: str) -> Optional[Participant]: """Get agent participant info""" agent = self._active_agents.get(agent_id) or chat_room_service.get_agent(agent_id) if agent: if agent_id not in self._active_agents: self._active_agents[agent_id] = agent return Participant.from_agent( agent.agent_id, agent.name, agent.role, agent.avatar ) return None # ==================== User ==================== def register_user(self, user) -> Participant: self._active_users[user.id] = user return Participant.from_user(user) def get_user_participant(self, user_id: int) -> Optional[Participant]: user = self._active_users.get(user_id) if not user: from luxx.core.database import SessionLocal from luxx.models.user import User db = SessionLocal() try: user = db.query(User).filter(User.id == user_id).first() if user: self._active_users[user_id] = user finally: db.close() return Participant.from_user(user) if user else None # ==================== Unified ==================== def get_participant(self, participant_id: str, ptype: str = "user") -> Optional[Participant]: if ptype == "agent": return self.get_agent_participant(participant_id) try: return self.get_user_participant(int(participant_id)) except (ValueError, TypeError): return None # ==================== Messages ==================== async def process_message( self, room_id: str, content: str, sender_id: str, sender_name: str, sender_type: str = "user", context: Dict = None ) -> AsyncGenerator[Dict[str, Any], None]: """Process a message in a chat room This is a wrapper around chat_room_service.process_message that handles broadcasting and typing indicators. """ cm = self._cm() # Save and broadcast message msg = chat_room_service.save_message( room_id=room_id, sender_type=sender_type, sender_name=sender_name, content=content, sender_id=str(sender_id) ) await cm.broadcast_to_room(room_id, {"event": "message", "data": {"message": msg}}) # Get room agents room_agents = chat_room_service.get_room_agents(room_id) if sender_type == "agent": room_agents = [a for a in room_agents if a.agent_id != sender_id] # Broadcast typing indicators for agent in room_agents: await cm.broadcast_to_room(room_id, { "event": "typing", "data": { "sender_id": agent.agent_id, "sender_type": "agent", "agent_name": agent.name, "is_typing": True } }) # Process and yield events ctx = (context or {}) ctx.update({"sender_type": sender_type, "sender_id": sender_id, "username": sender_name}) async for event in chat_room_service.process_message(room_id, content, sender_id, sender_name, ctx): yield event # Clear typing indicators for agent in room_agents: await cm.broadcast_to_room(room_id, { "event": "typing", "data": { "sender_id": agent.agent_id, "sender_type": "agent", "agent_name": agent.name, "is_typing": False } }) async def send_message( self, room_id: str, participant_id: str, participant_type: str, participant_name: str, content: str, mentions: List[str] = None, parent_id: str = None ): """Send a message as a participant""" cm = self._cm() msg = chat_room_service.save_message( room_id=room_id, sender_type=participant_type, sender_name=participant_name, content=content, sender_id=str(participant_id), mentions=mentions, parent_id=parent_id ) await cm.broadcast_to_room(room_id, {"event": "message", "data": {"message": msg}}) return msg participant_service = ParticipantService()