Contributing to GlassAlpha¶
Thank you for your interest in contributing to GlassAlpha! This guide will help you understand how to effectively contribute to our professional ML auditing toolkit.
Project philosophy¶
GlassAlpha follows an audit-first approach, prioritizing regulatory compliance and professional quality:
- Quality over features - Better to have fewer capabilities that work perfectly
- Determinism over performance - Reproducible results matter more than speed
- User value focus - Every change should improve audit quality or usability
- Professional standards - Code quality suitable for regulated industries
Development setup¶
Prerequisites¶
- Python 3.11+ (required for type hints and modern features)
- Git for version control
- Virtual environment tool (venv, conda, or poetry)
Quick setup¶
- Fork and clone the repository:
- Create and activate virtual environment:
python -m venv glassalpha-dev
source glassalpha-dev/bin/activate # Windows: glassalpha-dev\Scripts\activate
- Install in development mode:
- Set up development tools:
# Install pre-commit hooks
pre-commit install
# Verify installation
glassalpha --version
glassalpha list
- Run tests to verify setup:
Development dependencies¶
The [dev]
installation includes:
- Testing: pytest, pytest-cov
- Code Quality: ruff (linter), black (formatter), mypy (type checker)
- Pre-commit: Automated quality checks
- Documentation: Tools for docs development
Project structure¶
Understanding the codebase structure helps target contributions effectively:
glassalpha/packages/
├── src/glassalpha/ # Main package
│ ├── core/ # Interfaces and registries
│ ├── models/ # Model wrappers (XGBoost, LightGBM, etc.)
│ ├── explain/ # Explainers (TreeSHAP, KernelSHAP)
│ ├── metrics/ # Performance, fairness, drift metrics
│ ├── data/ # Data loading and processing
│ ├── pipeline/ # Audit pipeline orchestration
│ ├── report/ # PDF generation and templates
│ ├── config/ # Configuration management
│ ├── cli/ # Command-line interface
│ ├── profiles/ # Audit profiles
│ └── utils/ # Utilities (seeds, hashing, etc.)
├── tests/ # Test suite
├── configs/ # Example configurations
├── dev/ # Development resources (internal)
└── pyproject.toml # Package configuration
Key Extension Points:
- Add new models by implementing
ModelInterface
- Add new explainers by implementing
ExplainerInterface
- Add new metrics by implementing
MetricInterface
- Add new audit profiles for specific compliance needs
Code quality standards¶
Type safety¶
All code must include type hints and pass mypy --strict
:
from typing import Dict, List, Optional
import pandas as pd
import numpy as np
def compute_fairness_metrics(
y_true: np.ndarray,
y_pred: np.ndarray,
sensitive_features: pd.DataFrame,
metrics: List[str]
) -> Dict[str, float]:
"""Compute fairness metrics across demographic groups."""
...
Code formatting and linting¶
We use automated tools to ensure consistent code quality:
# Check code formatting
black --check src/
# Check linting
ruff check src/
# Check type hints
mypy src/
# Or use the convenience script
./lint-and-fix.sh
Testing requirements¶
Coverage Target: 50%+ for core modules
Test Categories:
- Unit tests - Individual component testing
- Integration tests - Component interaction testing
- Determinism tests - Reproducibility verification
- End-to-end tests - Full audit pipeline
Example Test:
def test_xgboost_wrapper_deterministic():
"""Test that XGBoost produces identical results with same seed."""
# Arrange
X, y = make_classification(n_samples=100, random_state=42)
model1 = XGBoostWrapper(random_state=42)
model2 = XGBoostWrapper(random_state=42)
# Act
model1.fit(X, y)
model2.fit(X, y)
pred1 = model1.predict(X)
pred2 = model2.predict(X)
# Assert
np.testing.assert_array_equal(pred1, pred2)
Performance testing¶
CLI performance is critical for user experience. We maintain automated performance regression tests.
Performance Expectations:
Command | Target | Current Baseline |
---|---|---|
glassalpha --help |
<300ms | ~108ms |
glassalpha --version |
<100ms | ~46ms |
glassalpha datasets list |
<500ms | ~248ms |
Full audit (german_credit) | <60s | ~5s |
Run Performance Tests:
# Run all performance tests
pytest tests/test_cli_performance.py -v
# Skip slow tests during rapid iteration
pytest -m "not slow" -v
# Test specific performance aspects
pytest tests/test_cli_performance.py::TestCLIPerformance::test_help_command_fast -v
Performance Test Categories:
- CLI Speed Tests - Ensure commands stay responsive
- Import Cleanness Tests - Verify no heavy imports at module level
- Audit Smoke Tests - Validate end-to-end audit completion
Maintaining Performance:
When adding new features, follow lazy loading patterns:
# ❌ BAD: Eager import at module level
from sklearn.model_selection import train_test_split
def split_data(data):
return train_test_split(data)
# ✅ GOOD: Lazy import within function
def split_data(data):
from sklearn.model_selection import train_test_split
return train_test_split(data)
# ✅ GOOD: __getattr__ pattern for backward compatibility
def __getattr__(name):
if name == "load_german_credit":
from .german_credit import load_german_credit
return load_german_credit
raise AttributeError(f"module has no attribute {name!r}")
Check for Regressions:
If your changes affect CLI startup or command performance:
# Measure CLI help time
time glassalpha --help
# Profile import time
python -X importtime -c "import glassalpha.__main__" 2> importtime.txt
cat importtime.txt
# Run full performance suite
pytest tests/test_cli_performance.py -v -s
Performance tests run automatically in CI and will fail if:
--help
exceeds 300ms (currently 108ms with 3x safety margin)- Heavy ML libraries (pandas, sklearn, xgboost) load during
--help
- Full audits take longer than 60s
Contribution workflow¶
1. Choose contribution type¶
High-Value Contributions:
- Bug fixes - Especially for determinism or audit quality issues
- Performance improvements - Faster audit generation
- New model support - Additional ML library wrappers
- Enhanced explanations - Better SHAP integration or visualizations
- Improved error handling - Clearer error messages and recovery
- Documentation - Examples, guides, API documentation
Lower Priority:
- Complex features without clear audit benefit
- Breaking API changes
- Features requiring significant maintenance overhead
2. Create feature branch¶
3. Development process¶
Before coding:
- Check existing issues and discussions
- Create an issue for significant changes
- Discuss architecture for major features
While coding:
- Write tests first (TDD approach recommended)
- Follow existing patterns and interfaces
- Maintain deterministic behavior
- Add comprehensive docstrings
Code example pattern:
@ModelRegistry.register("new_model")
class NewModelWrapper:
"""Wrapper for NewML library following GlassAlpha patterns."""
capabilities = {
"supports_shap": True,
"data_modality": "tabular"
}
version = "1.0.0"
def __init__(self, **kwargs):
"""Initialize with deterministic defaults."""
self._set_deterministic_params(kwargs)
def predict(self, X: pd.DataFrame) -> np.ndarray:
"""Generate predictions with type safety."""
# Implementation
...
4. Testing your changes¶
# Run specific test categories
pytest tests/test_models/ # Model tests
pytest tests/test_explainers/ # Explainer tests
pytest tests/test_integration/ # Integration tests
# Run all tests with coverage
pytest --cov=src/glassalpha --cov-report=html
# Test determinism (crucial for audit reproducibility)
pytest -k deterministic
# Test end-to-end workflow
glassalpha audit --config configs/german_credit_simple.yaml --output test.pdf
5. Quality checks¶
# Comprehensive quality check
./lint-and-fix.sh
# Manual checks if needed
black src/
ruff check --fix src/
mypy src/
6. Commit your changes¶
Use conventional commit format:
git add .
git commit -m "feat(models): add RandomForest wrapper with TreeSHAP support
- Implement RandomForestWrapper following ModelInterface
- Add comprehensive test suite with determinism checks
- Include capability declarations for SHAP compatibility
- Update model registry and documentation
Closes #123"
Commit Types:
feat:
New featuresfix:
Bug fixesdocs:
Documentation updatestest:
Test improvementsrefactor:
Code restructuringperf:
Performance improvements
Pull request process¶
Before submitting¶
- Rebase on main to ensure clean history
- Run full test suite and ensure all pass
- Check coverage hasn't decreased significantly
- Update documentation for any user-visible changes
- Add changelog entry if user-facing
PR description template¶
## Description
Brief description of changes and motivation.
## Type of Change
- [ ] Bug fix (non-breaking change fixing an issue)
- [ ] New feature (non-breaking change adding functionality)
- [ ] Breaking change (would cause existing functionality to not work as expected)
- [ ] Documentation update
## Testing
- [ ] Unit tests pass
- [ ] Integration tests pass
- [ ] Manual testing completed
- [ ] Determinism verified (if applicable)
## Checklist
- [ ] Code follows style guidelines
- [ ] Self-review completed
- [ ] Documentation updated
- [ ] Tests added/updated
- [ ] No breaking changes (or clearly documented)
Review process¶
- Automated checks must pass (CI/CD pipeline)
- Code review by maintainer
- Testing verification in review environment
- Documentation review for user-facing changes
- Merge after approval
Architecture guidelines¶
Plugin system¶
GlassAlpha uses a registry-based plugin architecture. Follow these patterns:
Adding New Components:
# 1. Implement the interface
class MyExplainer:
capabilities = {"model_types": ["xgboost"]}
def explain(self, model, X, y=None):
# Implementation
...
# 2. Register the component
@ExplainerRegistry.register("my_explainer", priority=75)
class MyExplainer:
...
# 3. Add tests
def test_my_explainer_registration():
assert "my_explainer" in ExplainerRegistry.get_all()
Enterprise/OSS separation¶
Maintain clear boundaries between open source and enterprise features:
# OSS implementation - always available
@ModelRegistry.register("basic_model")
class BasicModel:
...
# Enterprise feature - gated behind license check
@check_feature("advanced_models")
def create_advanced_model():
if not is_enterprise():
raise FeatureNotAvailable("Requires enterprise license")
# Enterprise implementation
...
Deterministic design¶
All operations must be reproducible:
def deterministic_operation(data, random_state=None):
"""Ensure operation can be reproduced exactly."""
if random_state is not None:
np.random.seed(random_state)
# Deterministic processing
result = process_data(data)
# Include randomness source in output for audit trail
return {
"result": result,
"random_state": random_state,
"timestamp": datetime.utcnow(),
"version": __version__
}
Testing guidelines¶
Test organization¶
tests/
├── test_core/ # Core architecture tests
├── test_models/ # Model wrapper tests
├── test_explainers/ # Explainer tests
├── test_metrics/ # Metrics tests
├── test_integration/ # Component integration
├── test_cli/ # CLI interface tests
├── test_deterministic/ # Reproducibility tests
└── test_end_to_end/ # Full pipeline tests
Writing effective tests¶
Test Structure (Arrange-Act-Assert):
def test_specific_behavior():
"""Test description explaining what behavior is verified."""
# Arrange - Set up test data and conditions
data = create_test_data()
config = create_test_config()
# Act - Execute the operation being tested
result = perform_operation(data, config)
# Assert - Verify expected outcomes
assert result.success
assert len(result.explanations) > 0
assert result.manifest["random_seed"] == config.random_seed
Testing Patterns:
- Determinism Tests:
def test_audit_determinism():
"""Verify identical configs produce identical results."""
config = load_config("test_config.yaml")
result1 = run_audit(config)
result2 = run_audit(config)
# Critical for regulatory compliance
assert result1.manifest == result2.manifest
np.testing.assert_array_equal(result1.shap_values, result2.shap_values)
- Error Handling Tests:
def test_graceful_error_handling():
"""Verify clear error messages for common failures."""
invalid_config = {"model": {"type": "nonexistent"}}
with pytest.raises(ComponentNotFoundError) as exc_info:
run_audit(invalid_config)
assert "nonexistent" in str(exc_info.value)
assert "available models" in str(exc_info.value)
Documentation standards¶
Docstring format¶
Use Google-style docstrings:
def explain_model_decision(
model: ModelInterface,
instance: pd.Series,
background_data: Optional[pd.DataFrame] = None,
explainer_type: str = "auto"
) -> Dict[str, Any]:
"""Generate explanation for a single model decision.
This function provides detailed explanations for individual predictions,
helping users understand which features contributed to the model's decision
and by how much.
Args:
model: Trained model implementing ModelInterface protocol
instance: Single data instance to explain (must match model input schema)
background_data: Reference data for SHAP baseline. If None, uses
model training data when available
explainer_type: Type of explainer to use ("auto", "shap", "lime").
"auto" selects best explainer based on model capabilities
Returns:
Dictionary containing:
- "explanations": Feature importance scores
- "baseline": Reference prediction value
- "prediction": Model prediction for this instance
- "confidence": Prediction confidence if available
- "metadata": Explainer type, version, parameters used
Raises:
ValueError: If instance shape doesn't match model input requirements
ExplainerNotSupportedError: If no compatible explainer found for model
DataValidationError: If background_data format is incompatible
Example:
>>> model = XGBoostWrapper()
>>> model.fit(X_train, y_train)
>>> explanation = explain_model_decision(model, X_test.iloc[0])
>>> print(explanation["explanations"])
{'feature1': 0.23, 'feature2': -0.15, ...}
Generate explanation plot:
>>> plot_explanation_waterfall(explanation)
Note:
Explanations are computed using the model's most compatible explainer.
For tree-based models (XGBoost, LightGBM), TreeSHAP provides exact
Shapley values. For other models, KernelSHAP provides approximations.
"""
Example updates¶
When adding new features, update relevant examples:
- Configuration examples - Show how to use new features
- Tutorial updates - Integrate new capabilities into user journey
- API documentation - Document new interfaces and parameters
Security and privacy guidelines¶
Data handling¶
- No PII in logs - Never log personally identifiable information
- Sanitize inputs - Validate and clean all user inputs
- Hash sensitive data - Use SHA-256 for any identifier hashing
- Local processing - Core library must work completely offline
Example safe logging¶
import logging
from glassalpha.utils import hash_dataframe
logger = logging.getLogger(__name__)
def process_audit_data(data: pd.DataFrame):
"""Process audit data safely."""
data_hash = hash_dataframe(data)
# Safe: Log hash and metadata, never actual data
logger.info(f"Processing dataset: hash={data_hash}, shape={data.shape}")
# NEVER do this:
# logger.info(f"Processing data: {data.to_dict()}") # Could contain PII
return process_data(data)
Getting help¶
Communication channels¶
- GitHub Issues - Bug reports, feature requests
- GitHub Discussions - General questions, usage help
- Code Review - Feedback on pull requests
Before asking questions¶
- Search existing issues and discussions
- Check documentation including FAQ and troubleshooting
- Try the quickstart tutorial to understand basic usage
- Look at existing code examples for implementation patterns
How to ask effective questions¶
Good Question Format:
**What I'm trying to do:** Add support for CatBoost models
**What I've tried:**
- Implemented CatBoostWrapper following ModelInterface
- Added basic tests following XGBoost example
- Getting error: "TreeSHAP not compatible with CatBoost"
**Expected behavior:** TreeSHAP should work with CatBoost like other tree models
**Environment:**
- GlassAlpha version: 0.1.0
- Python version: 3.11.5
- CatBoost version: 1.2.0
**Code snippet:** [minimal reproducing example]
Recognition and credits¶
Contributors are recognized in several ways:
- Changelog entries for significant contributions
- GitHub contributor list automatically maintained
- Documentation credits for major documentation contributions
- Release notes highlighting key contributions
Development roadmap¶
Understanding our direction helps target valuable contributions:
Current Focus Areas:
- Enhanced model support (additional ML libraries)
- Improved explanation quality and performance
- Better error handling and user experience
- Comprehensive documentation and examples
Potential Future Considerations (community-driven):
- Additional data modalities based on demand
- Extended compliance framework support
- Enhanced integration capabilities
- Advanced visualization options
Quick reference¶
Essential Commands:
# Setup
pip install -e ".[dev]" && pre-commit install
# Development
./lint-and-fix.sh # Code quality checks
pytest # Run tests
glassalpha audit --config configs/german_credit_simple.yaml --output test.pdf
# Quality gates
black --check src/ # Formatting
ruff check src/ # Linting
mypy src/ # Type checking
pytest --cov=src/glassalpha # Coverage
Getting Unstuck:
- Check Trust & Deployment Guide for system design
- Look at existing implementations for patterns
- Search GitHub Issues
- Ask in GitHub Discussions
Thank you for contributing to GlassAlpha! Your contributions help make ML auditing more transparent, reliable, and accessible for regulated industries.