pipecat / bot_registry.py
Deadmon's picture
Upload 12 files
1366db9 verified
raw
history blame
5.09 kB
# bot_registry.py
"""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."""
# First check for test mode bots
for name, bot in self.bots.items():
if bot.has_test_mode(body):
return name
# Then check for specific combinations of settings
for name, bot in self.bots.items():
if name in body and all(req in body for req in bot.required_settings):
return name
# Default for dialin settings
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]
# Check each bot type against its incompatible list
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."""
# Ensure dialout_settings is an array if present
body = ensure_dialout_settings_array(body)
# Detect which bot type to use
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 we have a dialin scenario but no explicit bot type, add the default
if "dialin_settings" in body and bot_type_name == DEFAULT_DIALIN_EXAMPLE:
if bot_type_name not in body:
body[bot_type_name] = {}
# Get the bot type object
bot_type = self.get_bot(bot_type_name)
# Create/update settings for the bot type
body = bot_type.create_settings(body)
# If in test mode, add any required settings
if bot_type.has_test_mode(body):
body = bot_type.prepare_for_test(body)
# Validate bot combinations
errors = self.validate_bot_combination(body)
if errors:
error_message = "Invalid configuration: " + "; ".join(errors)
raise HTTPException(status_code=400, detail=error_message)
return body