"""meb_ml — SDK importabile dal codice utente dentro il container runner. API: from meb_ml import emit_metric, emit_series, emit_matrix, emit_log, save_artifact emit_metric(iter=10, loss=0.23) emit_series("roc_curve", x=fpr, y=tpr, kind="line") emit_matrix("confusion", labels=[...], values=[[...],[...]]) emit_log("info", "epoch done") Scrive righe JSON su stdout; il parent (ml-service) le inoltra su Redis/Influx. Per risultati finali scrivere `out/metrics.json` con: {"metrics": {...}, "plots": {"loss_curve": {"x": [...], "y": [...]}, ...}} """ from __future__ import annotations import json import os import sys from pathlib import Path from typing import Any, Iterable, Sequence def _print(obj: dict) -> None: sys.stdout.write(json.dumps(obj, default=float) + "\n") sys.stdout.flush() def emit_metric(**fields: Any) -> None: _print({"type": "metric", **fields}) def emit_series(name: str, x: Sequence, y: Sequence, kind: str = "line") -> None: _print({ "type": "series", "name": name, "kind": kind, "x": list(x), "y": list(y), }) def emit_matrix(name: str, labels: Sequence, values: Sequence[Sequence]) -> None: _print({ "type": "matrix", "name": name, "labels": list(labels), "values": [list(row) for row in values], }) def emit_log(level: str, message: str) -> None: _print({"type": "log", "level": level, "message": message}) def save_artifact(path: str) -> str: """Copia `path` nella cartella artefatti (MEB_ARTIFACTS_DIR). Ritorna la dest.""" dest_dir = Path(os.environ.get("MEB_ARTIFACTS_DIR", "/workdir/out")) dest_dir.mkdir(parents=True, exist_ok=True) src = Path(path) dest = dest_dir / src.name dest.write_bytes(src.read_bytes()) return str(dest) def dataset_path() -> str: return os.environ["MEB_DATASET_PATH"] def artifacts_dir() -> str: return os.environ.get("MEB_ARTIFACTS_DIR", "/workdir/out") def read_test_input() -> dict: """Legge un singolo JSON da stdin (per script di test).""" return json.loads(sys.stdin.readline()) def write_test_output(outputs: dict) -> None: _print({"type": "result", "outputs": outputs})