FlowProt / smoke_checks.py
alibtsd's picture
Deploy FlowProt Docker Space
f34af6f verified
Raw
History Blame Contribute Delete
4.53 kB
"""Smoke checks for FlowProt Space runtime tiers."""
from __future__ import annotations
import argparse
import importlib
import logging
import sys
from inference import (
ArtifactResolutionError,
FlowProtInferenceService,
InferenceError,
ModelLoadError,
)
LOGGER = logging.getLogger(__name__)
def run_import_test() -> None:
modules = ["model_loader", "inference", "app"]
for module_name in modules:
importlib.import_module(module_name)
LOGGER.info("Imported module: %s", module_name)
def run_model_load_test() -> None:
service = FlowProtInferenceService()
service.preload_model()
health = service.health_check()
if not health.get("model_loaded"):
raise RuntimeError(f"Model did not report loaded status: {health}")
LOGGER.info("Model load check passed on device=%s", health.get("device"))
def run_handlers_test() -> None:
"""Exercise pure UI helper logic that does not require a loaded model."""
import app
assert app._parse_optional_seed("") is None
assert app._parse_optional_seed("123") == 123
try:
app._parse_optional_seed("not-an-int")
raise RuntimeError("Expected seed parse to reject non-integer input.")
except InferenceError:
pass
assert app._parse_fixed_residues("") is None
assert app._parse_fixed_residues("10-12,15") == [10, 11, 12, 15]
assert app._parse_fixed_residues("A:5-6, A:5") == [5, 6]
metrics = [
{"sample_display": "sample_0_1", "sample": "sample_0_1", "scTM": 0.9, "scRMSD": 1.0, "esmfold_mean_plddt": 0.8},
{"sample_display": "sample_0_2", "sample": "sample_0_2", "scTM": 0.4, "scRMSD": 6.0, "esmfold_mean_plddt": 0.5},
]
filtered = app._filter_metrics(
metrics=metrics,
sample_labels=[],
sample_query="",
min_tm=0.5,
max_rmsd=10.0,
min_plddt=0.0,
)
assert len(filtered) == 1 and filtered[0]["sample_display"] == "sample_0_1"
badge = app._status_badge_html()
assert "Model" in badge
label = app.update_selection_label(None)
assert "No sample" in label
LOGGER.info("Handler smoke checks passed.")
def run_inference_test(length: int, num_samples: int, seed: int | None = None) -> None:
service = FlowProtInferenceService()
result = service.generate(
mode="unconditional",
length=length,
num_samples=num_samples,
seed=seed,
)
if not result.sample_files:
raise RuntimeError("Inference returned zero sample files.")
LOGGER.info("Inference smoke check produced %d sample file(s).", len(result.sample_files))
LOGGER.info("Seed used: %s", result.seed)
LOGGER.info("Run directory: %s", result.run_dir)
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(description="FlowProt Space smoke checks.")
parser.add_argument(
"--tier",
required=True,
choices=["import", "handlers", "model-load", "inference"],
help="Smoke check tier to run.",
)
parser.add_argument("--length", type=int, default=64, help="Inference length for inference tier.")
parser.add_argument(
"--num-samples",
type=int,
default=1,
help="Samples for inference tier.",
)
parser.add_argument(
"--seed",
type=int,
default=None,
help="Optional seed override for inference tier.",
)
return parser.parse_args()
def main() -> int:
args = parse_args()
logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(name)s - %(message)s")
try:
if args.tier == "import":
run_import_test()
elif args.tier == "handlers":
run_handlers_test()
elif args.tier == "model-load":
run_model_load_test()
else:
run_inference_test(length=args.length, num_samples=args.num_samples, seed=args.seed)
LOGGER.info("Smoke check tier '%s' passed.", args.tier)
return 0
except (ArtifactResolutionError, ModelLoadError, InferenceError) as exc:
LOGGER.error("Smoke check tier '%s' failed: %s", args.tier, exc)
return 1
except Exception as exc: # pragma: no cover
LOGGER.exception("Unexpected failure in smoke check tier '%s': %s", args.tier, exc)
return 2
if __name__ == "__main__":
sys.exit(main())