The HF Space build keeps failing somewhere in the giant build-time RUN
block (BBB train + EEG + MRI + RAG ingest), and the failure log gets
truncated past 50KB so we can't see the actual error.
Strategy change: build only installs deps + copies code + runs the fast
seed_demo_artifacts.py stub generator. Everything else moves to
docker-entrypoint.sh as idempotent guards. The image is smaller, the
build is fast and reliable, and pipeline failures (if any) become
runtime warnings instead of build aborts.
The entrypoint now runs (with NEUROBRIDGE_DISABLE_MLFLOW=1, each step
'|| true' so a single failure doesn't kill startup):
- BBB feature extraction + classifier train (if joblib missing)
- EEG feature extraction (if parquet missing)
- MRI feature extraction + ComBat (if parquet missing)
- RAG FAISS index build (if index.bin missing)
- seed_demo_artifacts.py (idempotent — fills any missing stub)
Cold-start cost: +~30s on first container start. Build cost: drops
from ~6 min to ~3 min and…
The previous build's truncated log showed pip install succeeded but
something later in the heavy pipeline-train RUN block aborted. Three
defensive changes so the next build either succeeds or fails with a
clearer diagnosis:
- apt-get install git: silences the MLflow 'Bad git executable' warning
and lets MLflow tag runs with a proper SHA when needed.
- NEUROBRIDGE_DISABLE_MLFLOW=1 prefixed on every build-time pipeline
invocation: avoids MLflow run-tagging fragility in the slim image
during build. The runtime entrypoint can re-enable MLflow if desired.
- Move 'python scripts/seed_demo_artifacts.py' BEFORE the pipeline
train block so the core showcase paths (MRI 2D / MRI ONNX / EEG joblib
/ clinical RAG / axial PNG) are guaranteed to land even if the BBB
classifier train or MRI ComBat pipeline trips. The seed step is also
re-run after RAG ingest (idempotent — only fills missing artifacts).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
fix(docker): install CPU-only torch to avoid 2GB NVIDIA CUDA wheels in HF Space build
HF Space deployment needs every showcase path to work without external
data. Five artifacts are gitignored (model binaries, RAG index, derived
fixtures) so they have to be (re)generated at image-build time.
- scripts/seed_demo_artifacts.py: idempotent generator for
data/processed/mri_dl_2d/best_model.pt (random resnet18 4-class),
data/processed/mri_model.onnx (dynamic-D/H/W ONNX biased to 'abnormal'),
data/processed/eeg_clf.joblib (synthetic-separable RandomForest),
data/external_rag/index/rag_index.pkl (4-chunk synthetic clinical
TF-IDF index), and tests/fixtures/mri_sample/subject_0_axial.png
(axial slice from the bundled NIfTI). Each step is a no-op when the
artifact already exists.
- Dockerfile.hf: COPY scripts/, RUN seed_demo_artifacts.py after the
pipeline + FAISS-index build steps. Keeps Dockerfile (HF
auto-discovered name) byte-identical to Dockerfile.hf.
- docker-entrypoint.sh: re-run the seed script on container start so a
mounted-volume deploy can re-s…
fix(demo): wire every showcase path end-to-end with auto-resolved artifacts
Goal: every endpoint and every Streamlit tab must work in a 2-minute
hackathon demo without requiring the demoer to fetch external data.
Code fixes:
- src/api/routes.py + src/agents/tools.py: fix two stale references —
bbb_model.load (not load_model) and bbb_model.predict_with_proba (not
predict_one); also corrects the agent-side default BBB artifact path
from .pkl to .joblib so the auto-resolve in adjust_drug_dose works.
- src/api/routes.py: replace hard-coded _AGENT_DEFAULT_MODEL = google/
gemini-2.0-flash-exp:free (now 404 on OpenRouter) with a fallback
chain probed at orchestrator-build time. Picks the first model that
returns a non-404/429 ping. Override via NEUROBRIDGE_AGENT_MODEL
(single id) or NEUROBRIDGE_AGENT_MODEL_CHAIN (csv).
- src/llm/explainer.py: prepend openai/gpt-oss-20b:free (verified 2026-
05-02) to the explainer fallback chain so /explain/* doesn't fall
back to the deterministic template when free-tier ids churn.
- src/frontend/app.py: drop the en…
Adds the only legitimate BBB <-> MRI bridge in the platform — researcher
lane only. Fusion engine still excludes BBB as a diagnostic modality.
Modules:
- src/models/bbb_permeability_map.py: scalar BBB leakage score in [0,1]
with two modes. heuristic_proxy (default) reuses the 2D resnet18
Alzheimer classifier and computes 1 - P(NonDemented). dce_onnx loads
a real 4D DCE-MRI ONNX model from BBB_PERMEABILITY_DCE_PATH and
averages a normalised Ktrans map; raises a clear FileNotFoundError
with contract documentation when no artifact exists.
- src/research/drug_dose_adjuster.py: pure-function dose revision keyed
on (BBB intact?, drug BBB-permeable?). Permeable drugs see an
aggressive linear cut floored at 30%; non-permeable drugs see a mild
cut floored at 60%; intact BBB returns baseline unchanged. Unknown
drug permeability is treated as permeable (safer). Rationale always
includes the 'research suggestion, not medical advice' disclaimer.
API:
- POST /predict/bbb_permea…
fix(mri): try weights_only=True first; fall back only for trusted module pickles
Adds Day-10 (fusion engine) and Day-11 (external assets integration) rows to
the status table, plus three new feature sections:
- 'MRI Deep-Learning Backends' — volumetric_onnx default vs resnet18_2d
(4-class Alzheimer's). Streamlit auto-adapts the form; switch via
MRI_MODEL_KIND env without restarting workers.
- 'Clinical Corpus (TF-IDF, Turkish + English)' — 14-PDF index with
TR->EN query expansion (egzersiz/beslenme/unutkanlik/...). Agent calls
retrieve_context(corpus="clinical"); CLI smoke at scripts/clinical_rag_smoke.py.
- 'EEG Pretrained Classifier (stub-able for demo)' — POST /predict/eeg loads
any sklearn predict_proba joblib. Default stub flows into fusion as the
eeg modality with zero code changes when the real artifact arrives.
Updates Quick Start and Executive Summary test counts to 330+2 and lists
the new env-var demo lifelines (MRI_MODEL_PATH_2D, EEG_CLF_ARTIFACT,
CLINICAL_RAG_INDEX_PATH).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com…
feat(eeg,frontend): EEG fusion-flow test + Streamlit EEG form + real-artifact sanity
- tests/fusion/test_eeg_modality_flow.py: end-to-end EEG -> fusion `eeg`
modality flow (alzheimers feature spike lifts AD score; control profile
doesn't inflate it).
- tests/models/test_eeg_model_real.py: skip-if-absent sanity for the real
EEG joblib; runs automatically once the file lands at the canonical path.
- src/frontend/app.py: doctor MRI tab grows an EEG predict form
(comma-separated features) calling POST /predict/eeg. 503 surfaces as a
human-readable warning instead of a stack trace.
feat(api): add POST /predict/eeg route (stub-able for demo)
Pinned torch==2.4.1, torchvision==0.19.1, pillow==10.4.0 to enable the
2D resnet18 4-class Alzheimer's MRI inference path. These pull no
duplicate transitive deps that conflict with onnxruntime; both backends
coexist for MRI prediction (selected via MRI_MODEL_KIND env in upcoming
commits).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
User clarified intent:
1. MRI training is final — best_model.pt is the artifact, just load+predict.
Removed the ambiguous 'state_dict vs full model' blocker framing; tests
already use a synthetic dummy resnet18 so TDD works before the real .pt
lands. Real-artifact sanity test auto-skips when absent.
2. EEG pretrained model assumed present for the hackathon demo. Replaced
the OASIS-tabular fork with a stub-able EEG plan: src/models/eeg_model.py
loads any sklearn predict_proba classifier from joblib, with a synthetic
stub fixture so tests pass today. Real artifact swaps in at
data/processed/eeg_clf.joblib with zero code changes.
Roadmap, MRI plan, and the new EEG plan all preserve independence
guarantees and align on the same loader+stub pattern.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Roadmap + three sub-plans for integrating user-supplied external assets:
1. MRI DL 2D — pretrained resnet18 4-class Alzheimer's classifier from
user's training run (BEST_PARAMS: image_size=160, lr=3.75e-4,
weight_decay=1.96e-4, dropout=0.31). Adds src/models/mri_dl_2d.py
parallel to volumetric ONNX path, dispatched via MRI_MODEL_KIND env.
2. TF-IDF clinical RAG — 14 medical PDFs (Alzheimer/Parkinson/lifestyle)
with Turkish+English query expansion. Wraps user's pre-built sklearn
TF-IDF index as src/rag/clinical/. Existing FAISS RAG kept.
3. OASIS tabular classifier — sklearn RF on OASIS longitudinal biomarkers
(MMSE/eTIV/nWBV/ASF/...). NOTE: user described notebook as 'EEG model'
but it is OASIS tabular. Plan flags this prominently with branch 3a
(default: integrate as fusion modality) vs 3b (await real EEG model).
All three plans flag prerequisite blockers (artifact transfer, dataset
acquisition) and preserve independence guarantees from the clinical
platform …
fix(fusion): correct logit-scale comment; top_disease=None on empty input
Roadmap indexes six independent sub-projects with locked-in independence
guarantees: BBB / MRI / EEG / fusion pipelines all run standalone, with
sub-plan #3 (BBB-from-MRI) the only legitimate bridge.
Fusion engine plan is the foundation: 8 TDD tasks producing a pure-Python
multi-modal combiner (MRI + EEG + clinical scores -> per-disease confidence
with attribution) plus FastAPI route and agent tool registration. BBB is
explicitly excluded as a fusion modality and pinned by a regression test.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
All 5 tests pass on existing code — the bare-directory-without-slash case
already works because Path.exists() resolves relative to cwd, which
monkeypatch.chdir() sets correctly in the test. No routing.py change needed.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
feat(frontend/mri): expose target_shape as 3 number inputs (was hardcoded 64³)
Adds network-gated live test that skips without OPENROUTER_API_KEY (and
without the BBB model artifact, since explainer.py auto-loads .env at
import time). Adds /diag/agent diagnostic endpoint reporting key presence,
agent model, RAG index status, and registered tool names.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>