feat(llm): add governance context (CDC/movements/PII/lineage) to lab snapshot
lab_context now includes an in-process governance section so the LLM knows the live CDC volume, data movements + last-run state, the PII catalog (OM+heuristic) with masked/unmasked status, and source->masked lineage.
This commit is contained in:
+81
-1
@@ -632,6 +632,77 @@ def _section_command_center(c: dict[str, Any]) -> list[str]:
|
|||||||
return lines
|
return lines
|
||||||
|
|
||||||
|
|
||||||
|
def collect_governance(log: TerminalLogFn | None = None) -> dict[str, Any]:
|
||||||
|
"""In-process governance snapshot: CDC, movements, PII catalog, lineage.
|
||||||
|
|
||||||
|
Reads directly from the live modules (no HTTP) so the LLM always has the
|
||||||
|
current data-platform governance picture.
|
||||||
|
"""
|
||||||
|
out: dict[str, Any] = {"openmetadata_url": os.getenv("OPENMETADATA_URL", "http://10.0.21.47:8585")}
|
||||||
|
try:
|
||||||
|
from cdc_consumer import snapshot as _cdc_snapshot
|
||||||
|
out["cdc"] = _cdc_snapshot(15)
|
||||||
|
except Exception as exc:
|
||||||
|
out["cdc"] = {"error": str(exc)}
|
||||||
|
try:
|
||||||
|
from movements import MOVEMENTS, last_runs
|
||||||
|
runs = last_runs()
|
||||||
|
out["movements"] = [
|
||||||
|
{"id": m["id"], "label": m["label"], "kind": m["kind"], "from": m["from"], "to": m["to"],
|
||||||
|
"last": runs.get(m["id"])}
|
||||||
|
for m in MOVEMENTS
|
||||||
|
]
|
||||||
|
except Exception as exc:
|
||||||
|
out["movements"] = {"error": str(exc)}
|
||||||
|
try:
|
||||||
|
from pii_catalog import get_pii
|
||||||
|
pii = get_pii()
|
||||||
|
out["pii_summary"] = pii.get("summary", {})
|
||||||
|
out["pii_source"] = pii.get("source")
|
||||||
|
out["pii_datasets"] = [
|
||||||
|
{"label": d["label"], "pii_count": d["pii_count"], "all_masked": d["all_masked"],
|
||||||
|
"columns": [f"{c['name']}:{c['category']}{'(masked)' if c['masked'] else ''}" for c in d["pii_columns"]]}
|
||||||
|
for d in pii.get("datasets", [])
|
||||||
|
]
|
||||||
|
except Exception as exc:
|
||||||
|
out["pii_summary"] = {"error": str(exc)}
|
||||||
|
out["lineage"] = [
|
||||||
|
"postgres_sales.public.sales_orders -> iceberg.curated_masked.sales_orders_masked (PII masked)",
|
||||||
|
"mysql_hr.hr.employee_events -> iceberg.curated_masked.employee_events_masked (PII masked)",
|
||||||
|
"hdfs:/data/historical/sales_orders -> iceberg.hadoop.historical_sales_hdfs",
|
||||||
|
]
|
||||||
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
def _section_governance(g: dict[str, Any]) -> list[str]:
|
||||||
|
lines = ["Data governance (CDC · movements · PII · lineage):",
|
||||||
|
f" OpenMetadata catalog: {g.get('openmetadata_url')} (catalog, lineage, PII auto-classification)"]
|
||||||
|
cdc = g.get("cdc") or {}
|
||||||
|
if "error" not in cdc:
|
||||||
|
by_src = ", ".join(f"{k}={v}" for k, v in (cdc.get("by_source") or {}).items()) or "none"
|
||||||
|
lines.append(f" CDC stream: connected={cdc.get('connected')} consumed={cdc.get('consumed')} "
|
||||||
|
f"changes/15m={cdc.get('window_total')} ({by_src})")
|
||||||
|
movements = g.get("movements")
|
||||||
|
if isinstance(movements, list):
|
||||||
|
lines.append(" Data movements (trigger via Command Center / ETL agent):")
|
||||||
|
for m in movements:
|
||||||
|
last = m.get("last") or {}
|
||||||
|
st = last.get("state", "never run")
|
||||||
|
extra = f" rows={last.get('rows')}" if last.get("rows") is not None else ""
|
||||||
|
lines.append(f" - {m['id']}: {m['from']}→{m['to']} [{m['kind']}] last={st}{extra}")
|
||||||
|
ps = g.get("pii_summary") or {}
|
||||||
|
if "error" not in ps:
|
||||||
|
lines.append(f" PII catalog ({g.get('pii_source')}): {ps.get('pii_columns', 0)} PII cols, "
|
||||||
|
f"{ps.get('masked_columns', 0)} masked / {ps.get('unmasked_columns', 0)} unmasked")
|
||||||
|
for d in g.get("pii_datasets") or []:
|
||||||
|
tag = "all masked" if d["all_masked"] else "UNMASKED"
|
||||||
|
lines.append(f" - {d['label']}: {d['pii_count']} PII [{tag}] {', '.join(d['columns'][:8])}")
|
||||||
|
lines.append(" Lineage:")
|
||||||
|
for ln in g.get("lineage") or []:
|
||||||
|
lines.append(f" - {ln}")
|
||||||
|
return lines
|
||||||
|
|
||||||
|
|
||||||
def _section_cluster_registry(_: dict[str, Any]) -> list[str]:
|
def _section_cluster_registry(_: dict[str, Any]) -> list[str]:
|
||||||
"""Static cluster map — always available even when probes fail."""
|
"""Static cluster map — always available even when probes fail."""
|
||||||
lines = ["Cluster infrastructure map (Proxmox VMs & roles):"]
|
lines = ["Cluster infrastructure map (Proxmox VMs & roles):"]
|
||||||
@@ -661,10 +732,11 @@ SECTION_BUILDERS = {
|
|||||||
"gpu": _section_gpu,
|
"gpu": _section_gpu,
|
||||||
"objectscale": _section_objectscale,
|
"objectscale": _section_objectscale,
|
||||||
"command_center": _section_command_center,
|
"command_center": _section_command_center,
|
||||||
|
"governance": _section_governance,
|
||||||
"cluster_registry": _section_cluster_registry,
|
"cluster_registry": _section_cluster_registry,
|
||||||
}
|
}
|
||||||
|
|
||||||
DOMAIN_ORDER = ["docker", "databases", "lakehouse", "etl", "hadoop", "gpu", "objectscale", "command_center", "cluster_registry"]
|
DOMAIN_ORDER = ["docker", "databases", "lakehouse", "etl", "hadoop", "gpu", "objectscale", "command_center", "governance", "cluster_registry"]
|
||||||
|
|
||||||
|
|
||||||
async def collect_full_lab_context(
|
async def collect_full_lab_context(
|
||||||
@@ -700,6 +772,13 @@ async def collect_full_lab_context(
|
|||||||
lakehouse = await collect_lakehouse(client, lake_raw, log)
|
lakehouse = await collect_lakehouse(client, lake_raw, log)
|
||||||
command_center = await collect_command_center(client, cc_raw, log)
|
command_center = await collect_command_center(client, cc_raw, log)
|
||||||
|
|
||||||
|
try:
|
||||||
|
governance = collect_governance(log)
|
||||||
|
await _log(log, "ok", "fetch", "← Governance: CDC/movements/PII/lineage")
|
||||||
|
except Exception as exc:
|
||||||
|
await _log(log, "warn", "fetch", f"✗ Governance: {exc}")
|
||||||
|
governance = {"error": str(exc)}
|
||||||
|
|
||||||
await _log(log, "ok", "fetch", "═══ Lab snapshot complete ═══")
|
await _log(log, "ok", "fetch", "═══ Lab snapshot complete ═══")
|
||||||
return {
|
return {
|
||||||
"ts": datetime.now(timezone.utc).isoformat(),
|
"ts": datetime.now(timezone.utc).isoformat(),
|
||||||
@@ -711,6 +790,7 @@ async def collect_full_lab_context(
|
|||||||
"gpu": gpu_data,
|
"gpu": gpu_data,
|
||||||
"objectscale": objectscale,
|
"objectscale": objectscale,
|
||||||
"command_center": command_center,
|
"command_center": command_center,
|
||||||
|
"governance": governance,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user