You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Every change below was made against the existing codebase (no rewrite,
no new project) and verified with the existing + expanded pytest suite
(78 tests, all passing), pyflakes (clean, no dead code/unused imports),
and Streamlit's AppTest harness (every page executes with zero runtime
exceptions, including the full form-submit → predict → PDF-download path).
New files
File
Purpose
src/exceptions.py
Typed domain error hierarchy (UnknownDiseaseError, ModelNotTrainedError, PredictionError) shared by the API and dashboard, so both surfaces agree on what "went wrong" means.
api/deps.py
FastAPI dependency-injection providers for ModelRegistry/Settings, enabling app.dependency_overrides in tests without touching disk.
api/middleware.py
RequestContextMiddleware — request-ID correlation + latency logging on every HTTP call.
api/routers/health.py
/healthz liveness probe and /readyz readiness probe (reports per-disease model load status — the right thing for a container orchestrator to gate on).
api/routers/diseases.py
/v1/diseases catalog, /v1/diseases/{key}/metrics, new /v1/diseases/{key}/schema.
api/routers/predict.py
One rate-limited POST route per disease, dynamically generated from DiseaseSpec.
.env.example
Documents every HDPP_-prefixed environment variable the new Settings class reads.
Modified files
File
What changed and why
src/config.py
Added Settings (pydantic-settings) for env-driven CORS, log level, and per-route rate limits. DiseaseSpec/DISEASES are unchanged — they were already clean.
src/prediction.py
Added ModelRegistry (lazy-loading, cached, injectable) alongside the existing DiseaseModel. get_model() is kept as a back-compat wrapper so the dashboard's existing call sites didn't need touching. predict() now wraps failures in PredictionError instead of leaking raw exceptions.
api/main.py
Rewritten as assembly only: mounts the three routers, adds RequestContextMiddleware, CORS, and slowapi rate limiting, and registers one global exception handler per domain error type (previously each route had its own inline try/except HTTPException).
dashboard/theme.py
Full rewrite: colors moved to CSS custom properties so light/dark mode is one variable swap instead of two stylesheets; added top_nav() (sticky, real cross-page links verified against Streamlit's actual URL-slug rules), theme_toggle_control(), breadcrumb(), skeleton(), kpi_card(). Risk-rail and footer logic unchanged.
dashboard/app.py
Added sticky top nav, dark-mode toggle, functional search/filter over the 5 disease cards (verified the search actually matches real description text — an earlier placeholder example didn't and was fixed), animated-looking KPI cards with icons, trained/not-trained status badges.
dashboard/common.py
Added top nav, breadcrumb, a skeleton-loader placeholder shown while model.predict() runs, a success toast on completion, and proper ModelNotTrainedError/PredictionError handling instead of a bare FileNotFoundError catch.
dashboard/pages/1–5_*.py
Added page icons (page_icon=) for a more polished browser tab / nav appearance. Logic unchanged — they're intentionally thin wrappers around common.render_disease_page.
dashboard/pages/6_Model_Insights.py
Wired in top nav/breadcrumb/theme toggle. Added functional CSV and Excel download buttons for the metrics table (real pandas/openpyxl export, not decorative).
dashboard/pages/7_About.py
Wired in top nav/breadcrumb/theme toggle. Content unchanged — it was already accurate.
tests/test_api.py
Added tests for /healthz, /readyz, /v1/diseases/{key}/schema, and the request-ID response header.
tests/test_prediction.py
Updated the unknown-disease test to expect the new typed UnknownDiseaseError instead of a bare KeyError.
requirements.txt
Added pydantic-settings, slowapi, openpyxl.
README.md
Added an "API architecture" section documenting the router split and Settings; updated the project-layout tree and endpoint list.
Bugs found and fixed during this work (not present before, or pre-existing)
Closure-scoped type hints under from __future__ import annotations: the dynamically-generated per-disease Pydantic request models in the predict router were silently unresolvable (FastAPI treated the request body as a missing query parameter, breaking every predict endpoint with a 422). Fixed by removing the PEP 563 future-import from that one file — necessary because dynamic create_model() closures can't be resolved as string annotations.
Misleading search placeholder: the home page's search box advertised "cardiology" as an example term, but that word doesn't appear anywhere in the disease descriptions it searches — it would have returned zero results. Fixed to use terms that actually match (glucose, bilirubin), and verified via AppTest.
Deliberately not done (and why)
Pagination was in the original request list but wasn't added: there are 5 disease models and one small metrics table — paginating either would be decorative UI with no real content behind it, which conflicts with "no placeholders."
Auth, model registry/experiment tracking, SHAP-dashboard-as-separate-page, migrations, full Docker hardening, and a documentation rewrite were intentionally left for a follow-up pass (as scoped in-chat) rather than attempted shallowly in the same session as the backend + UI work above.