QTZPOC-20

QTZPOC-20: Implement .sientia model bundle import workflow

Introduces a new Temporal workflow and activities to import .sientia model bundles. This enables:
- Secure decryption and validation of bundles through a seven-gate process.
- Provisioning imported models in MLflow and updating the MongoDB model listing.
- Comprehensive status tracking in the public.experiment_run table with new IMPORT_SUCCESS/IMPORT_ERROR states.
- Dedicated metrics, robust error handling, and configurable settings.
- Includes updated e2e database fixtures and extensive unit/integration tests for bundle format, reader, and overall workflow.
This commit is contained in:
2026-08-25 10:40:46 -03:00
parent 291d16efa5
commit 0e445de386
27 changed files with 4471 additions and 11 deletions

View File

@@ -1,5 +1,6 @@
import json
from os import getenv
import tempfile
from os import getenv, path
from typing import Any
@@ -90,3 +91,72 @@ def build_minio_config() -> dict[str, Any]:
'retention_hours': int(getenv('MINIO_RETENTION_HOURS', '24')),
'secure': getenv('MINIO_SECURE', 'false') == 'true',
}
def build_import_config() -> dict[str, Any]:
"""
Build the `.sientia` model-import configuration from environment variables.
The password key is read as the raw base64 string and **not** validated here: the worker must
start whether or not `IMPORT_MODEL_ENABLED` is set, and a missing or malformed key is an import
failure at the decryption step (`laborious.utils.import_password.load_envelope_key`), not a
boot failure.
Environment Variables:
IMPORT_PASSWORD_KEY: base64 of the 32-byte AES-256-GCM key that wraps bundle passwords
IMPORT_BUNDLE_BUCKET: bucket holding the uploaded bundles (default: sientia)
IMPORT_BUNDLE_PREFIX: key prefix the uploads must sit under (default: imported_models/)
IMPORT_WORK_DIR: worker scratch directory (default: <tempdir>/sientia-import)
IMPORT_MAX_OBJECT_BYTES: gate 1 object size ceiling (default: 1 GiB)
IMPORT_MAX_ARCHIVE_ENTRIES: gate 4 entry count ceiling (default: 5000)
IMPORT_MAX_UNCOMPRESSED_BYTES: gate 4 uncompressed size ceiling (default: 4 GiB)
IMPORT_MAX_COMPRESSION_RATIO: gate 4 compression ratio ceiling (default: 200)
IMPORT_MODELS_COLLECTION: MongoDB collection holding the model listing (default: models)
IMPORT_BUNDLE_RETENTION_DAYS: lifecycle expiry for the uploaded bundle (default: 7)
Returns:
dict: import configuration dictionary
"""
prefix = getenv('IMPORT_BUNDLE_PREFIX', 'imported_models/')
return {
'password_key': getenv('IMPORT_PASSWORD_KEY'),
'bucket': getenv('IMPORT_BUNDLE_BUCKET', 'sientia'),
'prefix': prefix if prefix.endswith('/') else f'{prefix}/',
'work_dir': getenv('IMPORT_WORK_DIR', path.join(tempfile.gettempdir(), 'sientia-import')),
'max_object_bytes': int(getenv('IMPORT_MAX_OBJECT_BYTES', str(1024 * 1024 * 1024))),
'max_entries': int(getenv('IMPORT_MAX_ARCHIVE_ENTRIES', '5000')),
'max_uncompressed_bytes': int(
getenv('IMPORT_MAX_UNCOMPRESSED_BYTES', str(4 * 1024 * 1024 * 1024))
),
'max_compression_ratio': float(getenv('IMPORT_MAX_COMPRESSION_RATIO', '200')),
'models_collection': getenv('IMPORT_MODELS_COLLECTION', 'models'),
'retention_days': int(getenv('IMPORT_BUNDLE_RETENTION_DAYS', '7')),
}
def build_import_status_config() -> dict[str, Any]:
"""
Build the connection to the import log database — a **second** Postgres connection.
The import log is `public.experiment_run` in the database owned by the Spring Boot BFF
(`sientia-core-mlops-bff`), not the `sientia` database Laborious itself uses. The table and
schema are constants of the code, never inputs; only the connection is configuration.
Environment Variables:
IMPORT_STATUS_DB_HOST: host (default: localhost)
IMPORT_STATUS_DB_PORT: port (default: 5432)
IMPORT_STATUS_DB_NAME: database (default: sientia-core-mlops-bff)
IMPORT_STATUS_DB_USER: user (default: mlops_bff_user)
IMPORT_STATUS_DB_PASSWORD: password (default: empty)
Returns:
dict: import status database configuration dictionary
"""
return {
'host': getenv('IMPORT_STATUS_DB_HOST', 'localhost'),
'port': int(getenv('IMPORT_STATUS_DB_PORT', '5432')),
'dbname': getenv('IMPORT_STATUS_DB_NAME', 'sientia-core-mlops-bff'),
'user': getenv('IMPORT_STATUS_DB_USER', 'mlops_bff_user'),
'password': getenv('IMPORT_STATUS_DB_PASSWORD', ''),
'schema': 'public',
}