The Configuration Management system provides centralized, type-safe configuration handling for all Trixy components across different deployment modes (server, client, standalone).
Location: ./trixy_core/config/
Main configuration management class with hot-reloading and validation.
ServerConfig: Server-specific configurationClientConfig: Client/satellite configurationStandaloneConfig: Standalone mode configurationValidates configuration values and dependencies.
from trixy_core.config import ConfigManager, DeploymentMode
config_manager = ConfigManager(
mode=DeploymentMode.SERVER,
config_file="config/server_config.json",
debug_mode=False
)
load_config()Loads and validates configuration from file.
config = config_manager.load_config()
print(f"Command port: {config.network.command_port}")
get_config()Returns current loaded configuration.
current_config = config_manager.get_config()
save_config(config=None)Saves configuration to file with backup.
# Modify and save
config = config_manager.get_config()
config.network.command_port = 2105
config_manager.save_config(config)
reload_config()Reloads configuration from file.
config_manager.reload_config()
override_config(overrides: Dict)Apply runtime configuration overrides.
config_manager.override_config({
"network": {"command_port": 2105},
"max_satellites": 100,
"debug": {"enable_verbose_logging": True}
})
enable_hot_reload(interval: float = 1.0)Enables automatic configuration reloading.
# Check for config changes every 2 seconds
config_manager.enable_hot_reload(interval=2.0)
# Register callback for config changes
def on_config_changed(old_config, new_config):
print("Configuration updated!")
config_manager.enable_hot_reload(callback=on_config_changed)
disable_hot_reload()Disables automatic configuration reloading.
config_manager.disable_hot_reload()
validate_config(config=None)Validates configuration against schema.
is_valid, errors = config_manager.validate_config()
if not is_valid:
for error in errors:
print(f"Config error: {error}")
backup_config()Creates manual backup of current configuration.
backup_path = config_manager.backup_config()
print(f"Config backed up to: {backup_path}")
restore_backup(backup_file: str)Restores configuration from backup.
config_manager.restore_backup("config/backups/server_config_20241201_120000.json")
@dataclass
class ServerConfig:
# Network configuration
network: NetworkConfig
# Satellite management
max_satellites: int = 50
registration_timeout: int = 60
# Plugin settings
plugins: PluginConfig
# Audio processing
audio: AudioConfig
# ML models
models: ModelConfig
# Logging
logging: LoggingConfig
# Security
security: SecurityConfig
@dataclass
class ClientConfig:
# Network configuration
network: ClientNetworkConfig
# Server connection
server: ServerConnectionConfig
# Audio capture
audio: ClientAudioConfig
# ML models (wakeword, voice recognition)
models: ClientModelConfig
# Hardware settings
hardware: HardwareConfig
# Logging
logging: LoggingConfig
@dataclass
class StandaloneConfig:
# Combines server and client features
network: NetworkConfig
audio: AudioConfig
models: ModelConfig
plugins: PluginConfig
logging: LoggingConfig
# Standalone-specific
sync: SyncConfig # Server synchronization settings
@dataclass
class NetworkConfig:
command_port: int = 2101
audio_input_port: int = 2102
audio_output_port: int = 2103
music_output_port: int = 2104
bind_address: str = "0.0.0.0"
max_connections: int = 100
connection_timeout: int = 30
keepalive_interval: int = 60
@dataclass
class AudioConfig:
sample_rate: int = 16000
channels: int = 1
bit_depth: int = 16
chunk_size: int = 1024
input_device: Optional[str] = None
output_device: Optional[str] = None
noise_reduction: bool = True
auto_gain_control: bool = True
@dataclass
class ModelConfig:
wakeword_model: str = "models/wakeword/default.pth"
voice_recognition_model: str = "models/voice_recognition/default.pth"
model_password: str = ""
use_gpu: bool = False
model_cache_size: int = 2
inference_threads: int = 1
The configuration system supports environment variable overrides with the TRIXY_ prefix:
# Network settings
export TRIXY_NETWORK_COMMAND_PORT=2201
export TRIXY_NETWORK_BIND_ADDRESS="127.0.0.1"
# Model settings
export TRIXY_MODELS_USE_GPU=true
export TRIXY_MODELS_MODEL_PASSWORD="secret123"
# Debug settings
export TRIXY_DEBUG_ENABLE_VERBOSE_LOGGING=true
{
"network": {
"command_port": 2101,
"audio_input_port": 2102,
"audio_output_port": 2103,
"music_output_port": 2104,
"bind_address": "0.0.0.0",
"max_connections": 50
},
"max_satellites": 50,
"registration_timeout": 60,
"plugins": {
"directory": "plugins",
"auto_load": true,
"hot_reload": true
},
"audio": {
"sample_rate": 16000,
"channels": 1,
"bit_depth": 16
},
"models": {
"wakeword_model": "models/wakeword/default.pth",
"voice_recognition_model": "models/voice_recognition/default.pth"
}
}
{
"network": {
"local_ports": {
"audio_input": 3102,
"audio_output": 3103,
"music_output": 3104
}
},
"server": {
"host": "localhost",
"port": 2101,
"reconnect_interval": 5,
"max_reconnect_attempts": -1
},
"audio": {
"sample_rate": 16000,
"channels": 1,
"input_device": null,
"noise_reduction": true
},
"models": {
"wakeword_model": "models/wakeword/client.pth",
"voice_recognition_model": "models/voice_recognition/client.pth",
"use_gpu": false
}
}
class ConfigurableComponent:
def __init__(self, config_manager):
self.config_manager = config_manager
self.config_manager.enable_hot_reload(callback=self.on_config_changed)
def on_config_changed(self, old_config, new_config):
# Handle configuration changes
if old_config.network.command_port != new_config.network.command_port:
self.restart_network_listener(new_config.network.command_port)
from trixy_core.config import ConfigValidator
validator = ConfigValidator()
# Add custom validation rules
@validator.add_rule("network.command_port")
def validate_port(value):
if not (1024 <= value <= 65535):
return False, "Port must be between 1024 and 65535"
return True, None
# Validate configuration
is_valid, errors = validator.validate(config)
def migrate_config_v1_to_v2(config_dict):
"""Migrate configuration from version 1 to 2"""
if config_dict.get("version", 1) == 1:
# Rename old settings
if "server_port" in config_dict:
config_dict["network"] = {"command_port": config_dict.pop("server_port")}
config_dict["version"] = 2
return config_dict
config_manager.add_migration(migrate_config_v1_to_v2)
from trixy_core.plugins import TrixyPlugin
class MyPlugin(TrixyPlugin):
def initialize(self):
# Access global config
config = self.application.get_config_manager().get_config()
# Use configuration values
if config.debug.enable_plugin_debugging:
self.enable_debug_mode()
# Watch for config changes
self.application.get_config_manager().enable_hot_reload(
callback=self.on_config_changed
)
def on_config_changed(self, old_config, new_config):
# React to configuration changes
if old_config.audio.sample_rate != new_config.audio.sample_rate:
self.restart_audio_processing()
class NetworkManager:
def __init__(self, application):
self.application = application
self.config_manager = application.get_config_manager()
self.current_config = self.config_manager.get_config()
# Enable hot-reload for network settings
self.config_manager.enable_hot_reload(
callback=self.on_network_config_changed
)
def on_network_config_changed(self, old_config, new_config):
# Check if network settings changed
if old_config.network != new_config.network:
self.restart_network_services(new_config.network)
from trixy_core.config import (
ConfigurationError,
ConfigurationValidationError,
ConfigurationLoadError,
ConfigurationSaveError
)
try:
config = config_manager.load_config()
except ConfigurationLoadError as e:
print(f"Failed to load config: {e}")
# Use default configuration
config = config_manager.get_default_config()
except ConfigurationValidationError as e:
print(f"Invalid configuration: {e}")
# Show validation errors to user
for error in e.validation_errors:
print(f" - {error}")
try:
config_manager.save_config()
except ConfigurationSaveError as e:
print(f"Failed to save config: {e}")
# Try saving to alternate location
backup_path = config_manager.backup_config()
print(f"Configuration saved to backup: {backup_path}")
# Good: Logical grouping
{
"network": {"command_port": 2101, "bind_address": "0.0.0.0"},
"audio": {"sample_rate": 16000, "channels": 1},
"models": {"wakeword_model": "path/to/model.pth"}
}
# Avoid: Flat structure
{
"command_port": 2101,
"bind_address": "0.0.0.0",
"sample_rate": 16000
}
# Use environment variables for sensitive data
{
"api_keys": {
"openai_key": "${TRIXY_OPENAI_API_KEY}",
"google_key": "${TRIXY_GOOGLE_API_KEY}"
}
}
class MyComponent:
def __init__(self, config_manager):
self.config = config_manager.get_config()
# Validate required configuration
if not self.config.network.command_port:
raise ConfigurationError("Command port not configured")
if not (1024 <= self.config.network.command_port <= 65535):
raise ConfigurationError("Invalid command port range")
def on_config_changed(self, old_config, new_config):
# Only react to relevant changes
if old_config.audio != new_config.audio:
self.restart_audio_system()
# Avoid unnecessary restarts
if old_config.logging != new_config.logging:
self.update_log_level(new_config.logging.level)
# Don't restart entire component