A modular, interface-driven runtime for continuous biosignal processing and real-time inference.
NeuroStream-Edge is an open-source runtime for building real-time biosignal processing pipelines. It focuses on the infrastructure between a continuous sensor stream and machine learning inference — providing modular components for streaming, buffering, window generation, runtime orchestration, storage, and performance analysis.
The runtime is designed to support multiple biosignal modalities — EEG, ECG, EMG, and PPG — while remaining independent of any specific downstream task or machine-learning architecture.
- Interface-driven architecture — every layer exposes an abstract base class. The runtime depends only on interfaces, never on concrete implementations.
- Streaming-first, fixed-memory execution — pre-allocated ring buffers with O(1) writes and circular overwrite semantics. No growth, no GC pressure in the hot path.
- Model-agnostic & signal-agnostic — bring your own ONNX / TorchScript model
or implement the
InferenceEngineinterface. - Minimal core dependencies — the runtime requires only NumPy. Every
heavy backend (ONNX, PyTorch, BrainFlow, LSL) is an optional extra with a
clear
ImportErrorand lazy import. - Observable by design — a decoupled Observer-based event system feeds a logger, profiler, and metrics collector without coupling into the execution path.
- Online runtime and offline pipeline are intentionally separated — reflecting the different engineering requirements of low-latency inference and large-scale dataset processing.
Alpha. All eight core modules are implemented and covered by 95 passing tests (~78% line coverage). The runtime is functional end-to-end with a reference simulated pipeline, but is not yet benchmarked against external baselines and the public API is still stabilising. See the Roadmap for what is planned.
| Module | Interface | Role | Status |
|---|---|---|---|
acquisition |
SignalSource |
Continuous sample streams from sensors or simulators | ✅ |
streaming |
Buffer |
Fixed-capacity, overwrite-on-full buffers | ✅ |
windowing |
WindowGenerator |
Overlapping inference-ready windows | ✅ |
runtime |
— | Pipeline orchestration + event system | ✅ |
models |
InferenceEngine |
Model-agnostic prediction backends | ✅ |
storage |
StorageBackend |
Offline dataset recording / playback | ✅ |
observability |
Observer |
Decoupled logging, profiling, metrics | ✅ |
benchmarking |
— | Throughput & latency characterisation | ✅ |
pip install neurostream-edgegit clone https://github.com/tarunc997/NeuroStream-Edge.git
cd NeuroStream-Edge
pip install -e .Heavy backends are never installed automatically — install only what you need:
pip install "neurostream-edge[onnx]" # ONNX Runtime
pip install "neurostream-edge[torch]" # TorchScript
pip install "neurostream-edge[brainflow]" # BrainFlow hardware adapters
pip install "neurostream-edge[lsl]" # Lab Streaming Layer
pip install "neurostream-edge[dev]" # pytest, pytest-covExtras can be combined, e.g. pip install "neurostream-edge[onnx,dev]".
A minimal online pipeline: SimulatedSignalSource → RingBuffer → SlidingWindowGenerator → DummyInferenceEngine.
import time
import neurostream as ns
# 1. Build components (dependency injection)
source = ns.SimulatedSignalSource(sample_rate=250.0, n_channels=8, seed=42)
buffer = ns.RingBuffer(capacity=2500, n_channels=8) # 10 s @ 250 Hz
window = ns.SlidingWindowGenerator(buffer, window_size=250, # 1 s window
stride=50) # 0.2 s stride
engine = ns.DummyInferenceEngine(n_outputs=3)
# 2. Optional observability
bus = ns.EventBus()
metrics = ns.Metrics()
profiler = ns.Profiler()
bus.subscribe("*", metrics)
bus.subscribe("*", profiler)
# 3. Wire everything into the runtime
runtime = ns.Runtime(source=source, buffer=buffer, window=window,
inference=engine, event_bus=bus, read_chunk=10)
# 4. Run
runtime.start()
runtime.run_for(2.0) # run for 2 seconds
runtime.stop()
print(f"Predictions: {len(runtime.predictions)}")
print(f"Throughput : { {k: f'{v:.1f}/s' for k, v in metrics.throughput().items()} }")More examples live in examples/:
online_pipeline.py— full online inference pipelineoffline_recording.py— record a stream to disk and replay itbenchmark_buffers.py— compare buffer backends
The online inference runtime and offline data pipeline are intentionally separated, reflecting the different engineering requirements of low-latency inference and large-scale dataset processing.
Continuous Signal
│
▼
Acquisition Layer
│
▼
Streaming Layer
(Ring Buffers)
│
▼
Windowing Layer
│
▼
Inference Runtime
│
▼
Model Interface
│
▼
Prediction
Runtime (orchestrator)
│
┌────────────────┼────────────────┐
▼ ▼ ▼
Acquisition Streaming Windowing
(SignalSource) (Buffer) (WindowGenerator)
│ │ │
└────────────────┼────────────────┘
▼
Models (InferenceEngine)
│
Storage (offline)
Observability (Logger · Profiler · Metrics) ← Runtime events
Benchmarking (per-component measurement)
- Streaming-first — designed around continuous signals, not static datasets.
- Interface-driven — the runtime depends only on abstractions; components are interchangeable (Strategy pattern).
- Composition over inheritance — no per-modality
EEGRuntime/ECGRuntimeclasses; one runtime serves every signal type. - Fixed-memory execution — storage is allocated once and never grows.
- Storage-independent & model-agnostic — backends are pluggable.
Allocation note: the streaming write path is O(1) and allocation-free for contiguous writes. Read paths (
read(),latest()) allocate copies to avoid exposing internal buffers.
docs/ # architectural & per-module reference
examples/ # runnable scripts
neurostream/
├── acquisition/ # signal sources & adapters
├── streaming/ # fixed-memory buffers
├── windowing/ # sliding-window generation
├── runtime/ # pipeline orchestration & events
├── models/ # inference engines & factory
├── storage/ # dataset backends
├── observability/ # logger, profiler, metrics
└── benchmarking/ # benchmarks & latency profiling
tests/ # 95 tests
Detailed per-module guides cover each layer's purpose, its abstract interface, the concrete implementations and their trade-offs, and runnable usage examples.
| Module | Guide |
|---|---|
| Acquisition | docs/modules/acquisition.md |
| Streaming | docs/modules/streaming.md |
| Windowing | docs/modules/windowing.md |
| Models | docs/modules/models.md |
| Storage | docs/modules/storage.md |
| Runtime | docs/modules/runtime.md |
| Observability | docs/modules/observability.md |
| Benchmarking | docs/modules/benchmarking.md |
Start at docs/index.md for the architecture overview.
The suite uses real concurrency — actual background threads, real timing, real predictions — rather than mocks, so integration behaviour is exercised directly.
pip install "neurostream-edge[dev]"
pytestWith a coverage report:
pytest --cov=neurostream --cov-report=term-missingNeuroStream-Edge is infrastructure, not an end-user application. It is not tied to a specific machine-learning task such as seizure detection, sleep staging, emotion recognition, or motor imagery. Instead, it provides the runtime required to support continuous biosignal inference across multiple research and deployment scenarios.
- End-to-end reference pipeline on a public dataset (e.g. TUH EEG, BCI Comp IV)
- Benchmark against a naive baseline using the built-in
benchmarking/layer - Convenience
quickstart()constructor for low-friction onboarding - Config-driven pipelines +
neurostream runCLI (YAML → wired runtime) - Plugin entry points (
[project.entry-points]) for third-party engines/sources - Explicit backpressure / drop policies (
block/drop-newest/drop-oldest) - Source & storage factories to match the existing
InferenceFactory - Async / queued EventBus option to fully decouple observers from the tick
-
CITATION.cffand first tagged release
Contributions are welcome. The project follows an interface-driven architecture — every layer exposes an abstract base class, and the runtime depends only on interfaces. Before adding new functionality, open an issue to discuss scope.
When contributing, please:
- Keep the runtime free of concrete-component dependencies (depend on interfaces).
- Add or update tests for any behavioural change.
- Run
pytestand ensure the full suite passes before submitting a PR. - Follow the existing numpy-style docstring conventions.
MIT License © Tarun Chilkur