feat(forecast,ui): MLZOO-D frontend, registry, and explainability polish (#256)#257
Conversation
…rror (#256) Surface the v0.2.16 advanced-model metadata (LightGBM / XGBoost / regression / prophet_like) via two new read-only forecasting endpoints — the registry- keyed twin of PRP-28's /explain/runs/{run_id} and the job-keyed twin of /explain/jobs/{job_id}. Adds the computed model_family field on RunResponse, the FeatureImportanceUnavailableError + UnprocessableEntityError exception classes, and matching unit + route tests. Backend additions: - app/features/forecasting/feature_metadata.py — model_family_for, importance_type_for, extract_feature_importance. Pure-function; lightgbm / xgboost never imported at module scope (optional extras). Detects sklearn's HistGradientBoostingRegressor.feature_importances_ gap and raises FeatureImportanceUnavailableError → 422. Detects the SimpleImputer-drops-all-NaN-columns case for prophet_like and pads ridge.coef_ back to the canonical 14-column width with 0.0 (regression caught during dogfood; days_since_launch on a product with no launch date triggers it). - app/features/forecasting/schemas.py — ModelFamily str enum, FeatureImportanceItem (signed importance for linear_coef kind), FeatureMetadataResponse. - app/features/forecasting/service.py — get_feature_metadata_for_run + for_job. The two cross-slice imports (RegistryService, JobService) are LAZY (inside the methods) to break the registry ↔ forecasting cycle the computed field would otherwise close. - app/features/forecasting/routes.py — GET /forecasting/runs/{run_id}/ feature-metadata + GET /forecasting/jobs/{job_id}/feature-metadata. Mirror PRP-28's exception-flow contract: ForecastLabError subclasses flow through to forecastlab_exception_handler, SQLAlchemyError becomes DatabaseError. Also fixes the /train docstring drift — now lists xgboost, regression, prophet_like alongside lightgbm. - app/core/exceptions.py + problem_details.py — UnprocessableEntityError (status_code=422, code='UNPROCESSABLE_ENTITY') and matching ERROR_TYPES entry. Distinct from ValidationError (code='VALIDATION_ERROR') so consumers can disambiguate state-prevented operations from input failures via the RFC 7807 type URI. - app/features/registry/schemas.py — model_family computed_field on RunResponse (no DB column, no migration). Tests (37 new): test_feature_metadata.py (17, including the imputer-drop regression), test_routes_feature_metadata.py (20, full matrix for both endpoints — 200 / 400 / 404 / 422 with mocked RegistryService / JobService / load_model_bundle), and 10 new RunResponse model_family cases in test_schemas.py. Closes the MLZOO sequence backend gap.
Surface the new /forecasting/{runs,jobs}/{id}/feature-metadata endpoints
and the computed model_family field on every ModelRun across the
explorer + visualize pages. One panel, two display modes (tree: positive
bars; linear_coef: signed bars with direction colour + icon).
Frontend additions:
- types/api.ts — ModelFamily ('baseline' | 'tree' | 'additive');
ModelRun.model_family; FeatureImportanceItem (signed importance);
FeatureMetadataResponse.
- hooks/use-feature-metadata.ts — useRunFeatureMetadata +
useJobFeatureMetadata sibling hooks. Mirror useRunExplanation /
useJobExplanation exactly (retry: false; query-key shape).
- components/common/model-family-badge.tsx + test — pure derivation,
no hooks: baseline → secondary + Activity, tree → default + TreePine,
additive → outline + LineChart. data-family attr + testid.
- components/explainability/feature-importance-panel.tsx + test —
one card, branches on FeatureImportanceItem.kind. Linear coef rows
render with sign-coloured bars + TrendingUp / TrendingDown icons.
Verbatim correlation-vs-causation caveat in the card footer.
Neutral muted message for ApiError 400 (baseline family) and 422
(no artifact / missing extra / HistGBR-no-importance). Destructive
ErrorDisplay for unexpected.
Page wiring (additive, in-place edits only):
- explorer/runs.tsx — new 'Family' column with ModelFamilyBadge,
MODEL_TYPES allow-list extended to regression / lightgbm / xgboost /
prophet_like, csvColumns + Model filter dropdown extended.
- explorer/run-detail.tsx — ModelFamilyBadge in profile header + row,
'Feature Metadata' card listing the 14 canonical feature columns,
FeatureImportancePanel — all gated on model_family !== 'baseline'
with enabled:false on the TanStack Query so baseline runs never
trigger a 400 burst.
- explorer/run-compare.tsx — Family row in the profile table, new
collapsible 'Feature Importance (Run A vs Run B)' card. Side-by-side
panels only when both runs share a non-baseline family; cross-family
pairs render a muted explanatory message and DO NOT fetch (enabled:
false on both hooks).
- visualize/forecast.tsx — CRITICAL: uses useJobFeatureMetadata
(NOT useRunFeatureMetadata). trainJob.result.run_id is the FORECAST
ARTIFACT KEY (uuid.uuid4().hex[:12], see forecasting/service.py:270),
NOT a registry UUID; calling the run-keyed hook would 404. Inline
CRITICAL comment + memory cross-link to scenario-run-id-vs-registry-
run-id. Collapsible defaultOpen=false to preserve scan flow.
- visualize/backtest.tsx — MODEL_OPTIONS extended to all seven types
(B.2's feature-aware backtest is what makes the four advanced
families reachable from the UI).
Tests (10 new): model-family-badge.test.tsx (3 cases),
feature-importance-panel.test.tsx (7 cases). Both use afterEach
(cleanup) because vitest is not configured for auto-cleanup.
There was a problem hiding this comment.
Sorry @w7-mgfcode, your pull request is larger than the review limit of 150000 diff characters
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Summary
Implements
PRPs/PRP-31-mlzoo-d-frontend-registry-explainability.md(v3, READY verdict fromprp-quality-agent, conf 9.5/10). Closes #256.Surfaces Advanced ML Model Zoo capabilities in the React dashboard — runs explorer Family column, run-detail / run-compare feature-metadata cards, forecast-viz collapsible importance panel, backtest model-options extension, plus user-guide docs touch-up. No data-model change, no migration, no agent-surface change, no model-contract change.
feature_importances_gap (addedFeatureImportanceUnavailableError→ 422),SimpleImputer(keep_empty_features=False)drops empty cols (pad viaimputer.statistics_NaN inspection), and a registry ↔ forecasting circular import on alembic cold-boot (fixed via lazy imports followingexplainability/service.py:57precedent)Validation
All gates green locally:
ruff check .+ruff format --check .✅mypy --strict app/✅ (276 source files, 0 issues)pyright --strict app/✅ (0 errors, 68 pre-existing warnings)pytest -m "not integration"✅ 1423 passed (+47 new)pytest -m integrationon touched slices ✅ 34 passedpnpm tsc --noEmit✅ ·pnpm lint✅ ·vitest run✅ 119 passed (+10 new)alembic upgrade head✅ on fresh DBTest plan
devstatus checks (Lint & Format, Type Check, Test, Migration Check) green in CINotes for reviewer
.agents/execution-reports/prp-31-mlzoo-d-frontend-registry-explainability.mddocuments divergences, defects caught mid-execution, and recommended PRP/process improvements.registry/tests/test_routes.py::TestListRunsEndpoint::test_list_runs_empty) is order-dependent locally; will pass on a fresh CI DB.[[scenario-run-id-vs-registry-run-id]]— load-bearing for the job-keyed sibling endpoint design (forecast.tsxonly hasjob_id, not a registry UUID)