Spaces:
Build error
Build error
"""Container setup module for registering all services and dependencies.""" | |
import logging | |
from typing import Optional | |
from .app_config import AppConfig | |
from .dependency_container import DependencyContainer, ServiceLifetime | |
from ..tts.provider_factory import TTSProviderFactory | |
from ..stt.provider_factory import STTProviderFactory | |
from ..translation.provider_factory import TranslationProviderFactory | |
from ...domain.interfaces.audio_processing import IAudioProcessingService | |
from ...domain.interfaces.speech_recognition import ISpeechRecognitionService | |
from ...domain.interfaces.translation import ITranslationService | |
from ...domain.interfaces.speech_synthesis import ISpeechSynthesisService | |
from ...domain.services.audio_processing_service import AudioProcessingService | |
from ...application.services.audio_processing_service import AudioProcessingApplicationService | |
from ...application.services.configuration_service import ConfigurationApplicationService | |
logger = logging.getLogger(__name__) | |
def setup_container(config_file: Optional[str] = None) -> DependencyContainer: | |
""" | |
Set up and configure the dependency injection container with all services. | |
Args: | |
config_file: Optional path to configuration file | |
Returns: | |
DependencyContainer: Configured container instance | |
""" | |
logger.info("Setting up dependency injection container") | |
# Create configuration | |
config = AppConfig(config_file=config_file) | |
# Create container with configuration | |
container = DependencyContainer(config) | |
# Register core services | |
_register_core_services(container, config) | |
# Register domain services | |
_register_domain_services(container) | |
# Register application services | |
_register_application_services(container) | |
# Register provider services | |
_register_provider_services(container) | |
logger.info("Dependency injection container setup completed") | |
return container | |
def _register_core_services(container: DependencyContainer, config: AppConfig) -> None: | |
""" | |
Register core infrastructure services. | |
Args: | |
container: Dependency container | |
config: Application configuration | |
""" | |
logger.info("Registering core services") | |
# Configuration is already registered as singleton in container constructor | |
# But we ensure it's the same instance | |
container.register_singleton(AppConfig, config) | |
# Register provider factories as singletons | |
container.register_singleton( | |
TTSProviderFactory, | |
lambda: TTSProviderFactory() | |
) | |
container.register_singleton( | |
STTProviderFactory, | |
lambda: STTProviderFactory() | |
) | |
container.register_singleton( | |
TranslationProviderFactory, | |
lambda: TranslationProviderFactory() | |
) | |
logger.info("Core services registered") | |
def _register_domain_services(container: DependencyContainer) -> None: | |
""" | |
Register domain services. | |
Args: | |
container: Dependency container | |
""" | |
logger.info("Registering domain services") | |
# Register domain audio processing service as transient | |
# It requires other services to be injected | |
def create_audio_processing_service() -> IAudioProcessingService: | |
"""Factory function for creating audio processing service.""" | |
# Get provider services from container | |
stt_provider = container.get_stt_provider() | |
translation_provider = container.get_translation_provider() | |
tts_provider = container.get_tts_provider() | |
return AudioProcessingService( | |
speech_recognition_service=stt_provider, | |
translation_service=translation_provider, | |
speech_synthesis_service=tts_provider | |
) | |
container.register_transient( | |
IAudioProcessingService, | |
create_audio_processing_service | |
) | |
logger.info("Domain services registered") | |
def _register_application_services(container: DependencyContainer) -> None: | |
""" | |
Register application services. | |
Args: | |
container: Dependency container | |
""" | |
logger.info("Registering application services") | |
# Register audio processing application service as scoped | |
# It manages resources and should be scoped to request/session | |
def create_audio_processing_app_service() -> AudioProcessingApplicationService: | |
"""Factory function for creating audio processing application service.""" | |
return AudioProcessingApplicationService(container) | |
container.register_scoped( | |
AudioProcessingApplicationService, | |
create_audio_processing_app_service | |
) | |
# Register configuration application service as singleton | |
# Configuration service can be shared across requests | |
def create_configuration_app_service() -> ConfigurationApplicationService: | |
"""Factory function for creating configuration application service.""" | |
return ConfigurationApplicationService(container) | |
container.register_singleton( | |
ConfigurationApplicationService, | |
create_configuration_app_service | |
) | |
logger.info("Application services registered") | |
def _register_provider_services(container: DependencyContainer) -> None: | |
""" | |
Register provider services with fallback logic. | |
Args: | |
container: Dependency container | |
""" | |
logger.info("Registering provider services") | |
# Register TTS provider service as transient with fallback | |
def create_tts_provider() -> ISpeechSynthesisService: | |
"""Factory function for creating TTS provider with fallback.""" | |
return container.get_tts_provider() | |
container.register_transient( | |
ISpeechSynthesisService, | |
create_tts_provider | |
) | |
# Register STT provider service as transient with fallback | |
def create_stt_provider() -> ISpeechRecognitionService: | |
"""Factory function for creating STT provider with fallback.""" | |
return container.get_stt_provider() | |
container.register_transient( | |
ISpeechRecognitionService, | |
create_stt_provider | |
) | |
# Register translation provider service as transient with fallback | |
def create_translation_provider() -> ITranslationService: | |
"""Factory function for creating translation provider with fallback.""" | |
return container.get_translation_provider() | |
container.register_transient( | |
ITranslationService, | |
create_translation_provider | |
) | |
logger.info("Provider services registered") | |
def configure_logging(config: AppConfig) -> None: | |
""" | |
Configure application logging based on configuration. | |
Args: | |
config: Application configuration | |
""" | |
try: | |
logging_config = config.get_logging_config() | |
# Configure root logger | |
root_logger = logging.getLogger() | |
root_logger.setLevel(getattr(logging, logging_config['level'].upper(), logging.INFO)) | |
# Clear existing handlers | |
for handler in root_logger.handlers[:]: | |
root_logger.removeHandler(handler) | |
# Create console handler | |
console_handler = logging.StreamHandler() | |
console_handler.setLevel(root_logger.level) | |
# Create formatter | |
formatter = logging.Formatter(logging_config['format']) | |
console_handler.setFormatter(formatter) | |
# Add console handler | |
root_logger.addHandler(console_handler) | |
# Add file handler if enabled | |
if logging_config.get('enable_file_logging', False): | |
from logging.handlers import RotatingFileHandler | |
file_handler = RotatingFileHandler( | |
logging_config['log_file_path'], | |
maxBytes=logging_config['max_log_file_size_mb'] * 1024 * 1024, | |
backupCount=logging_config['backup_count'] | |
) | |
file_handler.setLevel(root_logger.level) | |
file_handler.setFormatter(formatter) | |
root_logger.addHandler(file_handler) | |
logger.info(f"Logging configured: level={logging_config['level']}") | |
except Exception as e: | |
# Fallback to basic logging configuration | |
logging.basicConfig( | |
level=logging.INFO, | |
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' | |
) | |
logger.warning(f"Failed to configure logging from config, using defaults: {e}") | |
def create_configured_container(config_file: Optional[str] = None) -> DependencyContainer: | |
""" | |
Create and configure a complete dependency injection container. | |
This is the main entry point for setting up the application's dependency injection. | |
Args: | |
config_file: Optional path to configuration file | |
Returns: | |
DependencyContainer: Fully configured container | |
""" | |
try: | |
logger.info("Creating configured container...") | |
# Setup container | |
logger.info("Setting up container...") | |
container = setup_container(config_file) | |
logger.info("Container setup completed") | |
# Configure logging | |
logger.info("Configuring logging...") | |
config = container.resolve(AppConfig) | |
configure_logging(config) | |
logger.info("Logging configuration completed") | |
# Validate container setup | |
logger.info("Starting container validation...") | |
try: | |
_validate_container_setup(container) | |
logger.info("Container validation completed") | |
except Exception as validation_error: | |
logger.error(f"Container validation failed: {validation_error}", exception=validation_error) | |
# For now, let's continue even if validation fails to see if the app works | |
logger.warning("Continuing despite validation failure...") | |
logger.info("Application container created and configured successfully") | |
return container | |
except Exception as e: | |
logger.error(f"Failed to create configured container: {e}", exception=e) | |
raise | |
def _validate_container_setup(container: DependencyContainer) -> None: | |
""" | |
Validate that the container is properly set up. | |
Args: | |
container: Container to validate | |
Raises: | |
RuntimeError: If validation fails | |
""" | |
logger.info("Validating container setup") | |
required_services = [ | |
AppConfig, | |
TTSProviderFactory, | |
STTProviderFactory, | |
TranslationProviderFactory, | |
IAudioProcessingService, | |
AudioProcessingApplicationService, | |
ConfigurationApplicationService, | |
ISpeechSynthesisService, | |
ISpeechRecognitionService, | |
ITranslationService | |
] | |
missing_services = [] | |
logger.info("Checking service registrations...") | |
for service_type in required_services: | |
logger.info(f"Checking registration for: {service_type.__name__}") | |
if not container.is_registered(service_type): | |
missing_services.append(service_type.__name__) | |
logger.error(f"Service not registered: {service_type.__name__}") | |
else: | |
logger.info(f"Service registered: {service_type.__name__}") | |
if missing_services: | |
error_msg = f"Container validation failed. Missing services: {missing_services}" | |
logger.error(error_msg) | |
raise RuntimeError(error_msg) | |
# Test service resolution | |
logger.info("Testing service resolution...") | |
try: | |
logger.info("Resolving AppConfig...") | |
config = container.resolve(AppConfig) | |
logger.info("AppConfig resolved successfully") | |
logger.info("Resolving AudioProcessingApplicationService...") | |
app_service = container.resolve(AudioProcessingApplicationService) | |
logger.info("AudioProcessingApplicationService resolved successfully") | |
logger.info("Resolving ConfigurationApplicationService...") | |
config_service = container.resolve(ConfigurationApplicationService) | |
logger.info("ConfigurationApplicationService resolved successfully") | |
logger.info("Container validation successful") | |
except Exception as e: | |
error_msg = f"Container validation failed during service resolution: {e}" | |
logger.error(error_msg, exception=e) | |
raise RuntimeError(error_msg) | |
def get_service_registry_info(container: DependencyContainer) -> dict: | |
""" | |
Get information about registered services in the container. | |
Args: | |
container: Container to inspect | |
Returns: | |
dict: Service registry information | |
""" | |
try: | |
registered_services = container.get_registered_services() | |
# Get provider availability | |
config = container.resolve(AppConfig) | |
tts_factory = container.resolve(TTSProviderFactory) | |
stt_factory = container.resolve(STTProviderFactory) | |
translation_factory = container.resolve(TranslationProviderFactory) | |
provider_info = { | |
'tts_providers': tts_factory.get_available_providers(), | |
'stt_providers': stt_factory.get_available_providers(), | |
'translation_providers': [p.value for p in translation_factory.get_available_providers()] | |
} | |
return { | |
'registered_services': registered_services, | |
'provider_availability': provider_info, | |
'configuration_summary': { | |
'config_file': config.config_file, | |
'temp_dir': config.processing.temp_dir, | |
'log_level': config.logging.level, | |
'tts_preferred_providers': config.tts.preferred_providers, | |
'stt_default_model': config.stt.default_model, | |
'translation_default_provider': config.translation.default_provider | |
} | |
} | |
except Exception as e: | |
logger.error(f"Failed to get service registry info: {e}") | |
return { | |
'error': str(e), | |
'registered_services': container.get_registered_services() if container else {} | |
} | |
# Global container instance management | |
_global_container: Optional[DependencyContainer] = None | |
def initialize_global_container(config_file: Optional[str] = None) -> DependencyContainer: | |
""" | |
Initialize the global container instance. | |
Args: | |
config_file: Optional configuration file path | |
Returns: | |
DependencyContainer: Global container instance | |
""" | |
global _global_container | |
if _global_container is not None: | |
logger.warning("Global container already initialized, cleaning up previous instance") | |
_global_container.cleanup() | |
_global_container = create_configured_container(config_file) | |
logger.info("Global container initialized") | |
return _global_container | |
def get_global_container() -> DependencyContainer: | |
""" | |
Get the global container instance. | |
Returns: | |
DependencyContainer: Global container instance | |
Raises: | |
RuntimeError: If container is not initialized | |
""" | |
global _global_container | |
if _global_container is None: | |
logger.info("Global container not initialized, creating with defaults") | |
_global_container = create_configured_container() | |
return _global_container | |
def cleanup_global_container() -> None: | |
"""Cleanup the global container instance.""" | |
global _global_container | |
if _global_container is not None: | |
_global_container.cleanup() | |
_global_container = None | |
logger.info("Global container cleaned up") |