Contributing to Shekel¶
Thanks for your interest in contributing to shekel!
Getting Started¶
Setup Development Environment¶
# Clone the repository
git clone https://github.com/arieradle/shekel
cd shekel
# Install in editable mode with all dependencies
pip install -e ".[all-models,dev]"
This installs: - Shekel in editable mode - All optional dependencies (OpenAI, Anthropic, tokencost) - Development tools (pytest, black, ruff, mypy, etc.)
Development Workflow¶
Running Code Quality Checks¶
# Format code
black .
# Sort imports
isort .
# Lint
ruff check .
# Type check
mypy shekel/
# Run all checks
black . && isort . && ruff check . && mypy shekel/
Running Tests¶
# Run all tests
pytest tests/ -v
# Run with coverage
pytest tests/ --cov=shekel --cov-report=term-missing
# Run specific test file
pytest tests/test_budget.py -v
# Run specific test
pytest tests/test_budget.py::test_basic_tracking -v
Test Coverage¶
Aim for >95% coverage. Check with:
Contributing Guidelines¶
Adding a New Model¶
- Edit
shekel/prices.json:
{
"gpt-4o": {
"input_per_1k": 0.0025,
"output_per_1k": 0.01
},
"your-new-model": {
"input_per_1k": 0.002,
"output_per_1k": 0.006
}
}
- Add test in
tests/test_pricing.py:
def test_your_new_model():
cost = calculate_cost("your-new-model", input_tokens=1000, output_tokens=500)
expected = (1000 / 1000 * 0.002) + (500 / 1000 * 0.006)
assert cost == expected
- Update documentation:
- Add model to
README.md - Add model to
docs/models.md - Update
CHANGELOG.mdunder[Unreleased]
Adding a Feature¶
- Create an issue describing the feature
- Write tests first (TDD approach)
- Implement the feature
- Update documentation
- Update
CHANGELOG.mdunder[Unreleased] - Submit pull request
Fixing a Bug¶
- Create an issue with reproduction steps
- Write a failing test that demonstrates the bug
- Fix the bug
- Verify the test passes
- Update
CHANGELOG.mdunder[Unreleased] - Submit pull request
Pull Request Guidelines¶
Before Submitting¶
- [ ] All tests pass (
pytest tests/) - [ ] Code is formatted (
black .) - [ ] Imports are sorted (
isort .) - [ ] No linter errors (
ruff check .) - [ ] Type checks pass (
mypy shekel/) - [ ] Documentation is updated
- [ ]
CHANGELOG.mdis updated - [ ] PR description explains the change
PR Best Practices¶
- Keep PRs focused - One change per PR
- Write clear commit messages
- Add tests for new functionality
- Update docs for user-facing changes
- Reference issues in PR description
PR Title Format¶
Use conventional commits format:
feat: Add model fallback featurefix: Handle streaming edge casedocs: Update installation guidetest: Add coverage for async budgetschore: Update dependencies
Code Style¶
Python Style¶
- Follow PEP 8
- Use type hints
- Write docstrings for public APIs
- Keep functions focused and small
- Use meaningful variable names
Example¶
from __future__ import annotations
def calculate_cost(
model: str,
input_tokens: int,
output_tokens: int,
price_override: dict[str, float] | None = None,
) -> float:
"""Calculate the cost in USD for a given model call.
Args:
model: Model name (e.g., "gpt-4o")
input_tokens: Number of input tokens
output_tokens: Number of output tokens
price_override: Optional custom pricing
Returns:
Cost in USD as a float
"""
# Implementation...
Testing Guidelines¶
Test Structure¶
def test_feature_name():
"""Test description."""
# Arrange
budget_obj = budget(max_usd=1.00)
# Act
with budget_obj:
result = do_something()
# Assert
assert result == expected
Test Categories¶
- Unit tests: Test individual functions
- Integration tests: Test full workflows
- Async tests: Test async functionality
- Edge cases: Test error conditions
Mocking¶
Use pytest fixtures for mocking:
@pytest.fixture
def mock_openai_client(monkeypatch):
"""Mock OpenAI client."""
# Setup mock...
return mock_client
Documentation¶
Docstring Format¶
def budget(max_usd: float | None = None) -> Budget:
"""Create a budget context manager.
Args:
max_usd: Maximum spend in USD. None = track-only mode.
Returns:
Budget context manager object.
Raises:
ValueError: If max_usd is negative.
Example:
```python
with budget(max_usd=1.00) as b:
run_agent()
print(f"Spent: ${b.spent:.4f}")
```
"""
Documentation Files¶
When adding features, update:
README.md- If user-facingdocs/- Relevant documentation pagesCHANGELOG.md- Under[Unreleased]- Docstrings - For public APIs
Reporting Bugs¶
Bug Report Template¶
Open an issue at github.com/arieradle/shekel/issues with:
Environment:
- Python version: python --version
- Shekel version: python -c "import shekel; print(shekel.__version__)"
- OS: macOS/Linux/Windows
Bug Description: Clear description of the issue.
Reproduction:
# Minimal code to reproduce
from shekel import budget
with budget(max_usd=1.00):
# Bug occurs here
...
Expected Behavior: What should happen.
Actual Behavior: What actually happens.
Traceback:
Asking Questions¶
- Usage questions: Open a Discussion
- Bug reports: Open an Issue
- Feature requests: Open an Issue
Release Process¶
(For maintainers)
- Update version in
shekel/__init__.py - Update
CHANGELOG.mdwith release date - Create git tag:
git tag v0.X.Y - Push tag:
git push origin v0.X.Y - GitHub Actions publishes to PyPI
Code of Conduct¶
Be respectful and inclusive. We welcome contributions from everyone.
License¶
By contributing, you agree that your contributions will be licensed under the MIT License.
Questions?¶
Feel free to: - Open a Discussion - Reach out to @arieradle
Thank you for contributing to shekel! 🎉