Skip to content

Commit 0811f5d

Browse files
feat: Python ampersend SDK
1 parent 3f08523 commit 0811f5d

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+4719
-0
lines changed

.env.example

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# Ampersend SDK Environment Variables
2+
# Copy this file to .env and fill in your values
3+
4+
EXAMPLES_A2A_BUYER__PRIVATE_KEY=
5+
EXAMPLES_A2A_BUYER__SELLER_AGENT_URL=http://localhost:8001
6+
7+
GOOGLE_API_KEY=
8+
EXAMPLES_A2A_SELLER__PAY_TO_ADDRESS=

.github/workflows/python-ci.yml

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
name: Python CI
2+
3+
on:
4+
pull_request:
5+
push:
6+
branches: [main]
7+
workflow_dispatch:
8+
9+
jobs:
10+
python-ci:
11+
name: "py: ci"
12+
runs-on: ubuntu-latest
13+
timeout-minutes: 10
14+
15+
steps:
16+
- uses: actions/checkout@v5
17+
18+
- uses: astral-sh/setup-uv@v6
19+
with:
20+
enable-cache: true
21+
cache-dependency-glob: "uv.lock"
22+
23+
- name: Check lockfile
24+
run: uv lock --check
25+
26+
- name: Cache mypy
27+
uses: actions/cache@v4
28+
with:
29+
path: .mypy_cache
30+
key: mypy-${{ runner.os }}-py3.13-${{ hashFiles('uv.lock') }}
31+
restore-keys: |
32+
mypy-${{ runner.os }}-py3.13-
33+
mypy-${{ runner.os }}-
34+
35+
- name: Install Python 3.13
36+
run: uv python install 3.13
37+
38+
- name: Install deps
39+
run: uv sync --frozen --group dev
40+
41+
- name: Lint
42+
run: uv run -- ruff check --output-format=github python
43+
44+
- name: Format
45+
if: always()
46+
run: uv run -- ruff format --diff python
47+
48+
- name: Typecheck
49+
if: always()
50+
run: uv run -- mypy python
51+
52+
- name: Test
53+
run: uv run -- pytest

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,4 @@
11
.DS_STORE
2+
.env
3+
**/__pycache__
4+
node_modules

.ignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
!.env
2+
!.envrc

.python-version

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
3.13

CLAUDE.md

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
# CLAUDE.md
2+
3+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4+
5+
## Project Overview
6+
7+
This is a Python-based SDK that extends the A2A (Agent-to-Agent) protocol with x402 payment capabilities. The SDK enables agents to handle payments during A2A interactions, supporting both buyer (client) and seller (server) roles.
8+
9+
## Development Commands
10+
11+
### Setup
12+
13+
```bash
14+
# Install Python 3.13
15+
uv python install 3.13
16+
17+
# Install dependencies including dev tools
18+
uv sync --frozen --group dev
19+
```
20+
21+
### Testing
22+
23+
```bash
24+
# Run all tests
25+
uv run -- pytest
26+
27+
# Run specific test file
28+
uv run -- pytest python/ampersend-sdk/tests/unit/x402/treasurers/test_naive.py
29+
30+
# Run only slow tests
31+
uv run -- pytest -m slow
32+
```
33+
34+
### Linting & Formatting
35+
36+
```bash
37+
# Check linting (uses ruff for imports and unused imports)
38+
uv run -- ruff check --output-format=github python
39+
40+
# Check formatting
41+
uv run -- ruff format --diff python
42+
43+
# Apply formatting
44+
uv run -- ruff format python
45+
46+
# Type checking (strict mode enabled)
47+
uv run -- mypy python
48+
```
49+
50+
### Lockfile
51+
52+
```bash
53+
# Verify lockfile is up to date
54+
uv lock --check
55+
56+
# Update lockfile
57+
uv lock
58+
```
59+
60+
## Architecture
61+
62+
### Workspace Structure
63+
64+
This is a uv workspace with two main packages:
65+
66+
- `python/ampersend-sdk/`: Core SDK implementation
67+
- `python/examples/`: Example buyer and seller agents
68+
69+
### Core Components
70+
71+
**X402Treasurer (Abstract Base Class)**
72+
73+
- Handles payment authorization decisions via `onPaymentRequired()`
74+
- Receives payment status updates via `onStatus()`
75+
- Implementation example: `NaiveTreasurer` auto-approves all payments
76+
77+
**X402Wallet (Protocol)**
78+
79+
- Creates payment payloads from requirements
80+
- Two implementations:
81+
- `AccountWallet`: For EOA (Externally Owned Accounts)
82+
- `SmartAccountWallet`: For smart contract wallets with ERC-1271 signatures
83+
84+
**Client Side (Buyer)**
85+
86+
- `X402Client`: Extends A2A BaseClient with payment middleware
87+
- `X402RemoteA2aAgent`: Remote agent wrapper with treasurer integration
88+
- `x402_middleware`: Intercepts responses, handles PAYMENT_REQUIRED states, submits payments recursively
89+
90+
**Server Side (Seller)**
91+
92+
- `X402A2aAgentExecutor`: Wraps ADK agents with payment verification
93+
- `make_x402_before_agent_callback()`: Creates callbacks that check payment before agent execution
94+
- `to_a2a()`: Converts ADK agent to A2A app with x402 support
95+
- Uses layered executor pattern: OuterA2aAgentExecutor → X402ServerExecutor → InnerA2aAgentExecutor
96+
97+
### Key Architectural Patterns
98+
99+
**Middleware Pattern**: Client uses `x402_middleware` to recursively handle payment required responses by:
100+
101+
1. Detecting PAYMENT_REQUIRED status in task responses
102+
2. Calling treasurer to authorize payment
103+
3. Submitting payment and recursing with new message
104+
105+
**Executor Composition**: Server uses nested executors to separate concerns:
106+
107+
- Outer layer handles A2A task lifecycle events
108+
- Middle layer (X402ServerExecutor) verifies payments
109+
- Inner layer runs the actual agent
110+
111+
**Protocol-based Wallets**: X402Wallet is a Protocol (structural typing), allowing any object with `create_payment()` to be used without inheritance.
112+
113+
## Environment Variables
114+
115+
Examples require these variables (see `.env.example`):
116+
117+
- `EXAMPLES_A2A_BUYER__PRIVATE_KEY`: Private key for buyer's EOA wallet
118+
- `EXAMPLES_A2A_BUYER__SELLER_AGENT_URL`: URL of seller agent (default: <http://localhost:8001>)
119+
- `GOOGLE_API_KEY`: Required for seller's google_search tool
120+
- `EXAMPLES_A2A_SELLER__PAY_TO_ADDRESS`: Ethereum address to receive payments
121+
122+
## Important Notes
123+
124+
- Python version: 3.13+ required
125+
- Type checking is strict mode (`mypy --strict`)
126+
- The x402-a2a dependency comes from a git repository with a specific revision
127+
- This SDK is marked as "unofficial" in the package description
128+
- Tests use async mode with function-scoped fixtures

README.md

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
# Ampersend SDK
2+
3+
A Python SDK that extends the [A2A (Agent-to-Agent) protocol](https://github.com/google/a2a) with x402 payment capabilities. Build AI agents that can send and receive payments during interactions.
4+
5+
## 🚀 Prerequisites
6+
7+
**uv** is required for dependency management. Install with:
8+
9+
```bash
10+
# macOS/Linux
11+
curl -LsSf https://astral.sh/uv/install.sh | sh
12+
13+
# Windows
14+
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
15+
16+
# Alternative: Homebrew
17+
brew install uv
18+
```
19+
20+
## ⚙️ Setup
21+
22+
1. **Install Python 3.13:**
23+
24+
```bash
25+
uv python install 3.13
26+
```
27+
28+
2. **Install dependencies:**
29+
30+
```bash
31+
uv sync --frozen --group dev
32+
```
33+
34+
3. **Configure environment variables:**
35+
36+
```bash
37+
cp .env.example .env
38+
# Edit .env and fill in your values:
39+
# - EXAMPLES_A2A_BUYER__PRIVATE_KEY: Private key for buyer's wallet
40+
# - EXAMPLES_A2A_BUYER__SELLER_AGENT_URL: URL of seller agent (default: http://localhost:8001)
41+
# - GOOGLE_API_KEY: Required for seller's Google Search tool
42+
# - EXAMPLES_A2A_SELLER__PAY_TO_ADDRESS: Ethereum address to receive payments
43+
```
44+
45+
4. **Obtain required credentials:**
46+
47+
**Google API Key** (required for seller example):
48+
49+
1. Visit [Google AI Studio](https://aistudio.google.com/app/apikey)
50+
2. Sign in with your Google account
51+
3. Click "Get API key" or "Create API key"
52+
4. Copy the generated key and add it to your `.env` file
53+
54+
**USDC Testnet Tokens** (required for buyer example):
55+
56+
Use the [Circle USDC testnet faucet](https://faucet.circle.com) to obtain test USDC for your buyer wallet address.
57+
58+
### 💡 Optional: direnv
59+
60+
For automatic environment variable loading, install [direnv](https://direnv.net/):
61+
62+
```bash
63+
# macOS
64+
brew install direnv
65+
66+
# Then add to your shell config (e.g., ~/.zshrc):
67+
eval "$(direnv hook zsh)"
68+
69+
# Allow direnv in this directory:
70+
direnv allow
71+
```
72+
73+
## 🔧 Development
74+
75+
**Run tests:**
76+
77+
```bash
78+
uv run -- pytest # All tests
79+
```
80+
81+
**Linting and formatting:**
82+
83+
```bash
84+
uv run -- ruff check python # Lint
85+
uv run -- ruff format python # Format
86+
uv run -- mypy python # Type check
87+
```
88+
89+
## 🤖 Running Examples
90+
91+
The examples demonstrate a buyer agent querying a seller agent that requires payment.
92+
93+
**Terminal 1 - Start the seller agent:**
94+
95+
```bash
96+
uv --directory=python/examples run -- uvicorn examples.a2a.seller.adk.agent:a2a_app --host localhost --port 8001
97+
```
98+
99+
**Terminal 2 - Run the buyer agent:**
100+
101+
Option 1: Command-line query
102+
103+
```bash
104+
echo "when was x402 founded?" | uv --directory=python/examples run -- adk run src/examples/a2a/buyer/adk
105+
```
106+
107+
Option 2: Interactive web UI
108+
109+
```bash
110+
uv --directory=python/examples run -- adk web src/examples/a2a/buyer
111+
# Open http://localhost:8000 in your browser
112+
```

pyproject.toml

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
[project]
2+
name = "ampersend-sdk-python"
3+
version = "0.0.1"
4+
requires-python = ">=3.13"
5+
dependencies = [
6+
"ampersend-sdk",
7+
]
8+
9+
[tool.uv.sources]
10+
ampersend-sdk = { workspace = true }
11+
x402-a2a = { git = "https://github.com/google-agentic-commerce/a2a-x402", subdirectory = "python/x402_a2a", rev = "a50b512a61880c180bc82c1332aee271f46942b6" }
12+
13+
[tool.uv.workspace]
14+
members = ["python/*"]
15+
16+
[tool.ruff]
17+
target-version = "py313"
18+
19+
[tool.ruff.lint]
20+
select = ["I", "F401"]
21+
22+
[tool.mypy]
23+
python_version = "3.13"
24+
strict = true
25+
explicit_package_bases = true
26+
27+
[[tool.mypy.overrides]]
28+
module = ["x402_a2a.*"]
29+
follow_untyped_imports = true
30+
31+
[tool.pytest.ini_options]
32+
testpaths = [
33+
"python/ampersend-sdk/tests",
34+
]
35+
asyncio_mode = "auto"
36+
asyncio_default_fixture_loop_scope = "function"
37+
addopts = "-v --tb=short"
38+
markers = [
39+
"slow: marks tests as slow running",
40+
]
41+
42+
[dependency-groups]
43+
dev = [
44+
"ruff>=0.13.2",
45+
"mypy>=1.8.0",
46+
]
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
[project]
2+
name = "ampersend-sdk"
3+
version = "0.0.1"
4+
description = "Unofficial x402 payment protocol extension for A2A"
5+
dependencies = [
6+
"a2a-sdk",
7+
"google-adk>=1.14.0,<=1.14.1",
8+
"x402-a2a",
9+
"x402>=0.1.4",
10+
]
11+
12+
[build-system]
13+
requires = ["uv_build>=0.8.22,<0.9.0"]
14+
build-backend = "uv_build"

python/ampersend-sdk/src/ampersend_sdk/__init__.py

Whitespace-only changes.

0 commit comments

Comments
 (0)