Metadata-Version: 2.5
Name: senselab
Version: 1.3.1a42
Summary: senselab is a Python package that simplifies building pipelines for speech and voice analysis.
Project-URL: Homepage, https://github.com/sensein/senselab
Project-URL: Repository, https://github.com/sensein/senselab
Project-URL: Documentation, https://sensein.group/senselab
Author-email: Fabio Catania <fabiocat@mit.edu>, Satra Ghosh <satra@mit.edu>
Maintainer-email: Fabio Catania <fabiocat@mit.edu>, Satra Ghosh <satra@mit.edu>, Jordan Wilke <wilke18@mit.edu>
License: # LICENSE
        
        This project is licensed under the **Apache License, Version 2.0**.
        
        Some **optional** components depend on third-party packages that are licensed under terms other than Apache-2.0. If you install, enable, or distribute the project together with those optional components, your use and distribution of the combined work must also comply with the license obligations of those third-party components.
        
        See the **Third-party licenses** section below for details and references.
        
        
        ## Third-party licenses
        
        
        | Dependency                 | License                                                                                     |
        | -------------------------- | ------------------------------------------------------------------------------------------- |
        | accelerate                 | [Apache-2.0](https://github.com/huggingface/accelerate/blob/main/LICENSE)                   |
        | audiomentations            | [MIT](https://github.com/iver56/audiomentations/blob/master/LICENSE)                        |
        | av                         | [BSD-3-Clause](https://github.com/PyAV-Org/PyAV/blob/main/LICENSE.txt)                      |
        | coqui-tts                  | [MPL-2.0](https://github.com/idiap/coqui-ai-TTS/blob/dev/LICENSE.txt)                       |
        | datasets                   | [Apache-2.0](https://github.com/huggingface/datasets/blob/main/LICENSE)                     |
        | huggingface-hub            | [Apache-2.0](https://github.com/huggingface/huggingface_hub/blob/main/LICENSE)              |
        | ipykernel                  | [BSD-3-Clause](https://github.com/ipython/ipykernel/blob/main/LICENSE)                      |
        | ipywidgets                 | [BSD-3-Clause](https://github.com/jupyter-widgets/ipywidgets/blob/main/LICENSE)             |
        | iso639                     | [MIT](https://github.com/janpipek/iso639-python/blob/master/LICENSE)                        |
        | jiwer                      | [Apache-2.0](https://github.com/jitsi/jiwer/blob/master/LICENSE)                            |
        | joblib                      | [BSD-3-Clause](https://github.com/joblib/joblib/blob/main/LICENSE.txt)                            |
        | matplotlib                 | [CUSTOM](https://github.com/matplotlib/matplotlib/blob/main/LICENSE)                        |
        | mediapipe                  | [Apache-2.0](https://github.com/google-ai-edge/mediapipe/blob/master/LICENSE)                        |
        | nbformat                   | [BSD-3-Clause](https://github.com/jupyter/nbformat/blob/main/LICENSE)                       |
        | nbss-upload                | [BSD-3-Clause](https://github.com/notebook-sharing-space/nbss-upload/blob/main/LICENSE)     |
        | nemo-toolkit               | [Apache-2.0](https://github.com/NVIDIA/NeMo/blob/main/LICENSE)                              |
        | nest-asyncio               | [BSD-2-Clause](https://github.com/erdewit/nest_asyncio/blob/master/LICENSE)                 |
        | nltk                       | [Apache-2.0](https://github.com/nltk/nltk/blob/develop/LICENSE.txt)                         |
        | notebook-intelligence      | [GPL-3.0](https://github.com/notebook-intelligence/notebook-intelligence/blob/main/LICENSE) |
        | opencv-python-headless     | [MIT](https://github.com/opencv/opencv-python/blob/4.x/LICENSE.txt)                         |
        | opensmile                  | [CUSTOM](https://github.com/audeering/opensmile/blob/master/LICENSE)                        |
        | praat-parselmouth          | [GPL-3.0](https://github.com/YannickJadoul/Parselmouth/blob/master/LICENSE)                 |
        | pyannote-audio             | [MIT](https://github.com/pyannote/pyannote-audio/blob/develop/LICENSE)                      |
        | pycountry                  | [LGPL-2.1](https://github.com/pycountry/pycountry/blob/main/LICENSE.txt)                    |
        | pydantic                   | [MIT](https://github.com/pydantic/pydantic/blob/main/LICENSE)                               |
        | pylangacq                  | [MIT](https://github.com/jacksonllee/pylangacq/blob/main/LICENSE.txt)                       |
        | python-ffmpeg              | [CUSTOM](https://github.com/jonghwanhyeon/python-ffmpeg/blob/main/LICENSE)                  |
        | scikit-learn               | [BSD-3-Clause](https://github.com/scikit-learn/scikit-learn/blob/main/COPYING)              |
        | sentence-transformers      | [Apache-2.0](https://github.com/UKPLab/sentence-transformers/blob/master/LICENSE)           |
        | speech-articulatory-coding | Not defined                                                                                 |
        | speechbrain                | [Apache-2.0](https://github.com/speechbrain/speechbrain/blob/develop/LICENSE)               |
        | torch                      | [CUSTOM](https://github.com/pytorch/pytorch/blob/main/LICENSE)                              |
        | torch-audiomentations      | [MIT](https://github.com/asteroid-team/torch-audiomentations/blob/master/LICENSE)           |
        | torchaudio                 | [BSD-2-Clause](https://github.com/pytorch/audio/blob/main/LICENSE)                          |
        | torchvision                | [BSD-3-Clause](https://github.com/pytorch/vision/blob/main/LICENSE)                         |
        | transformers               | [Apache-2.0](https://github.com/huggingface/transformers/blob/main/LICENSE)                 |
        | types-requests             | [Apache-2.0](https://github.com/python/typeshed/blob/main/LICENSE)                          |
        | ultralytics                | [AGPL-3.0](https://github.com/ultralytics/ultralytics/blob/main/LICENSE)                    |
        | umap-learn                 | [BSD-3-Clause](https://github.com/lmcinnes/umap/blob/master/LICENSE.txt)                    |
        | vocos                      | [MIT](https://github.com/gemelo-ai/vocos/blob/main/LICENSE)                                 |
        
        
        For clarity:
        
        * The **core** of this project is licensed under **Apache-2.0**.
        * **Optional** functionality depending on:
          - **`notebook-intelligence`** (GPL-3.0) and **`praat-parselmouth`** (GPL-3.0)
            → Using or distributing the project together with these components subjects the combined distribution to GPL-3.0 obligations.
          - **`ultralytics`** (AGPL-3.0)
            → Using or distributing the project together with this component subjects the combined distribution to AGPL-3.0 obligations.
          - **`pycountry`** (LGPL-2.1)
            → The project may depend dynamically on this component while remaining Apache-2.0; however, modifications or static incorporation are subject to LGPL-2.1 obligations.
          - **`coqui-tts`** (MPL-2.0)
            → Modifications to MPL-licensed files must be released under MPL-2.0, though the rest of the project remains Apache-2.0.
          - **`opensmile`** (audEERING Research License)
            → Licensed for non-commercial use only (academic research, teaching, evaluation).
              Any use in products or other commercial applications requires a separate commercial license from audEERING GmbH.
        * If you do **not** install or distribute the GPL, AGPL, LGPL, MPL, or custom-licensed optional components, your use of this project remains under the **Apache-2.0 License** only.
        
        **Note:** If you are uncertain about your rights or obligations under these licenses, you should seek advice from a qualified legal professional.
        
        ---
        
        ## Apache License, Version 2.0
        
        Apache License
        Version 2.0, January 2004
        http://www.apache.org/licenses/
        
        TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
        
        1. Definitions.
        
        "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.
        
        "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.
        
        "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
        
        "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License.
        
        "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.
        
        "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.
        
        "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).
        
        "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.
        
        "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution."
        
        "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.
        
        2. Grant of Copyright License.
        
        Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.
        
        3. Grant of Patent License.
        
        Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.
        
        4. Redistribution.
        
        You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:
        
            You must give any other recipients of the Work or Derivative Works a copy of this License; and
            You must cause any modified files to carry prominent notices stating that You changed the files; and
            You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and
            If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License.
        
        You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.
        
        5. Submission of Contributions.
        
        Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.
        
        6. Trademarks.
        
        This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.
        
        7. Disclaimer of Warranty.
        
        Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.
        
        8. Limitation of Liability.
        
        In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.
        
        9. Accepting Warranty or Additional Liability.
        
        While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.
        
        END OF TERMS AND CONDITIONS
License-File: LICENSE.md
Keywords: audio,face,nlp,pose,speech,text,video,voice
Classifier: Development Status :: 3 - Alpha
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Requires-Python: <3.15,>=3.11
Requires-Dist: accelerate
Requires-Dist: audiomentations>=0.42
Requires-Dist: datasets>=3.6
Requires-Dist: huggingface-hub>=1.3
Requires-Dist: iso639>=0.1
Requires-Dist: librosa>=0.11
Requires-Dist: nest-asyncio>=1.6
Requires-Dist: opensmile>=2.6
Requires-Dist: peft>=0.13
Requires-Dist: praat-parselmouth>=0.4.3
Requires-Dist: pyannote-audio>=4.0
Requires-Dist: pycountry>=24.6
Requires-Dist: pydantic>=2.11
Requires-Dist: pyloudnorm>=0.1.1
Requires-Dist: python-dotenv>=1.0
Requires-Dist: scikit-learn>=1.7
Requires-Dist: speechbrain>=1.0
Requires-Dist: torch>=2.8; sys_platform != 'darwin' or platform_machine == 'arm64'
Requires-Dist: torchaudio>=2.8
Requires-Dist: torchcodec>=0.7
Requires-Dist: torchvision>=0.23
Requires-Dist: transformers>=5.3
Requires-Dist: umap-learn>=0.5.4
Requires-Dist: vocos>=0.1
Provides-Extra: all
Requires-Dist: av>=15; extra == 'all'
Requires-Dist: g2p-en>=2.1; extra == 'all'
Requires-Dist: ipykernel~=7.1; extra == 'all'
Requires-Dist: ipywidgets~=8.1; extra == 'all'
Requires-Dist: jiwer>=3.0; extra == 'all'
Requires-Dist: jupyterlab~=4.4; extra == 'all'
Requires-Dist: nbformat~=5.10; extra == 'all'
Requires-Dist: nbss-upload~=0.1; extra == 'all'
Requires-Dist: nltk>=3.9; extra == 'all'
Requires-Dist: notebook-intelligence~=2.3.2; extra == 'all'
Requires-Dist: opencv-python-headless>=4.11; extra == 'all'
Requires-Dist: pylangacq>=0.20; extra == 'all'
Requires-Dist: sentence-transformers>=5.1; extra == 'all'
Requires-Dist: ultralytics>=8.3; extra == 'all'
Requires-Dist: uroman>=1.3; extra == 'all'
Provides-Extra: nlp
Requires-Dist: g2p-en>=2.1; extra == 'nlp'
Requires-Dist: jiwer>=3.0; extra == 'nlp'
Requires-Dist: nltk>=3.9; extra == 'nlp'
Requires-Dist: uroman>=1.3; extra == 'nlp'
Provides-Extra: senselab-ai
Requires-Dist: ipykernel~=7.1; extra == 'senselab-ai'
Requires-Dist: ipywidgets~=8.1; extra == 'senselab-ai'
Requires-Dist: jupyterlab~=4.4; extra == 'senselab-ai'
Requires-Dist: nbformat~=5.10; extra == 'senselab-ai'
Requires-Dist: nbss-upload~=0.1; extra == 'senselab-ai'
Requires-Dist: notebook-intelligence~=2.3.2; extra == 'senselab-ai'
Provides-Extra: text
Requires-Dist: pylangacq>=0.20; extra == 'text'
Requires-Dist: sentence-transformers>=5.1; extra == 'text'
Provides-Extra: video
Requires-Dist: av>=15; extra == 'video'
Requires-Dist: opencv-python-headless>=4.11; extra == 'video'
Requires-Dist: ultralytics>=8.3; extra == 'video'
Description-Content-Type: text/markdown

[![Build](https://github.com/sensein/senselab/actions/workflows/main-branch-status.yaml/badge.svg)](https://github.com/sensein/senselab/actions/workflows/main-branch-status.yaml)
[![codecov](https://codecov.io/gh/sensein/senselab/graph/badge.svg?token=9S8WY128PO)](https://codecov.io/gh/sensein/senselab)
[![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)

[![PyPI](https://img.shields.io/pypi/v/senselab.svg)](https://pypi.org/project/senselab/)
[![Python Version](https://img.shields.io/pypi/pyversions/senselab)](https://pypi.org/project/senselab)
[![License](https://img.shields.io/pypi/l/senselab)](https://opensource.org/licenses/Apache-2.0)

[![pages](https://img.shields.io/badge/api-docs-blue)](https://sensein.group/senselab)
[![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/sensein/senselab)

# ```senselab```
This Python package **streamlines**, **optimizes**, and **enforces best open-science practices** for processing and analyzing _behavioral data_ (primarily voice and speech, but also text and video) using robust reproducible pipelines and utilities.

## Quick start
```Python
from senselab.audio.data_structures import Audio
from senselab.audio.tasks.preprocessing import resample_audios
from senselab.audio.tasks.features_extraction import extract_features_from_audios
from senselab.audio.tasks.speech_to_text import transcribe_audios

audio = Audio(filepath='path_to_audio_file.wav')
print(audio.sampling_rate)
# ➡️ 44100

[resampled_audio] = resample_audios([audio], resample_rate=16000)
print(resampled_audio.sampling_rate)
# ➡️ 16000

audio_features = extract_features_from_audios([audio])
print(audio_features[0].keys())
# ➡️ dict_keys(['opensmile', 'praat_parselmouth', 'torchaudio', 'torchaudio_squim', ...])

transcript = transcribe_audios([audio])
print(transcript)
# ➡️ "The quick brown fox jumps over the lazy dog."
```

For more detailed information, check out our [**Documentation**](https://sensein.group/senselab) and our [**Tutorials**](https://github.com/sensein/senselab/blob/main/tutorials/audio/00_getting_started.ipynb).

💡 **Tip**: Many tutorials include Google Colab badges and you can try them instantly without installing anything on your local machine.



### Why should you use ```senselab```?
- **Modular design**: Easily integrate or use standalone transformations for flexible data manipulation.
- **Pre-built pipelines**: Access pre-configured pipelines to reduce setup time and effort.
- **Reproducibility**: Ensure consistent and verifiable results with fixed seeds and version-controlled steps.
- **Easy integration**: Seamlessly fit into existing workflows with minimal configuration.
- **Extensible**: Modify and contribute custom transformations and pipelines to meet specific research needs.
- **Comprehensive documentation**: Detailed guides, examples, and documentation for all features and modules.
- **Performance optimized**: Efficiently process large datasets with optimized code and algorithms.
- **Interactive examples**: Jupyter notebooks provide practical examples for deriving insights from real-world datasets.
- **senselab AI**: Interact with your data through an AI-based chatbot. The AI agent generates and runs senselab-based code for you, making exploration easier and giving you both the results and the code used to produce them (perfect for quick experiments or for users who prefer not to code).


---

## Adaptive audio analysis (uncertainty-driven)

senselab can analyze a recording with its full task suite (diarization, scene/event tagging, quality
metrics, multi-model ASR + forced alignment, speaker embeddings), quantify **where** the models are
uncertain along three temporal axes — *presence* (is someone speaking?), *identity* (who?), and
*utterance* (what was said?) — and then **act on that uncertainty**: a deterministic, budgeted loop
re-processes only the uncertain regions (extra ASR models, embedding re-clustering, overlap
detection), fuses a consensus transcript/diarization, and explains any residual uncertainty instead
of hiding it. Design + results: `specs/20260723-225523-dynamic-uncertainty-workflow/`.

It runs in two steps:

```bash
# Step 1 — analyze: run every model on the recording (results are content-addressably
# cached, so re-runs are cheap). Two arguments: the audio, and where results go.
uv run python scripts/analyze_audio.py path/to/recording.wav
# → artifacts/analyze_audio/<name>_<timestamp>/  (L1 per-signal parquets, L2 fused
#   axes, Label Studio bundle, disagreements.json, final/ deliverables)

# Step 2 — adapt: run the uncertainty-driven loop over that run directory.
uv run python scripts/adaptive_loop.py artifacts/analyze_audio/<run_dir> \
    --cache-dir artifacts/analyze_audio_cache \
    --ground-truth path/to/labelstudio_export.json   # optional: scores vs human labels
```

**`analyze_audio.py` takes an audio file and `--out`, and nothing else.** Every other value — the
model ids, the bucket grid, the aggregator, the task type, the triage and enhancement gates, which
stages run — lives in one versioned file with its derivation recorded beside it:
`src/senselab/audio/workflows/audio_analysis/data/run_config/default.yaml`. To change something,
write a YAML holding only the keys you are changing and pass `--config my.yaml`; it deep-merges over
the packaged one, and the merged mapping's hash is stamped into every artifact's provenance, so a run
can always be named. There are deliberately no per-knob flags: the seventy that preceded this
differed in ways a reader had no basis to choose between, and the *shipped defaults* of the four grid
flags put the four uncertainty axes on four spacings that shared no bucket keys — silently disabling
every cross-axis coupling in the pipeline.

The loop writes, under the run directory (or `--out`):

- `final/transcript.json` — consensus word-level transcript (family-weighted voting across all ASR
  models) with speaker attribution, per-word confidence, and alternates where models disagree;
- `final/diarization.json` — refined speaker segments (embedding change-point + re-clustering
  repair);
- `final/estimates/<axis>.parquet` — the last round's estimate of every active axis, extracted
  verbatim from `L2/round/<last>/estimates/`. A number in `final/` that is not in the last round
  was computed at the wrong stage, so this directory only copies;
- `final/speakers.json` + `final/per_speaker_presence.parquet` — the speaker-count posterior with
  its per-speaker hypotheses, and one presence track per hypothesised speaker;
- `final/decisions.json` — what the loop did and why: every intervention (fired / deferred /
  blocked) with trigger values and measured uncertainty deltas, budget accounting, and regions
  marked `converged` / `irreducible` (with a machine-readable reason). Each round's own slice of
  this is in its `L2/round/<n>/summary.json`;
- `final/timeline.png` — ground truth (if given) vs presence / identity / utterance uncertainty per
  round, interventions, and the confidence-colored fused words;
- `final/labelstudio_{tasks,config}.{json,xml}` + `disagreements_resolved.json` — the original Label
  Studio bundle with `final__*` consensus tracks added, and the round-1 disagreements annotated with
  their resolutions.

All thresholds, budgets and model pools live in the `adaptive:` section of that same run config —
round count, aggregator, per-run intervention budgets, ASR reserve/escalation pools, identity-repair
parameters — and it keeps its own `policy_hash` beside the config's `config_hash`, because a policy
change and a model change are not the same event. `scripts/adaptive_loop.py` takes `--config` too.
Runs are **deterministic**: identical inputs + config produce byte-identical decision logs. `HF_TOKEN` enables the gated
`pyannote/segmentation-3.0` overlap detector; without it the loop degrades gracefully and records
the skipped intervention in `final/decisions.json → convergence.next_actions`.

---

## Background scene characterization

Detects background sound sources — people, machines, environment — beneath a
near-microphone foreground speaker, and reports **how far above the noise floor** each one
sits so a marginal finding is never mistaken for a confident one.

```bash
# `task.type` in the run config selects what counts as the participant's own activity.
uv run python scripts/analyze_audio.py recording.wav
```

Three things worth knowing before reading the output:

**Detection is floor subtraction, not amplification.** Amplification moves a source and the
leaked foreground together, so it changes no signal-to-noise ratio. It is capped at 10 dB
and used only to keep a classifier's absolute floor from destroying quiet content.

**Every finding carries its margin above the band noise floor**, on a 3 / 6 / 10 dB ladder
corroborated independently by human masked-threshold criteria, a dozen bioacoustics and
noise-standard traditions, and the classifiers' own measured detection floors.

**A null result is attributable.** Suppression depth is reported alongside, so "no
background found" is distinguishable from "suppression was too shallow to look".

The background mask marks where claims are trustworthy without relying on suppression at
all. `task.type` matters: in a breathing or cough task the target event *is* a non-speech
vocal sound, and a mask built from voice activity alone would report the collected signal as
a background source.

## ⚠️ System Requirements
1. **If on macOS, this package requires an ARM64 architecture** due to PyTorch 2.2.2+ dropping support for x86-64 on macOS.

    ❌ Unsupported systems include:
    - macOS (Intel x86-64)
    - Other platforms where dependencies are unavailable

    To check your system compatibility, please run this command:
    ```bash
    python -c "import platform; print(platform.machine())"
    ```

    If the output is:
    - `arm64` → ✅ Your system is compatible.
    - `x86_64` → ❌ Your system is not supported.

    If you attempt to install this package on an unsupported system, the installation or execution will fail.

2. **`FFmpeg` shared libraries** are required. The consumer is `torchcodec`, which `dlopen`s them at
   import time *by soname* (`libavutil.so.56` / `.57` / `.58` / `.59`, one attempt per supported major).
   Two consequences worth knowing before you debug this:

   - **The `av` (PyAV) wheel does not satisfy it**, even though it ships ffmpeg libraries inside your
     environment. PyAV mangles their filenames on purpose (`av.libs/libavutil-3591eddc.so.60.8.100`) so
     they cannot collide with a system ffmpeg, which also makes them invisible to a soname lookup.
   - **Without them, no test collects at all.** `src/tests/conftest.py` reports
     `Dependencies failed to import — test environment is broken`, including for tests that never open
     an audio file.

   If you have no system ffmpeg, or no root, this repo installs it for you via conda-forge into a
   prefix you choose:

   ```bash
   bash scripts/install-ffmpeg.sh                       # defaults to /opt/miniforge
   CONDA_PREFIX=~/ffmpeg bash scripts/install-ffmpeg.sh # anywhere writable
   export LD_LIBRARY_PATH="$CONDA_PREFIX/lib:$LD_LIBRARY_PATH"    # macOS: DYLD_LIBRARY_PATH
   ```

   This is what CI uses on every platform. Otherwise install ffmpeg (`<8`) system-wide — see
   [ffmpeg.org](https://www.ffmpeg.org/download.html).

3. CUDA libraries matching the CUDA version expected by the PyTorch wheels (e.g., the latest pytorch 2.8 expects cuda-12.8). To install those with conda, please do:
  - ```conda config --add channels nvidia```
  - ```conda install -y nvidia/label/cuda-12.8.1::cuda-libraries-dev```

    **Hosts with newer system CUDA** (e.g., CUDA 12.9): the subprocess-venv backends (`nemo-canary-qwen`, `nemo`, `qwen-asr`) auto-detect the host's CUDA version via `nvidia-smi` and route their `torch`/`torchaudio` installs through the matching PyTorch wheel index (`cu128` / `cu126` / `cu124` / `cu121` / `cpu`). No manual configuration needed.

    **Internal mirrors / unsupported CUDA / CPU fallback**: set the `SENSELAB_TORCH_INDEX_URL` environment variable to override the chosen index. Common values:

    ```bash
    # Force CPU wheels (e.g. testing CPU path on a GPU host, or unsupported CUDA)
    export SENSELAB_TORCH_INDEX_URL=https://download.pytorch.org/whl/cpu

    # Internal PyPI mirror that proxies PyTorch wheels
    export SENSELAB_TORCH_INDEX_URL=https://pypi.internal.example.com/pytorch/cu128
    ```

    When no compatible `torch`+`torchaudio` binary pair exists for your host (rare; happens in the days after a CUDA major release), installation fails with a named `SenselabCudaCompatibilityError` that lists the detected host CUDA, the attempted index URL, and the recommended action — no opaque stack traces from inside `torchaudio`.
4. Docker is required and must be running for some video models (e.g., MediaPipe-based estimators).
Please follow the official installation instructions for your platform: [Install Docker](https://docs.docker.com/get-started/get-docker/).
5. Some functionalities rely on HuggingFace models, and increasingly, models require authentication and signed license agreements. Instructions on how to generate a Hugging Face access token can be found here: https://huggingface.co/docs/hub/security-tokens
  - You can provide your HuggingFace token either by exporting it in your shell:
    ```bash
    export HF_TOKEN=your_token_here
    ```
  - or by adding it to your `.env` file (see `.env.example` for reference).

---

## Installation

**Python 3.11–3.14**, declared as `>=3.11,<3.15` in `pyproject.toml`. For development the repo pins
the interpreter in `.python-version` (**3.12**, matching CI's default), which `uv` reads, so a bare
`uv sync` is deterministic.

Install this package via:

```sh
pip install 'senselab[all]'
```

Or get the newest development version via:

```sh
pip install 'git+https://github.com/sensein/senselab.git#egg=senselab[all]'
```

If you want to install only audio dependencies, you do:
```sh
pip install 'senselab'
```

The declared extras are `nlp`, `text`, `video`, `senselab-ai`, and `all` (every one of them).
To pick a subset:
```sh
pip install 'senselab[video,text,senselab-ai]'
```

There is no `articulatory` extra — it was documented here and in `CONTRIBUTING.md` but never declared,
so `uv sync --extra articulatory` fails outright and `pip install 'senselab[articulatory]'` warns and
installs base only.

### Released vs pre-release

Merging a PR into `alpha` publishes an **alpha pre-release** automatically (`release.yaml` →
`auto shipit`); merging into `main` publishes a release. So there are two lines on PyPI, and `--pre`
is how you choose:

```sh
pip install 'senselab[all]'          # the released line (currently 1.3.0)
pip install --pre 'senselab[all]'    # the newest alpha from the alpha branch (1.3.1aN)
```

This is why every tutorial carries `--pre` — notebooks track the alpha branch:

```python
!pip install -q uv
!uv pip install --pre --system "senselab[nlp,text,video]"
```

Colab images happen to ship `ffmpeg`, so notebooks work there without installing it. The guarded
fallback for images that do not — and the `HF_TOKEN`-from-Colab-secrets snippet — is the setup-cell
template in [`tutorials/README.md`](tutorials/README.md).

**None of this applies to development.** `--pre` installs a *published artifact*; a developer wants
the working tree. See [Development](#development) below, which builds from source and never fetches
`senselab` from PyPI.

---

## Development

Three steps, and the second is the one people miss:

Development installs **from source** — the checkout you are standing in. `uv sync` puts the working
tree in the environment, so an edit is live with no reinstall; nothing here fetches `senselab` from
PyPI, and `--pre` has no role. (The version you will see, `1.3.1aN.devM`, comes from `hatch-vcs`
reading git describe, which is also why a shallow clone with no tags reports a wrong version.)

```bash
# 1. Environment. --all-extras is what every CI workflow uses: it cannot go stale when an
#    extra is added, which `--extra all` can. The interpreter comes from .python-version
#    (3.12, matching CI's default), so no --python flag.
uv sync --all-extras --group dev --group docs

# 2. FFmpeg shared libraries for torchcodec. Skip this and NOTHING collects —
#    conftest.py aborts with "Dependencies failed to import", even for tests that
#    never open an audio file. See System Requirements above for why the PyAV wheel
#    does not cover it.
bash scripts/install-ffmpeg.sh
export LD_LIBRARY_PATH="/opt/miniforge/lib:$LD_LIBRARY_PATH"   # macOS: DYLD_LIBRARY_PATH

# 3. Hooks, required before committing.
uv run pre-commit install
```

Then:

```bash
uv run pytest                                    # everything, with coverage
uv run pytest src/tests/audio/tasks/preprocessing_test.py          # one file
uv run pytest src/tests/audio/tasks/preprocessing_test.py::test_x  # one test
uv run mypy .
uv run ruff check          # --fix to autofix
uv run ruff format
uv run codespell
```

**On `pytest -n auto`.** It is tempting and it is a memory hazard: `pytest-xdist` gives each worker its
own interpreter, and each one imports torch + transformers + speechbrain independently — measured at
**535 MB resident per worker before a single test runs**, plus a private copy of any model weights that
worker's tests load. On a 10-core / 32 GB laptop `-n auto` has exhausted memory. Prefer running the
directory you changed, or cap the workers (`-n 4`). The pure-Python workflow tests are fast serially:
`uv run pytest src/tests/audio/workflows/audio_analysis` is ~1400 tests in about 17 s.

Docs build locally with:

```bash
uv run pdoc src/senselab -t docs_style/pdoc-theme --docformat google
```

---

## senselab AI (our AI-based chatbot)

#### Development (with uv)

```bash
uv sync --extra senselab-ai
uv run senselab-ai
```

#### Production (with pip)

```bash
pip install 'senselab[senselab-ai]'
senselab-ai
```

Once started, you can open the provided JupyterLab interface, setup the agent and chat with it, and let it create and execute code for you.

![Example of how senselab-ai works](<tutorials/senselab-ai/resources/Screenshot 2025-09-02 at 8.52.31 PM.png>)

For a walkthrough, see: [`tutorials/senselab-ai/senselab_ai_intro.ipynb`](tutorials/senselab-ai/senselab_ai_intro.ipynb).


---

## Contributing
<ins>We welcome contributions from the community!</ins> Before proceeding with that, please review our [**CONTRIBUTING.md**](https://github.com/sensein/senselab/blob/main/CONTRIBUTING.md).

---

## Funding
`senselab` is mostly supported by the following organizations and initiatives:
- McGovern Institute ICON Fellowship
- NIH Bridge2AI Precision Public Health (OT2OD032720)
- Child Mind Institute
- ReadNet Project
- Chris and Lann Woehrle Psychiatric Fund

---

## Acknowledgments

`senselab` builds on the work of many open-source projects. We gratefully acknowledge the developers and maintainers of the following key dependencies:

* [PyTorch](https://github.com/pytorch/pytorch), [Torchvision](https://github.com/pytorch/vision), [Torchaudio](https://github.com/pytorch/audio)
_deep learning framework and audio/vision extensions_
* [Transformers](https://github.com/huggingface/transformers), [Datasets](https://github.com/huggingface/datasets), [Accelerate](https://github.com/huggingface/accelerate), [Huggingface Hub](https://github.com/huggingface/huggingface_hub)
_training and inference utilities plus (pre-)trained models and datasets_
* [Scikit-learn](https://github.com/scikit-learn/scikit-learn), [UMAP-learn](https://github.com/lmcinnes/umap)
_machine learning utilities_
* [Matplotlib](https://github.com/matplotlib/matplotlib)
_visualization toolkit_
* [Praat-Parselmouth](https://github.com/YannickJadoul/Parselmouth), [OpenSMILE](https://github.com/audeering/opensmile), [SpeechBrain](https://github.com/speechbrain/speechbrain), [SPARC](speech-articulatory-coding), [Pyannote-audio](https://github.com/pyannote/pyannote-audio), [Coqui-TTS](https://github.com/idiap/coqui-ai-TTS), [NVIDIA NeMo](https://github.com/NVIDIA/NeMo), [Vocos](https://github.com/gemelo-ai/vocos), [Audiomentations](https://github.com/iver56/audiomentations), [Torch-audiomentations](https://github.com/asteroid-team/torch-audiomentations)
_speech and audio processing tools_
* [NLTK](https://github.com/nltk/nltk), [Sentence-Transformers](https://github.com/UKPLab/sentence-transformers), [Pylangacq](https://github.com/jacksonllee/pylangacq), [Jiwer](https://github.com/jitsi/jiwer)
_text and language processing tools_
* [OpenCV](https://github.com/opencv/opencv-python), [Ultralytics](https://github.com/ultralytics/ultralytics), [mediapipe](https://github.com/google-ai-edge/mediapipe), [Python-ffmpeg](https://github.com/jonghwanhyeon/python-ffmpeg), [AV](https://github.com/PyAV-Org/PyAV)
_computer vision and pose estimation_
* [Pydantic](https://github.com/pydantic/pydantic), [Iso639](https://github.com/janpipek/iso639-python), [PyCountry](https://github.com/pycountry/pycountry), [Nest-asyncio](https://github.com/erdewit/nest_asyncio)
_validation, and utilities_
* [Ipywidgets](https://github.com/jupyter-widgets/ipywidgets), [IpKernel](https://github.com/ipython/ipykernel), [Nbformat](https://github.com/jupyter/nbformat), [Nbss-upload](https://github.com/notebook-sharing-space/nbss-upload), [Notebook-intelligence](https://github.com/notebook-intelligence/notebook-intelligence)
_Jupyter and notebook-related tools_

We are thankful to the open-source community for enabling this project! 🙏
