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
19 changes: 19 additions & 0 deletions langchain-paid-agent-py/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,22 @@ OPENAI_API_KEY=sk-your-openai-key

# Optional query override.
# QUERY=What's the market insight on electric vehicles?

# --- Observability (optional) -------------------------------------------
# Uncomment the LANGSMITH_* vars below to send traces to LangSmith. With
# tracing enabled, `@requires_payment` automatically emits dedicated
# `nvm:verify` and `nvm:settlement` child spans nested under the tool
# span, carrying nvm.* attributes (credits_redeemed, tx_hash, balance,
# duration_ms, …). Get an API key at https://smith.langchain.com
# → Settings → API keys. Requires `payments-py[langsmith]`.
# LANGSMITH_TRACING=true
# LANGSMITH_API_KEY=lsv2_pt_your_key
# LANGSMITH_PROJECT=nvm-langchain-sprint-1
# Region-dependent endpoint — only needed if your LangSmith account is
# NOT in GCP US (the default). 403 Forbidden on /runs/multipart means
# you're hitting the wrong region.
# GCP US (default): https://api.smith.langchain.com
# GCP EU: https://eu.api.smith.langchain.com
# GCP APAC: https://apac.api.smith.langchain.com
# AWS US: https://aws.api.smith.langchain.com
# LANGSMITH_ENDPOINT=https://eu.api.smith.langchain.com
48 changes: 48 additions & 0 deletions langchain-paid-agent-py/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,54 @@ Expected output (truncated):
payer: 0x...
```

## Observability with LangSmith (optional)

The tutorial works as-is without any observability, but every paid tool call can also surface as structured spans in [LangSmith](https://smith.langchain.com) — no code changes required.

### 1. Install with the LangSmith extra

The pyproject already pulls in `payments-py[langchain,langsmith]`, so a fresh `poetry install` is all that's needed. The `[langsmith]` extra adds the `langsmith` Python package and activates the span-emission bridge inside `@requires_payment`.

### 2. Enable tracing

In your `.env`, uncomment the LangSmith block (see [`.env.example`](./.env.example)):

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

Get an API key at https://smith.langchain.com → Settings → API keys. Pick the matching `LANGSMITH_ENDPOINT` if your account isn't on GCP US — without it, the SDK posts to the wrong region and you'll see `403 Forbidden` on `/runs/multipart` (the payment flow still succeeds; only the trace upload fails).

### 3. Re-run

```bash
poetry run buyer
```

### 4. What you should see

Open the trace in the LangSmith UI. Under the tool span you'll find two dedicated Nevermined child spans:

```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 carries `nvm.*` metadata: `nvm.plan_ids`, `nvm.scheme`, `nvm.payer`, `nvm.payment_token` (abbreviated), `nvm.credits_redeemed`, `nvm.tx_hash`, `nvm.balance.after`, `nvm.network`, `nvm.verify.duration_ms`, `nvm.settle.duration_ms`. The same metadata is also attached to the parent tool span so cmd-F finds it from either level.

The **failed discovery probe** (the first invocation without a token) also produces an `nvm:verify` span — marked failed by the raised `PaymentRequiredError`, but carrying the static `nvm.plan_ids` / `nvm.scheme` / `nvm.network`. So in the LangSmith UI it's still filterable as "verify failure for plan X", not an opaque LangChain crash.

### Privacy note

The full x402 access token the buyer passes through `config["configurable"]["payment_token"]` is normally captured by LangChain into the parent tool span's metadata. `@requires_payment` proactively strips it before opening any `nvm:*` span, so the full credential never reaches a Nevermined-emitted attribute. Only an abbreviated `nvm.payment_token` (`<first 16>…<last 4>`) remains, for correlation purposes. For blanket coverage across other channels (custom callbacks, tool args, etc.), set `LANGSMITH_HIDE_INPUTS=true`.

## Code walkthrough

### Seller: protect the tool (`src/agent.py`)
Expand Down
4 changes: 2 additions & 2 deletions langchain-paid-agent-py/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "langchain-paid-agent-py"
version = "1.0.0"
version = "1.1.0"
description = "Minimal LangChain agent with a Nevermined-protected tool"
authors = ["Nevermined <tech@nevermined.io>"]
license = "MIT"
Expand All @@ -13,7 +13,7 @@ python-dotenv = "^1.0.0"
langchain-core = "^0.3.0"
langchain-openai = "^0.3.0"
langgraph = "^0.6.0"
payments-py = {version = "^1.7.0", extras = ["langchain"]}
payments-py = {version = "^1.8.0", extras = ["langchain", "langsmith"]}

[tool.poetry.scripts]
buyer = "src.buyer:main"
Expand Down