Promote, reject, and roll back model versions#
Goal. Work through a bounded, reproducible example and inspect the evidence before connecting an external service.
Prerequisites. Base FraudTwin install. Optional extras and Docker commands are clearly marked.
Produces. Tables, fingerprints, manifests, and verification output.
Source size. The default cells generate approximately 1,000 logical payments; increase duration and population together for a 10,000-payment run.
Offline path. All marked offline cells run without Docker or network services. Service cells are optional and explicitly marked in notebook metadata.
Cleanup. Outputs are written under a temporary directory; remove any local run directory if you changed the output location.
!pip install mlflow
import os
os.environ["MLFLOW_TRACKING_URI"] = "http://127.0.0.1:5000"
print("MLflow tracking URI configured: http://127.0.0.1:5000")
Requirement already satisfied: mlflow in /home/emc/Projects/test/.venv/lib/python3.14/site-packages (3.16.1)
Requirement already satisfied: mlflow-skinny==3.16.1 in /home/emc/Projects/test/.venv/lib/python3.14/site-packages (from mlflow) (3.16.1)
Requirement already satisfied: mlflow-tracing==3.16.1 in /home/emc/Projects/test/.venv/lib/python3.14/site-packages (from mlflow) (3.16.1)
Requirement already satisfied: Flask-CORS<7 in /home/emc/Projects/test/.venv/lib/python3.14/site-packages (from mlflow) (6.0.5)
Requirement already satisfied: Flask<4 in /home/emc/Projects/test/.venv/lib/python3.14/site-packages (from mlflow) (3.1.3)
Requirement already satisfied: aiohttp<4,>=3.7.0 in /home/emc/Projects/test/.venv/lib/python3.14/site-packages (from mlflow) (3.14.3)
Requirement already satisfied: alembic!=1.10.0,<2 in /home/emc/Projects/test/.venv/lib/python3.14/site-packages (from mlflow) (1.20.0)
Requirement already satisfied: cryptography<51,>=43.0.0 in /home/emc/Projects/test/.venv/lib/python3.14/site-packages (from mlflow) (50.0.1)
Requirement already satisfied: docker<8,>=4.0.0 in /home/emc/Projects/test/.venv/lib/python3.14/site-packages (from mlflow) (7.2.0)
Requirement already satisfied: graphene<4 in /home/emc/Projects/test/.venv/lib/python3.14/site-packages (from mlflow) (3.4.3)
Requirement already satisfied: gunicorn<27 in /home/emc/Projects/test/.venv/lib/python3.14/site-packages (from mlflow) (26.2.0)
Requirement already satisfied: huey<4,>=2.5.4 in /home/emc/Projects/test/.venv/lib/python3.14/site-packages (from mlflow) (3.4.0)
Requirement already satisfied: matplotlib<4 in /home/emc/Projects/test/.venv/lib/python3.14/site-packages (from mlflow) (3.11.2)
Requirement already satisfied: numpy<3 in /home/emc/Projects/test/.venv/lib/python3.14/site-packages (from mlflow) (2.5.3)
Requirement already satisfied: pandas<4 in /home/emc/Projects/test/.venv/lib/python3.14/site-packages (from mlflow) (2.3.3)
Requirement already satisfied: pyarrow<26,>=4.0.0 in /home/emc/Projects/test/.venv/lib/python3.14/site-packages (from mlflow) (25.0.1)
Requirement already satisfied: scikit-learn<2 in /home/emc/Projects/test/.venv/lib/python3.14/site-packages (from mlflow) (1.9.1)
Requirement already satisfied: scipy<2 in /home/emc/Projects/test/.venv/lib/python3.14/site-packages (from mlflow) (1.18.1)
Requirement already satisfied: skops<1 in /home/emc/Projects/test/.venv/lib/python3.14/site-packages (from mlflow) (0.16.0)
Requirement already satisfied: sqlalchemy<3,>=1.4.0 in /home/emc/Projects/test/.venv/lib/python3.14/site-packages (from mlflow) (2.0.53)
Requirement already satisfied: anyio!=4.15.0,<5,>=3.6.2 in /home/emc/Projects/test/.venv/lib/python3.14/site-packages (from mlflow-skinny==3.16.1->mlflow) (4.15.1)
Requirement already satisfied: cachetools<8,>=5.0.0 in /home/emc/Projects/test/.venv/lib/python3.14/site-packages (from mlflow-skinny==3.16.1->mlflow) (7.2.0)
Requirement already satisfied: click<9,>=7.0 in /home/emc/Projects/test/.venv/lib/python3.14/site-packages (from mlflow-skinny==3.16.1->mlflow) (8.5.0)
Requirement already satisfied: cloudpickle<4 in /home/emc/Projects/test/.venv/lib/python3.14/site-packages (from mlflow-skinny==3.16.1->mlflow) (3.1.2)
Requirement already satisfied: databricks-sdk<1,>=0.20.0 in /home/emc/Projects/test/.venv/lib/python3.14/site-packages (from mlflow-skinny==3.16.1->mlflow) (0.141.0)
Requirement already satisfied: fastapi<1 in /home/emc/Projects/test/.venv/lib/python3.14/site-packages (from mlflow-skinny==3.16.1->mlflow) (0.141.1)
Requirement already satisfied: gitpython<4,>=3.1.9 in /home/emc/Projects/test/.venv/lib/python3.14/site-packages (from mlflow-skinny==3.16.1->mlflow) (3.1.62)
Requirement already satisfied: importlib_metadata!=4.7.0,<10,>=3.7.0 in /home/emc/Projects/test/.venv/lib/python3.14/site-packages (from mlflow-skinny==3.16.1->mlflow) (9.0.1)
Requirement already satisfied: opentelemetry-api<3,>=1.9.0 in /home/emc/Projects/test/.venv/lib/python3.14/site-packages (from mlflow-skinny==3.16.1->mlflow) (1.44.0)
Requirement already satisfied: opentelemetry-proto<3,>=1.9.0 in /home/emc/Projects/test/.venv/lib/python3.14/site-packages (from mlflow-skinny==3.16.1->mlflow) (1.44.0)
Requirement already satisfied: opentelemetry-sdk<3,>=1.9.0 in /home/emc/Projects/test/.venv/lib/python3.14/site-packages (from mlflow-skinny==3.16.1->mlflow) (1.44.0)
Requirement already satisfied: packaging<27 in /home/emc/Projects/test/.venv/lib/python3.14/site-packages (from mlflow-skinny==3.16.1->mlflow) (26.3)
Requirement already satisfied: protobuf<8,>=3.12.0 in /home/emc/Projects/test/.venv/lib/python3.14/site-packages (from mlflow-skinny==3.16.1->mlflow) (6.33.6)
Requirement already satisfied: pydantic<3,>=2.0.0 in /home/emc/Projects/test/.venv/lib/python3.14/site-packages (from mlflow-skinny==3.16.1->mlflow) (2.13.5)
Requirement already satisfied: python-dotenv<2,>=0.19.0 in /home/emc/Projects/test/.venv/lib/python3.14/site-packages (from mlflow-skinny==3.16.1->mlflow) (1.2.3)
Requirement already satisfied: pyyaml<7,>=5.1 in /home/emc/Projects/test/.venv/lib/python3.14/site-packages (from mlflow-skinny==3.16.1->mlflow) (6.0.3)
Requirement already satisfied: requests<3,>=2.17.3 in /home/emc/Projects/test/.venv/lib/python3.14/site-packages (from mlflow-skinny==3.16.1->mlflow) (2.34.2)
Requirement already satisfied: sqlparse<1,>=0.4.0 in /home/emc/Projects/test/.venv/lib/python3.14/site-packages (from mlflow-skinny==3.16.1->mlflow) (0.6.0)
Requirement already satisfied: starlette<2 in /home/emc/Projects/test/.venv/lib/python3.14/site-packages (from mlflow-skinny==3.16.1->mlflow) (1.7.0)
Requirement already satisfied: typing-extensions<5,>=4.0.0 in /home/emc/Projects/test/.venv/lib/python3.14/site-packages (from mlflow-skinny==3.16.1->mlflow) (4.16.0)
Requirement already satisfied: uvicorn<1 in /home/emc/Projects/test/.venv/lib/python3.14/site-packages (from mlflow-skinny==3.16.1->mlflow) (0.53.0)
Requirement already satisfied: aiohappyeyeballs>=2.5.0 in /home/emc/Projects/test/.venv/lib/python3.14/site-packages (from aiohttp<4,>=3.7.0->mlflow) (2.7.1)
Requirement already satisfied: aiosignal>=1.4.0 in /home/emc/Projects/test/.venv/lib/python3.14/site-packages (from aiohttp<4,>=3.7.0->mlflow) (1.4.0)
Requirement already satisfied: attrs>=17.3.0 in /home/emc/Projects/test/.venv/lib/python3.14/site-packages (from aiohttp<4,>=3.7.0->mlflow) (26.1.0)
Requirement already satisfied: frozenlist>=1.1.1 in /home/emc/Projects/test/.venv/lib/python3.14/site-packages (from aiohttp<4,>=3.7.0->mlflow) (1.8.0)
Requirement already satisfied: multidict<7.0,>=4.5 in /home/emc/Projects/test/.venv/lib/python3.14/site-packages (from aiohttp<4,>=3.7.0->mlflow) (6.9.1)
Requirement already satisfied: propcache>=0.2.0 in /home/emc/Projects/test/.venv/lib/python3.14/site-packages (from aiohttp<4,>=3.7.0->mlflow) (0.5.4)
Requirement already satisfied: yarl<2.0,>=1.17.0 in /home/emc/Projects/test/.venv/lib/python3.14/site-packages (from aiohttp<4,>=3.7.0->mlflow) (1.25.1)
Requirement already satisfied: Mako in /home/emc/Projects/test/.venv/lib/python3.14/site-packages (from alembic!=1.10.0,<2->mlflow) (1.4.3)
Requirement already satisfied: idna>=2.8 in /home/emc/Projects/test/.venv/lib/python3.14/site-packages (from anyio!=4.15.0,<5,>=3.6.2->mlflow-skinny==3.16.1->mlflow) (3.19)
Requirement already satisfied: cffi>=2.0.0 in /home/emc/Projects/test/.venv/lib/python3.14/site-packages (from cryptography<51,>=43.0.0->mlflow) (2.1.1)
Requirement already satisfied: google-auth~=2.0 in /home/emc/Projects/test/.venv/lib/python3.14/site-packages (from databricks-sdk<1,>=0.20.0->mlflow-skinny==3.16.1->mlflow) (2.58.0)
Requirement already satisfied: urllib3<3,>=1.26 in /home/emc/Projects/test/.venv/lib/python3.14/site-packages (from databricks-sdk<1,>=0.20.0->mlflow-skinny==3.16.1->mlflow) (2.7.0)
Requirement already satisfied: typing-inspection>=0.4.2 in /home/emc/Projects/test/.venv/lib/python3.14/site-packages (from fastapi<1->mlflow-skinny==3.16.1->mlflow) (0.4.4)
Requirement already satisfied: annotated-doc>=0.0.2 in /home/emc/Projects/test/.venv/lib/python3.14/site-packages (from fastapi<1->mlflow-skinny==3.16.1->mlflow) (0.0.5)
Requirement already satisfied: blinker>=1.9.0 in /home/emc/Projects/test/.venv/lib/python3.14/site-packages (from Flask<4->mlflow) (1.9.0)
Requirement already satisfied: itsdangerous>=2.2.0 in /home/emc/Projects/test/.venv/lib/python3.14/site-packages (from Flask<4->mlflow) (2.2.0)
Requirement already satisfied: jinja2>=3.1.2 in /home/emc/Projects/test/.venv/lib/python3.14/site-packages (from Flask<4->mlflow) (3.1.6)
Requirement already satisfied: markupsafe>=2.1.1 in /home/emc/Projects/test/.venv/lib/python3.14/site-packages (from Flask<4->mlflow) (3.0.3)
Requirement already satisfied: werkzeug>=3.1.0 in /home/emc/Projects/test/.venv/lib/python3.14/site-packages (from Flask<4->mlflow) (3.1.8)
Requirement already satisfied: gitdb<5,>=4.0.1 in /home/emc/Projects/test/.venv/lib/python3.14/site-packages (from gitpython<4,>=3.1.9->mlflow-skinny==3.16.1->mlflow) (4.0.12)
Requirement already satisfied: smmap<6,>=3.0.1 in /home/emc/Projects/test/.venv/lib/python3.14/site-packages (from gitdb<5,>=4.0.1->gitpython<4,>=3.1.9->mlflow-skinny==3.16.1->mlflow) (5.0.3)
Requirement already satisfied: pyasn1-modules>=0.2.1 in /home/emc/Projects/test/.venv/lib/python3.14/site-packages (from google-auth~=2.0->databricks-sdk<1,>=0.20.0->mlflow-skinny==3.16.1->mlflow) (0.4.2)
Requirement already satisfied: graphql-core<3.3,>=3.1 in /home/emc/Projects/test/.venv/lib/python3.14/site-packages (from graphene<4->mlflow) (3.2.12)
Requirement already satisfied: graphql-relay<3.3,>=3.1 in /home/emc/Projects/test/.venv/lib/python3.14/site-packages (from graphene<4->mlflow) (3.2.0)
Requirement already satisfied: python-dateutil<3,>=2.7.0 in /home/emc/Projects/test/.venv/lib/python3.14/site-packages (from graphene<4->mlflow) (2.9.0.post0)
Requirement already satisfied: zipp>=3.20 in /home/emc/Projects/test/.venv/lib/python3.14/site-packages (from importlib_metadata!=4.7.0,<10,>=3.7.0->mlflow-skinny==3.16.1->mlflow) (4.1.0)
Requirement already satisfied: contourpy>=1.0.1 in /home/emc/Projects/test/.venv/lib/python3.14/site-packages (from matplotlib<4->mlflow) (1.4.0)
Requirement already satisfied: cycler>=0.10 in /home/emc/Projects/test/.venv/lib/python3.14/site-packages (from matplotlib<4->mlflow) (0.12.1)
Requirement already satisfied: fonttools>=4.28.2 in /home/emc/Projects/test/.venv/lib/python3.14/site-packages (from matplotlib<4->mlflow) (4.66.0)
Requirement already satisfied: kiwisolver>=1.3.1 in /home/emc/Projects/test/.venv/lib/python3.14/site-packages (from matplotlib<4->mlflow) (1.5.1)
Requirement already satisfied: pillow>=9 in /home/emc/Projects/test/.venv/lib/python3.14/site-packages (from matplotlib<4->mlflow) (12.3.0)
Requirement already satisfied: pyparsing>=3 in /home/emc/Projects/test/.venv/lib/python3.14/site-packages (from matplotlib<4->mlflow) (3.3.3)
Requirement already satisfied: opentelemetry-semantic-conventions==0.65b0 in /home/emc/Projects/test/.venv/lib/python3.14/site-packages (from opentelemetry-sdk<3,>=1.9.0->mlflow-skinny==3.16.1->mlflow) (0.65b0)
Requirement already satisfied: pytz>=2020.1 in /home/emc/Projects/test/.venv/lib/python3.14/site-packages (from pandas<4->mlflow) (2026.3.post1)
Requirement already satisfied: tzdata>=2022.7 in /home/emc/Projects/test/.venv/lib/python3.14/site-packages (from pandas<4->mlflow) (2026.4)
Requirement already satisfied: annotated-types>=0.6.0 in /home/emc/Projects/test/.venv/lib/python3.14/site-packages (from pydantic<3,>=2.0.0->mlflow-skinny==3.16.1->mlflow) (0.8.0)
Requirement already satisfied: pydantic-core==2.46.5 in /home/emc/Projects/test/.venv/lib/python3.14/site-packages (from pydantic<3,>=2.0.0->mlflow-skinny==3.16.1->mlflow) (2.46.5)
Requirement already satisfied: six>=1.5 in /home/emc/Projects/test/.venv/lib/python3.14/site-packages (from python-dateutil<3,>=2.7.0->graphene<4->mlflow) (1.17.0)
Requirement already satisfied: charset_normalizer<4,>=2 in /home/emc/Projects/test/.venv/lib/python3.14/site-packages (from requests<3,>=2.17.3->mlflow-skinny==3.16.1->mlflow) (3.5.1)
Requirement already satisfied: certifi>=2023.5.7 in /home/emc/Projects/test/.venv/lib/python3.14/site-packages (from requests<3,>=2.17.3->mlflow-skinny==3.16.1->mlflow) (2026.7.22)
Requirement already satisfied: joblib>=1.4.0 in /home/emc/Projects/test/.venv/lib/python3.14/site-packages (from scikit-learn<2->mlflow) (1.6.0)
Requirement already satisfied: narwhals>=2.0.1 in /home/emc/Projects/test/.venv/lib/python3.14/site-packages (from scikit-learn<2->mlflow) (2.26.0)
Requirement already satisfied: threadpoolctl>=3.5.0 in /home/emc/Projects/test/.venv/lib/python3.14/site-packages (from scikit-learn<2->mlflow) (3.6.0)
Requirement already satisfied: prettytable>=3.9 in /home/emc/Projects/test/.venv/lib/python3.14/site-packages (from skops<1->mlflow) (3.18.0)
Requirement already satisfied: greenlet>=1 in /home/emc/Projects/test/.venv/lib/python3.14/site-packages (from sqlalchemy<3,>=1.4.0->mlflow) (3.5.6)
Requirement already satisfied: h11>=0.8 in /home/emc/Projects/test/.venv/lib/python3.14/site-packages (from uvicorn<1->mlflow-skinny==3.16.1->mlflow) (0.16.0)
Requirement already satisfied: pycparser in /home/emc/Projects/test/.venv/lib/python3.14/site-packages (from cffi>=2.0.0->cryptography<51,>=43.0.0->mlflow) (3.0)
Requirement already satisfied: wcwidth>=0.3.5 in /home/emc/Projects/test/.venv/lib/python3.14/site-packages (from prettytable>=3.9->skops<1->mlflow) (0.8.3)
Requirement already satisfied: pyasn1<0.7.0,>=0.6.1 in /home/emc/Projects/test/.venv/lib/python3.14/site-packages (from pyasn1-modules>=0.2.1->google-auth~=2.0->databricks-sdk<1,>=0.20.0->mlflow-skinny==3.16.1->mlflow) (0.6.4)
MLflow tracking URI configured: http://127.0.0.1:5000
Optional MLflow setup#
The notebook works without a server by using a temporary local tracking store. To make the run appear in the MLflow web UI, start the tracking server from a separate terminal in the repository root and keep that terminal running:
poetry install -E mlflow
poetry run mlflow server --host 127.0.0.1 --port 5000
Then open http://127.0.0.1:5000 in the same machine’s browser. localhost:5000 works too, but only while the command above is running; opening the URL alone cannot start MLflow. If the notebook runs in Docker, WSL, or a remote Jupyter server, use the forwarded/public host and port instead of your browser’s localhost.
After the server is running, execute the first optional MLflow setup cell below. It sets the tracking URI in the Jupyter kernel. Then execute the MLflow tracking cell near the end of the notebook; merely reading this Markdown code block does not configure the kernel:
import os
os.environ["MLFLOW_TRACKING_URI"] = "http://127.0.0.1:5000"
If the setup cell is skipped, the notebook intentionally uses a temporary local store and the browser UI will remain empty. This tutorial logs a standard MLflow tracking run, not a GenAI trace. In MLflow 3, choose the Model training tab at the top of the experiment page; the GenAI overview will correctly show zero traces. If the URL contains workflowType=genai, remove that query parameter or use the direct run link printed by the tracking cell. See the MLflow tracking server guide for server and client configuration details.
Set up a deterministic source run
import json
from pathlib import Path
from tempfile import TemporaryDirectory
import polars as pl
from fraudtwin.config import load_config
from fraudtwin.generation import generate
root = next(
(p for p in (Path.cwd(), *Path.cwd().parents) if (p / "configs" / "minimal.yaml").exists()),
Path.cwd(),
)
base = load_config(root / "configs" / "minimal.yaml")
# Scale the population so the bounded example produces about 1,000 payments.
population = base.population.model_copy(
update={
"customers": 200,
"accounts": 300,
"cards": 240,
"devices": 240,
"pix_keys": 160,
"merchants": 60,
}
)
simulation = base.simulation.model_copy(update={"duration_days": 10})
fraud = base.fraud.model_copy(update={"enabled": True, "target_rate": 0.05})
config = base.model_copy(
update={"population": population, "simulation": simulation, "fraud": fraud}
)
data = generate(config, write=False)
run_id = data.run_id
payments = pl.DataFrame([item.model_dump(mode="json") for item in data.behavior.payments])
print("Generated source run")
print(f" run id: {run_id}")
print(f" payments: {len(payments):,}")
print(f" events: {len(data.behavior.payment_events):,}")
Generated source run
run id: RUN-2a3ad02ee370aeb8
payments: 1,092
events: 4,699
Score a small evaluation sample
from fraudtwin.ml import heuristic_predictions
dataset = data.require_dataset()
policy_rows = dataset.rows[:20]
scores = heuristic_predictions(policy_rows)
metrics = {"rows": len(scores), "mean_score": sum(s.fraud_score for s in scores) / len(scores)}
print("Candidate metrics")
print(f" rows scored: {metrics['rows']:,}")
print(f" mean fraud score: {metrics['mean_score']:.3f}")
Candidate metrics
rows scored: 20
mean fraud score: 0.245
Create a candidate model record
candidate = {"version": "candidate-1", "schema": "pit-v1", "metrics": metrics}
display(
pl.DataFrame(
[{"version": candidate["version"], "schema": candidate["schema"], **candidate["metrics"]}]
)
)
| version | schema | rows | mean_score |
|---|---|---|---|
| str | str | i64 | f64 |
| "candidate-1" | "pit-v1" | 20 | 0.244821 |
Promote the candidate
registry = {"Production": None, "Staging": candidate}
registry["Production"] = registry["Staging"]
print("Promotion")
print(f" staging -> production: {registry['Production']['version']}")
Promotion
staging -> production: candidate-1
Roll back to the previous version
registry["Staging"] = {**candidate, "version": "candidate-2"}
registry["Production"] = candidate
print("Rollback")
print(f" production restored to: {registry['Production']['version']}")
Rollback
production restored to: candidate-1
Verify the promotion contract
assert registry["Production"]["schema"] == "pit-v1"
print("Optional MLflow registry calls can replace this local state machine.")
Optional MLflow registry calls can replace this local state machine.
Verify invariants and clean up
# A compact inspection is more useful than printing an entire run.
sample_columns = [
c for c in ("payment_id", "amount", "initiated_at", "payer_account_id") if c in payments.columns
]
sample_rows = payments.select(sample_columns).head(8).to_dicts()
print(f"Sample payments ({len(sample_rows)} of {payments.height} rows):")
for row in sample_rows:
print(
f" - {row.get('payment_id')}: amount={row.get('amount')}, "
f"initiated_at={row.get('initiated_at')}, payer={row.get('payer_account_id')}"
)
nulls = {name: count for name, count in payments.null_count().to_dicts()[0].items() if count}
print("\nData quality summary:")
print(f" rows: {payments.height}")
print(f" columns: {payments.width}")
if not nulls:
print(" nulls: none")
else:
print(" columns with nulls:")
for name, count in sorted(nulls.items()):
print(f" - {name}: {count}")
Sample payments (8 of 1092 rows):
- PAY-00000001: amount=70.7, initiated_at=2026-01-03T16:25:00Z, payer=ACC-000123
- PAY-00000002: amount=25.52, initiated_at=2026-01-05T11:37:00Z, payer=ACC-000174
- PAY-00000003: amount=18.37, initiated_at=2026-01-05T11:21:00Z, payer=ACC-000174
- PAY-00000004: amount=5.54, initiated_at=2026-01-02T22:30:00Z, payer=ACC-000003
- PAY-00000005: amount=13.71, initiated_at=2026-01-02T09:41:00Z, payer=ACC-000029
- PAY-00000006: amount=70.06, initiated_at=2026-01-04T10:40:00Z, payer=ACC-000179
- PAY-00000007: amount=42.73, initiated_at=2026-01-02T18:04:00Z, payer=ACC-000247
- PAY-00000008: amount=36.42, initiated_at=2026-01-05T09:27:00Z, payer=ACC-000255
Data quality summary:
rows: 1092
columns: 15
columns with nulls:
- card_id: 565
- merchant_id: 565
- payee_account_id: 86
- payee_institution_id: 86
- payee_pix_key_id: 857
- payer_institution_id: 86
- payer_pix_key_id: 857
Optional service integration
summary = {
"run_id": run_id,
"payments": len(data.behavior.payments),
"payment_events": len(data.behavior.payment_events),
"fraud_records": len(data.behavior.fraud_records),
}
assert summary["payments"] == len(payments)
assert summary["payments"] > 0
print("Generated dataset")
print(f" run id: {summary['run_id']}")
print(f" payments: {summary['payments']:,}")
print(f" payment events: {summary['payment_events']:,}")
print(f" fraud records: {summary['fraud_records']:,}")
Generated dataset
run id: RUN-2a3ad02ee370aeb8
payments: 1,092
payment events: 4,699
fraud records: 51
Review the expected outcome
import os
import warnings
from urllib.parse import urlparse
warnings.filterwarnings("ignore", message="IProgress not found.*")
try:
import mlflow
from IPython.display import Markdown, display
from mlflow.tracking import MlflowClient
tracking_uri = os.getenv("MLFLOW_TRACKING_URI")
with TemporaryDirectory(prefix="fraudtwin-mlflow-") as mlflow_dir:
mlflow.set_tracking_uri(tracking_uri or Path(mlflow_dir).as_uri())
mlflow.set_experiment("fraudtwin-tutorial")
with mlflow.start_run() as run:
mlflow.log_params({"schema": candidate["schema"]})
mlflow.log_metric("mean_score", metrics["mean_score"])
artifact = Path(mlflow_dir) / "candidate.json"
artifact.write_text(json.dumps(candidate), encoding="utf-8")
mlflow.log_artifact(str(artifact))
tracked_id = run.info.run_id
client = MlflowClient()
experiment = client.get_experiment_by_name("fraudtwin-tutorial")
tracked = client.get_run(tracked_id)
experiments = client.search_experiments()
print("MLflow tracking")
print(f" tracking URI: {mlflow.get_tracking_uri()}")
print(f" experiment: {experiment.name} (id={experiment.experiment_id})")
display(
pl.DataFrame(
[
{"experiment": item.name, "experiment_id": item.experiment_id}
for item in experiments
]
)
)
print(" tracked: yes")
print(f" run id: {tracked.info.run_id}")
print(f" metrics logged: {len(tracked.data.metrics)}")
if urlparse(tracking_uri or "").scheme in {"http", "https"}:
run_url = (
f"{tracking_uri.rstrip('/')}/#/experiments/"
f"{tracked.info.experiment_id}/runs/{tracked.info.run_id}"
)
display(Markdown(f"[Open this run in MLflow]({run_url})"))
else:
print(" browser link: unavailable for the temporary local store")
except Exception as exc:
print("MLflow tracking is unavailable; the offline promotion state remains valid.")
print(" tracked: no")
print(" offline_fallback: yes")
print(f" reason: {type(exc).__name__}")
🏃 View run angry-shoat-568 at: http://127.0.0.1:5000/#/experiments/1/runs/bdb270b811f3440d8afaa0ae041ddd01
🧪 View experiment at: http://127.0.0.1:5000/#/experiments/1
MLflow tracking
tracking URI: http://127.0.0.1:5000
experiment: fraudtwin-tutorial (id=1)
| experiment | experiment_id |
|---|---|
| str | str |
| "fraudtwin-tutorial" | "1" |
| "Default" | "0" |
tracked: yes
run id: bdb270b811f3440d8afaa0ae041ddd01
metrics logged: 1
Record the generated shape and tutorial contract.#
summary = {
"payments": len(data.behavior.payments),
"events": len(data.behavior.payment_events),
}
print("Tutorial contract")
print(f" payments: {summary['payments']:,}")
print(f" events: {summary['events']:,}")
assert summary["payments"] >= 0
Tutorial contract
payments: 1,092
events: 4,699
assert registry["Production"]["schema"] == "pit-v1"
print("Offline promotion fallback")
print(" offline_fallback: yes")
print(f" production version: {registry['Production']['version']}")
Offline promotion fallback
offline_fallback: yes
production version: candidate-1