Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/python-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ jobs:
run: uv python install 3.13

- name: Install deps
run: uv sync --frozen --group dev
run: uv sync --frozen --all-packages --group dev

- name: Lint
run: uv run -- ruff check --output-format=github python
Expand Down
76 changes: 76 additions & 0 deletions .github/workflows/python-langchain-ampersend-release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
name: Python langchain-ampersend Release

on:
push:
tags:
- "py-langchain-ampersend-v*.*.*"
- "py-langchain-ampersend-v*.*.*a*"
- "py-langchain-ampersend-v*.*.*b*"
- "py-langchain-ampersend-v*.*.*rc*"

permissions:
contents: read
id-token: write # Required for PyPI trusted publishing

jobs:
verify-version:
name: "py-langchain-ampersend: verify version"
runs-on: ubuntu-latest

steps:
- name: Checkout
uses: actions/checkout@v5

- name: Setup uv
uses: astral-sh/setup-uv@v6

- name: Install Python 3.13
run: uv python install 3.13

- name: Extract version from tag
id: tag
run: |
TAG=${GITHUB_REF#refs/tags/py-langchain-ampersend-v}
echo "version=$TAG" >> $GITHUB_OUTPUT
echo "Tagged version: $TAG"

- name: Read pyproject.toml version
id: package
run: |
VERSION=$(uv run python -c "import tomllib; print(tomllib.load(open('python/langchain-ampersend/pyproject.toml', 'rb'))['project']['version'])")
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "Package version: $VERSION"

- name: Compare versions
run: |
if [ "${{ steps.tag.outputs.version }}" != "${{ steps.package.outputs.version }}" ]; then
echo "ERROR: Tag version (${{ steps.tag.outputs.version }}) does not match pyproject.toml version (${{ steps.package.outputs.version }})"
exit 1
fi
echo "✓ Versions match: ${{ steps.tag.outputs.version }}"

publish:
name: "py-langchain-ampersend: publish to PyPI"
runs-on: ubuntu-latest
needs: verify-version
environment:
name: pypi
url: https://pypi.org/p/langchain-ampersend

steps:
- name: Checkout
uses: actions/checkout@v5

- name: Setup uv
uses: astral-sh/setup-uv@v6

- name: Install Python 3.13
run: uv python install 3.13

- name: Build package
run: uv build --package langchain-ampersend

- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
packages-dir: dist/
3 changes: 2 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ support buyer (client) and seller (server) roles with flexible payment authoriza
uv python install 3.13

# Install dependencies including dev tools
uv sync --frozen --group dev
uv sync --frozen --all-packages --group dev
```

### TypeScript Setup
Expand Down Expand Up @@ -119,6 +119,7 @@ This is a multi-language monorepo with both workspace at the repository root:
**Python** (uv workspace):

- `python/ampersend-sdk/`: Python SDK with A2A protocol integration
- `python/langchain-ampersend/`: LangChain integration for A2A with x402 payments
- Configured via `pyproject.toml`

**TypeScript** (pnpm workspace):
Expand Down
10 changes: 6 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ Supports both buyer (client) and seller (server) roles with flexible payment ver
## 📦 Language Support

- **Python** - A2A protocol integration with wallet implementations and payment middleware
- [Python SDK Documentation](./python/README.md)
- [Python SDK Documentation](./python/ampersend-sdk/README.md)
- [LangChain Integration](./python/langchain-ampersend/README.md)

- **TypeScript** - MCP protocol integration with client, proxy, and server implementations
- [TypeScript SDK Documentation](./typescript/README.md)
Expand All @@ -22,7 +23,7 @@ Supports both buyer (client) and seller (server) roles with flexible payment ver
uv python install 3.13

# Install dependencies
uv sync --frozen --group dev
uv sync --frozen --all-packages --group dev
```

**→ [Full Python documentation](./python/README.md)**
Expand Down Expand Up @@ -71,10 +72,11 @@ patterns. See [x402 specification](https://github.com/coinbase/x402).
```
ampersend-sdk/
├── python/
│ └── ampersend-sdk/ # Python SDK package
│ ├── ampersend-sdk/ # Python SDK package
│ └── langchain-ampersend/ # LangChain integration
└── typescript/
└── packages/
└── ampersend-sdk/ # TypeScript SDK package
└── ampersend-sdk/ # TypeScript SDK package
```

## 🔧 Prerequisites
Expand Down
6 changes: 6 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ disable_error_code = ["no-untyped-def", "arg-type", "index", "type-arg"]
[tool.pytest.ini_options]
testpaths = [
"python/ampersend-sdk/tests",
"python/langchain-ampersend/tests",
]
asyncio_mode = "auto"
asyncio_default_fixture_loop_scope = "function"
Expand All @@ -48,4 +49,9 @@ markers = [
dev = [
"ruff>=0.13.2",
"mypy>=1.8.0",
"pytest>=8.0.0",
"pytest-asyncio>=0.24.0",
"pydantic-settings>=2.0.0",
"python-dotenv>=1.0.0",
"langchain-tests==1.1.2",
]
61 changes: 61 additions & 0 deletions python/langchain-ampersend/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# langchain-ampersend

LangChain integration for Ampersend x402 payments. Call remote A2A agents with automatic payment handling.

## Installation

```bash
pip install langchain-ampersend
```

## Usage

```python
from langchain_ampersend import (
A2AToolkit,
AmpersendTreasurer,
ApiClient,
ApiClientOptions,
SmartAccountWallet,
)
from langchain.agents import create_agent
from langchain_openai import ChatOpenAI

# Setup Ampersend API client
api_client = ApiClient(ApiClientOptions(
base_url="https://api.ampersend.ai",
session_key_private_key="0x...", # Your session key
))

# Setup wallet (smart account)
wallet = SmartAccountWallet(
owner_private_key="0x...",
smart_account_address="0x...",
)

# Create treasurer
treasurer = AmpersendTreasurer(api_client=api_client, wallet=wallet)

# Create toolkit for a remote agent
toolkit = A2AToolkit(
remote_agent_url="https://agent.example.com",
treasurer=treasurer,
)

# Initialize (discovers the agent)
await toolkit.initialize()

# Use with LangGraph
llm = ChatOpenAI(model="gpt-4")
agent = create_agent(llm, toolkit.get_tools())

# Run
result = await agent.ainvoke({"messages": [("user", "Query some data")]})
```

## Tools

The toolkit provides two tools:

- `a2a_get_agent_details` - Get the capabilities and skills of the remote agent
- `a2a_send_message` - Send a message to the remote agent (payments handled automatically)
22 changes: 22 additions & 0 deletions python/langchain-ampersend/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
[project]
name = "langchain-ampersend"
version = "0.0.1"
description = "LangChain integration for Ampersend x402 payments"
readme = "README.md"
requires-python = ">=3.13"
dependencies = [
"langchain-core>=0.3.0",
"ampersend-sdk",
"httpx>=0.27.0",
]

[build-system]
requires = ["uv_build>=0.8.22,<0.9.0"]
build-backend = "uv_build"

[dependency-groups]
test = [
"pytest>=8.0.0",
"pytest-asyncio>=0.24.0",
"langchain-tests==1.1.2", # Pin to avoid breaking CI
]
17 changes: 17 additions & 0 deletions python/langchain-ampersend/src/langchain_ampersend/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
"""LangChain integration for Ampersend x402 payments."""

# Re-export common items from ampersend-sdk for convenience
from ampersend_sdk.ampersend import AmpersendTreasurer, ApiClient, ApiClientOptions
from ampersend_sdk.x402.treasurer import X402Treasurer
from ampersend_sdk.x402.wallets.smart_account import SmartAccountWallet

from .a2a import A2AToolkit

__all__ = [
"A2AToolkit",
"X402Treasurer",
"AmpersendTreasurer",
"ApiClient",
"ApiClientOptions",
"SmartAccountWallet",
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
"""A2A toolkit for LangChain."""

from .toolkit import A2AToolkit

__all__ = ["A2AToolkit"]
Loading