MLOps Console¶
The MLOps Console is the dashboard's single pane for model registry, lifecycle health, and governed promotion. It surfaces the platform's already-shipped MLOps backend — drift, cost, serving traffic, and promotion policy — so operators can see a model's state and whether it is safe to promote without dropping to the CLI.
Open it from the sidebar (MLOps, the Boxes icon) or navigate to /mlops.
- Feature: F9 · Design: ADR 0060 (
design/adr/0060-dashboard-mlops-console.md) · Spec:design/vision/specs/F9-mlops-console.md - Backend:
platform/services/dashboard/backend/mlops.py+routers/mlops.py - Frontend:
platform/services/dashboard/frontend/src/lib/mlops.ts+pages/MlopsConsole.tsx
What you see¶
Registry grid¶
A row per model the platform knows about — composed from every model that has recorded drift, cost, a serving-traffic rule, or a promotion policy. Columns:
| Column | Meaning |
|---|---|
| Model | Display name (uppercase, e.g. JPCP). The MLflow registry name (lowercase jpcp) is carried alongside for API calls. |
| Ver | Latest version seen in model_costs (— if no cost has been recorded). |
| Stage | Latest alias observed for the model (e.g. Production, Staging). |
| Health | Colour-blind-safe status token: ok (drift-tracked and governed), warn (one but not both), unknown (name only). Rendered as a StatusPill — never colour alone. |
| Governed | Yes when an enabled promotion policy exists, No policy otherwise. |
| Freshness | Most recent timestamp across the model's drift/cost/traffic records. |
Select a row to open its promotion gate on the right.
Promotion gate (guided)¶
For the selected model, the gate shows — inline — whether a promotion is allowed and, when it is not, exactly why:
- Policy — the configured rule (
promote Staging → Production when rmse < 5.0). Missing or disabled policies are listed as blocking reasons. - Approval — the phase-11 sysadmin approval step, always surfaced so the human gate is never hidden even for an otherwise-eligible model.
The one-line verdict (Blocked — …, Eligible — awaiting approval, or Ready to promote) comes
from the pure, unit-tested promotionVerdict() helper, so the UI's decision matches the backend's.
How it maps to the CLI¶
The console is read-only and complements these commands (which perform the actual mutations):
exa models diff jpcp 17 18 # the version-compare the detail tabs summarise
exa models cost jpcp # the cost tab
exa drift status # the drift tab / health token
exa serve traffic JPCP --production 90 --canary 10 # the traffic tab
exa pipeline promote jpcp --if-rmse-lt 5.0 # the promotion the gate evaluates
Endpoints¶
All require the viewer role and are composed through the F8 BFF substrate (partial-failure safe):
| Endpoint | Returns |
|---|---|
GET /api/v1/mlops/registry |
{registry: {rows, count}} |
GET /api/v1/mlops/model/{name} |
{detail: {cost, drift, traffic, promotion}} |
GET /api/v1/mlops/promotion/{name} |
{promotion: {policy, eval, approval, allowed}} |
name is case-insensitive — the central mlflow_name() / display_name() mapping in mlops.py
resolves registry (uppercase) vs MLflow (lowercase) casing in one place.
See docs/reference/api.md for full request/response shapes and
docs/dashboard/architecture.md for the component
diagram.
Notes & limits¶
- The console reads from
platform.db(PLATFORM_DB). A model only appears once it has produced at least one drift/cost/traffic/policy record. - Promotion decisions and traffic changes made via the CLI are audited to
audit_events(D4); the console itself performs no writes. - Deeper tabs from the F9 spec (SLO, fairness, model-card, A/B significance) build on this substrate and are tracked in the dashboard-nextgen plan.