| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988 |
- """
- Application Container and Factory System for Trixy
- This module provides the central ApplicationContainer class that implements:
- - Dependency injection pattern with lazy loading
- - Component lifecycle management (initialization, startup, shutdown)
- - Thread-safe singleton pattern for different deployment modes
- - Integration with event system and configuration system
- - Component health monitoring and status reporting
- - Graceful shutdown with proper cleanup
- The ApplicationContainer serves as the central hub for all Trixy components,
- providing easy access to:
- - Event handler system
- - Configuration manager
- - Plugin system
- - Satellite manager
- - And all other core components
- Usage:
- from trixy_core.application import ApplicationContainer
-
- # Create application container
- application = ApplicationContainer(
- mode="server",
- config_path="config/server_config.json",
- debug_mode=False
- )
-
- # Start the application
- application.start()
-
- # Access components
- event_handler = application.get_event_handler()
- plugin_system = application.get_plugin_system()
- satellite_manager = application.get_satellite_manager()
- config_manager = application.get_config_manager()
-
- # Graceful shutdown
- application.shutdown()
- """
- import os
- import sys
- import threading
- import time
- import traceback
- import weakref
- from typing import Dict, Any, Optional, Set, List, Type, Callable, Union
- from pathlib import Path
- from dataclasses import dataclass
- from enum import Enum
- import json
- from pprint import pformat
- # Import configuration system
- try:
- from .config import (
- ConfigManager,
- DeploymentMode,
- ServerConfig,
- ClientConfig,
- StandaloneConfig,
- ConfigurationError
- )
- from .config import pprint as config_pprint
- except ImportError as e:
- print(f"Failed to import configuration system: {e}")
- sys.exit(1)
- # Import event system
- try:
- from .events import (
- EventHandler,
- TrixyEvent,
- SystemStartupEventData,
- SystemShutdownEventData,
- EventDataFactory
- )
- from .events import pprint as events_pprint
- except ImportError as e:
- print(f"Failed to import event system: {e}")
- sys.exit(1)
- class ComponentStatus(Enum):
- """Status of individual components in the application container."""
- NOT_INITIALIZED = "not_initialized"
- INITIALIZING = "initializing"
- INITIALIZED = "initialized"
- STARTING = "starting"
- RUNNING = "running"
- STOPPING = "stopping"
- STOPPED = "stopped"
- ERROR = "error"
- class ApplicationStatus(Enum):
- """Overall status of the application container."""
- NOT_STARTED = "not_started"
- STARTING = "starting"
- RUNNING = "running"
- SHUTTING_DOWN = "shutting_down"
- STOPPED = "stopped"
- ERROR = "error"
- @dataclass
- class ComponentHealth:
- """Health information for a component."""
- name: str
- status: ComponentStatus
- last_check: float
- errors: List[str]
- warnings: List[str]
- info: Dict[str, Any]
-
- def is_healthy(self) -> bool:
- """Check if component is healthy."""
- return self.status in [ComponentStatus.INITIALIZED, ComponentStatus.RUNNING] and len(self.errors) == 0
- class ApplicationContainerError(Exception):
- """Base exception for application container errors."""
- pass
- class ComponentInitializationError(ApplicationContainerError):
- """Raised when a component fails to initialize."""
- pass
- class ComponentLifecycleError(ApplicationContainerError):
- """Raised when a component lifecycle operation fails."""
- pass
- def pprint(message: str) -> None:
- """
- Application container logging function that adapts based on mode.
- Uses the same pattern as specified in CLAUDE.md.
- """
- # For now use simple printing - will integrate with proper logging
- print(f"[APPLICATION] {message}")
- class ApplicationContainer:
- """
- Central application container providing dependency injection and lifecycle management.
-
- This is a thread-safe singleton that manages all core components of the Trixy application.
- It supports three deployment modes: server, client, and standalone.
-
- Features:
- - Lazy component initialization
- - Dependency injection
- - Component lifecycle management
- - Health monitoring
- - Graceful shutdown
- - Thread-safe access
- - Event-driven architecture
- """
-
- _instances: Dict[str, 'ApplicationContainer'] = {}
- _lock = threading.RLock()
-
- def __init__(
- self,
- mode: Union[str, DeploymentMode],
- config_path: Optional[str] = None,
- debug_mode: bool = False,
- **kwargs
- ):
- """
- Initialize the application container.
-
- Args:
- mode: Deployment mode (server, client, standalone)
- config_path: Path to configuration file
- debug_mode: Enable debug mode
- **kwargs: Additional configuration overrides
- """
- # Convert string mode to enum
- if isinstance(mode, str):
- mode = DeploymentMode(mode.lower())
-
- self._mode = mode
- self._config_path = config_path
- self._debug_mode = debug_mode
- self._kwargs = kwargs
-
- # Application state
- self._status = ApplicationStatus.NOT_STARTED
- self._startup_time: Optional[float] = None
- self._shutdown_time: Optional[float] = None
-
- # Component storage
- self._components: Dict[str, Any] = {}
- self._component_factories: Dict[str, Callable] = {}
- self._component_health: Dict[str, ComponentHealth] = {}
- self._component_dependencies: Dict[str, Set[str]] = {}
- self._component_lock = threading.RLock()
-
- # Core components that will be initialized
- self._config_manager: Optional[ConfigManager] = None
- self._event_handler: Optional[EventHandler] = None
-
- # Initialize logging
- self._init_logging()
-
- pprint(f"ApplicationContainer created for {mode.value} mode")
-
- # Register component factories
- self._register_component_factories()
-
- # Store in singleton registry
- with self._lock:
- instance_key = f"{mode.value}_{id(self)}"
- self._instances[instance_key] = self
- self._instance_key = instance_key
-
- def _init_logging(self) -> None:
- """Initialize logging for the application container."""
- # This will be enhanced when integrated with the main logging system
- pass
-
- def _register_component_factories(self) -> None:
- """Register factory methods for all components."""
- self._component_factories = {
- 'config_manager': self._create_config_manager,
- 'event_handler': self._create_event_handler,
- 'plugin_system': self._create_plugin_system,
- 'satellite_manager': self._create_satellite_manager,
- 'network_manager': self._create_network_manager,
- 'asset_manager': self._create_asset_manager,
- 'scheduler': self._create_scheduler,
- 'conversation_manager': self._create_conversation_manager,
- 'arbitration_manager': self._create_arbitration_manager,
- }
-
- # Define component dependencies
- self._component_dependencies = {
- 'config_manager': set(), # No dependencies
- 'event_handler': {'config_manager'},
- 'plugin_system': {'config_manager', 'event_handler'},
- 'satellite_manager': {'config_manager', 'event_handler'},
- 'network_manager': {'config_manager', 'event_handler'},
- 'asset_manager': {'config_manager'},
- 'scheduler': {'config_manager', 'event_handler'},
- 'conversation_manager': {'config_manager', 'event_handler'},
- 'arbitration_manager': {'config_manager', 'event_handler'},
- }
-
- pprint(f"Registered {len(self._component_factories)} component factories")
-
- def start(self) -> None:
- """
- Start the application container and all components.
-
- This performs:
- 1. Configuration loading and validation
- 2. Core component initialization
- 3. Component startup in dependency order
- 4. Event system startup
- 5. Application ready signal
-
- Raises:
- ApplicationContainerError: If startup fails
- """
- if self._status != ApplicationStatus.NOT_STARTED:
- raise ApplicationContainerError(f"Application already started (status: {self._status.value})")
-
- pprint("Starting application container...")
- self._status = ApplicationStatus.STARTING
- self._startup_time = time.time()
-
- try:
- # Initialize configuration first (it has no dependencies)
- config_manager = self.get_config_manager()
- pprint("Configuration manager initialized")
-
- # Initialize event handler early (depends on config)
- event_handler = self.get_event_handler()
- pprint("Event handler initialized")
-
- # Trigger startup event
- startup_data = EventDataFactory.create_event_data(
- "system_startup",
- mode=self._mode.value,
- config_file=self._config_path or "default",
- debug_mode=self._debug_mode,
- startup_time_ms=self._startup_time * 1000
- )
- event_handler.trigger_event("system_startup", startup_data)
-
- # Initialize other components based on mode
- self._initialize_mode_specific_components()
-
- self._status = ApplicationStatus.RUNNING
- pprint(f"Application container started successfully in {time.time() - self._startup_time:.2f} seconds")
-
- except Exception as e:
- self._status = ApplicationStatus.ERROR
- pprint(f"Failed to start application container: {e}")
- if self._debug_mode:
- pprint(traceback.format_exc())
- raise ApplicationContainerError(f"Application startup failed: {e}") from e
-
- def _initialize_mode_specific_components(self) -> None:
- """Initialize components specific to the current deployment mode."""
- if self._mode == DeploymentMode.SERVER:
- pprint("Initializing server mode components...")
- self.get_plugin_system()
- self.get_satellite_manager()
- self.get_network_manager()
- self.get_scheduler()
- self.get_conversation_manager()
- self.get_arbitration_manager()
-
- elif self._mode == DeploymentMode.CLIENT:
- pprint("Initializing client mode components...")
- self.get_network_manager()
- # Client mode has minimal components
-
- elif self._mode == DeploymentMode.STANDALONE:
- pprint("Initializing standalone mode components...")
- self.get_plugin_system()
- self.get_network_manager()
- self.get_scheduler()
- self.get_conversation_manager()
- # Standalone has most components but no satellite manager
-
- # Asset manager is available in all modes
- self.get_asset_manager()
-
- def shutdown(self) -> None:
- """
- Gracefully shutdown the application container.
-
- This performs:
- 1. Component shutdown in reverse dependency order
- 2. Resource cleanup
- 3. Event system shutdown
- 4. Final status update
- """
- if self._status in [ApplicationStatus.SHUTTING_DOWN, ApplicationStatus.STOPPED]:
- pprint("Application container already shutting down or stopped")
- return
-
- pprint("Shutting down application container...")
- self._status = ApplicationStatus.SHUTTING_DOWN
- self._shutdown_time = time.time()
-
- try:
- # Trigger shutdown event if event handler is available
- if self._event_handler:
- shutdown_data = EventDataFactory.create_event_data(
- "system_shutdown",
- reason="normal_shutdown",
- graceful_shutdown=True,
- uptime_seconds=self._shutdown_time - (self._startup_time or 0)
- )
- self._event_handler.trigger_event("system_shutdown", shutdown_data)
-
- # Shutdown components in reverse dependency order
- shutdown_order = self._get_shutdown_order()
- for component_name in shutdown_order:
- self._shutdown_component(component_name)
-
- self._status = ApplicationStatus.STOPPED
- pprint("Application container shutdown completed")
-
- except Exception as e:
- self._status = ApplicationStatus.ERROR
- pprint(f"Error during shutdown: {e}")
- if self._debug_mode:
- pprint(traceback.format_exc())
- finally:
- # Clean up from singleton registry
- with self._lock:
- if hasattr(self, '_instance_key') and self._instance_key in self._instances:
- del self._instances[self._instance_key]
-
- def _get_shutdown_order(self) -> List[str]:
- """Get the order in which components should be shut down (reverse of dependency order)."""
- # For now, use a simple reverse order
- # This could be enhanced with proper topological sorting
- order = [
- 'arbitration_manager',
- 'conversation_manager',
- 'scheduler',
- 'asset_manager',
- 'network_manager',
- 'satellite_manager',
- 'plugin_system',
- 'event_handler',
- 'config_manager'
- ]
- return [name for name in order if name in self._components]
-
- def _shutdown_component(self, component_name: str) -> None:
- """Shutdown a specific component safely."""
- if component_name not in self._components:
- return
-
- component = self._components[component_name]
-
- try:
- pprint(f"Shutting down {component_name}...")
-
- # Call shutdown method if available
- if hasattr(component, 'shutdown'):
- component.shutdown()
- elif hasattr(component, 'stop'):
- component.stop()
- elif hasattr(component, 'close'):
- component.close()
-
- # Update health status
- if component_name in self._component_health:
- self._component_health[component_name].status = ComponentStatus.STOPPED
-
- pprint(f"{component_name} shutdown completed")
-
- except Exception as e:
- pprint(f"Error shutting down {component_name}: {e}")
- if self._debug_mode:
- pprint(traceback.format_exc())
-
- # Component Factory Methods
-
- def _create_config_manager(self) -> ConfigManager:
- """Create and initialize the configuration manager."""
- pprint("Creating configuration manager...")
-
- config_manager = ConfigManager(
- mode=self._mode,
- config_file=self._config_path,
- debug_mode=self._debug_mode
- )
-
- # Load configuration
- config = config_manager.load_config()
- pprint(f"Configuration loaded successfully for {self._mode.value} mode")
-
- self._update_component_health('config_manager', ComponentStatus.INITIALIZED)
- return config_manager
-
- def _create_event_handler(self) -> EventHandler:
- """Create and initialize the event handler."""
- pprint("Creating event handler...")
-
- # Get configuration for event handler settings
- config_manager = self.get_config_manager()
- config = config_manager.get_config()
-
- # Create event handler with configuration
- event_handler = EventHandler(
- max_history_size=getattr(config.logging, 'max_event_history', 1000)
- )
-
- # Enable debug mode if needed
- if self._debug_mode:
- event_handler.enable_debug_mode(True)
-
- pprint("Event handler created successfully")
-
- self._update_component_health('event_handler', ComponentStatus.INITIALIZED)
- return event_handler
-
- def _create_plugin_system(self):
- """Create and initialize the plugin system."""
- pprint("Creating plugin system...")
-
- try:
- # Import the real plugin manager
- from .plugins import PluginManager
-
- # Get configuration for plugin system
- config_manager = self.get_config_manager()
- config = config_manager.get_config()
-
- # Create plugin manager with configuration
- plugin_manager = PluginManager(
- application=self,
- plugins_directory=getattr(config, 'plugins_directory', None),
- auto_load=getattr(config, 'auto_load_plugins', True),
- enable_hot_reload=self._debug_mode, # Enable hot-reload in debug mode
- health_check_interval=getattr(config, 'plugin_health_check_interval', 60.0),
- max_error_count=getattr(config, 'plugin_max_error_count', 5)
- )
-
- pprint("Plugin manager created successfully")
- self._update_component_health('plugin_system', ComponentStatus.INITIALIZED)
- return plugin_manager
-
- except ImportError as e:
- pprint(f"Failed to import PluginManager: {e}")
- # Fall back to placeholder
- plugin_system = PluginSystemPlaceholder(self)
- self._update_component_health('plugin_system', ComponentStatus.INITIALIZED)
- return plugin_system
- except Exception as e:
- pprint(f"Failed to create plugin manager: {e}")
- # Fall back to placeholder on any error
- plugin_system = PluginSystemPlaceholder(self)
- self._update_component_health('plugin_system', ComponentStatus.ERROR, errors=[str(e)])
- return plugin_system
-
- def _create_satellite_manager(self):
- """Create and initialize the satellite manager."""
- pprint("Creating satellite manager...")
-
- # Satellite manager will be implemented in future
- # For now, return a placeholder
- satellite_manager = SatelliteManagerPlaceholder(self)
-
- self._update_component_health('satellite_manager', ComponentStatus.INITIALIZED)
- return satellite_manager
-
- def _create_network_manager(self):
- """Create and initialize the network manager."""
- pprint("Creating network manager...")
-
- # Import the actual network manager
- try:
- from .network import NetworkManager
- network_manager = NetworkManager(self)
- pprint("Network manager created successfully")
- except ImportError as e:
- pprint(f"Failed to import NetworkManager: {e}")
- # Fall back to placeholder for now
- network_manager = NetworkManagerPlaceholder(self)
-
- self._update_component_health('network_manager', ComponentStatus.INITIALIZED)
- return network_manager
-
- def _create_asset_manager(self):
- """Create and initialize the asset manager."""
- pprint("Creating asset manager...")
-
- try:
- # Import the real asset manager
- from .assets import AssetManager, AssetManagerConfig, ProfileMode
-
- # Get configuration for asset manager
- config_manager = self.get_config_manager()
- config = config_manager.get_config()
-
- # Determine assets directory and profile from configuration
- assets_directory = getattr(config, 'assets_directory', './assets')
- asset_profile = getattr(config, 'asset_profile', None)
-
- # Create asset manager configuration
- asset_config = AssetManagerConfig(
- assets_directory=assets_directory,
- current_profile=asset_profile,
- cache_enabled=getattr(config, 'asset_cache_enabled', True),
- cache_max_size=getattr(config, 'asset_cache_max_size', 100),
- cache_max_memory=getattr(config, 'asset_cache_max_memory', 100 * 1024 * 1024),
- validation_enabled=getattr(config, 'asset_validation_enabled', True),
- hot_reload_enabled=self._debug_mode, # Enable hot-reload in debug mode
- hot_reload_debounce=getattr(config, 'asset_hot_reload_debounce', 0.5),
- preload_patterns=getattr(config, 'asset_preload_patterns', [
- "audio/success.wav",
- "audio/error.wav",
- "audio/notification.wav",
- "audio/wakeword_detected.wav"
- ]),
- profile_mode=ProfileMode.FALLBACK,
- thread_pool_size=getattr(config, 'asset_thread_pool_size', 4),
- enable_integrity_checks=getattr(config, 'asset_integrity_checks', True),
- auto_create_directories=True,
- compress_cached_assets=getattr(config, 'asset_cache_compression', False),
- asset_timeout=getattr(config, 'asset_timeout', 30.0)
- )
-
- # Create asset manager with configuration
- asset_manager = AssetManager(
- application_container=self,
- config=asset_config
- )
-
- pprint("Asset manager created successfully")
- self._update_component_health('asset_manager', ComponentStatus.INITIALIZED)
- return asset_manager
-
- except ImportError as e:
- pprint(f"Failed to import AssetManager: {e}")
- # Fall back to placeholder
- asset_manager = AssetManagerPlaceholder(self)
- self._update_component_health('asset_manager', ComponentStatus.INITIALIZED)
- return asset_manager
- except Exception as e:
- pprint(f"Failed to create asset manager: {e}")
- # Fall back to placeholder on any error
- asset_manager = AssetManagerPlaceholder(self)
- self._update_component_health('asset_manager', ComponentStatus.ERROR, errors=[str(e)])
- return asset_manager
-
- def _create_scheduler(self):
- """Create and initialize the scheduler."""
- pprint("Creating scheduler...")
-
- # Scheduler will be implemented in future
- # For now, return a placeholder
- scheduler = SchedulerPlaceholder(self)
-
- self._update_component_health('scheduler', ComponentStatus.INITIALIZED)
- return scheduler
-
- def _create_conversation_manager(self):
- """Create and initialize the conversation manager."""
- pprint("Creating conversation manager...")
-
- try:
- # Import the real conversation manager
- from .conversation import ConversationManager
-
- # Get configuration for conversation manager
- config_manager = self.get_config_manager()
- config = config_manager.get_config()
-
- # Create conversation manager with configuration
- conversation_manager = ConversationManager(
- application=self,
- max_concurrent_conversations=getattr(config, 'max_concurrent_conversations', 100),
- default_conversation_timeout=getattr(config, 'default_conversation_timeout', 300.0),
- plugin_question_timeout=getattr(config, 'plugin_question_timeout', 30.0),
- cleanup_interval_seconds=getattr(config, 'conversation_cleanup_interval', 60.0),
- max_conversation_history=getattr(config, 'max_conversation_history', 1000),
- enable_context_persistence=getattr(config, 'enable_conversation_persistence', False),
- context_storage_path=getattr(config, 'conversation_storage_path', None),
- enable_analytics=getattr(config, 'enable_conversation_analytics', True)
- )
-
- pprint("Conversation manager created successfully")
- self._update_component_health('conversation_manager', ComponentStatus.INITIALIZED)
- return conversation_manager
-
- except ImportError as e:
- pprint(f"Failed to import ConversationManager: {e}")
- # Fall back to placeholder
- conversation_manager = ConversationManagerPlaceholder(self)
- self._update_component_health('conversation_manager', ComponentStatus.INITIALIZED)
- return conversation_manager
- except Exception as e:
- pprint(f"Failed to create conversation manager: {e}")
- # Fall back to placeholder on any error
- conversation_manager = ConversationManagerPlaceholder(self)
- self._update_component_health('conversation_manager', ComponentStatus.ERROR, errors=[str(e)])
- return conversation_manager
-
- def _create_arbitration_manager(self):
- """Create and initialize the arbitration manager."""
- pprint("Creating arbitration manager...")
-
- # Arbitration manager will be implemented in future
- # For now, return a placeholder
- arbitration_manager = ArbitrationManagerPlaceholder(self)
-
- self._update_component_health('arbitration_manager', ComponentStatus.INITIALIZED)
- return arbitration_manager
-
- # Component Access Methods (Public API as specified in CLAUDE.md)
-
- def get_config_manager(self) -> ConfigManager:
- """Get the configuration manager instance."""
- return self._get_component('config_manager')
-
- def get_event_handler(self) -> EventHandler:
- """Get the event handler instance."""
- return self._get_component('event_handler')
-
- def get_plugin_system(self):
- """Get the plugin system instance."""
- return self._get_component('plugin_system')
-
- def get_satellite_manager(self):
- """Get the satellite manager instance."""
- return self._get_component('satellite_manager')
-
- def get_network_manager(self):
- """Get the network manager instance."""
- return self._get_component('network_manager')
-
- def get_asset_manager(self):
- """Get the asset manager instance."""
- return self._get_component('asset_manager')
-
- def get_scheduler(self):
- """Get the scheduler instance."""
- return self._get_component('scheduler')
-
- def get_conversation_manager(self):
- """Get the conversation manager instance."""
- return self._get_component('conversation_manager')
-
- def get_arbitration_manager(self):
- """Get the arbitration manager instance."""
- return self._get_component('arbitration_manager')
-
- def _get_component(self, component_name: str):
- """
- Get a component instance, creating it if necessary (lazy loading).
-
- Args:
- component_name: Name of the component
-
- Returns:
- Component instance
-
- Raises:
- ComponentInitializationError: If component creation fails
- """
- with self._component_lock:
- # Return existing component if available
- if component_name in self._components:
- return self._components[component_name]
-
- # Check if factory exists
- if component_name not in self._component_factories:
- raise ComponentInitializationError(f"No factory registered for component: {component_name}")
-
- # Initialize dependencies first
- self._ensure_dependencies(component_name)
-
- # Create the component
- try:
- self._update_component_health(component_name, ComponentStatus.INITIALIZING)
-
- factory = self._component_factories[component_name]
- component = factory()
-
- self._components[component_name] = component
- self._update_component_health(component_name, ComponentStatus.RUNNING)
-
- pprint(f"Component '{component_name}' initialized successfully")
- return component
-
- except Exception as e:
- self._update_component_health(component_name, ComponentStatus.ERROR, errors=[str(e)])
- raise ComponentInitializationError(f"Failed to initialize {component_name}: {e}") from e
-
- def _ensure_dependencies(self, component_name: str) -> None:
- """Ensure all dependencies for a component are initialized."""
- if component_name not in self._component_dependencies:
- return
-
- dependencies = self._component_dependencies[component_name]
- for dep_name in dependencies:
- if dep_name not in self._components:
- pprint(f"Initializing dependency '{dep_name}' for '{component_name}'")
- self._get_component(dep_name)
-
- def _update_component_health(
- self,
- component_name: str,
- status: ComponentStatus,
- errors: Optional[List[str]] = None,
- warnings: Optional[List[str]] = None,
- info: Optional[Dict[str, Any]] = None
- ) -> None:
- """Update the health status of a component."""
- self._component_health[component_name] = ComponentHealth(
- name=component_name,
- status=status,
- last_check=time.time(),
- errors=errors or [],
- warnings=warnings or [],
- info=info or {}
- )
-
- # Health and Status Methods
-
- def get_status(self) -> ApplicationStatus:
- """Get the current application status."""
- return self._status
-
- def get_health_report(self) -> Dict[str, Any]:
- """
- Get a comprehensive health report for the application and all components.
-
- Returns:
- Dictionary containing health information
- """
- return {
- 'application': {
- 'status': self._status.value,
- 'mode': self._mode.value,
- 'uptime': time.time() - (self._startup_time or time.time()),
- 'startup_time': self._startup_time,
- 'debug_mode': self._debug_mode,
- },
- 'components': {
- name: {
- 'status': health.status.value,
- 'is_healthy': health.is_healthy(),
- 'last_check': health.last_check,
- 'errors': health.errors,
- 'warnings': health.warnings,
- 'info': health.info
- }
- for name, health in self._component_health.items()
- },
- 'summary': {
- 'total_components': len(self._component_health),
- 'healthy_components': sum(1 for h in self._component_health.values() if h.is_healthy()),
- 'error_components': sum(1 for h in self._component_health.values() if h.status == ComponentStatus.ERROR),
- }
- }
-
- def is_healthy(self) -> bool:
- """Check if the application and all components are healthy."""
- if self._status != ApplicationStatus.RUNNING:
- return False
-
- return all(health.is_healthy() for health in self._component_health.values())
-
- # Utility Methods
-
- def get_version(self) -> str:
- """Get the application version."""
- return "1.0.0"
-
- def get_mode(self) -> DeploymentMode:
- """Get the deployment mode."""
- return self._mode
-
- def is_debug_mode(self) -> bool:
- """Check if debug mode is enabled."""
- return self._debug_mode
-
- def get_uptime(self) -> float:
- """Get application uptime in seconds."""
- if self._startup_time is None:
- return 0.0
- return time.time() - self._startup_time
-
- @classmethod
- def get_instance(cls, mode: Union[str, DeploymentMode] = None) -> Optional['ApplicationContainer']:
- """
- Get an existing application container instance.
-
- Args:
- mode: Deployment mode to find (optional)
-
- Returns:
- ApplicationContainer instance or None if not found
- """
- with cls._lock:
- if mode is None:
- # Return any available instance
- return next(iter(cls._instances.values()), None)
-
- if isinstance(mode, str):
- mode = DeploymentMode(mode.lower())
-
- # Find instance with matching mode
- for instance in cls._instances.values():
- if instance._mode == mode:
- return instance
-
- return None
-
- @classmethod
- def cleanup_instances(cls) -> None:
- """Clean up all application container instances."""
- with cls._lock:
- for instance in list(cls._instances.values()):
- try:
- instance.shutdown()
- except Exception as e:
- pprint(f"Error shutting down instance: {e}")
- cls._instances.clear()
- # Placeholder classes for components that will be implemented later
- # These provide the basic interface expected by the application container
- class ComponentPlaceholder:
- """Base placeholder for components that haven't been implemented yet."""
-
- def __init__(self, application: ApplicationContainer):
- self.application = application
- self.application_ref = weakref.ref(application)
-
- def shutdown(self):
- """Shutdown method for proper cleanup."""
- pass
- class PluginSystemPlaceholder(ComponentPlaceholder):
- """Placeholder for the plugin system."""
- pass
- class SatelliteManagerPlaceholder(ComponentPlaceholder):
- """Placeholder for the satellite manager."""
- pass
- class NetworkManagerPlaceholder(ComponentPlaceholder):
- """Placeholder for the network manager."""
- pass
- class AssetManagerPlaceholder(ComponentPlaceholder):
- """Placeholder for the asset manager."""
- pass
- class SchedulerPlaceholder(ComponentPlaceholder):
- """Placeholder for the scheduler."""
- pass
- class ConversationManagerPlaceholder(ComponentPlaceholder):
- """Placeholder for the conversation manager."""
- pass
- class ArbitrationManagerPlaceholder(ComponentPlaceholder):
- """Placeholder for the arbitration manager."""
- pass
- # Utility Functions
- def create_application_container(
- mode: Union[str, DeploymentMode],
- config_path: Optional[str] = None,
- debug_mode: bool = False,
- **kwargs
- ) -> ApplicationContainer:
- """
- Convenience function to create an ApplicationContainer.
-
- Args:
- mode: Deployment mode
- config_path: Path to configuration file
- debug_mode: Enable debug mode
- **kwargs: Additional arguments
-
- Returns:
- ApplicationContainer instance
- """
- return ApplicationContainer(
- mode=mode,
- config_path=config_path,
- debug_mode=debug_mode,
- **kwargs
- )
- def get_application_container(mode: Union[str, DeploymentMode] = None) -> Optional[ApplicationContainer]:
- """
- Get an existing application container instance.
-
- Args:
- mode: Deployment mode (optional)
-
- Returns:
- ApplicationContainer instance or None
- """
- return ApplicationContainer.get_instance(mode)
- def cleanup_application_containers() -> None:
- """Clean up all application container instances."""
- ApplicationContainer.cleanup_instances()
- # Module exports
- __all__ = [
- 'ApplicationContainer',
- 'ApplicationContainerError',
- 'ComponentInitializationError',
- 'ComponentLifecycleError',
- 'ApplicationStatus',
- 'ComponentStatus',
- 'ComponentHealth',
- 'create_application_container',
- 'get_application_container',
- 'cleanup_application_containers',
- 'pprint'
- ]
|