Governed clinical imaging workflows, research experimentation, and agent-assisted medical reasoning.
RadSysX is a medical imaging and analysis platform with two distinct product surfaces:
clinical: the governed migration target, built around FastAPI contracts, worklist-driven launch, opaque viewer sessions, audited workflow state, and a dedicated OHIF viewer runtime.research: the experimentation surface for prototype workflows, a LangGraph/deepagents-based multi-agent stack, MCP/FHIR integrations, and imaging/AI exploration that is explicitly not the clinical source of truth.
The two surfaces are not interchangeable. The repo also includes an Electron desktop fast path that opens directly into OHIF local DICOM mode and runs the local clinical shell without the Docker/nginx/Orthanc composition required for full governed archive validation.
The current repo contains both:
- a governed clinical platform with backend-authoritative workflow state and OHIF as the only supported clinical viewer
- a research stack with direct chat, multi-agent orchestration, MCP tool integration, and BiomedParse-oriented imaging/AI experimentation
That distinction is intentional. The clinical path is the migration target; the research path remains useful, but it does not define clinical architecture.
The current clinical baseline on this branch is:
- FastAPI is the backend authority for clinical auth, launch, workspace, report, AI, derived-result, and audit workflows.
- OHIF is the only supported clinical viewer runtime.
- The old Next.js
/viewerfallback route is removed. - Backend-issued signed cookies provide local seeded clinical identity until institutional auth replaces them.
- Derived DICOM writeback stays backend-mediated through STOW.
- The local stack is designed to run as one origin through nginx, frontend, viewer, backend, and Orthanc.
- The OHIF app exposes
/viewer/fhir-viewerfor FHIR R4 ImagingStudy/DocumentReference discovery and SMART on FHIR EHR launch with PKCE. - The desktop app starts FastAPI, Next.js, and a local OHIF viewer bridge under one localhost origin for a no-Docker local run path, opening OHIF first with local import ready, keeping the visible app name as RadSysX, and including a frontend-only RadSysX AI chat panel in the OHIF right sidebar.
- The desktop app enables native local file/folder selection plus browser drag-and-drop fallback for backend-owned local imaging import of DICOM, DICOMDIR, NIFTI
.nii/.nii.gz/paired.hdr+.img, NRRD.nrrd, ZIP archives containing supported files, and common image files, with safe imported-study asset summaries, backend-mediated NIFTI slice previews/common-image previews including TIFF SVG header previews, NRRD header/voxel metrics, and deterministic technical analysis for local analysis readiness.
The current research/agent baseline still includes:
backend/radsysx.pyfor multi-agent orchestrationbackend/chat_interface.pyfor direct LLM chatbackend/mcp/*for MCP/FHIR tooling and server installationbackend/biomedparse_api.pyfor research imaging analysis APIs
Those capabilities remain part of RadSysX, but they are not the clinical source of truth.
POST /api/auth/local-loginGET /api/auth/session- Open
/worklist POST /api/imaging/launch- Open
/viewer/?launch=... GET /api/imaging/launch/resolve- OHIF binds to the returned runtime and same-origin DICOMweb roots
GET /api/studies/{studyUid}/workspace- Persist reports, AI jobs, derived results, and audit through backend contracts
- Persist uploaded derived DICOM through
POST /api/derived-results/stow
/viewer/fhir-viewer is a separate SMART on FHIR entry into the RadSysX OHIF shell. A standard EHR launch supplies opaque iss and launch parameters; the public SMART client ID comes from RADSYSX_FHIR_CLIENT_ID at viewer build time or a client_id launch parameter. The FHIR server remains the metadata authority and must allow the RadSysX browser origin through CORS.
The route resolves FHIR R4 ImagingStudy and DocumentReference resources into OHIF studies while keeping access tokens in session storage. Do not place patient identifiers, FHIR payloads, or access tokens in its URL. SMART authorization does not create a governed RadSysX session, so report persistence, audit, derived-result writeback, and STOW remain unavailable until an explicit backend contract connects those authorities.
Optional viewer-build settings are:
RADSYSX_FHIR_SERVER_URLRADSYSX_FHIR_CLIENT_IDRADSYSX_FHIR_SCOPE(defaults tolaunch openid fhirUser patient/*.read)
backend/server.pybackend/clinical/*backend/clinical/local_imaging.pybackend/tests/test_clinical_platform.py
backend/radsysx.pybackend/chat_interface.pybackend/mcp/*backend/biomedparse_api.pybackend/tools/*
packages/clinical-web/*
frontend/app/login/page.tsxfrontend/app/worklist/page.tsxfrontend/app/page.tsx
viewer/scripts/build-ohif-dist.mjsviewer/assets/radsysx-bootstrap.jsviewer/assets/radsysx-fhir-extension.jsviewer/assets/radsysx-ohif-extension.jsviewer/assets/radsysx-ohif-mode.jsviewer/assets/radsysx-viewer.cssviewer/vendor/ohif-fhir-viewer/*
desktop/src/main.mjsdesktop/src/preload.cjsdesktop/scripts/launch.mjsdesktop/scripts/bootstrap.mjsdesktop/scripts/doctor.mjs
docker-compose.ymldeploy/clinical-stack/*
The research surface still includes a LangGraph/deepagents-style multi-agent stack in backend/radsysx.py, with a supervisor coordinating specialist agents for:
- pharmacist reasoning
- researcher/literature workflows
- medical analyst workflows
The repo still supports:
- direct chat via
backend/chat_interface.py - MCP-backed tool discovery and execution
- FHIR-oriented MCP tools in
backend/mcp/fhir_server.py - MCP installation flows in
backend/mcp/installer.py
Research-only imaging/AI experimentation still includes:
- BiomedParse-oriented APIs in
backend/biomedparse_api.py - prototype imaging upload/analyze routes in the Next.js research surface
- legacy viewer components kept for experimentation and parity work, not as the clinical viewer target
GET /api/auth/sessionPOST /api/auth/local-loginPOST /api/auth/logoutGET /api/platform/configGET /api/worklistPOST /api/imaging/launchGET /api/imaging/launch/resolvePOST /api/local-imaging/importGET /api/local-imaging/studies/{studyUid}/assetsGET /api/local-imaging/studies/{studyUid}/analysisGET /api/local-imaging/studies/{studyUid}/assets/{assetId}/previewGET /api/studies/{studyUid}/workspacePOST /api/reports/draftPOST /api/ai/jobsPOST /api/derived-resultsPOST /api/derived-results/stowGET /api/audit/studies/{studyUid}
POST /processPOST /streamGET /streamPOST /chatPOST /chat/streamGET /toolsPOST /execute_toolPOST /fhir/toolGET /mcp/statusPOST /mcp/togglePOST /mcp/install
Mode is controlled by RADSYSX_APP_MODE:
researchpilotclinical
Rules:
- Only
researchmay expose experimental upload/analyze flows. pilotandclinicaluse the clinical FastAPI surface and OHIF viewer flow.- Governed flows must not send DICOM bytes directly from the browser to third-party AI services.
The most important clinical env vars are:
RADSYSX_APP_MODERADSYSX_AUTH_MODERADSYSX_CLINICAL_API_SECRETRADSYSX_SESSION_SECRETRADSYSX_SESSION_COOKIE_SECURERADSYSX_VIEWER_BASE_URLRADSYSX_VIEWER_BASE_PATHRADSYSX_DICOMWEB_PUBLIC_BASE_URLRADSYSX_LOCAL_IMAGING_ENABLEDRADSYSX_LOCAL_IMAGING_STORAGE_DIRRADSYSX_ORTHANC_DICOMWEB_URLRADSYSX_ORTHANC_USERNAMERADSYSX_ORTHANC_PASSWORDRADSYSX_FHIR_SERVER_URLRADSYSX_FHIR_CLIENT_IDRADSYSX_FHIR_SCOPENEXT_PUBLIC_RADSYSX_APP_MODENEXT_PUBLIC_BACKEND_URLNEXT_PUBLIC_VIEWER_BASE_URL
Desktop-only knobs:
RADSYSX_DESKTOP_PORTRADSYSX_DESKTOP_BACKEND_PORTRADSYSX_DESKTOP_FRONTEND_PORTRADSYSX_DESKTOP_DICOMWEB_TARGETRADSYSX_DESKTOP_SKIP_BOOTSTRAP
Research-only integrations such as MCP/FHIR tools and BiomedParse still exist, but they do not define the clinical architecture.
The preferred development and validation host is now native Linux.
Operational guidance:
- use native Linux Python, Node, npm, and Docker Engine / Compose
- avoid WSL-specific path assumptions or Windows-only toolchain shims
- do not rely on temporary
PYTHONPATHhacks or undeclared global dependencies - prefer a repo-local
.venvplus workspace-installed Node dependencies - when starting work in a fresh chat on the Linux machine, do a short recon first, then wait for the user's report from the first Linux app test pass before making deeper code changes
- Python 3.12 if you need one interpreter for both the clinical and research/backend installs
- Python 3.13 is acceptable for the clinical bootstrap path only
- Node.js 20+
- npm
- Docker Engine with Compose plugin if you want the one-origin stack
python3 -m venv .venv
. .venv/bin/activate
python3 -m pip install --upgrade pip
python3 -m pip install -r backend/requirements-clinical.txt
npm install --legacy-peer-depsBackend dependencies should be installed into .venv, not into ad hoc machine-local paths.
Node dependencies should be installed from the repo root so the workspace-managed root package-lock.json remains authoritative.
backend/requirements-clinical.txt is the governed clinical bootstrap set. backend/requirements.txt remains the broader research/agent dependency set and may carry tighter interpreter constraints than the clinical slice.
The FHIR/SMART data-source slice is pinned in viewer/vendor/ohif-fhir-viewer/UPSTREAM.md; normal viewer builds are network-independent and do not fetch moving upstream code.
On a fresh clone, the shortest local path is:
npm run desktopdesktop is the user-facing local launcher. It checks the desktop bootstrap, runs the cross-platform bootstrap helper if setup is incomplete, then opens Electron directly to OHIF's local DICOM loader at /viewer/local. The lower-level desktop:bootstrap helper creates or reuses .venv, installs the clinical Python dependency set with the venv Python, installs workspace Node dependencies from the root lockfile, and runs the desktop doctor. For an existing install, npm run desktop -- --check-only or npm run desktop:bootstrap -- --check verifies the bootstrap without reinstalling dependencies. Use npm run desktop:run only when you intentionally want the direct Electron run after setup is already known good.
The desktop runtime, bootstrap, doctor, and smoke helpers resolve the repo-local Python venv using the host OS path convention: .venv/bin/python on Unix-like systems and .venv/Scripts/python.exe on Windows. Set RADSYSX_DESKTOP_PYTHON=/path/to/python when you need to force a specific interpreter.
This path is intentionally no-Docker. It is enough for seeded login, native local file/folder selection with direct Electron-main upload to the backend, browser drag-and-drop import, local import of DICOM/DICOMDIR/NIFTI .nii/.nii.gz/paired .hdr+.img/NRRD .nrrd/ZIP archives containing supported files/common image files including extensionless and multi-study DICOMDIR companion files, local worklist registration, local DICOM metadata/frame serving for imported DICOM studies, backend-mediated axial/coronal/sagittal NIFTI slice previews, common image previews including TIFF SVG header previews, NRRD header/voxel metrics, deterministic technical analysis, opaque launch/session resolution, OHIF rendering of imported DICOM from the first screen, workspace/report/AI/audit contract work, and local app use. Full Orthanc-backed DICOMweb retrieval, advanced archive behavior, and durable STOW validation still belong to the compose stack unless you set RADSYSX_DESKTOP_DICOMWEB_TARGET to a local archive.
By default Electron starts at /viewer/local, so the first visible screen is OHIF's native local DICOM loader. DICOM files load directly through OHIF's dicomlocal data source at /viewer/dicomlocal without a governed launch token or clinical workspace panel. NIFTI/NRRD/image/ZIP assets remain available through the local worklist inspection and analysis path. The OHIF right sidebar includes a RadSysX AI tab with a local chat composer, voice-input affordance, and frontend-only @ chips for ROI, segmentation, and measurement context.
The desktop launcher builds the frontend production shell on first launch, writes a small ignored stamp under frontend/.next/, and reuses that build while the same-origin public API/viewer settings match. This keeps the local app from rebuilding just because it chooses a fallback localhost port. Force a rebuild with RADSYSX_DESKTOP_REBUILD_FRONTEND=1 npm run desktop. For live frontend UI development, use:
npm run desktop:dev-frontendFor a quick launcher contract check:
npm run desktop:smoke:launchThat smoke runs the same user-facing launcher path as npm run desktop: bootstrap check first, then service-ready Electron startup with the OHIF-first default retained, using a short cross-platform auto-shutdown timer. The desktop:smoke:local-start check is the UI assertion that samples /viewer/local, confirms there is no RadSysX intermediate card or governed launch, and verifies local-DICOM OHIF rendering.
For a quick startup and cleanup check:
npm run desktop:smokeFor the first-screen OHIF local import/render check:
npm run desktop:smoke:local-startThat smoke starts Electron on /viewer/local, verifies OHIF's local file/folder controls are visible without a RadSysX bootstrap card, verifies the document title is RadSysX, loads a synthetic DICOM through OHIF's local file input, routes to /viewer/dicomlocal?datasources=dicomlocal, asserts that OHIF paints a nonblank canvas without a governed launch, and checks the RadSysX AI sidebar composer with voice and @ ROI/segmentation controls.
For the first-screen OHIF drag/drop import/render check:
npm run desktop:smoke:local-start-dropThat smoke starts Electron on the same OHIF local screen, drops a synthetic DICOM directly onto it, forwards the drop into OHIF's local file input, routes to /viewer/dicomlocal?datasources=dicomlocal, and asserts that OHIF paints a nonblank canvas with the RadSysX title and RadSysX AI sidebar composer, without a governed launch.
For the first-screen NIFTI/NRRD/image-only fallback check:
npm run desktop:smoke:local-start-nondicomThat smoke starts from the same OHIF local screen, then verifies NIFTI/NRRD/image/ZIP fixtures remain usable through /worklist local asset inspection, preview loading, NIFTI slice-axis switching, and backend technical analysis without exposing an OHIF viewer action for non-DICOM assets.
For a stronger no-Docker import/use check:
npm run desktop:smoke:importThat smoke starts the desktop runtime on high local ports, generates synthetic PHI-free DICOMDIR, DICOM, .nii, .nii.gz, paired .hdr/.img, .nrrd, ZIP with supported NIFTI/PNG members, PNG, JPEG, and TIFF files, imports them through the one-origin local bridge, verifies worklist registration, imported-study asset summaries/previews/analysis, local DICOMweb discovery, and opaque viewer launch, then shuts the desktop runtime down.
For a hydrated UI-level import check:
npm run desktop:smoke:ui-importThat smoke starts the same no-Docker runtime, drives the Electron worklist UI through the local bridge, drops synthetic DICOMDIR/DICOM/NIFTI .nii/.nii.gz/paired .hdr+.img, NRRD .nrrd, ZIP with supported NIFTI/PNG members, plus PNG/JPEG/TIFF files onto the import panel, verifies imported rows, inspects local assets, changes a NIFTI preview to a coronal slice, runs backend technical analysis, and shuts down.
For a native file picker bridge check:
npm run desktop:smoke:picker-files-importThat smoke drives the hydrated worklist Import files action through the Electron preload IPC bridge with smoke-injected individual fixture file paths. Electron main uploads those selected files directly to the backend import endpoint with the existing session cookie, so the renderer receives only the backend import response. It proves the file picker button, backend import, local inspection, NIFTI preview controls, and technical analysis path without automating the actual operating-system file dialog.
For a native folder picker bridge check:
npm run desktop:smoke:picker-importThat smoke drives the hydrated worklist Import folder action through the Electron preload IPC bridge and main-process recursive file collector with smoke-injected test paths. Electron main uploads the selected files directly to the backend import endpoint with the existing session cookie, so the renderer receives only the backend import response. It proves the folder picker bridge, backend import, local inspection, NIFTI preview controls, and technical analysis path without automating the actual operating-system file dialog.
For a larger native picker import check:
npm run desktop:smoke:picker-large-importThat variant adds an 8 MiB synthetic NIFTI volume to the picker fixture folder and verifies import, preview, and technical analysis through the same direct Electron-main upload path.
For a many-file native picker import check:
npm run desktop:smoke:picker-many-importThat variant adds a nested folder of 32 additional extensionless DICOM instances to the picker fixture folder and verifies recursive collection, import of 44 accepted files after ZIP expansion into 2 local studies, DICOM asset summary, and technical analysis through the same direct Electron-main upload path.
For an imported-DICOM viewer handoff check:
npm run desktop:smoke:viewer-launchThat smoke imports synthetic local DICOM/DICOMDIR data through the hydrated worklist, opens the governed OHIF viewer, verifies the opaque launch resolves under /viewer/, confirms the launch token is stripped from the browser URL, checks that viewer-origin local DICOMweb/workspace requests can find the imported study, and asserts that OHIF paints a nonblank canvas for the synthetic DICOM. It proves viewer handoff, local DICOMweb binding, and a basic imported-DICOM render path, not full diagnostic pixel-rendering parity across real-world archives.
If you want one local Python environment that can exercise both the governed clinical backend and the broader research/agent surface, use Python 3.12 and then install the full backend set:
. .venv/bin/activate
python3 -m pip install -r backend/requirements.txt. .venv/bin/activate
python3 backend/server.pyexport RADSYSX_APP_MODE=research
export NEXT_PUBLIC_RADSYSX_APP_MODE=research
. .venv/bin/activate
python3 backend/server.pyIn a second terminal:
npm run dev --workspace frontendUse the workspace script from the repo root rather than invoking next dev directly inside frontend/.
. .venv/bin/activate
python3 -m compileall backend/clinical backend/server.py backend/radsysx.py
python3 -m pytest backend/tests/test_clinical_platform.pynpm run type-check --workspace frontend
npm run type-check --workspace viewer
npm run build --workspace viewerSet explicit Orthanc credentials first:
export RADSYSX_ORTHANC_USERNAME=local-user
export RADSYSX_ORTHANC_PASSWORD=local-pass
docker compose up --buildThis compose stack validates the governed clinical surface only. It does not install or exercise the full research/agent backend dependency set.
Use http://localhost:3000 through nginx for governed validation. The raw viewer dev server on port 3001 is only an internal asset server and is not a supported clinical entry point. The governed viewer launch should resolve under /viewer/ so OHIF static assets stay mounted beneath the viewer base path.
If you need to test both RadSysX surfaces on the same Linux host, use Python 3.12 and run:
python3 -m venv .venv. .venv/bin/activatepython3 -m pip install --upgrade pippython3 -m pip install -r backend/requirements.txtnpm install --legacy-peer-depsnpm run desktop -- --check-onlynpm run desktop:doctornpm run desktop:smoke:launchnpm run desktop:smokenpm run desktop:smoke:local-startnpm run desktop:smoke:local-start-dropnpm run desktop:smoke:local-start-nondicomnpm run desktop:smoke:importnpm run desktop:smoke:ui-importnpm run desktop:smoke:picker-files-importnpm run desktop:smoke:picker-importnpm run desktop:smoke:picker-large-importnpm run desktop:smoke:picker-many-importnpm run desktop:smoke:viewer-launchpython3 -m compileall backend/clinical backend/server.py backend/radsysx.pypython3 -m pytest backend/tests/test_clinical_platform.pynpm run type-check --workspace frontendnpm run build --workspace frontendnpm run type-check --workspace viewernpm run build --workspace viewer- Start the research surface directly with
RADSYSX_APP_MODE=research python3 backend/server.pyplusNEXT_PUBLIC_RADSYSX_APP_MODE=research npm run dev --workspace frontend - Separately validate the governed clinical surface with
docker compose up --build
On the new Linux host, the first useful runtime checkpoint is:
- run
npm run desktopand confirm the app opens straight into OHIF local mode at/viewer/local - run
npm run desktop -- --check-only,npm run desktop:doctor,npm run desktop:smoke:launch,npm run desktop:smoke,npm run desktop:smoke:local-start,npm run desktop:smoke:local-start-drop,npm run desktop:smoke:local-start-nondicom,npm run desktop:smoke:import,npm run desktop:smoke:ui-import,npm run desktop:smoke:picker-files-import,npm run desktop:smoke:picker-import,npm run desktop:smoke:picker-large-import,npm run desktop:smoke:picker-many-import, andnpm run desktop:smoke:viewer-launch - run the focused backend and viewer checks
- attempt the actual app flow on Linux
- report what happened before widening the code-change scope
That first report should ideally cover desktop startup, backend startup, frontend startup, viewer build/load, login, worklist, viewer launch, and compose-stack behavior if Docker is available.
Public routes:
- shell: http://localhost:3000
- worklist: http://localhost:3000/worklist
- viewer: http://localhost:3000/viewer
- API: http://localhost:3000/api
- DICOMweb: http://localhost:3000/dicom-web
The authoritative contributor guidance is:
The current execution checklist for the next clinical tranche is:
The next major clinical tasks are:
- Keep docs and runtime guidance aligned with the shipped RadSysX architecture.
- Deepen the RadSysX OHIF extension/mode implementation.
- Wire OHIF measurement tracking and segmentation into governed SR/SEG export and reload flows.
- Validate the full local nginx + frontend + viewer + backend + Orthanc stack end to end.
- Move from seeded local identity to institutional identity/context.
