Michael Hu
fix dia tts
6514731
"""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")