@sapiom/langchain
LangChain v1.x middleware for agent tracking
Installation
Section titled “Installation”npm install @sapiom/langchainFunctions
Section titled “Functions”createSapiomMiddleware
Section titled “createSapiomMiddleware”Create Sapiom middleware for LangChain v1.x agents
This middleware automatically tracks:
- Agent invocations (start/end lifecycle)
- Model calls (token estimation, actual usage)
- Tool calls (pre-authorization, payment retry)
Signature:
function createSapiomMiddleware(config?: SapiomMiddlewareConfig): AgentMiddleware<any, any, any>Parameters:
| Name | Type | Description |
|---|---|---|
config | SapiomMiddlewareConfig | - |
Returns: AgentMiddleware<any, any, any> - Middleware object compatible with LangChain createAgent
Example: Basic usage
const agent = createAgent({model: "gpt-4",tools: [getWeather],middleware: [createSapiomMiddleware({apiKey: process.env.SAPIOM_API_KEY,}),],});Example: With configuration
createSapiomMiddleware({apiKey: process.env.SAPIOM_API_KEY,failureMode: "open", // or "closed"traceId: "my-workflow",agentName: "customer-support-bot",})Example: Per-invocation override
await agent.invoke({ messages: [...] },{ context: { sapiomTraceId: "conversation-456" } });generateSDKTraceId
Section titled “generateSDKTraceId”Generate SDK-prefixed trace ID
Used when user doesn’t provide traceId in config. Creates a UUID v4 with “sdk-” prefix for clarity.
Signature:
function generateSDKTraceId(): stringReturns: string - SDK-generated trace identifier (format: sdk-{uuid})
Example:
const traceId = generateSDKTraceId();// "sdk-a1b2c3d4-e5f6-7890-abcd-ef1234567890"isAuthorizationDenied
Section titled “isAuthorizationDenied”Check if error is authorization denied
Signature:
function isAuthorizationDenied(error: unknown): booleanParameters:
| Name | Type | Description |
|---|---|---|
error | unknown | - |
Returns: boolean - True if error is AuthorizationDeniedError
isAuthorizationDeniedOrTimeout
Section titled “isAuthorizationDeniedOrTimeout”Check if error is an authorization denial or timeout
These errors should always be thrown regardless of failureMode, as they represent business logic decisions rather than system failures.
Signature:
function isAuthorizationDeniedOrTimeout(error: unknown): booleanParameters:
| Name | Type | Description |
|---|---|---|
error | unknown | - |
Returns: boolean - True if error is authorization denied or timeout
isMCPPaymentError
Section titled “isMCPPaymentError”Detects if an error is an MCP payment required error (402)
MCP tools return payment errors as ToolException with specific format matching the x402-mcp spec.
Signature:
function isMCPPaymentError(error: unknown): booleanParameters:
| Name | Type | Description |
|---|---|---|
error | unknown | - |
Returns: boolean - True if error is MCP payment required
extractPaymentFromMCPError
Section titled “extractPaymentFromMCPError”Extract payment data from MCP error
Parses x402 payment response from error message or structured content.
Signature:
function extractPaymentFromMCPError(error: Record<string, unknown>): X402PaymentResponseParameters:
| Name | Type | Description |
|---|---|---|
error | Record<string, unknown> | - |
Returns: X402PaymentResponse - Parsed x402 payment response
estimateInputTokens
Section titled “estimateInputTokens”Estimate input tokens from messages
Uses character-based heuristic (~4 chars per token). LangChain v1.x middleware doesn’t have direct access to model’s getNumTokens, so we use this generic approach.
Signature:
function estimateInputTokens(messages: { content?: unknown; }[]): numberParameters:
| Name | Type | Description |
|---|---|---|
messages | { content?: unknown; }[] | - |
Returns: number - Estimated input token count
getModelId
Section titled “getModelId”Extract model ID from model object
LangChain wraps models in ConfigurableModel, RunnableBinding, etc. We check _defaultConfig or cached instances for model ID.
Signature:
function getModelId(model: unknown, depth?: number): stringParameters:
| Name | Type | Description |
|---|---|---|
model | unknown | - |
depth | number | - |
Returns: string - Model identifier string
extractActualTokens
Section titled “extractActualTokens”Extract actual token usage from model response
Checks standard LangChain usage_metadata first, falls back to response_metadata for older patterns.
Signature:
function extractActualTokens(result: unknown): TokenUsage | nullParameters:
| Name | Type | Description |
|---|---|---|
result | unknown | - |
Returns: TokenUsage | null - Token usage or null if not available
Classes
Section titled “Classes”AuthorizationDeniedError
Section titled “AuthorizationDeniedError”Error thrown when transaction authorization is denied
Constructor:
new AuthorizationDeniedError(message: string, txId: string)| Parameter | Type | Description |
|---|---|---|
message | string | - |
txId | string | - |
Interfaces
Section titled “Interfaces”SapiomMiddlewareConfig
Section titled “SapiomMiddlewareConfig”Configuration for Sapiom middleware
Extends: BaseSapiomIntegrationConfig
| Property | Type | Required | Description |
|---|---|---|---|
traceId | string | undefined | No | Workflow trace identifier Purpose: - Groups related transactions (LLM calls + tool calls) under one trace - Enables workflow cost tracking and duration analysis - Allows correlation with your distributed tracing system Behavior: - Provided: Uses your ID (e.g., “checkout-123”, “user-456-conv”) - Not provided: SDK auto-generates UUID with “sdk-” prefix Per-invoke override via context: - Pass different ID in context.sapiomTraceId |
agentId | string | undefined | No | Agent identifier (UUID or numeric ID like AG-001) Purpose: - Tags transactions with a specific agent for filtering and analytics - Enables agent-level cost tracking and usage analysis Behavior: - Provided: Uses existing agent with this ID - Agent must be ACTIVE to be used - Cannot be used with agentName |
agentName | string | undefined | No | Agent name for find-or-create behavior Purpose: - Automatically creates agent if it doesn’t exist - Convenient for dynamic agent creation - Ensures consistent agent tagging across runs Behavior: - Provided: Finds existing agent by name or creates new ACTIVE agent - Cannot be used with agentId - Agents are tenant-scoped (unique per organization) |
SapiomMiddlewareContext
Section titled “SapiomMiddlewareContext”Context passed per-invocation to override middleware config
| Property | Type | Required | Description |
|---|---|---|---|
sapiomTraceId | string | undefined | No | Override trace ID for this invocation |
sapiomAgentId | string | undefined | No | Override agent ID for this invocation |
sapiomAgentName | string | undefined | No | Override agent name for this invocation |
SapiomMiddlewareState
Section titled “SapiomMiddlewareState”State maintained by the middleware across agent lifecycle
| Property | Type | Required | Description |
|---|---|---|---|
__sapiomTraceId | string | undefined | No | Current trace ID for this agent execution |
__sapiomAgentTxId | string | undefined | No | Agent transaction ID (created in beforeAgent) |
__sapiomStartTime | number | undefined | No | Start time for duration tracking |
X402PaymentResponse
Section titled “X402PaymentResponse”x402 Payment Response Structure From x402-mcp specification
| Property | Type | Required | Description |
|---|---|---|---|
x402Version | number | Yes | x402 protocol version |
accepts | { [key: string]: unknown; scheme: string; amount?: string; minAmount?: string; maxAmount?: string; unit?: string; to?: string; }[] | Yes | Accepted payment methods |
TokenUsage
Section titled “TokenUsage”Token usage information
| Property | Type | Required | Description |
|---|---|---|---|
promptTokens | number | Yes | - |
completionTokens | number | undefined | No | - |
totalTokens | number | undefined | No | - |
AgentRequestFacts
Section titled “AgentRequestFacts”Request facts (pre-execution, from beforeAgent)
| Property | Type | Required | Description |
|---|---|---|---|
agentType | "react" | "unknown" | Yes | Agent type (react is standard for createAgent) |
entryMethod | "invoke" | "stream" | Yes | Entry method |
messageCount | number | Yes | Number of input messages |
timestamp | string | Yes | Timestamp |
AgentResponseFacts
Section titled “AgentResponseFacts”Response facts (post-execution, from afterAgent)
| Property | Type | Required | Description |
|---|---|---|---|
success | boolean | Yes | Whether execution succeeded |
durationMs | number | Yes | Total execution duration in ms |
outputMessageCount | number | Yes | Number of output messages |
AgentErrorFacts
Section titled “AgentErrorFacts”Error facts
| Property | Type | Required | Description |
|---|---|---|---|
errorType | string | Yes | Error type/name |
errorMessage | string | Yes | Error message |
elapsedMs | number | Yes | Time elapsed before error |
AgentFacts
Section titled “AgentFacts”Complete agent facts envelope
| Property | Type | Required | Description |
|---|---|---|---|
source | "langchain-agent" | Yes | - |
version | "v1" | Yes | - |
sdk | { name: string; version: string; } | Yes | - |
request | AgentRequestFacts | Yes | - |
response | AgentResponseFacts | undefined | No | - |
error | AgentErrorFacts | undefined | No | - |
ModelRequestFacts
Section titled “ModelRequestFacts”Request facts (pre-execution, from wrapModelCall)
| Property | Type | Required | Description |
|---|---|---|---|
modelId | string | Yes | Model identifier |
estimatedInputTokens | number | Yes | Estimated input tokens |
messageCount | number | Yes | Number of messages in conversation |
hasTools | boolean | Yes | Whether tools are available |
toolCount | number | Yes | Number of tools available |
timestamp | string | Yes | Timestamp |
ModelResponseFacts
Section titled “ModelResponseFacts”Response facts (post-execution)
| Property | Type | Required | Description |
|---|---|---|---|
actualInputTokens | number | Yes | Actual input tokens from provider |
actualOutputTokens | number | Yes | Actual output tokens from provider |
actualTotalTokens | number | Yes | Total tokens |
durationMs | number | Yes | Execution duration in ms |
hadToolCalls | boolean | Yes | Whether response included tool calls |
toolCallCount | number | undefined | No | Number of tool calls in response |
toolCallNames | string[] | undefined | No | Names of tools called |
finishReason | string | undefined | No | Finish reason from provider |
ModelErrorFacts
Section titled “ModelErrorFacts”Error facts
| Property | Type | Required | Description |
|---|---|---|---|
errorType | string | Yes | Error type/name |
errorMessage | string | Yes | Error message |
elapsedMs | number | Yes | Time elapsed before error |
httpStatus | number | undefined | No | HTTP status if applicable |
ModelFacts
Section titled “ModelFacts”Complete model facts envelope
| Property | Type | Required | Description |
|---|---|---|---|
source | "langchain-llm" | Yes | - |
version | "v1" | Yes | - |
sdk | { name: string; version: string; } | Yes | - |
request | ModelRequestFacts | Yes | - |
response | ModelResponseFacts | undefined | No | - |
error | ModelErrorFacts | undefined | No | - |
ToolRequestFacts
Section titled “ToolRequestFacts”Request facts (pre-execution, from wrapToolCall)
| Property | Type | Required | Description |
|---|---|---|---|
toolName | string | Yes | Tool name |
toolDescription | string | undefined | No | Tool description |
hasArguments | boolean | Yes | Whether arguments were provided |
argumentKeys | string[] | Yes | Argument keys (not values - privacy!) |
timestamp | string | Yes | Timestamp |
ToolResponseFacts
Section titled “ToolResponseFacts”Response facts (post-execution)
| Property | Type | Required | Description |
|---|---|---|---|
success | boolean | Yes | Whether execution succeeded |
durationMs | number | Yes | Execution duration in ms |
hasResult | boolean | undefined | No | Whether result was returned |
resultType | string | undefined | No | Type of result |
ToolErrorFacts
Section titled “ToolErrorFacts”Error facts
| Property | Type | Required | Description |
|---|---|---|---|
errorType | string | Yes | Error type/name |
errorMessage | string | Yes | Error message |
durationMs | number | Yes | Time elapsed before error |
isMCPPaymentError | boolean | undefined | No | Whether this was a payment required error |
ToolFacts
Section titled “ToolFacts”Complete tool facts envelope
| Property | Type | Required | Description |
|---|---|---|---|
source | "langchain-tool" | Yes | - |
version | "v1" | Yes | - |
sdk | { name: string; version: string; } | Yes | - |
request | ToolRequestFacts | Yes | - |
response | ToolResponseFacts | undefined | No | - |
error | ToolErrorFacts | undefined | No | - |