neurabridge 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,28 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [0.1.0] - 2024-06-15
9
+
10
+ ### Added
11
+ - Initial public release of the Neurabridge Python SDK
12
+ - Client authentication and API access
13
+ - Experiment discovery and run listing
14
+ - Run download and caching functionality
15
+ - Signal data loading as pandas DataFrames
16
+ - Image artifact loading with Pillow support
17
+ - Local analysis session management with manifest support
18
+ - Support for bulk downloading across multiple experiments
19
+ - Channel name metadata support in dataset loading
20
+ - Progress tracking for long-running operations
21
+ - Comprehensive test suite with pytest
22
+
23
+ ### Features
24
+ - `Client` class for API interaction
25
+ - `Experiment`, `Run`, and `LocalRun` classes for data representation
26
+ - `ExperimentCollection` and `LocalRunCollection` for batch operations
27
+ - `AnalysisSession` for reproducible local analysis workflows
28
+ - Full test coverage for core functionality
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Neurabridge
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,263 @@
1
+ Metadata-Version: 2.4
2
+ Name: neurabridge
3
+ Version: 0.1.0
4
+ Summary: Neurabridge Python SDK for experiment access and analysis workflows
5
+ Project-URL: Homepage, https://neurabridge.io
6
+ Project-URL: Repository, https://github.com/neurabridge-io/neurabridge_sdk
7
+ Project-URL: Documentation, https://neurabridge.io
8
+ Project-URL: Bug Tracker, https://github.com/neurabridge-io/neurabridge_sdk/issues
9
+ Author: Neurabridge
10
+ License: MIT
11
+ License-File: LICENSE
12
+ Keywords: brain-computer-interface,data-analysis,eeg,neuroscience,signal-processing
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Intended Audience :: Science/Research
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Topic :: Scientific/Engineering :: Medical Science Apps.
21
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
22
+ Requires-Python: >=3.10
23
+ Requires-Dist: httpx>=0.27
24
+ Requires-Dist: numpy>=1.26
25
+ Requires-Dist: pandas>=2.0
26
+ Requires-Dist: pillow>=10
27
+ Requires-Dist: tqdm>=4.66
28
+ Provides-Extra: dev
29
+ Requires-Dist: pytest>=8.0; extra == 'dev'
30
+ Description-Content-Type: text/markdown
31
+
32
+ # neurabridge
33
+
34
+ Neurabridge.io is the software layer for turning brain signal data into usable models and APIs.
35
+
36
+ It brings hardware-integrated research workflows, analysis pipelines, and application development into one system so teams can move from signal collection to deployable intelligence faster.
37
+
38
+ `neurabridge` is the official Python SDK for accessing that workflow programmatically.
39
+ It provides a focused interface for discovering experiments, downloading run artifacts, decoding datasets, and organizing reproducible local analysis sessions.
40
+
41
+ Neurabridge is built for engineers, researchers, institutions, and hardware-adjacent product teams working with EEG and related brain signals. The platform is designed to support model development for studies, regressors, classifiers, and intent or activity segmentation systems, then expose those outputs through software interfaces that manufacturers and entrepreneurs can build on.
42
+
43
+ ## Installation
44
+
45
+ ```bash
46
+ pip install .
47
+ ```
48
+
49
+ For local development:
50
+
51
+ ```bash
52
+ pip install -e .[dev]
53
+ ```
54
+
55
+ ## What The SDK Covers
56
+
57
+ - Authenticate against a Neurabridge API environment
58
+ - List experiments and runs visible to the current token
59
+ - Download a single run or all runs in a collection
60
+ - Load decoded signal datasets as pandas DataFrames
61
+ - Load associated image artifacts with Pillow
62
+ - Create portable local analysis sessions with a manifest
63
+
64
+ ## Why It Exists
65
+
66
+ Brain-computer application development is fragmented across vendors, protocols, experimental setups, device integrations, raw data handling, one-off analysis scripts, and ad hoc model pipelines.
67
+ Neurabridge consolidates those steps into a software-first foundation that makes brain signal data easier to inspect, organize, model, and expose through downstream products.
68
+
69
+ For technical teams, that means less time spent stitching together infrastructure and more time spent building useful systems on top of brain data.
70
+
71
+ ## Quick Start
72
+
73
+ ```python
74
+ import neurabridge as nb
75
+
76
+ client = nb.Client(
77
+ base_url="https://api.neurabridge.dev",
78
+ token="nbk_live_xxxxx",
79
+ )
80
+
81
+ whoami = client.whoami()
82
+ experiments = client.experiments()
83
+
84
+ experiment = experiments[0]
85
+ run = client.runs(experiment.id)[0]
86
+ local_run = client.download(experiment.id, run.id)
87
+
88
+ dataset = local_run.dataset
89
+ metadata_named_dataset = local_run.load_dataset(use_metadata_channel_names=True)
90
+ images = local_run.images()
91
+ ```
92
+
93
+ ## Standard Workflow
94
+
95
+ ```python
96
+ import neurabridge as nb
97
+ from neurabridge import session
98
+
99
+ client = nb.Client(base_url="https://api.neurabridge.dev", token="nbk_live_xxxxx")
100
+
101
+ experiments = client.experiments()
102
+ selected_experiment = experiments[0]
103
+ selected_run = client.runs(selected_experiment.id)[0]
104
+
105
+ local_run = client.download(selected_experiment.id, selected_run.id, show_progress=False)
106
+
107
+ analysis_session = session.AnalysisSession.create(
108
+ name="example-session",
109
+ runs=[local_run],
110
+ base_dir="./analysis",
111
+ )
112
+
113
+ signals = analysis_session.load_signals(
114
+ exp_id=local_run.experiment_id,
115
+ run_id=local_run.run_id,
116
+ )
117
+ images = analysis_session.load_images(
118
+ exp_id=local_run.experiment_id,
119
+ run_id=local_run.run_id,
120
+ )
121
+ ```
122
+
123
+ ## Working Across Runs
124
+
125
+ ```python
126
+ client = nb.Client(base_url="https://api.neurabridge.dev", token="nbk_live_xxxxx")
127
+
128
+ experiments = client.experiments()
129
+ all_runs = experiments.download_all(show_progress=False)
130
+ stacked_dataset = all_runs.dataset
131
+ ```
132
+
133
+ ## Platform Fit
134
+
135
+ Neurabridge does not build hardware devices today. It is intended to sit above hardware and unify the software layer around it: experiment data access, signal decoding, analysis workflows, model development, and API-ready outputs.
136
+
137
+ That makes it useful both for internal research teams and for external partners who want a faster path from hardware-generated brain data to applications.
138
+
139
+ ## Public API
140
+
141
+ The root package exposes the main stable entry points:
142
+
143
+ - `neurabridge.Client`
144
+ - `neurabridge.AnalysisSession`
145
+ - `neurabridge.Experiment`
146
+ - `neurabridge.Run`
147
+ - `neurabridge.LocalRun`
148
+ - `neurabridge.ExperimentCollection`
149
+ - `neurabridge.LocalRunCollection`
150
+
151
+ Segmented namespaces are also available when you want to keep imports explicit:
152
+
153
+ ```python
154
+ from neurabridge import experiment, session
155
+ ```
156
+
157
+ ### Client
158
+
159
+ Main entry point for interacting with the Neurabridge API.
160
+
161
+ | Method | Parameters | Returns | Description |
162
+ |--------|-----------|---------|-------------|
163
+ | `whoami()` | — | `dict` | Get the authenticated user information |
164
+ | `experiments()` | — | `ExperimentCollection` | List all experiments accessible to the token |
165
+ | `runs(exp_id)` | `exp_id: str` | `list[Run]` | Get all runs for a specific experiment |
166
+ | `download(exp_id, run_id, show_progress)` | `exp_id: str`, `run_id: str`, `show_progress: bool = True` | `LocalRun` | Download a single run to local cache |
167
+ | `download_all(show_progress)` | `show_progress: bool = True` | `LocalRunCollection` | Download all runs from all experiments |
168
+ | `delete_run(exp_id, run_id)` | `exp_id: str`, `run_id: str` | `int` | Delete a specific run from the server |
169
+ | `delete_experiment(exp_id)` | `exp_id: str` | `int` | Delete an entire experiment from the server |
170
+ | `close()` | — | — | Close the client and clean up resources |
171
+
172
+ ### Experiment
173
+
174
+ Metadata and stats about a remote experiment.
175
+
176
+ | Property | Type | Description |
177
+ |----------|------|-------------|
178
+ | `id` | `str` | Unique experiment identifier |
179
+ | `path` | `str` | Experiment path/location on server |
180
+ | `run_count` | `int` | Number of runs in this experiment |
181
+ | `updated_at` | `str \| None` | Last update timestamp |
182
+
183
+ ### Run
184
+
185
+ Metadata about a remote run (before download).
186
+
187
+ | Property | Type | Description |
188
+ |----------|------|-------------|
189
+ | `id` | `str` | Unique run identifier |
190
+ | `experiment_id` | `str` | ID of parent experiment |
191
+ | `path` | `str` | Run path/location on server |
192
+ | `artifact_count` | `int` | Number of artifacts (signals, images, etc.) |
193
+ | `size_bytes` | `int` | Total size of run data in bytes |
194
+ | `updated_at` | `str \| None` | Last update timestamp |
195
+
196
+ ### LocalRun
197
+
198
+ A downloaded run with local file access and data loading capabilities.
199
+
200
+ | Property/Method | Returns | Description |
201
+ |---------|---------|-------------|
202
+ | `id` | `str` | Alias for `run_id` |
203
+ | `run_id` | `str` | The run's unique identifier |
204
+ | `experiment_id` | `str` | Parent experiment ID |
205
+ | `path` | `Path` | Local filesystem path to downloaded run |
206
+ | `signal_files` | `list[Path]` | Paths to signal artifact files |
207
+ | `image_files` | `list[Path]` | Paths to image artifact files |
208
+ | `dataset` | `DataFrame` | Cached property: all signal data as pandas DataFrame |
209
+ | `metadata` | `dict` | Experiment metadata (channel names, sampling rates, etc.) |
210
+ | `image` | `Image` | First image artifact as PIL Image object |
211
+ | `images` | `list[tuple[dict, Image]]` | All image artifacts with metadata |
212
+ | `load_dataset(use_metadata_channel_names)` | `DataFrame` | Load signal data with optional metadata-based column names |
213
+
214
+ ### ExperimentCollection
215
+
216
+ A list-like collection of `Experiment` objects with batch operations.
217
+
218
+ | Method | Parameters | Returns | Description |
219
+ |--------|-----------|---------|-------------|
220
+ | `download_all(show_progress)` | `show_progress: bool = True` | `LocalRunCollection` | Download all runs from all experiments in collection |
221
+
222
+ ### LocalRunCollection
223
+
224
+ A list-like collection of `LocalRun` objects with aggregation operations.
225
+
226
+ | Property/Method | Returns | Description |
227
+ |---------|---------|-------------|
228
+ | `dataset` | `DataFrame` | Cached property: stacked signal data from all runs |
229
+ | `load_dataset(use_metadata_channel_names, force_reload)` | `DataFrame` | Load and concatenate all run datasets with optional cache control |
230
+ | `profile_dataset_load(use_metadata_channel_names, force_reload)` | `dict` | Load all datasets while measuring timing, row/column counts per run |
231
+
232
+ ### AnalysisSession
233
+
234
+ Local writable workspace for reproducible analysis across downloaded runs.
235
+
236
+ | Method | Parameters | Returns | Description |
237
+ |--------|-----------|---------|-------------|
238
+ | `create(name, runs, base_dir)` | `name: str`, `runs: Iterable[LocalRun]`, `base_dir: str \| Path` | `AnalysisSession` | Create a new analysis session workspace with run data |
239
+ | `open(session_path)` | `session_path: str \| Path` | `AnalysisSession` | Open an existing session from disk |
240
+ | `list(base_dir)` | `base_dir: str \| Path` | `list[AnalysisSession]` | List all valid sessions under a directory |
241
+ | `load_signals(exp_id, run_id, use_metadata_channel_names)` | `exp_id: str`, `run_id: str`, `use_metadata_channel_names: bool = False` | `DataFrame` | Load signal data from session workspace |
242
+ | `load_images(exp_id, run_id)` | `exp_id: str`, `run_id: str` | `list[tuple[dict, Image]]` | Load image artifacts from session workspace |
243
+ | `delete()` | — | — | Delete the session directory from disk |
244
+
245
+ ### Session Properties
246
+
247
+ | Property | Type | Description |
248
+ |----------|------|-------------|
249
+ | `name` | `str` | Session name |
250
+ | `path` | `Path` | Local filesystem path to session directory |
251
+ | `manifest` | `dict` | Session metadata manifest (JSON-serializable dict with run info) |
252
+
253
+ ## Notes
254
+
255
+ - Signal data is returned as pandas DataFrames
256
+ - Image artifacts are returned as Pillow images
257
+ - Downloads are cached locally under the SDK cache directory
258
+ - Analysis sessions store a lightweight manifest so downloaded runs can be reopened consistently
259
+ - The SDK is intentionally read-oriented and centered on analysis and downstream application workflows
260
+
261
+ ## Validation Notebook
262
+
263
+ The repository includes a smoke-test notebook that exercises the main SDK flow from authentication through dataset loading, manifest decoding, image rendering, and bulk download.
@@ -0,0 +1,232 @@
1
+ # neurabridge
2
+
3
+ Neurabridge.io is the software layer for turning brain signal data into usable models and APIs.
4
+
5
+ It brings hardware-integrated research workflows, analysis pipelines, and application development into one system so teams can move from signal collection to deployable intelligence faster.
6
+
7
+ `neurabridge` is the official Python SDK for accessing that workflow programmatically.
8
+ It provides a focused interface for discovering experiments, downloading run artifacts, decoding datasets, and organizing reproducible local analysis sessions.
9
+
10
+ Neurabridge is built for engineers, researchers, institutions, and hardware-adjacent product teams working with EEG and related brain signals. The platform is designed to support model development for studies, regressors, classifiers, and intent or activity segmentation systems, then expose those outputs through software interfaces that manufacturers and entrepreneurs can build on.
11
+
12
+ ## Installation
13
+
14
+ ```bash
15
+ pip install .
16
+ ```
17
+
18
+ For local development:
19
+
20
+ ```bash
21
+ pip install -e .[dev]
22
+ ```
23
+
24
+ ## What The SDK Covers
25
+
26
+ - Authenticate against a Neurabridge API environment
27
+ - List experiments and runs visible to the current token
28
+ - Download a single run or all runs in a collection
29
+ - Load decoded signal datasets as pandas DataFrames
30
+ - Load associated image artifacts with Pillow
31
+ - Create portable local analysis sessions with a manifest
32
+
33
+ ## Why It Exists
34
+
35
+ Brain-computer application development is fragmented across vendors, protocols, experimental setups, device integrations, raw data handling, one-off analysis scripts, and ad hoc model pipelines.
36
+ Neurabridge consolidates those steps into a software-first foundation that makes brain signal data easier to inspect, organize, model, and expose through downstream products.
37
+
38
+ For technical teams, that means less time spent stitching together infrastructure and more time spent building useful systems on top of brain data.
39
+
40
+ ## Quick Start
41
+
42
+ ```python
43
+ import neurabridge as nb
44
+
45
+ client = nb.Client(
46
+ base_url="https://api.neurabridge.dev",
47
+ token="nbk_live_xxxxx",
48
+ )
49
+
50
+ whoami = client.whoami()
51
+ experiments = client.experiments()
52
+
53
+ experiment = experiments[0]
54
+ run = client.runs(experiment.id)[0]
55
+ local_run = client.download(experiment.id, run.id)
56
+
57
+ dataset = local_run.dataset
58
+ metadata_named_dataset = local_run.load_dataset(use_metadata_channel_names=True)
59
+ images = local_run.images()
60
+ ```
61
+
62
+ ## Standard Workflow
63
+
64
+ ```python
65
+ import neurabridge as nb
66
+ from neurabridge import session
67
+
68
+ client = nb.Client(base_url="https://api.neurabridge.dev", token="nbk_live_xxxxx")
69
+
70
+ experiments = client.experiments()
71
+ selected_experiment = experiments[0]
72
+ selected_run = client.runs(selected_experiment.id)[0]
73
+
74
+ local_run = client.download(selected_experiment.id, selected_run.id, show_progress=False)
75
+
76
+ analysis_session = session.AnalysisSession.create(
77
+ name="example-session",
78
+ runs=[local_run],
79
+ base_dir="./analysis",
80
+ )
81
+
82
+ signals = analysis_session.load_signals(
83
+ exp_id=local_run.experiment_id,
84
+ run_id=local_run.run_id,
85
+ )
86
+ images = analysis_session.load_images(
87
+ exp_id=local_run.experiment_id,
88
+ run_id=local_run.run_id,
89
+ )
90
+ ```
91
+
92
+ ## Working Across Runs
93
+
94
+ ```python
95
+ client = nb.Client(base_url="https://api.neurabridge.dev", token="nbk_live_xxxxx")
96
+
97
+ experiments = client.experiments()
98
+ all_runs = experiments.download_all(show_progress=False)
99
+ stacked_dataset = all_runs.dataset
100
+ ```
101
+
102
+ ## Platform Fit
103
+
104
+ Neurabridge does not build hardware devices today. It is intended to sit above hardware and unify the software layer around it: experiment data access, signal decoding, analysis workflows, model development, and API-ready outputs.
105
+
106
+ That makes it useful both for internal research teams and for external partners who want a faster path from hardware-generated brain data to applications.
107
+
108
+ ## Public API
109
+
110
+ The root package exposes the main stable entry points:
111
+
112
+ - `neurabridge.Client`
113
+ - `neurabridge.AnalysisSession`
114
+ - `neurabridge.Experiment`
115
+ - `neurabridge.Run`
116
+ - `neurabridge.LocalRun`
117
+ - `neurabridge.ExperimentCollection`
118
+ - `neurabridge.LocalRunCollection`
119
+
120
+ Segmented namespaces are also available when you want to keep imports explicit:
121
+
122
+ ```python
123
+ from neurabridge import experiment, session
124
+ ```
125
+
126
+ ### Client
127
+
128
+ Main entry point for interacting with the Neurabridge API.
129
+
130
+ | Method | Parameters | Returns | Description |
131
+ |--------|-----------|---------|-------------|
132
+ | `whoami()` | — | `dict` | Get the authenticated user information |
133
+ | `experiments()` | — | `ExperimentCollection` | List all experiments accessible to the token |
134
+ | `runs(exp_id)` | `exp_id: str` | `list[Run]` | Get all runs for a specific experiment |
135
+ | `download(exp_id, run_id, show_progress)` | `exp_id: str`, `run_id: str`, `show_progress: bool = True` | `LocalRun` | Download a single run to local cache |
136
+ | `download_all(show_progress)` | `show_progress: bool = True` | `LocalRunCollection` | Download all runs from all experiments |
137
+ | `delete_run(exp_id, run_id)` | `exp_id: str`, `run_id: str` | `int` | Delete a specific run from the server |
138
+ | `delete_experiment(exp_id)` | `exp_id: str` | `int` | Delete an entire experiment from the server |
139
+ | `close()` | — | — | Close the client and clean up resources |
140
+
141
+ ### Experiment
142
+
143
+ Metadata and stats about a remote experiment.
144
+
145
+ | Property | Type | Description |
146
+ |----------|------|-------------|
147
+ | `id` | `str` | Unique experiment identifier |
148
+ | `path` | `str` | Experiment path/location on server |
149
+ | `run_count` | `int` | Number of runs in this experiment |
150
+ | `updated_at` | `str \| None` | Last update timestamp |
151
+
152
+ ### Run
153
+
154
+ Metadata about a remote run (before download).
155
+
156
+ | Property | Type | Description |
157
+ |----------|------|-------------|
158
+ | `id` | `str` | Unique run identifier |
159
+ | `experiment_id` | `str` | ID of parent experiment |
160
+ | `path` | `str` | Run path/location on server |
161
+ | `artifact_count` | `int` | Number of artifacts (signals, images, etc.) |
162
+ | `size_bytes` | `int` | Total size of run data in bytes |
163
+ | `updated_at` | `str \| None` | Last update timestamp |
164
+
165
+ ### LocalRun
166
+
167
+ A downloaded run with local file access and data loading capabilities.
168
+
169
+ | Property/Method | Returns | Description |
170
+ |---------|---------|-------------|
171
+ | `id` | `str` | Alias for `run_id` |
172
+ | `run_id` | `str` | The run's unique identifier |
173
+ | `experiment_id` | `str` | Parent experiment ID |
174
+ | `path` | `Path` | Local filesystem path to downloaded run |
175
+ | `signal_files` | `list[Path]` | Paths to signal artifact files |
176
+ | `image_files` | `list[Path]` | Paths to image artifact files |
177
+ | `dataset` | `DataFrame` | Cached property: all signal data as pandas DataFrame |
178
+ | `metadata` | `dict` | Experiment metadata (channel names, sampling rates, etc.) |
179
+ | `image` | `Image` | First image artifact as PIL Image object |
180
+ | `images` | `list[tuple[dict, Image]]` | All image artifacts with metadata |
181
+ | `load_dataset(use_metadata_channel_names)` | `DataFrame` | Load signal data with optional metadata-based column names |
182
+
183
+ ### ExperimentCollection
184
+
185
+ A list-like collection of `Experiment` objects with batch operations.
186
+
187
+ | Method | Parameters | Returns | Description |
188
+ |--------|-----------|---------|-------------|
189
+ | `download_all(show_progress)` | `show_progress: bool = True` | `LocalRunCollection` | Download all runs from all experiments in collection |
190
+
191
+ ### LocalRunCollection
192
+
193
+ A list-like collection of `LocalRun` objects with aggregation operations.
194
+
195
+ | Property/Method | Returns | Description |
196
+ |---------|---------|-------------|
197
+ | `dataset` | `DataFrame` | Cached property: stacked signal data from all runs |
198
+ | `load_dataset(use_metadata_channel_names, force_reload)` | `DataFrame` | Load and concatenate all run datasets with optional cache control |
199
+ | `profile_dataset_load(use_metadata_channel_names, force_reload)` | `dict` | Load all datasets while measuring timing, row/column counts per run |
200
+
201
+ ### AnalysisSession
202
+
203
+ Local writable workspace for reproducible analysis across downloaded runs.
204
+
205
+ | Method | Parameters | Returns | Description |
206
+ |--------|-----------|---------|-------------|
207
+ | `create(name, runs, base_dir)` | `name: str`, `runs: Iterable[LocalRun]`, `base_dir: str \| Path` | `AnalysisSession` | Create a new analysis session workspace with run data |
208
+ | `open(session_path)` | `session_path: str \| Path` | `AnalysisSession` | Open an existing session from disk |
209
+ | `list(base_dir)` | `base_dir: str \| Path` | `list[AnalysisSession]` | List all valid sessions under a directory |
210
+ | `load_signals(exp_id, run_id, use_metadata_channel_names)` | `exp_id: str`, `run_id: str`, `use_metadata_channel_names: bool = False` | `DataFrame` | Load signal data from session workspace |
211
+ | `load_images(exp_id, run_id)` | `exp_id: str`, `run_id: str` | `list[tuple[dict, Image]]` | Load image artifacts from session workspace |
212
+ | `delete()` | — | — | Delete the session directory from disk |
213
+
214
+ ### Session Properties
215
+
216
+ | Property | Type | Description |
217
+ |----------|------|-------------|
218
+ | `name` | `str` | Session name |
219
+ | `path` | `Path` | Local filesystem path to session directory |
220
+ | `manifest` | `dict` | Session metadata manifest (JSON-serializable dict with run info) |
221
+
222
+ ## Notes
223
+
224
+ - Signal data is returned as pandas DataFrames
225
+ - Image artifacts are returned as Pillow images
226
+ - Downloads are cached locally under the SDK cache directory
227
+ - Analysis sessions store a lightweight manifest so downloaded runs can be reopened consistently
228
+ - The SDK is intentionally read-oriented and centered on analysis and downstream application workflows
229
+
230
+ ## Validation Notebook
231
+
232
+ The repository includes a smoke-test notebook that exercises the main SDK flow from authentication through dataset loading, manifest decoding, image rendering, and bulk download.
@@ -0,0 +1,32 @@
1
+ from . import experiment, session
2
+ from .api import Client
3
+ from .experiment import Experiment, ExperimentCollection, LocalRun, LocalRunCollection, Run
4
+ from .session import AnalysisSession
5
+ from .version import __version__
6
+ from .errors import (
7
+ AuthError,
8
+ DownloadError,
9
+ NeurabridgeError,
10
+ NotFoundError,
11
+ PermissionError,
12
+ SessionError,
13
+ )
14
+
15
+ __all__ = [
16
+ "__version__",
17
+ "experiment",
18
+ "session",
19
+ "Client",
20
+ "AnalysisSession",
21
+ "Experiment",
22
+ "ExperimentCollection",
23
+ "Run",
24
+ "LocalRun",
25
+ "LocalRunCollection",
26
+ "NeurabridgeError",
27
+ "AuthError",
28
+ "PermissionError",
29
+ "NotFoundError",
30
+ "DownloadError",
31
+ "SessionError",
32
+ ]
@@ -0,0 +1,3 @@
1
+ from .client import Client
2
+
3
+ __all__ = ["Client"]
@@ -0,0 +1,91 @@
1
+ from __future__ import annotations
2
+
3
+ from pathlib import Path
4
+
5
+ import httpx
6
+
7
+ from ..experiment.catalog import delete_experiment, delete_run, list_experiments, list_runs
8
+ from ..experiment.downloads import download_run
9
+ from ..experiment.models import ExperimentCollection, LocalRun, LocalRunCollection, Run
10
+ from .transport import ApiTransport
11
+
12
+
13
+ class Client:
14
+ def __init__(
15
+ self,
16
+ *,
17
+ token: str | None = None,
18
+ base_url: str | None = None,
19
+ cache_dir: str | Path | None = None,
20
+ timeout: float = 30.0,
21
+ max_retries: int = 2,
22
+ retry_backoff_seconds: float = 0.3,
23
+ transport: httpx.BaseTransport | None = None,
24
+ download_transport: httpx.BaseTransport | None = None,
25
+ ) -> None:
26
+ self._transport = ApiTransport(
27
+ token=token,
28
+ base_url=base_url,
29
+ cache_dir=cache_dir,
30
+ timeout=timeout,
31
+ max_retries=max_retries,
32
+ retry_backoff_seconds=retry_backoff_seconds,
33
+ transport=transport,
34
+ download_transport=download_transport,
35
+ )
36
+
37
+ @property
38
+ def base_url(self) -> str:
39
+ return self._transport.base_url
40
+
41
+ @property
42
+ def token(self) -> str:
43
+ return self._transport.token
44
+
45
+ @property
46
+ def timeout(self) -> float:
47
+ return self._transport.timeout
48
+
49
+ @property
50
+ def max_retries(self) -> int:
51
+ return self._transport.max_retries
52
+
53
+ @property
54
+ def retry_backoff_seconds(self) -> float:
55
+ return self._transport.retry_backoff_seconds
56
+
57
+ @property
58
+ def cache_dir(self) -> Path:
59
+ return self._transport.cache_dir
60
+
61
+ def close(self) -> None:
62
+ self._transport.close()
63
+
64
+ def __enter__(self) -> "Client":
65
+ return self
66
+
67
+ def __exit__(self, exc_type: object, exc: object, tb: object) -> None:
68
+ self.close()
69
+
70
+ def whoami(self) -> dict[str, object]:
71
+ data = self._transport.request_json("GET", "/v1/whoami")
72
+ user = data.get("user", data)
73
+ return user if isinstance(user, dict) else {"user": user}
74
+
75
+ def experiments(self) -> ExperimentCollection:
76
+ return list_experiments(self)
77
+
78
+ def runs(self, exp_id: str) -> list[Run]:
79
+ return list_runs(self, exp_id)
80
+
81
+ def download(self, exp_id: str, run_id: str, *, show_progress: bool = True) -> LocalRun:
82
+ return download_run(self, exp_id=exp_id, run_id=run_id, show_progress=show_progress)
83
+
84
+ def download_all(self, *, show_progress: bool = True) -> LocalRunCollection:
85
+ return self.experiments().download_all(show_progress=show_progress)
86
+
87
+ def delete_run(self, exp_id: str, run_id: str) -> int:
88
+ return delete_run(self, exp_id, run_id)
89
+
90
+ def delete_experiment(self, exp_id: str) -> int:
91
+ return delete_experiment(self, exp_id)