Spaces:
Running on Zero
Running on Zero
File size: 4,284 Bytes
0828c2c | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 | """Configuration loader for YAML settings and environment variables."""
import logging
import os
from pathlib import Path
from typing import Any
import yaml
from dotenv import load_dotenv
logger = logging.getLogger(__name__)
class ConfigLoader:
"""Load and manage configuration from YAML and environment variables."""
def __init__(self, config_path: str = "config.yaml", env_path: str = ".env"):
"""Initialize configuration loader.
Args:
config_path: Path to YAML configuration file
env_path: Path to .env file
"""
self.config_path = Path(config_path)
self.env_path = Path(env_path)
self._config: dict[str, Any] = {}
# Load environment variables first
self._load_env()
# Load YAML configuration
self._load_yaml()
# Setup logging
self._setup_logging()
def _load_env(self) -> None:
"""Load environment variables from .env file."""
if self.env_path.exists():
load_dotenv(self.env_path)
logger.debug(f"Loaded environment variables from {self.env_path}")
else:
logger.warning(f".env file not found at {self.env_path}, using env.template defaults")
# Try to load from env.template as fallback
env_template = Path("env.template")
if env_template.exists():
load_dotenv(env_template)
def _load_yaml(self) -> None:
"""Load configuration from YAML file."""
if not self.config_path.exists():
msg = f"Configuration file not found: {self.config_path}"
raise FileNotFoundError(msg)
with open(self.config_path) as f:
self._config = yaml.safe_load(f)
logger.debug(f"Loaded configuration from {self.config_path}")
def _setup_logging(self) -> None:
"""Setup logging configuration."""
log_config = self._config.get("logging", {})
log_level = os.getenv("LOG_LEVEL", log_config.get("level", "INFO"))
logging.basicConfig(
level=getattr(logging, log_level),
format=log_config.get("format", "%(asctime)s - %(name)s - %(levelname)s - %(message)s"),
)
def get(self, key: str, default: Any = None) -> Any:
"""Get configuration value by key (supports nested keys with dot notation).
Args:
key: Configuration key (e.g., 'llm.model' for nested access)
default: Default value if key not found
Returns:
Configuration value
"""
keys = key.split(".")
value = self._config
for k in keys:
if isinstance(value, dict):
value = value.get(k)
if value is None:
return default
else:
return default
return value
def get_env(self, key: str, default: str | None = None) -> str | None:
"""Get environment variable.
Args:
key: Environment variable name
default: Default value if not found
Returns:
Environment variable value
"""
return os.getenv(key, default)
@property
def config(self) -> dict[str, Any]:
"""Get the entire configuration dictionary."""
return self._config
def format_template(self, template: str) -> str:
"""Format template string with profile information.
Args:
template: Template string with {name}, {title} placeholders
Returns:
Formatted string
"""
profile = self._config.get("profile", {})
return template.format(
name=profile.get("name", "the candidate"),
title=profile.get("title", "professional"),
)
# Singleton instance
_config_instance: ConfigLoader | None = None
def get_config() -> ConfigLoader:
"""Get singleton configuration instance."""
global _config_instance
if _config_instance is None:
_config_instance = ConfigLoader()
return _config_instance
def reload_config() -> ConfigLoader:
"""Reload configuration (useful for testing or config changes)."""
global _config_instance
_config_instance = ConfigLoader()
return _config_instance
|