Advanced Topics

Extending BackendConfig

BackendConfig is a Pydantic BaseModel and can be subclassed to add domain- or capability-specific fields to an Inference Backend. The example below extends BackendConfig and sets the subclass as the backend_config_model class attribute on a custom backend:

 1from typing import Any, List
 2
 3from packflow import BackendConfig, InferenceBackend
 4
 5
 6class CustomConfigModel(BackendConfig):
 7    # New domain-specific fields with defaults
 8    model_path: str = "./resources/model.joblib"
 9
10    # Set fields without defaults so the backend cannot start without this provided
11    output_class_names: list[str]
12
13
14class CustomBackend(InferenceBackend):
15    backend_config_model = CustomConfigModel
16
17    def initialize(self):
18        self.logger.info(f"Loading model from {self.config.model_path}")
19        self.model = lambda x: x  # Mocking loading of a model
20
21    def execute(self, inputs: Any) -> Any:
22        return self.model(inputs)
23
24    def transform_outputs(self, outputs: Any) -> List[dict]:
25        cleaned = []
26        for row in outputs:
27            cleaned.append(dict(zip(self.config.output_class_names, row)))
28        return cleaned

Config fields are accessed via the self.config instance attribute.

Note

BackendConfig subclasses are validated according to Pydantic’s validation rules. In the example above, output_class_names has no default, so Pydantic treats it as required. Any instance of CustomBackend must supply it — either as a keyword argument at instantiation time or via the backend_config: section of packflow.yaml when loading through InferenceBackendLoader.from_project():

# Direct instantiation
backend = CustomBackend(output_class_names=["doubled"])

# Via packflow.yaml backend_config: + from_project()
# packflow.yaml:
#   backend_config:
#     output_class_names: [doubled]
backend = InferenceBackendLoader.from_project(".")

Configuration Sources

The InferenceBackend in Packflow loads and validates configurations in the following priority order (lowest to highest):

  1. Base Config defaults
    • Default values defined in BackendConfig or a custom subclass.

  2. backend_config: values from packflow.yaml
    • Applied only when a project is loaded via InferenceBackendLoader.from_project(). Standalone loaders (LocalLoader, ModuleLoader) do not read packflow.yaml and skip this level entirely.

  3. Explicit keyword arguments passed at load time
    • Accepted by all loader methods (.load(), from_project()). These always override both defaults and any backend_config: values.

All configurations are validated through Pydantic at load time.

Configuring the Backend via packflow.yaml

Project-level backend configuration is specified in the backend_config: section of packflow.yaml. These values are applied automatically when a project is loaded via InferenceBackendLoader.from_project(), making them well-suited for deployment-time configuration that should not be hardcoded into the backend itself — such as environment-specific field names, feature lists, or preprocessor settings.

For example, if the input field names differ between the training environment and production, the mapping can be declared in packflow.yaml without modifying the Inference Backend code:

# === BACKEND CONFIG ===
backend_config:
  feature_names:
    - input_0
    - input_1
from packflow.loaders import InferenceBackendLoader

backend = InferenceBackendLoader.from_project(".")
backend({"input_0": 1.0, "input_1": 2.0})

This approach also works with custom BackendConfig subclasses — any field defined on the subclass can be supplied via backend_config: in the same way.

Creating Reusable Backends

An Inference Backend packaged as an installable Python module can be shared and loaded across multiple projects using ModuleLoader or by setting loader: module in packflow.yaml. The backend_config: section in each project’s packflow.yaml can then supply different configuration values to the same backend without modifying its code. See the Packflow Loaders section for details.

Logging Configuration

Packflow uses the loguru library for logging and defaults to the INFO log level to minimize noise in production environments. The log level can be controlled via the PACKFLOW_LOG_LEVEL environment variable.

Setting the Log Level

To change the log level, set the PACKFLOW_LOG_LEVEL environment variable to one of the following values:

  • DEBUG: Detailed diagnostic information useful for troubleshooting

  • INFO: General informational messages (default)

  • WARNING: Warning messages for potentially problematic situations

  • ERROR: Error messages for serious problems

  • CRITICAL: Critical messages for very serious errors

Example:

# Enable debug logging for detailed diagnostics
export PACKFLOW_LOG_LEVEL=DEBUG
python inference.py

# Use warning level to see only warnings and errors
export PACKFLOW_LOG_LEVEL=WARNING
python inference.py

Note

The verbose field in the BackendConfig controls whether execution metrics are logged during inference. This is separate from the overall log level and defaults to False. Set verbose=True to enable metrics logging.