Nodae BYOM SDK — NodaeBridge and NodaeModelAdapter for sponsor containers
Project description
nodae_bridge
Nodae BYOM SDK v1.0.4 — the bridge between your ML model and the Nodae federated healthcare platform by Intheris Health.
Overview
nodae_bridge is the Python package your Docker container uses when participating in federated training or inference across hospital sites. It provides:
NodaeBridge— file-based I/O helpers (/data→/output)NodaeModelAdapter— the interface your model class must implementrun.py— the container entrypoint dispatcher (python -m nodae_bridge.run)
Core principle: algorithms travel, data stays local. Patient data never leaves the hospital; your container receives a privacy-safe DataFrame and returns only model weights or aggregate statistics.
Installation
pip install nodae_bridge
Minimum Python version: 3.10. Add it alongside your ML framework in your Dockerfile:
RUN pip install nodae_bridge scikit-learn # or torch, xgboost, etc.
Quick Start
from nodae_bridge import NodaeModelAdapter, ModelSpec, TrainingConfig, LocalTrainResult, InferenceResult
import pandas as pd
class MyModel(NodaeModelAdapter):
def get_model_spec(self) -> ModelSpec:
return ModelSpec(
model_id="my-model-v1",
name="My Federated Model",
version="1.0.0",
input_columns=["age", "length_of_stay", "drg_weight"],
output_names=["risk_score"],
min_samples=50,
)
def initialize(self, config: dict) -> None:
pass
def get_weights(self) -> bytes:
return b""
def set_weights(self, weights: bytes) -> None:
pass
def local_train(self, data: pd.DataFrame, round_number: int, config: TrainingConfig) -> LocalTrainResult:
return LocalTrainResult(weights=self.get_weights(), sample_count=len(data), loss=0.0)
def local_inference(self, data: pd.DataFrame) -> InferenceResult:
return InferenceResult(prediction_distribution={"low": len(data)}, sample_count=len(data))
Your Dockerfile:
FROM python:3.11-slim
WORKDIR /app
RUN pip install nodae_bridge torch --index-url https://download.pytorch.org/whl/cpu
COPY model.py .
CMD ["python", "-m", "nodae_bridge.run"]
Data Schema
Your container receives local patient data at /data/input.parquet as a privacy-safe DataFrame. No PII is ever exposed.
| Column | Type | Description |
|---|---|---|
patient_id |
str | Anonymized SHA-256 hash |
age |
int | Computed from DOB |
gender |
str | MALE / FEMALE / OTHER / UNKNOWN |
admission_type |
str | EMERGENCY / ELECTIVE / TRANSFER / AMBULATORY |
discharge_disposition |
str | HOME / TRANSFER / DECEASED / REHABILITATION |
length_of_stay |
int | Hospitalization days |
primary_diagnosis |
str | ICD-10 code |
num_secondary_diagnoses |
int | Count |
drg_code |
str | SwissDRG code |
drg_weight |
float | DRG cost weight |
num_procedures |
int | Count |
num_medications |
int | Count |
num_lab_results |
int | Count |
insurance_type |
str | Swiss insurance category |
Always call fillna() before passing data to your model — not all columns are populated for every patient.
API Reference
Core I/O
| Method | Description |
|---|---|
NodaeBridge.get_data() |
Load /data/input.parquet as a DataFrame |
NodaeBridge.get_weights() |
Load global model weights; b"" on round 0 |
NodaeBridge.get_config() |
Load training/inference config dict |
NodaeBridge.get_site_metadata() |
Convenience wrapper for config["site_metadata"] |
NodaeBridge.save_weights(bytes) |
Write updated weights to /output/weights.bin (required) |
NodaeBridge.save_metrics(dict) |
Write aggregate metrics to /output/metrics.json |
NodaeBridge.log(str) |
Print timestamped message to stdout |
Extended scope (byom_data_scope="extended")
Register with byom_data_scope="extended" to receive additional raw data files. Returns empty DataFrame/dict when absent (standard scope), so imports are always safe.
| Method | Returns | Content |
|---|---|---|
NodaeBridge.get_labs() |
DataFrame | Per-result lab data: patient_id, loinc_code, test_name, value, unit, flag, date, … |
NodaeBridge.get_vitals() |
DataFrame | Latest vitals per patient: bp_systolic, heart_rate, temperature, weight_kg, … |
NodaeBridge.get_procedures() |
DataFrame | Per-procedure: patient_id, code, description, date, source |
NodaeBridge.get_signals() |
dict | Study-specific template signals: {patient_id: {signal_key: value}} |
Feature engineering is the container's responsibility — the platform exposes raw data only.
FHIR scope (byom_data_scope="fhir")
Available when registered with byom_data_scope="fhir". The host writes one FHIR R4 Bundle per cohort patient under /data/fhir/. The standard input.parquet is always present alongside the bundles.
| Method | Returns | Description |
|---|---|---|
NodaeBridge.list_fhir_patients() |
list[str] |
Patient IDs (anonymized hashes) with FHIR bundles; reads index.json |
NodaeBridge.get_fhir_bundle(patient_id) |
Optional[dict] |
Full FHIR R4 Bundle dict for one patient, or None |
Each bundle contains: Patient (age, gender, LOS, DRG — no PHI), Conditions (ICD-10 + SNOMED), Observations (LOINC-coded labs and vitals), Procedures (CHOP), MedicationStatements, AllergyIntolerances, a Basic resource for template_signals, and an ImagingStudy reference when DICOM data is present.
for pid in NodaeBridge.list_fhir_patients():
bundle = NodaeBridge.get_fhir_bundle(pid)
entries = {e["resource"]["resourceType"]: e["resource"] for e in bundle["entry"]}
# access any field directly — no platform-imposed field selection
SCAFFOLD (aggregation_strategy="scaffold")
Used when the study uses SCAFFOLD to correct client drift in non-IID populations.
| Method | Description |
|---|---|
NodaeBridge.get_control_variates() |
Load global control variate from /data/control_variates.bin; b"" when not active |
NodaeBridge.save_control_variate_delta(bytes) |
Write per-site delta to /output/control_variates.bin |
config["scaffold_enabled"] is true when the host has sent a global control variate. If save_control_variate_delta() is not called, the aggregator falls back to FedAvg for this site (backward compatible).
Imaging (imaging_access=True)
Available when registered with imaging_access=True and the SA host has BYOM_DICOM_STORE_ENABLED=True. A read-only DICOM store is mounted at /data/imaging/.
| Method | Returns | Description |
|---|---|---|
NodaeBridge.get_imaging() |
Optional[Path] |
Path to /data/imaging/, or None if unavailable |
NodaeBridge.list_imaging_series() |
list[dict] |
Manifest: patient_id, study_uid, modality, slice_count, path |
import pydicom
from pathlib import Path
base = NodaeBridge.get_imaging()
for series in NodaeBridge.list_imaging_series():
if series["modality"] != "CT":
continue
slices = sorted((base / series["path"]).glob("*.dcm"))
datasets = [pydicom.dcmread(str(s)) for s in slices]
# HU windowing, segmentation, feature extraction — container's responsibility
Changelog
1.0.5 (June 2026)
- Added
list_fhir_patients(),get_fhir_bundle()— FHIR R4 bundle access forbyom_data_scope="fhir" - Added
FHIR_PATHclass constant
1.0.4 (June 2026)
- Added
get_imaging(),list_imaging_series()— DICOM store access - Updated
get_site_metadata()docs to includeimaging.*keys
1.0.3 (June 2026)
- Added
get_control_variates(),save_control_variate_delta()— SCAFFOLD support
1.0.2 (June 2026)
- Added
get_signals()— template signal access - Added
get_labs(),get_vitals(),get_procedures()— extended data scope
1.0.1 (June 2026)
- Added
get_site_metadata()convenience wrapper
1.0.0 (June 2026)
- Initial release:
NodaeBridge,NodaeModelAdapter,run.pyentrypoint
License
Proprietary — Intheris Health. Contact support@intheris-health.com for access. \x00
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file nodae_bridge-1.0.5.tar.gz.
File metadata
- Download URL: nodae_bridge-1.0.5.tar.gz
- Upload date:
- Size: 15.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.0 {"installer":{"name":"uv","version":"0.11.0","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
caba747e22f70ab033dd430a53925408000639343cd2116fbe77a39c83292352
|
|
| MD5 |
f03dfb7a5c89e705f435b7af698840bf
|
|
| BLAKE2b-256 |
ed4781444fff6b9bd31ab77b854bb2513bba1bf894b2a930085b2143f152f64e
|
File details
Details for the file nodae_bridge-1.0.5-py3-none-any.whl.
File metadata
- Download URL: nodae_bridge-1.0.5-py3-none-any.whl
- Upload date:
- Size: 14.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.0 {"installer":{"name":"uv","version":"0.11.0","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
03dc5f2f8d5c8b6327ea8a3ddd1aa87acb64f5027d3e02138e32e749a6997a85
|
|
| MD5 |
865863fa05e169812a93c2a9de9fc1e1
|
|
| BLAKE2b-256 |
aae236131e51882f883374ddc0019b3cfcae6d3d09ee5b814d0f6b47160d9e0d
|