Skip to content
Go To Dashboard

@sapiom/langchain-classic

LangChain v0.x integration for agent tracking

Terminal window
npm install @sapiom/langchain-classic

Wraps an existing tool by mutating its func property.

⚠️ WARNING: This mutates the original tool. If the same tool instance is used elsewhere, it will have Sapiom tracking there too.

Use this when:

  • Wrapping tools from other libraries (MCP, custom, etc.)
  • You don’t control the tool creation
  • Mutation is acceptable (most cases)

For new tools, prefer sapiomTool() factory instead.

Signature:

function wrapSapiomTool(tool: T, config?: SapiomToolConfig | undefined): T & { __sapiomClient: SapiomClient; }

Parameters:

NameTypeDescription
toolT-
configSapiomToolConfig | undefined-

Returns: T & { __sapiomClient: SapiomClient; } - Same tool instance (mutated) with __sapiomClient marker

Example:

import { tool } from "@langchain/core/tools";
import { wrapSapiomTool } from "@sapiom/langchain-classic";
const existing = tool(
({ count }) => sendSMS(count),
{ name: "send_sms", schema: z.object({ count: z.number() }) }
);
const wrapped = wrapSapiomTool(existing, {
serviceName: "twilio",
resourceName: "send_sms"
});
// Now pre-authorized before each call!

Factory function - drop-in replacement for LangChain’s tool()

Use this when:

  • Creating new tools with Sapiom from start
  • Want clean, explicit code
  • No surprise mutations
  • Full TypeScript inference

Signature:

function sapiomTool(func: (input: SchemaOutputT, config?: RunnableConfig) => Promise<ToolOutputT> | ToolOutputT, fields: { name: string; description: string; schema: SchemaT; responseFormat?: "content" | "content_and_artifact"; returnDirect?: boolean; }, sapiomConfig?: SapiomToolConfig | undefined): SapiomDynamicTool<SchemaT, SchemaOutputT, any, ToolOutputT>

Parameters:

NameTypeDescription
func(input: SchemaOutputT, config?: RunnableConfig) => Promise<ToolOutputT> | ToolOutputT-
fields{ name: string; description: string; schema: SchemaT; responseFormat?: "content" | "content_and_artifact"; returnDirect?: boolean; }-
sapiomConfigSapiomToolConfig | undefined-

Returns: SapiomDynamicTool<SchemaT, SchemaOutputT, any, ToolOutputT> - SapiomDynamicTool instance

Example:

import { sapiomTool } from "@sapiom/langchain-classic";
import { z } from "zod";
const weatherTool = sapiomTool(
async ({ city }) => {
const weather = await getWeather(city);
return `Weather in ${city}: ${weather}`;
},
{
name: "get_weather",
description: "Get current weather for a city",
schema: z.object({
city: z.string().describe("City name"),
}),
},
{
serviceName: "weather-api",
resourceName: "current_weather",
}
);

Wrap an existing ChatOpenAI instance with Sapiom tracking

Convenience function for migrating existing code without changing instantiation. Extracts all configuration from the original model and creates a new Sapiom-tracked instance.

Signature:

function wrapChatOpenAI(model: ChatOpenAI<CallOptions>, config?: SapiomModelConfig | undefined): SapiomChatOpenAI<CallOptions>

Parameters:

NameTypeDescription
modelChatOpenAI<CallOptions>-
configSapiomModelConfig | undefined-

Returns: SapiomChatOpenAI<CallOptions> - New SapiomChatOpenAI instance with same configuration but Sapiom tracking

Example:

import { ChatOpenAI } from "@langchain/openai";
import { wrapChatOpenAI } from "@sapiom/langchain-classic";
// Existing model
const model = new ChatOpenAI({ model: "gpt-4" });
// Wrap with Sapiom tracking - one line change
const tracked = wrapChatOpenAI(model, {
sapiomApiKey: process.env.SAPIOM_API_KEY,
traceId: "conversation-123"
});
// All methods work the same, but now tracked
await tracked.invoke("Hello!");

Wrap an existing ChatAnthropic instance with Sapiom tracking

Convenience function for migrating existing code without changing instantiation. Extracts all configuration from the original model and creates a new Sapiom-tracked instance.

Signature:

function wrapChatAnthropic(model: any, config?: SapiomModelConfig | undefined): SapiomChatAnthropic<CallOptions>

Parameters:

NameTypeDescription
modelany-
configSapiomModelConfig | undefined-

Returns: SapiomChatAnthropic<CallOptions> - New SapiomChatAnthropic instance with same configuration but Sapiom tracking

Example:

import { ChatAnthropic } from "@langchain/anthropic";
import { wrapChatAnthropic } from "@sapiom/langchain-classic";
// Existing model
const model = new ChatAnthropic({ model: "claude-3-5-sonnet-20241022" });
// Wrap with Sapiom tracking - one line change
const tracked = wrapChatAnthropic(model, {
sapiomApiKey: process.env.SAPIOM_API_KEY,
traceId: "conversation-123"
});
// All methods work the same, but now tracked
await tracked.invoke("Hello!");

Wrap a LangChain agent graph with Sapiom trace tracking

This injects trace metadata into the agent’s config, ensuring all operations (model + tools) are grouped under one trace.

Signature:

function wrapSapiomAgent(graph: any, config: WrapSapiomAgentConfig): any

Parameters:

NameTypeDescription
graphany-
configWrapSapiomAgentConfig-

Returns: any - Wrapped graph with trace support

Example:

import { createReactAgent } from "@langchain/langgraph/prebuilt";
import { wrapSapiomAgent, SapiomChatOpenAI, wrapSapiomTool } from "@sapiom/langchain-classic";
// Create wrapped model
const model = new SapiomChatOpenAI({ model: "gpt-4" }, { sapiomClient });
// Wrap tools
const tools = [
wrapSapiomTool(weatherTool, { sapiomClient }),
wrapSapiomTool(calcTool, { sapiomClient })
];
// Create vanilla LangChain agent
const graph = createReactAgent({ llm: model, tools });
// Wrap with Sapiom trace tracking
const agent = wrapSapiomAgent(graph, {
sapiomClient,
traceId: "agent-workflow"
});
// All operations (model + tools) grouped under one trace
await agent.invoke({ messages: [{ role: "user", content: "Hello" }] });

Create a React agent with full Sapiom tracking

Drop-in replacement for LangChain’s createReactAgent that automatically:

  • Wraps the model with Sapiom tracking
  • Wraps all tools with Sapiom tracking
  • Wraps the agent graph for unified trace support

This is the easiest migration path - just rename createReactAgent to createSapiomReactAgent and add Sapiom config.

Signature:

function createSapiomReactAgent(params: { [key: string]: any; llm: any; tools: any[]; }, config?: SapiomModelConfig | undefined): Promise<any>

Parameters:

NameTypeDescription
params{ [key: string]: any; llm: any; tools: any[]; }-
configSapiomModelConfig | undefined-

Returns: Promise<any> - Compiled agent graph with full Sapiom tracking

Example:

import { createSapiomReactAgent } from "@sapiom/langchain-classic";
import { ChatOpenAI } from "@langchain/openai";
// Get tools
const tools = [...];
// Create Sapiom-tracked agent - just one function call
const agent = await createSapiomReactAgent(
{
llm: new ChatOpenAI({ model: "gpt-4" }),
tools,
},
{
apiKey: process.env.SAPIOM_API_KEY,
traceId: "agent-workflow"
}
);
// All operations (model + tools) are tracked
const result = await agent.invoke({
input: "What's the weather in SF?"
});

Extended DynamicStructuredTool with built-in Sapiom tracking.

Drop-in replacement for DynamicStructuredTool that includes authorization and payment handling from creation.

Constructor:

new SapiomDynamicTool(fields: { name: string; description: string; schema: SchemaT; func: (input: SchemaOutputT, config?: RunnableConfig) => Promise<ToolOutputT> | ToolOutputT; responseFormat?: "content" | "content_and_artifact"; returnDirect?: boolean; }, sapiomConfig: SapiomToolConfig | undefined)
ParameterTypeDescription
fields{ name: string; description: string; schema: SchemaT; func: (input: SchemaOutputT, config?: RunnableConfig) => Promise<ToolOutputT> | ToolOutputT; responseFormat?: "content" | "content_and_artifact"; returnDirect?: boolean; }-
sapiomConfigSapiomToolConfig | undefined-

Properties:

NameTypeDescription
#sapiomClientSapiomClient-
#sapiomConfigSapiomToolConfig-

Extended ChatOpenAI with built-in Sapiom transaction tracking and authorization

Drop-in replacement for ChatOpenAI that adds:

  • Token estimation and tracking
  • Pre-execution authorization
  • Trace-based workflow grouping
  • Real-time usage reporting

Trace Support:

  • Direct calls: Full trace support ✓
  • Vanilla agents (createReactAgent): Model traces only, tools get separate traces
  • SapiomReactAgent: Full trace support across model + tools ✓ (Phase 4)

Constructor:

new SapiomChatOpenAI(fields: any, sapiomConfig: SapiomModelConfig | undefined)
ParameterTypeDescription
fieldsany-
sapiomConfigSapiomModelConfig | undefined-

Properties:

NameTypeDescription
sapiomClientSapiomClient-
sapiomConfigSapiomModelConfig-
#defaultTraceIdstring-
#currentTraceIdstring-
#defaultAgentIdstring | undefined-
#defaultAgentNamestring | undefined-

Methods:

generate(messages: BaseMessageLike[][], options: string[] | CallOptions | undefined, callbacks: Callbacks | undefined): Promise<LLMResult>
withConfig(config: Partial<CallOptions>): any

Extended ChatAnthropic with built-in Sapiom transaction tracking and authorization

Drop-in replacement for ChatAnthropic that adds:

  • Token estimation and tracking
  • Pre-execution authorization
  • Trace-based workflow grouping
  • Real-time usage reporting

Constructor:

new SapiomChatAnthropic(fields: Partial<AnthropicInput> | undefined, sapiomConfig: SapiomModelConfig | undefined)
ParameterTypeDescription
fieldsPartial<AnthropicInput> | undefined-
sapiomConfigSapiomModelConfig | undefined-

Properties:

NameTypeDescription
sapiomClientSapiomClient-
sapiomConfigSapiomModelConfig-
#defaultTraceIdstring-
#currentTraceIdstring-
#defaultAgentIdstring | undefined-
#defaultAgentNamestring | undefined-

Methods:

generate(messages: BaseMessageLike[][], options: string[] | CallOptions | undefined, callbacks: Callbacks | undefined): Promise<LLMResult>
withConfig(config: Partial<CallOptions>): any

Configuration for Sapiom model tracking

Extends: BaseSapiomIntegrationConfig

PropertyTypeRequiredDescription
traceIdstring | undefinedNoWorkflow 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
- Reusable: Access via model.currentTraceId after invoke

Per-invoke override:
- Pass different ID in options.metadata.__sapiomTraceId
- Temporarily uses override, then returns to default

Backend behavior:
- First use: Creates trace with this ID
- Subsequent: Finds existing trace by this ID
- Race-safe: Concurrent calls with same ID share one trace
agentIdstring | undefinedNoAgent 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
agentNamestring | undefinedNoAgent 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)
serviceNamestring | undefinedNoService name for transactions
If not provided, inferred from model class name
Examples: “openai”, “anthropic”, “google”

Configuration for Sapiom tool tracking

Extends: BaseSapiomIntegrationConfig

PropertyTypeRequiredDescription
serviceNamestring | undefinedNoService name for transactions
If not provided, defaults to “tool” or inferred from tool metadata
actionNamestring | undefinedNoAction for transactions (Default: "call")
resourceNamestring | undefinedNoResource for transactions
If not provided, defaults to tool.name
qualifiersRecord<string, any> | undefinedNoAdditional qualifiers for transactions
requireAuthorizationboolean | undefinedNoWhether to require pre-authorization (Default: true)

Trace metadata injected into RunnableConfig Used internally to propagate trace context through LangChain execution

PropertyTypeRequiredDescription
__sapiomTraceIdstringYesCurrent trace ID for this execution context

This is the user’s workflow identifier that groups related transactions.
It can be:
- User-provided from config.traceId
- Auto-generated by SDK (prefixed with “sdk-”)
- Overridden per-invoke via options.metadata.__sapiomTraceId

Internally, this maps to backend’s trace.externalId field.
__sapiomAgentIdstring | undefinedNoAgent ID for tagging transactions

When set, all transactions in this context will be tagged with this agent.
Can be:
- User-provided from config.agentId (UUID or numeric ID like AG-001)
- Overridden per-invoke via options.metadata.__sapiomAgentId
__sapiomAgentNamestring | undefinedNoAgent name for find-or-create behavior

When set, transactions will use or create an agent with this name.
Can be:
- User-provided from config.agentName
- Overridden per-invoke via options.metadata.__sapiomAgentName
__sapiomClientSapiomClient | undefinedNoShared SapiomClient instance
All wrappers use this for consistency
__sapiomAgentTxIdstring | undefinedNoAgent invocation transaction ID (when using agents)
Root transaction for this trace
__sapiomEnabledboolean | undefinedNoWhether Sapiom tracking is enabled for this context
Can be false to disable tracking
__sapiomFailureModestring | undefinedNoFailure mode for this context
- ‘open’: Allow requests if Sapiom fails (prioritizes availability)
- ‘closed’: Block requests if Sapiom fails (prioritizes security)

Marker interface for Sapiom-wrapped objects

PropertyTypeRequiredDescription
__sapiomClientSapiomClientYes-
__sapiomWrappedtrueYes-

Configuration for wrapping an agent with Sapiom tracking

Extends: BaseSapiomIntegrationConfig

PropertyTypeRequiredDescription
traceIdstring | undefinedNoWorkflow trace identifier
- Provided: Uses your ID to group all agent operations
- Not provided: SDK auto-generates with “sdk-” prefix
agentIdstring | undefinedNoAgent identifier (UUID or numeric ID like AG-001)
- Uses existing agent
- Cannot be used with agentName
agentNamestring | undefinedNoAgent name for find-or-create behavior
- Creates agent if doesn’t exist
- Cannot be used with agentId