Major Features: - Backward compatibility: VALIDATE_INPUTS handles old boolean True/False values for enable_runtime_hooks parameter - Phase 3: XMP region metadata with MWG-RS compliance (faces, areas, coordinates, extensions) - Phase 4.4: Per-node UI toggle for runtime hooks (dropdown: Auto/Enabled/Disabled) - Runtime hooks enhancements: performance tracking, disagreement logging, configurable limits New Files: - config.json: Configuration file for runtime hooks and metadata settings - eric_metadata/hooks/runtime_capture.py: Runtime hook infrastructure - eric_metadata/utils/config.py: Configuration loader with priority system - eric_metadata/utils/version.py: Version helper for generated_by labels - eric_metadata/utils/a1111.py: Automatic1111 parameter string generation - eric_metadata/utils/jpeg_fallback.py: 4-stage JPEG metadata fallback - eric_metadata/workflow/: Workflow parsing and generation helpers - tests/test_backward_compatibility.py: 7 tests for parameter migration - tests/test_xmp_regions.py: 6 tests for XMP region round-trip - tests/test_per_node_runtime_hooks.py: 9 tests for per-node toggle Documentation: - CONFIG_GUIDE.md: Configuration options and usage - RUNTIME_HOOKS_QUICK_REFERENCE.md: Quick reference for runtime hooks - RUNTIME_HOOKS_ENHANCEMENTS_SUMMARY.md: Detailed enhancement documentation - SAFE_RUNTIME_HOOKS_RESEARCH.md: Safety analysis and validation - edit_log.md: Complete change history Test Results: All 143 tests passing - 7 backward compatibility tests - 6 XMP region tests - 9 per-node toggle tests - 17 runtime hooks enhancement tests - 104 existing tests Breaking Changes: None - fully backward compatible
207 lines
5.3 KiB
Python
207 lines
5.3 KiB
Python
"""Configuration loader for AAA Metadata System."""
|
|
|
|
import json
|
|
import os
|
|
from pathlib import Path
|
|
from typing import Any, Dict, Optional
|
|
|
|
|
|
_CONFIG_CACHE: Optional[Dict[str, Any]] = None
|
|
_CONFIG_PATH: Optional[Path] = None
|
|
|
|
|
|
def get_config_path() -> Path:
|
|
"""Get the path to the config.json file."""
|
|
global _CONFIG_PATH
|
|
if _CONFIG_PATH is not None:
|
|
return _CONFIG_PATH
|
|
|
|
# Get the root directory of the AAA_Metadata_System
|
|
current_file = Path(__file__).resolve()
|
|
root_dir = current_file.parent.parent
|
|
config_path = root_dir / "config.json"
|
|
|
|
_CONFIG_PATH = config_path
|
|
return config_path
|
|
|
|
|
|
def load_config() -> Dict[str, Any]:
|
|
"""
|
|
Load configuration from config.json file.
|
|
|
|
Returns:
|
|
dict: Configuration dictionary with defaults for missing values
|
|
"""
|
|
global _CONFIG_CACHE
|
|
|
|
# Return cached config if available
|
|
if _CONFIG_CACHE is not None:
|
|
return _CONFIG_CACHE
|
|
|
|
# Default configuration
|
|
default_config = {
|
|
"runtime_hooks": {
|
|
"enabled": False,
|
|
"log_disagreements": False,
|
|
"log_performance": False,
|
|
"inline_size_limit_kb": 32
|
|
},
|
|
"debug": {
|
|
"enabled": False
|
|
},
|
|
"metadata": {
|
|
"enable_hash_cache": True
|
|
}
|
|
}
|
|
|
|
config_path = get_config_path()
|
|
|
|
# Try to load config file
|
|
if config_path.exists():
|
|
try:
|
|
with open(config_path, 'r', encoding='utf-8') as f:
|
|
file_config = json.load(f)
|
|
|
|
# Merge with defaults (file config takes precedence)
|
|
merged_config = _deep_merge(default_config, file_config)
|
|
_CONFIG_CACHE = merged_config
|
|
return merged_config
|
|
except Exception as e:
|
|
print(f"[AAA Metadata Config] Warning: Failed to load config.json: {e}")
|
|
print(f"[AAA Metadata Config] Using default configuration")
|
|
|
|
# No config file or failed to load - use defaults
|
|
_CONFIG_CACHE = default_config
|
|
return default_config
|
|
|
|
|
|
def _deep_merge(base: Dict[str, Any], override: Dict[str, Any]) -> Dict[str, Any]:
|
|
"""
|
|
Deep merge two dictionaries, with override values taking precedence.
|
|
|
|
Args:
|
|
base: Base dictionary with defaults
|
|
override: Override dictionary with user values
|
|
|
|
Returns:
|
|
dict: Merged dictionary
|
|
"""
|
|
result = base.copy()
|
|
|
|
for key, value in override.items():
|
|
# Skip comment fields
|
|
if key.startswith('_'):
|
|
continue
|
|
|
|
if key in result and isinstance(result[key], dict) and isinstance(value, dict):
|
|
result[key] = _deep_merge(result[key], value)
|
|
else:
|
|
result[key] = value
|
|
|
|
return result
|
|
|
|
|
|
def get_runtime_hooks_enabled() -> bool:
|
|
"""
|
|
Check if runtime hooks should be enabled.
|
|
|
|
Priority order:
|
|
1. Environment variable AAA_METADATA_ENABLE_HOOKS
|
|
2. config.json runtime_hooks.enabled setting
|
|
3. Default: False
|
|
|
|
Returns:
|
|
bool: True if runtime hooks should be enabled
|
|
"""
|
|
# Check environment variable first (highest priority)
|
|
env_value = os.environ.get("AAA_METADATA_ENABLE_HOOKS", "").strip().lower()
|
|
if env_value in {"1", "true", "yes", "on"}:
|
|
return True
|
|
if env_value in {"0", "false", "no", "off"}:
|
|
return False
|
|
|
|
# Check config file
|
|
config = load_config()
|
|
return config.get("runtime_hooks", {}).get("enabled", False)
|
|
|
|
|
|
def get_debug_enabled() -> bool:
|
|
"""
|
|
Check if debug mode should be enabled.
|
|
|
|
Returns:
|
|
bool: True if debug mode should be enabled
|
|
"""
|
|
config = load_config()
|
|
return config.get("debug", {}).get("enabled", False)
|
|
|
|
|
|
def get_hash_cache_enabled() -> bool:
|
|
"""
|
|
Check if hash caching should be enabled.
|
|
|
|
Returns:
|
|
bool: True if hash caching should be enabled
|
|
"""
|
|
config = load_config()
|
|
return config.get("metadata", {}).get("enable_hash_cache", True)
|
|
|
|
|
|
def get_runtime_log_disagreements() -> bool:
|
|
"""
|
|
Check if parser/hook disagreements should be logged.
|
|
|
|
Returns:
|
|
bool: True if disagreements should be logged
|
|
"""
|
|
config = load_config()
|
|
return config.get("runtime_hooks", {}).get("log_disagreements", False)
|
|
|
|
|
|
def get_runtime_log_performance() -> bool:
|
|
"""
|
|
Check if runtime performance metrics should be logged.
|
|
|
|
Returns:
|
|
bool: True if performance should be logged
|
|
"""
|
|
config = load_config()
|
|
return config.get("runtime_hooks", {}).get("log_performance", False)
|
|
|
|
|
|
def get_runtime_inline_limit() -> int:
|
|
"""
|
|
Get the inline size limit for runtime data in bytes.
|
|
|
|
Returns:
|
|
int: Size limit in bytes (default 32KB)
|
|
"""
|
|
config = load_config()
|
|
limit_kb = config.get("runtime_hooks", {}).get("inline_size_limit_kb", 32)
|
|
return limit_kb * 1024
|
|
|
|
|
|
def reload_config() -> Dict[str, Any]:
|
|
"""
|
|
Reload configuration from disk, clearing the cache.
|
|
|
|
Returns:
|
|
dict: Reloaded configuration
|
|
"""
|
|
global _CONFIG_CACHE
|
|
_CONFIG_CACHE = None
|
|
return load_config()
|
|
|
|
|
|
__all__ = [
|
|
"load_config",
|
|
"get_config_path",
|
|
"get_runtime_hooks_enabled",
|
|
"get_debug_enabled",
|
|
"get_hash_cache_enabled",
|
|
"get_runtime_log_disagreements",
|
|
"get_runtime_log_performance",
|
|
"get_runtime_inline_limit",
|
|
"reload_config",
|
|
]
|