On-device emotion inference from biosignals (heart rate and RR intervals)
Project description
Synheart Emotion - Python SDK
On-device emotion inference from biosignals (heart rate and RR intervals) for Python applications.
Features
- Privacy-first: All processing happens on-device
- Real-time: <5ms inference latency
- Three emotion states: Amused, Calm, Stressed
- Sliding window: 60s window with 5s step (configurable)
- Python 3.8+: Modern Python with type hints
- Thread-safe: Concurrent data ingestion supported
- Zero dependencies: Core functionality requires only NumPy and Pandas
Installation
From PyPI (recommended)
pip install synheart-emotion
From source
git clone https://github.com/synheart-ai/synheart-emotion-python.git
cd synheart-emotion-python
pip install -e .
With optional ML dependencies
For advanced model loading (scikit-learn, XGBoost):
pip install synheart-emotion[ml]
Development installation
pip install synheart-emotion[dev]
Verify Installation
# Quick verification
python -c "from synheart_emotion import EmotionEngine, EmotionConfig; print('✓ Installation successful')"
# Run tests
pytest tests/
# Run examples
python examples/basic_usage.py
python examples/cli_demo.py --samples 15
Building from Source
# Install build tools
pip install build twine
# Build package
python -m build
# This creates:
# - dist/synheart_emotion-0.0.1.tar.gz (source distribution)
# - dist/synheart_emotion-0.0.1-py3-none-any.whl (wheel)
Troubleshooting
Import Error: Make sure the package is installed with pip list | grep synheart-emotion
Version Conflicts: Upgrade dependencies with pip install --upgrade numpy pandas
Missing Dependencies: Install all requirements with pip install -r requirements.txt
Quick Start
from datetime import datetime
from synheart_emotion import EmotionConfig, EmotionEngine
# Create engine with default configuration
config = EmotionConfig()
engine = EmotionEngine.from_pretrained(config)
# Push data from wearable
engine.push(
hr=72.0,
rr_intervals_ms=[850.0, 820.0, 830.0, 845.0, 825.0],
timestamp=datetime.now()
)
# Get inference result when ready
results = engine.consume_ready()
for result in results:
print(f"Emotion: {result.emotion}")
print(f"Confidence: {result.confidence:.1%}")
print(f"Probabilities: {result.probabilities}")
Examples
Basic Usage
from datetime import datetime
from synheart_emotion import EmotionConfig, EmotionEngine
# Initialize engine
config = EmotionConfig()
engine = EmotionEngine.from_pretrained(config)
# Simulate wearable data stream
hr_data = [72.0, 73.5, 71.8, 74.2, 72.5]
rr_data = [
[850.0, 820.0, 830.0, 845.0, 825.0],
[855.0, 815.0, 835.0, 840.0, 830.0],
# ... more data
]
# Push data
for hr, rr_intervals in zip(hr_data, rr_data):
engine.push(
hr=hr,
rr_intervals_ms=rr_intervals,
timestamp=datetime.now()
)
# Consume results
results = engine.consume_ready()
if results:
result = results[0]
print(f"Emotion: {result.emotion} ({result.confidence:.1%})")
See the examples/ directory for more comprehensive examples:
basic_usage.py- Simple emotion inferencecustom_config.py- Custom configuration and loggingstreaming_data.py- Continuous data stream simulation
Custom Configuration
config = EmotionConfig(
window_seconds=60.0, # 60 second window
step_seconds=5.0, # 5 second step
min_rr_count=30, # Minimum RR intervals
hr_baseline=65.0 # Personal HR baseline
)
Logging
def custom_logger(level, message, context):
print(f"[{level}] {message}")
engine = EmotionEngine.from_pretrained(
config=config,
on_log=custom_logger
)
Buffer Management
# Get buffer statistics
stats = engine.get_buffer_stats()
print(f"Data points: {stats['count']}")
print(f"Duration: {stats['duration_ms']}ms")
print(f"HR range: {stats['hr_range']}")
print(f"RR count: {stats['rr_count']}")
# Clear buffer
engine.clear()
API Reference
EmotionConfig
Configuration for the emotion inference engine.
@dataclass
class EmotionConfig:
model_id: str = "svm_linear_wrist_sdnn_v1_0"
window_seconds: float = 60.0
step_seconds: float = 5.0
min_rr_count: int = 30
return_all_probas: bool = True
hr_baseline: Optional[float] = None
priors: Optional[Dict[str, float]] = None
Attributes:
model_id- Model identifierwindow_seconds- Rolling window size (default: 60s)step_seconds- Emission cadence (default: 5s)min_rr_count- Minimum RR intervals required (default: 30)return_all_probas- Return all label probabilities (default: True)hr_baseline- Optional HR baseline for personalizationpriors- Optional label priors for calibration
EmotionEngine
Main emotion inference engine.
Class Methods:
@classmethod
def from_pretrained(
config: EmotionConfig,
model: Optional[LinearSvmModel] = None,
on_log: Optional[Callable] = None
) -> EmotionEngine
Create engine from pretrained model.
Instance Methods:
def push(
hr: float,
rr_intervals_ms: List[float],
timestamp: datetime,
motion: Optional[Dict[str, float]] = None
) -> None
Push new data point into the engine.
def consume_ready() -> List[EmotionResult]
Consume ready results (throttled by step interval).
def get_buffer_stats() -> Dict[str, Any]
Get current buffer statistics.
def clear() -> None
Clear all buffered data.
EmotionResult
Result of emotion inference.
@dataclass
class EmotionResult:
timestamp: datetime
emotion: str
confidence: float
probabilities: Dict[str, float]
features: Dict[str, float]
model: Dict[str, Any]
Attributes:
timestamp- Timestamp when inference was performedemotion- Predicted emotion label (top-1)confidence- Confidence score (0.0-1.0)probabilities- All label probabilitiesfeatures- Extracted features (hr_mean, sdnn, rmssd)model- Model metadata
Methods:
@classmethod
def from_inference(
timestamp: datetime,
probabilities: Dict[str, float],
features: Dict[str, float],
model: Dict[str, Any]
) -> EmotionResult
Create from raw inference data.
def to_dict() -> Dict[str, Any]
Convert to dictionary for JSON serialization.
FeatureExtractor
Static utility class for feature extraction.
class FeatureExtractor:
@staticmethod
def extract_hr_mean(hr_values: List[float]) -> float
@staticmethod
def extract_sdnn(rr_intervals_ms: List[float]) -> float
@staticmethod
def extract_rmssd(rr_intervals_ms: List[float]) -> float
@staticmethod
def extract_features(
hr_values: List[float],
rr_intervals_ms: List[float],
motion: Optional[Dict[str, float]] = None
) -> Dict[str, float]
EmotionError
Base exception class with subclasses:
TooFewRRError- Too few RR intervalsBadInputError- Invalid input dataModelIncompatibleError- Model incompatible with featuresFeatureExtractionError- Feature extraction failed
Running Examples
# Basic usage
python examples/basic_usage.py
# Custom configuration
python examples/custom_config.py
# Streaming data simulation
python examples/streaming_data.py
Requirements
- Python 3.8+
- NumPy >= 1.21.0
- Pandas >= 1.3.0
Optional (for ML model loading):
- scikit-learn >= 1.0.0
- joblib >= 1.1.0
- xgboost >= 1.5.0
Architecture
The package follows a modular architecture:
synheart_emotion/
├── __init__.py # Package exports
├── config.py # Configuration dataclass
├── engine.py # Main inference engine
├── error.py # Error classes
├── features.py # Feature extraction
├── models.py # Model classes
└── result.py # Result dataclass
Data Flow
- Push - Biosignal data (HR, RR intervals) pushed to engine
- Buffer - Data stored in sliding window ring buffer
- Extract - Features extracted when window is full
- Infer - Model predicts emotion probabilities
- Emit - Results emitted at configured intervals
Thread Safety
The engine uses threading.RLock() for thread-safe operations:
- Multiple threads can push data concurrently
- Buffer operations are protected
- Results can be consumed from any thread
Privacy & Security
IMPORTANT: This library uses demo placeholder model weights that are NOT trained on real biosignal data. For production use, you must provide your own trained model weights.
All processing happens on-device. No data is sent to external servers.
Development
Running Tests
pytest tests/
Code Formatting
black src/ examples/ tests/
isort src/ examples/ tests/
Type Checking
mypy src/
License
See LICENSE file for details.
Contributing
Contributions are welcome! See our Contributing Guidelines for details.
🔗 Links
- Main Repository: synheart-emotion (Source of Truth)
- Documentation: RFC E1.1
- Model Card: Model Card
- Examples: Examples
- Models: Pre-trained Models
- Tools: Development Tools
- Synheart AI: synheart.ai
- Issues: GitHub Issues
Citation
If you use this package in your research, please cite:
@software{synheart_emotion,
title = {Synheart Emotion: On-device emotion inference from biosignals},
author = {Goytom, Israel},
year = {2025},
version = {0.0.1},
url = {https://github.com/synheart-ai/synheart-emotion}
}
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 synheart_emotion-0.0.1.tar.gz.
File metadata
- Download URL: synheart_emotion-0.0.1.tar.gz
- Upload date:
- Size: 20.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5b4c606efdaff526165ef5f74360ba03ed559fb3803de661aba6cc5da0ad0676
|
|
| MD5 |
f998a47c7ae0bfaa88a2f9f14902679c
|
|
| BLAKE2b-256 |
72653238f1615a19a3c67a200ebfc536551308fc84798d1af3c035532d626266
|
Provenance
The following attestation bundles were made for synheart_emotion-0.0.1.tar.gz:
Publisher:
publish.yml on synheart-ai/synheart-emotion-python
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
synheart_emotion-0.0.1.tar.gz -
Subject digest:
5b4c606efdaff526165ef5f74360ba03ed559fb3803de661aba6cc5da0ad0676 - Sigstore transparency entry: 748526799
- Sigstore integration time:
-
Permalink:
synheart-ai/synheart-emotion-python@209a36a267e956e1fbcb82214115e23fbd38310a -
Branch / Tag:
refs/tags/v0.0.1 - Owner: https://github.com/synheart-ai
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@209a36a267e956e1fbcb82214115e23fbd38310a -
Trigger Event:
release
-
Statement type:
File details
Details for the file synheart_emotion-0.0.1-py3-none-any.whl.
File metadata
- Download URL: synheart_emotion-0.0.1-py3-none-any.whl
- Upload date:
- Size: 17.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9f0ac7bd3cff02d3d3580b0f64d859a51e4d9cbde0f689c1861f39988b1b29d4
|
|
| MD5 |
c95ee2527cef1d062e6368b95187dba3
|
|
| BLAKE2b-256 |
96c517feebf050ee57dcebb6129f309f394b9f671d13aab9b8d9bf3114c72353
|
Provenance
The following attestation bundles were made for synheart_emotion-0.0.1-py3-none-any.whl:
Publisher:
publish.yml on synheart-ai/synheart-emotion-python
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
synheart_emotion-0.0.1-py3-none-any.whl -
Subject digest:
9f0ac7bd3cff02d3d3580b0f64d859a51e4d9cbde0f689c1861f39988b1b29d4 - Sigstore transparency entry: 748526800
- Sigstore integration time:
-
Permalink:
synheart-ai/synheart-emotion-python@209a36a267e956e1fbcb82214115e23fbd38310a -
Branch / Tag:
refs/tags/v0.0.1 - Owner: https://github.com/synheart-ai
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@209a36a267e956e1fbcb82214115e23fbd38310a -
Trigger Event:
release
-
Statement type: