| 
							 | 
						 | 
					
					
						
						| 
							 | 
						"""Bot registry pattern for managing different bot types.""" | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						from typing import Any, Callable, Dict, List, Optional | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						from bot_constants import DEFAULT_DIALIN_EXAMPLE | 
					
					
						
						| 
							 | 
						from bot_runner_helpers import ensure_dialout_settings_array | 
					
					
						
						| 
							 | 
						from fastapi import HTTPException | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						class BotType: | 
					
					
						
						| 
							 | 
						    """Bot type configuration and handling.""" | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						    def __init__( | 
					
					
						
						| 
							 | 
						        self, | 
					
					
						
						| 
							 | 
						        name: str, | 
					
					
						
						| 
							 | 
						        settings_creator: Callable[[Dict[str, Any]], Dict[str, Any]], | 
					
					
						
						| 
							 | 
						        required_settings: list = None, | 
					
					
						
						| 
							 | 
						        incompatible_with: list = None, | 
					
					
						
						| 
							 | 
						        auto_add_settings: dict = None, | 
					
					
						
						| 
							 | 
						    ): | 
					
					
						
						| 
							 | 
						        """Initialize a bot type. | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						        Args: | 
					
					
						
						| 
							 | 
						            name: Name of the bot type | 
					
					
						
						| 
							 | 
						            settings_creator: Function to create/update settings for this bot type | 
					
					
						
						| 
							 | 
						            required_settings: List of settings this bot type requires | 
					
					
						
						| 
							 | 
						            incompatible_with: List of bot types this one cannot be used with | 
					
					
						
						| 
							 | 
						            auto_add_settings: Settings to add if this bot is being run in test mode | 
					
					
						
						| 
							 | 
						        """ | 
					
					
						
						| 
							 | 
						        self.name = name | 
					
					
						
						| 
							 | 
						        self.settings_creator = settings_creator | 
					
					
						
						| 
							 | 
						        self.required_settings = required_settings or [] | 
					
					
						
						| 
							 | 
						        self.incompatible_with = incompatible_with or [] | 
					
					
						
						| 
							 | 
						        self.auto_add_settings = auto_add_settings or {} | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						    def has_test_mode(self, body: Dict[str, Any]) -> bool: | 
					
					
						
						| 
							 | 
						        """Check if this bot type is configured for test mode.""" | 
					
					
						
						| 
							 | 
						        return self.name in body and body[self.name].get("testInPrebuilt", False) | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						    def create_settings(self, body: Dict[str, Any]) -> Dict[str, Any]: | 
					
					
						
						| 
							 | 
						        """Create or update settings for this bot type.""" | 
					
					
						
						| 
							 | 
						        body[self.name] = self.settings_creator(body) | 
					
					
						
						| 
							 | 
						        return body | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						    def prepare_for_test(self, body: Dict[str, Any]) -> Dict[str, Any]: | 
					
					
						
						| 
							 | 
						        """Add required settings for test mode if they don't exist.""" | 
					
					
						
						| 
							 | 
						        for setting, default_value in self.auto_add_settings.items(): | 
					
					
						
						| 
							 | 
						            if setting not in body: | 
					
					
						
						| 
							 | 
						                body[setting] = default_value | 
					
					
						
						| 
							 | 
						        return body | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						class BotRegistry: | 
					
					
						
						| 
							 | 
						    """Registry for managing different bot types.""" | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						    def __init__(self): | 
					
					
						
						| 
							 | 
						        self.bots = {} | 
					
					
						
						| 
							 | 
						        self.bot_validation_rules = [] | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						    def register(self, bot_type: BotType): | 
					
					
						
						| 
							 | 
						        """Register a bot type.""" | 
					
					
						
						| 
							 | 
						        self.bots[bot_type.name] = bot_type | 
					
					
						
						| 
							 | 
						        return self | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						    def get_bot(self, name: str) -> BotType: | 
					
					
						
						| 
							 | 
						        """Get a bot type by name.""" | 
					
					
						
						| 
							 | 
						        return self.bots.get(name) | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						    def detect_bot_type(self, body: Dict[str, Any]) -> Optional[str]: | 
					
					
						
						| 
							 | 
						        """Detect which bot type to use based on configuration.""" | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        for name, bot in self.bots.items(): | 
					
					
						
						| 
							 | 
						            if bot.has_test_mode(body): | 
					
					
						
						| 
							 | 
						                return name | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        for name, bot in self.bots.items(): | 
					
					
						
						| 
							 | 
						            if name in body and all(req in body for req in bot.required_settings): | 
					
					
						
						| 
							 | 
						                return name | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        if "dialin_settings" in body: | 
					
					
						
						| 
							 | 
						            return DEFAULT_DIALIN_EXAMPLE | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						        return None | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						    def validate_bot_combination(self, body: Dict[str, Any]) -> List[str]: | 
					
					
						
						| 
							 | 
						        """Validate that bot types in the configuration are compatible.""" | 
					
					
						
						| 
							 | 
						        errors = [] | 
					
					
						
						| 
							 | 
						        bot_types_in_config = [name for name in self.bots.keys() if name in body] | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        for bot_name in bot_types_in_config: | 
					
					
						
						| 
							 | 
						            bot = self.bots[bot_name] | 
					
					
						
						| 
							 | 
						            for incompatible in bot.incompatible_with: | 
					
					
						
						| 
							 | 
						                if incompatible in body: | 
					
					
						
						| 
							 | 
						                    errors.append( | 
					
					
						
						| 
							 | 
						                        f"Cannot have both '{bot_name}' and '{incompatible}' in the same configuration" | 
					
					
						
						| 
							 | 
						                    ) | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						        return errors | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						    def setup_configuration(self, body: Dict[str, Any]) -> Dict[str, Any]: | 
					
					
						
						| 
							 | 
						        """Set up bot configuration based on detected bot type.""" | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        body = ensure_dialout_settings_array(body) | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        bot_type_name = self.detect_bot_type(body) | 
					
					
						
						| 
							 | 
						        if not bot_type_name: | 
					
					
						
						| 
							 | 
						            raise HTTPException( | 
					
					
						
						| 
							 | 
						                status_code=400, detail="Configuration doesn't match any supported scenario" | 
					
					
						
						| 
							 | 
						            ) | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        if "dialin_settings" in body and bot_type_name == DEFAULT_DIALIN_EXAMPLE: | 
					
					
						
						| 
							 | 
						            if bot_type_name not in body: | 
					
					
						
						| 
							 | 
						                body[bot_type_name] = {} | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        bot_type = self.get_bot(bot_type_name) | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        body = bot_type.create_settings(body) | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        if bot_type.has_test_mode(body): | 
					
					
						
						| 
							 | 
						            body = bot_type.prepare_for_test(body) | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        errors = self.validate_bot_combination(body) | 
					
					
						
						| 
							 | 
						        if errors: | 
					
					
						
						| 
							 | 
						            error_message = "Invalid configuration: " + "; ".join(errors) | 
					
					
						
						| 
							 | 
						            raise HTTPException(status_code=400, detail=error_message) | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						        return body | 
					
					
						
						| 
							 | 
						
 |