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
51 changes: 51 additions & 0 deletions docs/integrate/add-to-your-agent/langchain.mdx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: "LangChain"
description: "Add Nevermined x402 payments to your LangChain and LangGraph agents"

Check warning on line 3 in docs/integrate/add-to-your-agent/langchain.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

docs/integrate/add-to-your-agent/langchain.mdx#L3

Did you really mean 'Nevermined'?
icon: "link-simple"
frameworks: ["langchain", "langgraph", "python", "typescript"]
---
Expand All @@ -25,14 +25,14 @@
| **`requiresPayment` wrapper/decorator** | Direct tool invocation, CLI scripts, notebooks | Per-tool wrapper |
| **Payment middleware on HTTP server** | Serving the agent over HTTP | HTTP middleware |

Both use the same Nevermined plan, credits, and settlement flow — choose whichever fits your deployment model.

Check warning on line 28 in docs/integrate/add-to-your-agent/langchain.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

docs/integrate/add-to-your-agent/langchain.mdx#L28

Did you really mean 'Nevermined'?

## Installation

<Tabs>
<Tab title="TypeScript">
```bash
npm install @nevermined-io/payments @langchain/core @langchain/openai zod

Check warning on line 35 in docs/integrate/add-to-your-agent/langchain.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

docs/integrate/add-to-your-agent/langchain.mdx#L35

Did you really mean 'zod'?
```

<Note>
Expand Down Expand Up @@ -109,7 +109,7 @@
{ payments, planId: PLAN_ID, credits: 1 }
),
{
name: 'search_data',

Check warning on line 112 in docs/integrate/add-to-your-agent/langchain.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

docs/integrate/add-to-your-agent/langchain.mdx#L112

Did you really mean 'search_data'?
description: 'Search for data on a given topic. Costs 1 credit.',
schema: z.object({ query: z.string() }),
}
Expand All @@ -126,7 +126,7 @@

```python filename="agent.py"
import os
from dotenv import load_dotenv

Check warning on line 129 in docs/integrate/add-to-your-agent/langchain.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

docs/integrate/add-to-your-agent/langchain.mdx#L129

Did you really mean 'dotenv'?
from langchain_core.runnables import RunnableConfig
from langchain_core.tools import tool
from payments_py import Payments, PaymentOptions
Expand Down Expand Up @@ -184,7 +184,7 @@
</Tab>
<Tab title="Python">
```python filename="client.py"
from payments_py import Payments, PaymentOptions

Check warning on line 187 in docs/integrate/add-to-your-agent/langchain.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

docs/integrate/add-to-your-agent/langchain.mdx#L187

Did you really mean 'payments_py'?

# Subscriber side — acquire token
subscriber = Payments.get_instance(
Expand Down Expand Up @@ -233,10 +233,10 @@
</Tab>
<Tab title="Python">
```python
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)

Check warning on line 236 in docs/integrate/add-to-your-agent/langchain.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

docs/integrate/add-to-your-agent/langchain.mdx#L236

Did you really mean 'llm'?
tools = [search_data, summarize_data, research_topic]

Check warning on line 237 in docs/integrate/add-to-your-agent/langchain.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

docs/integrate/add-to-your-agent/langchain.mdx#L237

Did you really mean 'summarize_data'?

Check warning on line 237 in docs/integrate/add-to-your-agent/langchain.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

docs/integrate/add-to-your-agent/langchain.mdx#L237

Did you really mean 'research_topic'?
llm_with_tools = llm.bind_tools(tools)

Check warning on line 238 in docs/integrate/add-to-your-agent/langchain.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

docs/integrate/add-to-your-agent/langchain.mdx#L238

Did you really mean 'llm_with_tools'?
tool_map = {t.name: t for t in tools}

Check warning on line 239 in docs/integrate/add-to-your-agent/langchain.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

docs/integrate/add-to-your-agent/langchain.mdx#L239

Did you really mean 'tool_map'?

messages = [HumanMessage(content="Search for AI trends")]
ai_message = llm_with_tools.invoke(messages)
Expand Down Expand Up @@ -292,7 +292,7 @@

### Discovery-first flow with `create_paid_react_agent` (Python)

If the buyer **doesn't** know the plan id / scheme / provider up front, the x402 way is to invoke the agent without a token, let the protected tool raise `PaymentRequiredError`, and read the requirements off the exception. By default LangGraph's `ToolNode` would catch that exception and stringify it into a `ToolMessage` for the LLM — losing the `X402PaymentRequired` payload. The SDK ships `create_paid_react_agent` for exactly this: it builds the underlying `ToolNode` with `handle_tool_errors=False`, so the exception propagates to `agent.invoke()`'s caller with the payload intact.

Check warning on line 295 in docs/integrate/add-to-your-agent/langchain.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

docs/integrate/add-to-your-agent/langchain.mdx#L295

Did you really mean 'stringify'?

```python
from payments_py.x402.langchain import (
Expand Down Expand Up @@ -350,8 +350,8 @@

<Note>
For card-delegation plans the SDK needs a `DelegationConfig` so it can
auto-create the Stripe / Braintree delegation that backs the payment

Check warning on line 353 in docs/integrate/add-to-your-agent/langchain.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

docs/integrate/add-to-your-agent/langchain.mdx#L353

Did you really mean 'Braintree'?
signature. Crypto plans (`nvm:erc4337`) need the same — see the [x402

Check warning on line 354 in docs/integrate/add-to-your-agent/langchain.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

docs/integrate/add-to-your-agent/langchain.mdx#L354

Did you really mean 'Crypto'?
Protocol module reference](/docs/api-reference/python/x402-module) for the
full token-options surface.
</Note>
Expand Down Expand Up @@ -382,6 +382,57 @@
layer instead.
</Warning>

### Trace payments in LangSmith (Python)

Once `@requires_payment` is wired up, you can have every paid tool call surface as structured spans in [LangSmith](https://smith.langchain.com) — no code changes required, just two env vars and an optional extra.

**Install the optional extra:**

```bash
pip install "payments-py[langchain,langsmith]"
```

**Enable tracing:**

```bash filename=".env"
LANGSMITH_TRACING=true
LANGSMITH_API_KEY=lsv2_pt_your-key
LANGSMITH_PROJECT=nvm-langchain # optional
# Only needed if your LangSmith account is NOT in GCP US:
# LANGSMITH_ENDPOINT=https://eu.api.smith.langchain.com
```

That's it. Running the same agent invocation now produces a trace tree with two dedicated Nevermined child spans nested under the tool:

Check warning on line 405 in docs/integrate/add-to-your-agent/langchain.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

docs/integrate/add-to-your-agent/langchain.mdx#L405

Did you really mean 'Nevermined'?

```text
LangGraph
└── tools
└── get_market_insight
├── nvm:verify 0.28s ← around payments.facilitator.verify_permissions
└── nvm:settlement 1.88s ← around payments.facilitator.settle_permissions
```

Each Nevermined span carries `nvm.*` metadata for audit + reconciliation:

Check warning on line 415 in docs/integrate/add-to-your-agent/langchain.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

docs/integrate/add-to-your-agent/langchain.mdx#L415

Did you really mean 'Nevermined'?

| Span | Attributes |
| ---- | ---------- |
| `nvm:verify` | `nvm.plan_ids`, `nvm.scheme`, `nvm.network`, `nvm.agent_id`, `nvm.payer`, `nvm.agent_request_id`, `nvm.payment_token` (abbreviated), `nvm.verify.duration_ms` |
| `nvm:settlement` | `nvm.credits_redeemed`, `nvm.balance.after`, `nvm.tx_hash`, `nvm.network`, `nvm.payer`, `nvm.payment_token` (abbreviated), `nvm.settle.duration_ms` |

The same `nvm.*` metadata is also attached to the parent tool span so cmd-F searches in the LangSmith UI land on either level.

**Failed discovery probes are first-class too.** When the buyer's first `agent.invoke()` runs without a `payment_token` (the discovery-first flow), the `nvm:verify` span still opens, carries the static `nvm.plan_ids` / `nvm.scheme` / `nvm.network`, and is marked failed by the raised `PaymentRequiredError`. That gives you "which plan was the probe against?" filterability instead of an opaque LangChain crash.

<Warning title="Sensitive data in traces">
The `payment_token` the buyer passes in `config["configurable"]["payment_token"]` would normally be captured into the parent tool span's metadata by LangChain and inherited by every child span. The full token grants access to the protected tool until it expires. `@requires_payment` **proactively strips it from the parent span's metadata** before opening any `nvm:*` child, so the full credential never reaches a Nevermined-emitted attribute. The abbreviated `nvm.payment_token` (first 16 chars + `…` + last 4 chars) remains available for correlation.

Other channels (custom callbacks, an explicit `add_metadata({"payment_token": ...})`, tool signatures that contain the token) are not covered — strip them yourself or set `export LANGSMITH_HIDE_INPUTS=true` for blanket coverage.
</Warning>

If a span failure ever occurs during metadata building or attachment, observability is silently dropped — the payment flow itself is never interrupted. Settlement receipts persist via `last_settlement()` regardless of whether the span emit succeeded.

For the full module reference (function signatures, manual usage from non-LangChain code paths), see the [Python LangChain module reference](/docs/api-reference/python/langchain-module#observability-with-langsmith).

### Dynamic Credits

<Note>
Expand Down Expand Up @@ -700,7 +751,7 @@
<Tab title="Python">
```python
from langchain_core.tools import tool
from langchain_openai import ChatOpenAI

Check warning on line 754 in docs/integrate/add-to-your-agent/langchain.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

docs/integrate/add-to-your-agent/langchain.mdx#L754

Did you really mean 'langchain_openai'?
from langgraph.prebuilt import create_react_agent

# Plain tools — no payment decorators
Expand Down Expand Up @@ -782,7 +833,7 @@
import json
import os
import httpx
from payments_py import Payments, PaymentOptions

Check warning on line 836 in docs/integrate/add-to-your-agent/langchain.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

docs/integrate/add-to-your-agent/langchain.mdx#L836

Did you really mean 'payments_py'?

SERVER_URL = os.environ.get("SERVER_URL", "http://localhost:8000")
PLAN_ID = os.environ["NVM_PLAN_ID"]
Expand Down Expand Up @@ -838,8 +889,8 @@
| ----- | ----------- |
| `creditsRedeemed` | Number of credits charged |
| `remainingBalance` | Subscriber's remaining credit balance |
| `transaction` | Blockchain transaction hash |

Check warning on line 892 in docs/integrate/add-to-your-agent/langchain.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

docs/integrate/add-to-your-agent/langchain.mdx#L892

Did you really mean 'Blockchain'?
| `network` | Blockchain network (CAIP-2 format) |

Check warning on line 893 in docs/integrate/add-to-your-agent/langchain.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

docs/integrate/add-to-your-agent/langchain.mdx#L893

Did you really mean 'Blockchain'?
| `payer` | Subscriber wallet address |

---
Expand All @@ -851,7 +902,7 @@
<Tabs>
<Tab title="TypeScript">
```typescript
const myTool = tool(

Check warning on line 905 in docs/integrate/add-to-your-agent/langchain.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

docs/integrate/add-to-your-agent/langchain.mdx#L905

Did you really mean 'myTool'?
requiresPayment(
(args) => `Result for ${args.query}`,
{
Expand Down Expand Up @@ -898,7 +949,7 @@
<Tabs>
<Tab title="TypeScript">
```typescript
const myTool = tool(

Check warning on line 952 in docs/integrate/add-to-your-agent/langchain.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

docs/integrate/add-to-your-agent/langchain.mdx#L952

Did you really mean 'myTool'?
requiresPayment(
(args) => `Result`,
{ payments, planId: PLAN_ID, credits: 1, network: 'eip155:84532' }
Expand Down Expand Up @@ -971,7 +1022,7 @@

<CardGroup cols={2}>
<Card title="Express Middleware (TS)" icon="js" href="/docs/integrate/add-to-your-agent/express">
Deep dive into paymentMiddleware for Express

Check warning on line 1025 in docs/integrate/add-to-your-agent/langchain.mdx

View check run for this annotation

Mintlify / Mintlify Validation (neverminedag) - vale-spellcheck

docs/integrate/add-to-your-agent/langchain.mdx#L1025

Did you really mean 'paymentMiddleware'?
</Card>

<Card title="FastAPI Middleware (Python)" icon="python" href="/docs/integrate/add-to-your-agent/fastapi">
Expand Down
18 changes: 18 additions & 0 deletions skills/nevermined-payments/references/langchain-integration.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,24 @@ if receipt:

**Single-tenant only.** The slot is process-global — in multi-tenant servers (concurrent settlements), the value reflects whichever invocation settled most recently. Use a callback or observability layer for multi-tenant scenarios.

## Observability with LangSmith — `payments-py[langsmith]`

Install the optional extra (`pip install "payments-py[langchain,langsmith]"`) and set `LANGSMITH_TRACING=true` + `LANGSMITH_API_KEY` (+ `LANGSMITH_ENDPOINT=https://eu.api.smith.langchain.com` for non-US accounts). `@requires_payment` then automatically emits two dedicated child spans nested under the active tool span — `nvm:verify` and `nvm:settlement` — each carrying `nvm.*` metadata for audit and reconciliation. No code changes required.

```text
LangGraph
└── tools
└── get_market_insight
├── nvm:verify attrs: nvm.plan_ids, nvm.scheme, nvm.network, nvm.payer, nvm.agent_request_id, nvm.payment_token (abbrev), nvm.verify.duration_ms
└── nvm:settlement attrs: nvm.credits_redeemed, nvm.balance.after, nvm.tx_hash, nvm.payer, nvm.payment_token (abbrev), nvm.settle.duration_ms
```

The same `nvm.*` metadata is also attached to the parent tool span. Failed discovery probes (no `payment_token` in config) still produce an `nvm:verify` span with the static attrs, marked failed by the raised `PaymentRequiredError` — so observability survives the first invocation of the discovery-first flow.

**Token redaction.** LangChain auto-captures every key in `config["configurable"]` into the parent tool span's metadata, which child spans inherit. The decorator strips `payment_token` from the parent span before opening any `nvm:*` child, so the full credential never reaches a Nevermined-emitted attribute. The abbreviated `nvm.payment_token` (`<first 16>…<last 4>`) remains for correlation. To cover non-configurable channels (custom callbacks, tool args, etc.) set `LANGSMITH_HIDE_INPUTS=true` for blanket coverage.

Observability failures are silently logged and dropped — the payment flow itself is never interrupted, and `last_settlement()` continues to return the on-chain receipt even if span emission fails.

## Decorator Configuration

### Single plan
Expand Down