from __future__ import annotations import argparse import json from pathlib import Path import joblib import pandas as pd from src.features.rdkit_features import featurize_smiles, FeatConfig def load_artifacts(artifact_dir: Path): meta = json.loads((artifact_dir / "metadata.json").read_text(encoding="utf-8")) pre = joblib.load(artifact_dir / "preprocess.joblib") models = {t: joblib.load(artifact_dir / "models" / f"{t}.joblib") for t in meta["targets"]} return meta, pre, models def main(): ap = argparse.ArgumentParser(description="Predict ADMET from SMILES using saved artifacts.") ap.add_argument("--artifact_dir",type=str,default="artifacts/admet_smiles_merge_didb_seed202") ap.add_argument("--smiles", type=str, required=True) ap.add_argument("--smiles_col", type=str, default="smiles") ap.add_argument("--threshold", type=float, default=0.5) args = ap.parse_args() meta, pre, models = load_artifacts(Path(args.artifact_dir)) cfg = FeatConfig(fp_radius=meta["features"]["fp"]["radius"], fp_nbits=meta["features"]["fp"]["nbits"]) df = pd.DataFrame({args.smiles_col: [args.smiles]}) X, valid = featurize_smiles(df, smiles_col=args.smiles_col, config=cfg, add_physchem=True, drop_invalid=False) if not valid.all(): raise ValueError("Invalid SMILES.") feature_names = X.columns.tolist() Xt = pd.DataFrame(pre.transform(X), columns=feature_names, index=X.index) out = {"smiles": args.smiles, "pred": {}, "meta": {"model_version": meta["model_version"]}} for t, m in models.items(): prob = float(m.predict_proba(Xt)[:, 1][0]) pred = int(prob >= args.threshold) inv = {v: k for k, v in meta["label_maps"][t]["mapping"].items()} out["pred"][t] = {"prob": prob, "pred": pred, "label": inv[pred]} print(json.dumps(out, indent=2, ensure_ascii=False)) if __name__ == "__main__": main()