Why a wrapper instead of a callback handler
LangChain's standard instrumentation path is the BaseCallbackHandler interface. We started there in v0.4 and hated it: every tool needed manual subscription, every chain change broke the integration, the receipt-issuance latency added up. v0.5 replaced it with `withReceipts(executor)` — one wrap, idempotent, captures every tool call and every intermediate step without per-tool wiring.
What the wrapper captures
Each invocation produces one receipt covering: input query, all tools called in order, intermediate-step reasoning (digest only by default), final output, total runtime cost, model used. Sub-steps are stored as `extensions.langchain_steps` on the receipt — a 12-tool agent run shows up as one receipt with 12 step entries, not 12 receipts.
Streaming + async support
LangChain agents commonly stream tokens for UX. The wrapper supports `executor.stream()` and async iteration; receipts are issued at end-of-stream, not at first token. Latency overhead is ~30ms per receipt (one round-trip to api.genzagents.com), which doesn't affect the user-perceived stream.
LangGraph-aware sub-receipts
LangGraph apps that use the wrapper produce parent-child receipt structures: the top-level graph run is the parent, each node's tool calls are children with parent_run_id pointing back. This makes long graph runs auditable end-to-end — you can see "the planner node decided X, the executor node did Y, the validator node rejected step Z".
Cost tracking across multi-model graphs
A common LangGraph pattern: planner uses GPT-5, executor uses Claude, validator uses Gemini. The wrapper logs cost per node per model. Your dashboard shows "this graph run cost £0.42, split £0.18 planner / £0.20 executor / £0.04 validator". That granularity is what makes multi-model architectures economically tunable.
Server-side deployment patterns
If you ship a LangChain app behind a /api/agent endpoint, configure the wrapper with the per-user `humanId` from the auth context. Every receipt then carries the right per-user attribution, even though the server runs all the LangChain code. Use this pattern for SaaS apps with thousands of end-users — each user's usage shows up as their own receipts on the agent.
Install
$ npm install @genzagentsio/langchain
import { withReceipts } from "@genzagentsio/langchain"
const executor = withReceipts(rawExecutor, {
agentDid: "did:genz:...",
apiKey: process.env.GENZAGENTS_API_KEY,
})
await executor.invoke({ input: userQuery })What we capture
Every AgentExecutor.invoke call: tools used, model, runtime cost, intermediate steps.
Verify it works
Run the wrapped executor → confirm one receipt per invocation on /dashboard.