hsg_rag_eea / docs /configuration_system_documentation.md
Pygmales's picture
Sync from GitHub dfc4b8c4f25180d9b176067af279b53980020593
a7bed5e verified
|
Raw
History Blame
5.15 kB

Configuration System Documentation

Overview

The project utilizes a centralized, robust configuration system located in src/config. This system transitions the application from a flat-file structure to a modular, object-oriented approach. It is designed to:

  • handle environment variables,
  • ensure type safety,
  • provide default values while maintaining backward compatibility with the root config.py file.

The system is exposed via a singleton instance config imported from src.config, ensuring consistent state throughout the application lifecycle.

The system hides logic fragments that were previously visible to the customer, guarding the application architecture from an urestricted access to the internal code parts.

Features

  • Modular Architecture: Settings are grouped by domain (e.g., config.llm, config.weaviate, config.scraping), improving code readability and IDE autocomplete support.

  • Type Safety: The system strictly enforces data types. It attempts to cast environment variables to the correct Python types (e.g., int, bool, float) and raises errors on startup if the conversion fails.

  • Priority Resolution: Parameters are resolved dynamically in the following order:

    • System Environment Variables (.env): Highest priority.
    • config.py: checked if the environment variable is missing.
    • Default Values: Defined explicitly in the loader logic.
  • Validation: Critical parameters can be marked as required. If a required setting is missing from both the environment and the config file, the application prevents startup to avoid runtime errors.

  • Constraint Checking: Specific fields support strict allowable values.

Basic Usage

For simple parameters or quick additions that do not require strict type enforcement or grouping, you can use the dynamic get() method.

Step 1: Define the Parameter

Add your parameter to the root config.py file or your .env file.

Option A: config.py

# config.py
NEW_FEATURE_ENABLED = True

Option B: .env

# .env
NEW_FEATURE_ENABLED=True

Step 2: Access the Parameter

Import the config object and use the get method.

from src.config import config

# Retrieve the parameter (returns None if not found)
is_enabled = config.get('NEW_FEATURE_ENABLED')

# Retrieve with a fallback default
retry_count = config.get('MAX_RETRIES', default=3)

Creating Subconfigs (Recommended)

Why use this approach?

The modular approach is the standard for this project. It provides IDE autocompletion, type hinting, and early error detection. By grouping related settings into classes, we prevent the "global variable soup" problem and ensure that if a configuration is invalid (e.g., a string passed where an integer is needed), the application fails fast with a clear error message.

Step 1: Place a new parameter in config.py

Define your default values in config.py to serve as the baseline configuration. Additionally, a documentation comment can be provided above the newly created parameter to explain it's usage in code.

# config.py
# ... existing configs ...

# ================= New Module Configuration =================

MY_NEW_TIMEOUT = 30

# A string, either 'standard' or 'enchanced'. Sets the operating mode of my new feature.
MY_NEW_MODE = 'standard'

Step 2: Create a new subconfig class

Open src/config/configs.py. Create a new class to group your settings.

Use the _get utility function to map the parameter name to a class attribute.

  • param: The exact name of the variable in config.py or .env.

  • type_: (Optional) The target Python type. The loader will attempt to cast the value. If it fails (e.g., "abc" -> int), a ValueError is raised.

  • default: (Optional) A hard fallback if the value exists nowhere else.

To enforce allowable values, use Python's Literal type hint.

# src/config/configs.py
from typing import Literal

# ... existing imports ...

class MyNewConfig:
    # Basic integer with type enforcement
    TIMEOUT: int = _get('MY_NEW_TIMEOUT', type_=int)
    
    # String with specific allowable values (for type hinting)
    MODE: Literal['standard', 'turbo'] = _get('MY_NEW_MODE', default='standard')

    # Boolean with a default
    ENABLE_LOGS: bool = _get('ENABLE_NEW_LOGS', default=False, type_=bool)

Step 3: Register the Subconfig

Open src/config/__init__.py. Initialize your new class within the AppConfig wrapper. This ensures it is instantiated as part of the global singleton.

# src/config/__init__.py
from src.config.configs import *
# ...

class AppConfig:
    # ... existing subconfigs ...
    convstate:  ConversationStateConfig = ConversationStateConfig()
    processing: ProcessingConfig        = ProcessingConfig()
    
    # ==== Add your new config here ====
    mynewmodule: MyNewConfig            = MyNewConfig()
    
    # ...

Usage:

You can now access your settings with full type support:

from src.config import config

if config.mynewmodule.MODE == 'turbo':
    print(f"Timeout is: {config.mynewmodule.TIMEOUT}")