SIENTIAPDE-1579: Added local and validation flags to test script

This commit is contained in:
Kou Kinoshita
2026-02-13 17:51:17 -03:00
parent f0cdace820
commit 9e3585afce
2 changed files with 206 additions and 3 deletions

View File

@@ -0,0 +1,29 @@
{
"_description": "Cenário angular-test-01: CV022 WIT230 com lag e intervalo de datas",
"experimentName": "angular-test-01",
"username": "lucas.kou@aignosi.com.br",
"modelName": "Linear Regression",
"targetVariable": "03CV022/CORRENTE_N_M1_PV(Value)",
"variableColumns": ["303-WIT-230(Value)"],
"lagTrain": {"303-WIT-230(Value)": 3},
"lagVal": {"303-WIT-230(Value)": 0},
"remStaticWin": false,
"lowLim": {},
"uppLim": {},
"window": 0,
"useScaler": false,
"includeAr": false,
"trainSize": 80,
"shuffle": true,
"lineSeparator": ",",
"decimalSeparator": ".",
"removedIntervals": [],
"degree": 1,
"interactionOnly": false,
"nanTreatment": "drop",
"startDate": "01/05/2022",
"endDate": "31/07/2022",
"scalerName": "None",
"supportFilters": {},
"staticThreshold": null
}

View File

@@ -1,12 +1,19 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
"""Utility script to trigger the training workflow end-to-end for testing. """Utility script to trigger the training workflow end-to-end for testing.
Steps performed: Steps performed (default):
1. Upload the CSV test dataset to MinIO using the configured `mc` alias. 1. Upload the CSV test dataset to MinIO using the configured `mc` alias.
2. Insert a new experiment_run record in Postgres and capture the generated ID. 2. Insert a new experiment_run record in Postgres and capture the generated ID.
3. Trigger the Temporal `train_model` workflow with the correct payload. 3. Trigger the Temporal `train_model` workflow with the correct payload.
Prerequisites: Local diagnosis (--local / --validate-only):
- --validate-only: Validates scenario parameters only (no MinIO, Postgres, Temporal).
- --local: Runs the same training pipeline locally (validate + load CSV + train +
after_train_calculation). Use to get full Python tracebacks for debugging.
Does not upload to MinIO, insert DB, or start Temporal. By default skips
MLflow save; use --local-save-mlflow to also test saving to MLflow.
Prerequisites (default flow):
- `mc` CLI configured with alias defined in MINIO_ALIAS. - `mc` CLI configured with alias defined in MINIO_ALIAS.
- PostgreSQL accessible with credentials in environment variables or defaults. - PostgreSQL accessible with credentials in environment variables or defaults.
- Temporal server reachable without TLS on TEMPORAL_HOST / TEMPORAL_NAMESPACE. - Temporal server reachable without TLS on TEMPORAL_HOST / TEMPORAL_NAMESPACE.
@@ -23,6 +30,7 @@ import subprocess
import sys import sys
import uuid import uuid
from datetime import datetime, timedelta from datetime import datetime, timedelta
from io import BytesIO
from pathlib import Path from pathlib import Path
import psycopg2 import psycopg2
@@ -220,6 +228,113 @@ async def trigger_temporal_workflow(workflow_input: dict) -> str:
return workflow_id return workflow_id
def _build_local_payload(request_data: dict, csv_path: Path) -> dict:
"""Build workflow payload for local run (no real experiment_run_id)."""
return build_workflow_payload(
experiment_run_id=0,
file_name=csv_path.name,
request_data=request_data,
)
def run_validate_only(scenario_name: str) -> dict:
"""Validate scenario parameters only. No MinIO, Postgres, or Temporal.
Returns:
dict: {'success': bool, 'error': str | None, 'scenario': str}
"""
from model_manager.utils.models.train_model_params import TrainModelParams
result = {'scenario': scenario_name, 'success': False, 'error': None}
try:
request_data = load_scenario(scenario_name)
except FileNotFoundError as exc:
result['error'] = str(exc)
return result
payload = _build_local_payload(request_data, Path('local.csv'))
try:
train_params = TrainModelParams.from_dict(payload)
train_params.validate_business_rules()
result['success'] = True
except (ValueError, TypeError, KeyError) as e:
result['error'] = str(e)
return result
def run_local_pipeline(
scenario_name: str,
csv_path: Path,
save_mlflow: bool = False,
) -> dict:
"""Run the same training pipeline locally (validate + train + after_train).
Reads CSV from disk, runs TrainingRepository.train and after_train_calculation.
Optionally saves to MLflow if save_mlflow is True (requires MLflow env).
Returns:
dict: {'success': bool, 'error': str | None, 'scenario': str, ...}
"""
from model_manager.utils.logger_helper import get_logger
from model_manager.utils.models.train_model_params import TrainModelParams
from model_manager.utils.repository.training_repository import TrainingRepository
result = {
'scenario': scenario_name,
'success': False,
'error': None,
}
try:
request_data = load_scenario(scenario_name)
except FileNotFoundError as exc:
result['error'] = str(exc)
return result
if not csv_path.is_absolute():
csv_path = PROJECT_ROOT / csv_path
_ensure_source_file(csv_path)
payload = _build_local_payload(request_data, csv_path)
try:
train_params = TrainModelParams.from_dict(payload)
train_params.validate_business_rules()
except (ValueError, TypeError, KeyError) as e:
result['error'] = f'Validation failed: {e}'
return result
logger = get_logger(__name__)
training_repository = TrainingRepository(logger)
with open(csv_path, 'rb') as f:
file_content = BytesIO(f.read())
try:
train_result = training_repository.train(file_content, train_params)
train_result = training_repository.after_train_calculation(
train_params, train_result
)
except Exception as e:
result['error'] = str(e)
raise # re-raise so caller gets full traceback for diagnosis
if save_mlflow:
from model_manager.utils.connectors_config import build_mlflow_config
from model_manager.utils.repository.model_repository import ModelRepository
mlflow_config = build_mlflow_config()
model_repository = ModelRepository(
url=mlflow_config['url'],
username=mlflow_config['username'],
password=mlflow_config['password'],
logger=logger,
)
train_result = model_repository.save_model(train_result)
result['success'] = True
result['run_name'] = getattr(train_result, 'run_name', None)
result['run_dir'] = getattr(train_result, 'run_dir', None)
return result
def parse_args() -> argparse.Namespace: def parse_args() -> argparse.Namespace:
"""Parse command line arguments.""" """Parse command line arguments."""
parser = argparse.ArgumentParser( parser = argparse.ArgumentParser(
@@ -241,6 +356,15 @@ Examples:
# Run all scenarios # Run all scenarios
python scripts/run_training_test.py --all python scripts/run_training_test.py --all
# Validate scenario parameters only (no external services)
python scripts/run_training_test.py --scenario linear-regression-basic --validate-only
# Run training pipeline locally to diagnose errors (full traceback)
python scripts/run_training_test.py --scenario linear-regression-basic --local --csv docs/test-model-data.csv
# Local run and save to MLflow (requires MLflow env)
python scripts/run_training_test.py --scenario linear-regression-basic --local --local-save-mlflow
""", """,
) )
parser.add_argument( parser.add_argument(
@@ -268,6 +392,21 @@ Examples:
action='store_true', action='store_true',
help='Run all available test scenarios sequentially.', help='Run all available test scenarios sequentially.',
) )
parser.add_argument(
'--validate-only',
action='store_true',
help='Only validate scenario parameters (no MinIO, Postgres, Temporal).',
)
parser.add_argument(
'--local',
action='store_true',
help='Run training pipeline locally (validate + train from CSV) to get full tracebacks.',
)
parser.add_argument(
'--local-save-mlflow',
action='store_true',
help='With --local, also save the model to MLflow (requires MLflow env).',
)
return parser.parse_args() return parser.parse_args()
@@ -405,12 +544,47 @@ def main() -> None:
failed_count = sum(1 for r in results if not r['success']) failed_count = sum(1 for r in results if not r['success'])
sys.exit(1 if failed_count > 0 else 0) sys.exit(1 if failed_count > 0 else 0)
# Validate-only mode: no external services
if args.validate_only:
if not args.scenario:
print('Error: --scenario is required with --validate-only.', file=sys.stderr)
sys.exit(1)
result = run_validate_only(args.scenario)
if result['success']:
print(f"Validation OK: {result['scenario']}")
else:
print(f"Validation failed: {result['error']}", file=sys.stderr)
sys.exit(1)
return
# Local pipeline: same code path as worker, full traceback on error
if args.local:
if not args.scenario:
print('Error: --scenario is required with --local.', file=sys.stderr)
sys.exit(1)
print(f'Running local pipeline: {args.scenario} (CSV: {args.csv})')
result = run_local_pipeline(
args.scenario,
args.csv,
save_mlflow=args.local_save_mlflow,
)
if not result['success']:
print(f"Error: {result['error']}", file=sys.stderr)
sys.exit(1)
out = {'scenario': result['scenario'], 'success': True}
if result.get('run_name') is not None:
out['run_name'] = result['run_name']
if result.get('run_dir') is not None:
out['run_dir'] = result['run_dir']
print(json.dumps(out, indent=2))
return
# Require scenario argument if not listing or running all # Require scenario argument if not listing or running all
if not args.scenario: if not args.scenario:
print('Error: --scenario or --all is required. Use --list to see available scenarios.', file=sys.stderr) print('Error: --scenario or --all is required. Use --list to see available scenarios.', file=sys.stderr)
sys.exit(1) sys.exit(1)
# Run single scenario # Run single scenario (MinIO + Postgres + Temporal)
print(f'Running scenario: {args.scenario}') print(f'Running scenario: {args.scenario}')
result = run_single_scenario(args.scenario, args.csv) result = run_single_scenario(args.scenario, args.csv)