from __future__ import annotations import html import json from pathlib import Path from typing import Any import joblib def export_artifacts( run_id: str, state: dict[str, Any], run_directory: Path, ) -> dict[str, str]: run_directory.mkdir(parents=True, exist_ok=True) bundle = state["model_bundle"] summary = state["summary_payload"] pipeline_path = run_directory / "model_pipeline.joblib" joblib.dump(bundle.pipeline, pipeline_path) metrics_path = run_directory / "metrics.json" metrics_path.write_text(json.dumps(summary, indent=2, default=str), encoding="utf-8") model_card_path = run_directory / "MODEL_CARD.md" model_card_path.write_text(_model_card(summary), encoding="utf-8") report_path = run_directory / "analysis_report.html" report_path.write_text(_html_report(summary), encoding="utf-8") requirements_path = run_directory / "reproduction.json" requirements_path.write_text( json.dumps( { "run_id": run_id, "random_state": state["settings"].random_state, "test_size": state["settings"].test_size, "target": summary["profile"]["target"], "task_type": summary["profile"]["task_type"], "best_model": summary["best_model"], }, indent=2, ), encoding="utf-8", ) return { "pipeline": str(pipeline_path), "metrics": str(metrics_path), "model_card": str(model_card_path), "report": str(report_path), "reproduction": str(requirements_path), } def _model_card(run: dict[str, Any]) -> str: best = run["model_results"][0] profile = run["profile"] issues = ( "\n".join(f"- {item['message']}" for item in run["quality_issues"]) or "- None detected" ) return f"""# Model Card — {run["dataset_name"]} ## Model details - Run ID: `{run["run_id"]}` - Task: {profile["task_type"]} - Target: `{profile["target"]}` - Selected model: **{run["best_model"]}** - Training-CV selection metric: `{best["primary_metric"]} = {best["selection_score"]:.4f}` - One-time untouched test metric: `{best["primary_metric"]} = {best["final_test_score"]:.4f}` - Training rows before split: {profile["rows"]:,} ## Intended use Exploratory decision support and portfolio demonstration. Validate with domain-specific, out-of-time data before any consequential or production use. ## Evaluation ```json {json.dumps(best["final_test_metrics"], indent=2)} ``` ## Data-quality observations {issues} ## Explainability Method: **{run["explainability"]["method"]}**. Importance values are predictive associations, not evidence of causation. ## Limitations - Results depend on the uploaded dataset and chosen target. - Automated task inference can be wrong; a domain owner should confirm the objective. - Fairness, privacy, and legal review are outside the automatic approval gate. """ def _html_report(run: dict[str, Any]) -> str: best = run["model_results"][0] summary_items = "".join(f"
{html.escape(run["dataset_name"])} · Run {html.escape(run["run_id"])}
| Metric | Value |
|---|
| Severity | Code | Observation |
|---|
Generated from computed evidence. Predictive findings do not establish causality.
"""