Thank you for your interest in contributing to the AxonFlow Python SDK! We welcome contributions from the community.
All contributions to this repository must be signed off under the Developer Certificate of Origin v1.1. The DCO is a per-commit affirmation that you wrote the code (or otherwise have the right to submit it) and are licensing it under the same license as the rest of this repository.
Add the sign-off automatically with -s (or --signoff) on every commit:
git commit -s -m "your commit message"This appends a trailer like:
Signed-off-by: Your Name <your.email@example.com>
The name and email must match git config user.name / git config user.email.
If you forgot -s on an existing commit, fix it with one of:
# most recent commit
git commit --amend --signoff --no-edit
# every commit on the current branch
git rebase --signoff origin/mainA DCO check runs automatically on every PR opened in the getaxonflow org. PRs with any unsigned commit will be blocked from merging until the missing sign-offs are added. No exceptions, including for maintainers.
- Fork the repository
- Clone your fork:
git clone https://github.com/YOUR_USERNAME/axonflow-sdk-python.git - Create a feature branch:
git checkout -b feature/your-feature-name - Make your changes
- Run tests:
pytest - Commit your changes:
git commit -m "Add your feature" - Push to your fork:
git push origin feature/your-feature-name - Open a Pull Request
- Python 3.9 or higher
- Git
git clone https://github.com/getaxonflow/axonflow-sdk-python.git
cd axonflow-sdk-python
pip install -e ".[dev]"# Run all tests
pytest
# Run tests with coverage
pytest --cov=axonflow --cov-report=term-missing
# Run tests with verbose output
pytest -vWhen you add or rename a pydantic BaseModel subclass whose class name
matches an OpenAPI schema in the platform specs, a CI job diffs the
fields against the spec and fails the PR on drift. This is enforced by
tests/test_wire_shape.py (opt-in via the wire_shape marker).
Run locally:
# Clone the community mirror — the specs live in docs/api/
git clone https://github.com/getaxonflow/axonflow.git ../axonflow
# Point the test at the specs dir and run just the wire-shape tests
AXONFLOW_OPENAPI_SPECS_DIR=../axonflow/docs/api \
pytest tests/test_wire_shape.py -m wire_shape -vWithout the env var, the tests skip cleanly — a plain pytest run
doesn't need the specs.
If you legitimately need to update the acknowledged baseline (e.g. a drift entry was burned down, or a new acknowledged divergence was added), regenerate it with:
# Pinning the SHA picks up the current HEAD of the community mirror.
# Alternately pass --sha <commit-sha> to pin explicitly.
python scripts/refresh_wire_shape_baseline.py ../axonflow/docs/apiNever regenerate to silence a failure without understanding what drifted; that defeats the gate.
The wire-shape gate pins the OpenAPI spec revision via
openapi_specs_sha in the baseline so a given SDK commit always diffs
against the same spec. Changing that SHA in the same PR that changes
SDK models can silently retarget the gate past drift it should have
caught, so the CI job enforces an extra guardrail: any PR that moves
openapi_specs_sha must also carry the spec-pin-bump label, which
surfaces the bump for explicit review.
Recommended flow:
- Open a dedicated PR that updates only
openapi_specs_sha(and the parts of the baseline that change as a consequence: drift entries, cross-spec shapes). - Apply the
spec-pin-bumplabel. - Merge.
- Follow up with the SDK-side changes that the new spec enables.
If it's genuinely one change (platform + SDK shipping together), apply the label to the single PR — the label just signals the reviewer to scrutinise the SHA move.
# Run Ruff linter
ruff check .
# Run Ruff formatter
ruff format .
# Run MyPy type checking
mypy axonflowExamples target a local AxonFlow community stack running on
http://localhost:8080. Start the stack before running:
git clone https://github.com/getaxonflow/axonflow.git
cd axonflow && docker compose up -dThen, from this repo:
export AXONFLOW_AGENT_URL="http://localhost:8080"
export AXONFLOW_CLIENT_ID="demo-client"
export AXONFLOW_CLIENT_SECRET="demo-secret"Run examples:
# Quickstart (async + sync patterns)
python examples/quickstart.py
# Gateway mode (pre-check + direct LLM + audit)
python examples/gateway_mode.py
# OpenAI interceptor (requires OPENAI_API_KEY)
python examples/openai_integration.py
# WCP retry_context + idempotency_key (enterprise features)
python examples/wcp_retry_idempotency.py- Follow PEP 8 style guide
- Use Ruff for linting and formatting:
ruff check . && ruff format . - Use type hints for all function signatures
- Run MyPy for type checking:
mypy axonflow - Keep functions focused and well-documented
- Use meaningful variable and function names
- Add docstrings for all public functions and classes
- Keep PRs focused: One feature or fix per PR
- Update documentation: If you change the API, update README.md
- Add tests: All new features should include tests
- Pass CI checks: Ensure all tests pass before submitting
- Write clear commit messages: Describe what and why, not how
Add feature: brief description
Detailed explanation of the changes and why they were made.
Any breaking changes should be clearly noted.
Have an idea for a new feature? We'd love to hear it!
- Check existing issues to avoid duplicates
- Open a new issue with the "Feature Request" label
- Describe the feature and its use case
- Discuss implementation approach
Found a bug? Help us fix it!
- Check existing issues to avoid duplicates
- Open a new issue with the "Bug" label
- Include:
- Python version
- Operating system
- Steps to reproduce
- Expected behavior
- Actual behavior
- Error messages or logs
We use pytest for testing. When adding new features:
- Add unit tests for new functions
- Add integration tests for API interactions
- Ensure test coverage remains above 80%
- Use pytest-httpx for mocking HTTP calls
Example test structure:
import pytest
from pytest_httpx import HTTPXMock
from axonflow import AxonFlow
@pytest.mark.asyncio
async def test_proxy_llm_call(httpx_mock: HTTPXMock):
httpx_mock.add_response(json={"success": True, "data": "result"})
async with AxonFlow(
endpoint="https://test.example.com",
client_id="test",
client_secret="test",
) as client:
result = await client.proxy_llm_call("token", "query", "chat")
assert result.success is True- Update README.md for user-facing changes
- Add docstrings for all public functions and classes
- Include usage examples in docstrings when helpful
- Keep documentation clear and concise
- All PRs require at least one approval
- Maintainers will review your PR within 3-5 business days
- Address feedback and update your PR
- Once approved, a maintainer will merge your PR
Several CI gates use a baseline file to grandfather pre-existing findings — the gate fails on any new finding but tolerates the listed ones. Baselines exist to land the gate without a giant cleanup PR; they are not intended to be permanent.
When your PR touches a baselined area (e.g. a function listed in .lint_baselines/falsey_clobber.json, or a type in tests/fixtures/wire-shape-baseline.json), do one of:
- Burn it down. Fix the baselined finding in this PR, remove the entry from the baseline file, and note "burndown:
<entry>" in the PR description. - Justify it. If the finding can't be fixed in this PR (different scope, blocked on a platform change, etc.), say so in the PR description in one line.
Baseline files in this repo:
.lint_baselines/falsey_clobber.json—or-falsey-clobber on wire-field accessestests/fixtures/wire_shape_baseline.json— wire-shape contract gate
CI does not block PRs that touch a baselined area without addressing it, but reviewers will ask the burndown-or-justify question.
By contributing to AxonFlow Python SDK, you agree that your contributions will be licensed under the MIT License.
If you have questions about contributing, feel free to:
- Open a discussion on GitHub
- Email us at hello@getaxonflow.com
- Check our documentation at https://docs.getaxonflow.com
Thank you for contributing to AxonFlow!