"""Gestion du MLflow Model Registry : consulter les versions et promouvoir via alias. La promotion = pointer un alias (mobile) vers une version precise (figee) du modele. L'API charge ensuite le modele par `models:/@` sans connaitre le numero. """ import logging import typer from mlflow import MlflowClient from .. import constants app = typer.Typer(help="MLflow Model Registry (versions + promotion par alias).") logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) @app.command() def versions( name: str = constants.REGISTERED_MODEL_NAME, ): """Lister les versions du modele enregistre, avec leurs alias.""" client = MlflowClient() results = client.search_model_versions(f"name = '{name}'") if not results: logger.warning(f"Aucune version pour le modele '{name}'.") raise typer.Exit(code=1) # Les alias sont portes par le modele enregistre (dict alias -> version), # pas par les objets renvoyes par search_model_versions. alias_by_version: dict[str, list[str]] = {} for alias, version in client.get_registered_model(name).aliases.items(): alias_by_version.setdefault(str(version), []).append(alias) for mv in sorted(results, key=lambda v: int(v.version)): aliases = ", ".join(alias_by_version.get(str(mv.version), [])) or "-" run = client.get_run(mv.run_id) if mv.run_id else None strategy = run.data.params.get("strategy", "?") if run else "?" val_rmse = run.data.metrics.get("validation_rmse") if run else None rmse_txt = f"{val_rmse:.3f}" if val_rmse is not None else "?" typer.echo( f"v{mv.version:<3} | alias: {aliases:<12} | strategy={strategy:<12}" f" | validation_rmse={rmse_txt} | run={mv.run_id}" ) @app.command() def promote( version: int = typer.Option(..., help="Numero de version a promouvoir."), alias: str = typer.Option(constants.MODEL_ALIAS, help="Alias a (re)pointer."), name: str = constants.REGISTERED_MODEL_NAME, ): """Promouvoir une version : (re)pointer l'alias vers cette version.""" client = MlflowClient() client.set_registered_model_alias(name=name, alias=alias, version=str(version)) mv = client.get_model_version_by_alias(name=name, alias=alias) logger.info(f"Alias '{alias}' -> {name} v{mv.version} (source: {mv.source})") if __name__ == "__main__": app()