"""Step 5B Integration: Enhanced Bot Orchestrator with Advanced Bot Management. This module demonstrates how the new advanced bot management features integrate with the existing bot orchestrator to provide: 1. AI Provider-powered bots with multiple backend support 2. Personality-driven bot behavior and responses 3. Conversation context and memory management 4. Dynamic bot configuration and health monitoring This integration enhances the existing bot discovery and management system without breaking compatibility with existing bot implementations. """ import os import json import time import asyncio from typing import Dict, Optional, Any from pathlib import Path # Import existing bot orchestrator functionality from bot_orchestrator import discover_bots # Import advanced bot management modules try: from voicebot.ai_providers import ai_provider_manager, AIProviderType from voicebot.personality_system import personality_manager from voicebot.conversation_context import context_manager AI_FEATURES_AVAILABLE = True except ImportError as e: print(f"Warning: Advanced AI features not available: {e}") AI_FEATURES_AVAILABLE = False from logger import logger class EnhancedBotOrchestrator: """Enhanced bot orchestrator with Step 5B advanced management features.""" def __init__(self): self.enhanced_bots = {} # Enhanced bots with AI features self.bot_configurations = {} # Bot-specific configurations self.health_stats = {} # Health monitoring data # Load configurations self._load_bot_configurations() # Initialize AI systems if available if AI_FEATURES_AVAILABLE: self._initialize_ai_systems() def _load_bot_configurations(self): """Load bot configurations from JSON file.""" config_path = Path(__file__).parent / "enhanced_bot_configs.json" default_configs = { "ai_chatbot": { "personality": "helpful_assistant", "ai_provider": "openai", "streaming": True, "memory_enabled": True, "advanced_features": True }, "technical_expert": { "personality": "technical_expert", "ai_provider": "anthropic", "streaming": False, "memory_enabled": True, "advanced_features": True }, "creative_companion": { "personality": "creative_companion", "ai_provider": "local", "streaming": True, "memory_enabled": True, "advanced_features": True } } try: if config_path.exists(): with open(config_path, 'r') as f: self.bot_configurations = json.load(f) else: self.bot_configurations = default_configs self._save_bot_configurations() except Exception as e: logger.error(f"Failed to load bot configurations: {e}") self.bot_configurations = default_configs def _save_bot_configurations(self): """Save bot configurations to JSON file.""" config_path = Path(__file__).parent / "enhanced_bot_configs.json" try: with open(config_path, 'w') as f: json.dump(self.bot_configurations, f, indent=2) except Exception as e: logger.error(f"Failed to save bot configurations: {e}") def _initialize_ai_systems(self): """Initialize AI provider and personality systems.""" try: # Ensure default personality templates are loaded personality_manager.ensure_default_templates() # Register available AI providers based on environment providers_to_init = [] if os.getenv("OPENAI_API_KEY"): providers_to_init.append(AIProviderType.OPENAI) if os.getenv("ANTHROPIC_API_KEY"): providers_to_init.append(AIProviderType.ANTHROPIC) # Local provider is always available providers_to_init.append(AIProviderType.LOCAL) for provider_type in providers_to_init: try: provider = ai_provider_manager.create_provider(provider_type) ai_provider_manager.register_provider(f"system_{provider_type.value}", provider) logger.info(f"Initialized AI provider: {provider_type.value}") except Exception as e: logger.warning(f"Failed to initialize provider {provider_type.value}: {e}") logger.info("AI systems initialized successfully") except Exception as e: logger.error(f"Failed to initialize AI systems: {e}") async def discover_enhanced_bots(self) -> Dict[str, Dict[str, Any]]: """Discover bots with enhanced information about AI capabilities.""" # Start with standard bot discovery standard_bots = discover_bots() # Returns List[BotInfoModel] enhanced_bot_info = {} # Convert BotInfoModel list to dict and enhance with AI capabilities for bot_info in standard_bots: bot_name = bot_info.name bot_info_dict = { "name": bot_name, "description": bot_info.description, "has_media": bot_info.has_media, "standard_info": { "name": bot_name, "description": bot_info.description, "has_media": bot_info.has_media }, "enhanced_features": False, "ai_capabilities": {}, "health_status": "unknown" } # Check if bot supports enhanced features if bot_name in self.bot_configurations: config = self.bot_configurations[bot_name] if config.get("advanced_features", False): bot_info_dict["enhanced_features"] = True bot_info_dict["ai_capabilities"] = { "personality": config.get("personality", "default"), "ai_provider": config.get("ai_provider", "local"), "streaming": config.get("streaming", False), "memory_enabled": config.get("memory_enabled", False) } # Check bot health if it supports it (would need to import the bot module) try: bot_module_path = f"voicebot.bots.{bot_name}" bot_module = __import__(bot_module_path, fromlist=[bot_name]) if hasattr(bot_module, 'get_bot_status'): status = await bot_module.get_bot_status() bot_info_dict["health_status"] = "healthy" bot_info_dict["detailed_status"] = status except Exception as e: bot_info_dict["health_status"] = f"import_error: {e}" enhanced_bot_info[bot_name] = bot_info_dict return enhanced_bot_info async def create_enhanced_bot_instance(self, bot_name: str, session_name: str) -> Optional[Any]: """Create an enhanced bot instance with AI features configured.""" if not AI_FEATURES_AVAILABLE: logger.warning(f"Cannot create enhanced bot {bot_name} - AI features not available") return None if bot_name not in self.bot_configurations: logger.warning(f"No configuration found for enhanced bot: {bot_name}") return None config = self.bot_configurations[bot_name] try: # Set environment variables for the bot based on configuration os.environ[f"{bot_name.upper()}_PERSONALITY"] = config.get("personality", "helpful_assistant") os.environ[f"{bot_name.upper()}_PROVIDER"] = config.get("ai_provider", "local") os.environ[f"{bot_name.upper()}_STREAMING"] = str(config.get("streaming", False)).lower() os.environ[f"{bot_name.upper()}_MEMORY"] = str(config.get("memory_enabled", False)).lower() # Import and create the bot bot_module_path = f"voicebot.bots.{bot_name}" bot_module = __import__(bot_module_path, fromlist=[bot_name]) # If the bot has a specific initialization function, use it if hasattr(bot_module, 'create_enhanced_instance'): bot_instance = await bot_module.create_enhanced_instance(session_name, config) else: # Create standard bot instance bot_instance = bot_module self.enhanced_bots[f"{bot_name}_{session_name}"] = { "instance": bot_instance, "config": config, "session": session_name, "created_at": time.time() } logger.info(f"Created enhanced bot instance: {bot_name} for session {session_name}") return bot_instance except Exception as e: logger.error(f"Failed to create enhanced bot instance {bot_name}: {e}") return None async def monitor_bot_health(self) -> Dict[str, Any]: """Monitor health of all enhanced bots and AI systems.""" health_report = { "timestamp": time.time(), "ai_systems_available": AI_FEATURES_AVAILABLE, "enhanced_bots": {}, "ai_providers": {}, "personality_system": {}, "conversation_contexts": {} } if not AI_FEATURES_AVAILABLE: health_report["status"] = "limited - AI features disabled" return health_report try: # Check AI providers for provider_id, provider in ai_provider_manager.list_providers().items(): try: provider_instance = ai_provider_manager.get_provider(provider_id) if provider_instance: is_healthy = await provider_instance.health_check() health_report["ai_providers"][provider_id] = { "status": "healthy" if is_healthy else "unhealthy", "type": provider.value if hasattr(provider, 'value') else str(provider) } except Exception as e: health_report["ai_providers"][provider_id] = { "status": f"error: {e}", "type": "unknown" } # Check personality system try: templates = personality_manager.list_templates() health_report["personality_system"] = { "status": "healthy", "available_templates": len(templates), "template_ids": [t.id for t in templates] } except Exception as e: health_report["personality_system"] = { "status": f"error: {e}" } # Check conversation context system try: context_stats = context_manager.get_statistics() health_report["conversation_contexts"] = { "status": "healthy", "statistics": context_stats } except Exception as e: health_report["conversation_contexts"] = { "status": f"error: {e}" } # Check enhanced bot instances for bot_key, bot_data in self.enhanced_bots.items(): try: bot_instance = bot_data["instance"] if hasattr(bot_instance, 'health_check'): bot_health = await bot_instance.health_check() health_report["enhanced_bots"][bot_key] = { "status": "healthy", "details": bot_health, "uptime": time.time() - bot_data["created_at"] } else: health_report["enhanced_bots"][bot_key] = { "status": "unknown - no health check available", "uptime": time.time() - bot_data["created_at"] } except Exception as e: health_report["enhanced_bots"][bot_key] = { "status": f"error: {e}", "uptime": time.time() - bot_data.get("created_at", time.time()) } health_report["status"] = "operational" except Exception as e: health_report["status"] = f"system_error: {e}" # Store health stats for trending self.health_stats[int(time.time())] = health_report # Keep only last 24 hours of health stats cutoff_time = time.time() - (24 * 60 * 60) self.health_stats = { timestamp: stats for timestamp, stats in self.health_stats.items() if timestamp > cutoff_time } return health_report async def configure_bot_runtime(self, bot_name: str, new_config: Dict[str, Any]) -> bool: """Dynamically reconfigure a bot at runtime.""" if bot_name not in self.bot_configurations: logger.error(f"Bot {bot_name} not found in configurations") return False try: # Update configuration old_config = self.bot_configurations[bot_name].copy() self.bot_configurations[bot_name].update(new_config) # Save updated configuration self._save_bot_configurations() # If there are active instances, try to update them updated_instances = [] for bot_key, bot_data in self.enhanced_bots.items(): if bot_key.startswith(f"{bot_name}_"): bot_instance = bot_data["instance"] # Try to update personality if changed if "personality" in new_config and hasattr(bot_instance, 'switch_personality'): success = await bot_instance.switch_personality(new_config["personality"]) if success: updated_instances.append(f"{bot_key} personality") # Try to update AI provider if changed if "ai_provider" in new_config and hasattr(bot_instance, 'switch_ai_provider'): success = await bot_instance.switch_ai_provider(new_config["ai_provider"]) if success: updated_instances.append(f"{bot_key} provider") # Update bot data configuration bot_data["config"] = self.bot_configurations[bot_name] logger.info(f"Bot {bot_name} configuration updated. Active instances updated: {updated_instances}") return True except Exception as e: # Rollback configuration on error self.bot_configurations[bot_name] = old_config logger.error(f"Failed to configure bot {bot_name}: {e}") return False def get_bot_analytics(self) -> Dict[str, Any]: """Get analytics and usage statistics for enhanced bots.""" analytics = { "enhanced_bots_count": len(self.enhanced_bots), "configurations_count": len(self.bot_configurations), "health_history_points": len(self.health_stats), "bot_breakdown": {}, "feature_usage": { "ai_providers": {}, "personalities": {}, "memory_enabled": 0, "streaming_enabled": 0 } } # Analyze bot configurations for bot_name, config in self.bot_configurations.items(): analytics["bot_breakdown"][bot_name] = { "enhanced_features": config.get("advanced_features", False), "ai_provider": config.get("ai_provider", "none"), "personality": config.get("personality", "none"), "active_instances": sum(1 for key in self.enhanced_bots.keys() if key.startswith(f"{bot_name}_")) } # Count feature usage provider = config.get("ai_provider", "none") analytics["feature_usage"]["ai_providers"][provider] = analytics["feature_usage"]["ai_providers"].get(provider, 0) + 1 personality = config.get("personality", "none") analytics["feature_usage"]["personalities"][personality] = analytics["feature_usage"]["personalities"].get(personality, 0) + 1 if config.get("memory_enabled", False): analytics["feature_usage"]["memory_enabled"] += 1 if config.get("streaming", False): analytics["feature_usage"]["streaming_enabled"] += 1 # Add conversation context statistics if available if AI_FEATURES_AVAILABLE: try: context_stats = context_manager.get_statistics() analytics["conversation_statistics"] = context_stats except Exception as e: analytics["conversation_statistics"] = {"error": str(e)} return analytics # Global enhanced orchestrator instance enhanced_orchestrator = EnhancedBotOrchestrator() async def demo_step_5b_integration(): """Demonstrate Step 5B integration capabilities.""" print("=== Step 5B Advanced Bot Management Demo ===\n") # 1. Discover enhanced bots print("1. Discovering bots with enhanced capabilities...") enhanced_bots = await enhanced_orchestrator.discover_enhanced_bots() for bot_name, info in enhanced_bots.items(): print(f" Bot: {bot_name}") print(f" Enhanced: {info['enhanced_features']}") if info['enhanced_features']: print(f" AI Capabilities: {info['ai_capabilities']}") print(f" Health: {info['health_status']}") print() # 2. Create enhanced bot instance print("2. Creating enhanced AI chatbot instance...") bot_instance = await enhanced_orchestrator.create_enhanced_bot_instance("ai_chatbot", "demo_session") if bot_instance: print(" ✓ Enhanced AI chatbot created successfully") else: print(" ✗ Failed to create enhanced bot") print() # 3. Monitor system health print("3. Monitoring system health...") health_report = await enhanced_orchestrator.monitor_bot_health() print(f" System Status: {health_report['status']}") print(f" AI Features Available: {health_report['ai_systems_available']}") if health_report['ai_systems_available']: print(f" AI Providers: {len(health_report['ai_providers'])} registered") print(f" Personality Templates: {health_report['personality_system'].get('available_templates', 0)}") print(f" Enhanced Bot Instances: {len(health_report['enhanced_bots'])}") print() # 4. Runtime configuration print("4. Demonstrating runtime configuration...") config_success = await enhanced_orchestrator.configure_bot_runtime("ai_chatbot", { "personality": "technical_expert", "streaming": False }) print(f" Configuration Update: {'✓ Success' if config_success else '✗ Failed'}") print() # 5. Analytics print("5. Bot analytics and usage statistics...") analytics = enhanced_orchestrator.get_bot_analytics() print(f" Enhanced Bots: {analytics['enhanced_bots_count']}") print(f" Configurations: {analytics['configurations_count']}") print(" Feature Usage:") for feature, usage in analytics['feature_usage'].items(): print(f" {feature}: {usage}") print() print("=== Step 5B Integration Demo Complete ===") if __name__ == "__main__": # Run the demo asyncio.run(demo_step_5b_integration())