@sapiom/langchain-classic
LangChain v0.x integration for agent tracking
Installation
Section titled “Installation”npm install @sapiom/langchain-classicFunctions
Section titled “Functions”wrapSapiomTool
Section titled “wrapSapiomTool”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:
| Name | Type | Description |
|---|---|---|
tool | T | - |
config | SapiomToolConfig | 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!sapiomTool
Section titled “sapiomTool”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:
| Name | Type | Description |
|---|---|---|
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 | - |
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", });wrapChatOpenAI
Section titled “wrapChatOpenAI”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:
| Name | Type | Description |
|---|---|---|
model | ChatOpenAI<CallOptions> | - |
config | SapiomModelConfig | 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 modelconst model = new ChatOpenAI({ model: "gpt-4" });
// Wrap with Sapiom tracking - one line changeconst tracked = wrapChatOpenAI(model, { sapiomApiKey: process.env.SAPIOM_API_KEY, traceId: "conversation-123"});
// All methods work the same, but now trackedawait tracked.invoke("Hello!");wrapChatAnthropic
Section titled “wrapChatAnthropic”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:
| Name | Type | Description |
|---|---|---|
model | any | - |
config | SapiomModelConfig | 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 modelconst model = new ChatAnthropic({ model: "claude-3-5-sonnet-20241022" });
// Wrap with Sapiom tracking - one line changeconst tracked = wrapChatAnthropic(model, { sapiomApiKey: process.env.SAPIOM_API_KEY, traceId: "conversation-123"});
// All methods work the same, but now trackedawait tracked.invoke("Hello!");wrapSapiomAgent
Section titled “wrapSapiomAgent”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): anyParameters:
| Name | Type | Description |
|---|---|---|
graph | any | - |
config | WrapSapiomAgentConfig | - |
Returns: any - Wrapped graph with trace support
Example:
import { createReactAgent } from "@langchain/langgraph/prebuilt";import { wrapSapiomAgent, SapiomChatOpenAI, wrapSapiomTool } from "@sapiom/langchain-classic";
// Create wrapped modelconst model = new SapiomChatOpenAI({ model: "gpt-4" }, { sapiomClient });
// Wrap toolsconst tools = [ wrapSapiomTool(weatherTool, { sapiomClient }), wrapSapiomTool(calcTool, { sapiomClient })];
// Create vanilla LangChain agentconst graph = createReactAgent({ llm: model, tools });
// Wrap with Sapiom trace trackingconst agent = wrapSapiomAgent(graph, { sapiomClient, traceId: "agent-workflow"});
// All operations (model + tools) grouped under one traceawait agent.invoke({ messages: [{ role: "user", content: "Hello" }] });createSapiomReactAgent
Section titled “createSapiomReactAgent”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:
| Name | Type | Description |
|---|---|---|
params | { [key: string]: any; llm: any; tools: any[]; } | - |
config | SapiomModelConfig | undefined | - |
Returns: Promise<any> - Compiled agent graph with full Sapiom tracking
Example:
import { createSapiomReactAgent } from "@sapiom/langchain-classic";import { ChatOpenAI } from "@langchain/openai";
// Get toolsconst tools = [...];
// Create Sapiom-tracked agent - just one function callconst agent = await createSapiomReactAgent( { llm: new ChatOpenAI({ model: "gpt-4" }), tools, }, { apiKey: process.env.SAPIOM_API_KEY, traceId: "agent-workflow" });
// All operations (model + tools) are trackedconst result = await agent.invoke({ input: "What's the weather in SF?"});Classes
Section titled “Classes”SapiomDynamicTool
Section titled “SapiomDynamicTool”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)| Parameter | Type | Description |
|---|---|---|
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 | - |
Properties:
| Name | Type | Description |
|---|---|---|
#sapiomClient | SapiomClient | - |
#sapiomConfig | SapiomToolConfig | - |
SapiomChatOpenAI
Section titled “SapiomChatOpenAI”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)| Parameter | Type | Description |
|---|---|---|
fields | any | - |
sapiomConfig | SapiomModelConfig | undefined | - |
Properties:
| Name | Type | Description |
|---|---|---|
sapiomClient | SapiomClient | - |
sapiomConfig | SapiomModelConfig | - |
#defaultTraceId | string | - |
#currentTraceId | string | - |
#defaultAgentId | string | undefined | - |
#defaultAgentName | string | undefined | - |
Methods:
generate
Section titled “generate”generate(messages: BaseMessageLike[][], options: string[] | CallOptions | undefined, callbacks: Callbacks | undefined): Promise<LLMResult>withConfig
Section titled “withConfig”withConfig(config: Partial<CallOptions>): anySapiomChatAnthropic
Section titled “SapiomChatAnthropic”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)| Parameter | Type | Description |
|---|---|---|
fields | Partial<AnthropicInput> | undefined | - |
sapiomConfig | SapiomModelConfig | undefined | - |
Properties:
| Name | Type | Description |
|---|---|---|
sapiomClient | SapiomClient | - |
sapiomConfig | SapiomModelConfig | - |
#defaultTraceId | string | - |
#currentTraceId | string | - |
#defaultAgentId | string | undefined | - |
#defaultAgentName | string | undefined | - |
Methods:
generate
Section titled “generate”generate(messages: BaseMessageLike[][], options: string[] | CallOptions | undefined, callbacks: Callbacks | undefined): Promise<LLMResult>withConfig
Section titled “withConfig”withConfig(config: Partial<CallOptions>): anyInterfaces
Section titled “Interfaces”SapiomModelConfig
Section titled “SapiomModelConfig”Configuration for Sapiom model tracking
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 - 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 |
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) |
serviceName | string | undefined | No | Service name for transactions If not provided, inferred from model class name Examples: “openai”, “anthropic”, “google” |
SapiomToolConfig
Section titled “SapiomToolConfig”Configuration for Sapiom tool tracking
Extends: BaseSapiomIntegrationConfig
| Property | Type | Required | Description |
|---|---|---|---|
serviceName | string | undefined | No | Service name for transactions If not provided, defaults to “tool” or inferred from tool metadata |
actionName | string | undefined | No | Action for transactions (Default: "call") |
resourceName | string | undefined | No | Resource for transactions If not provided, defaults to tool.name |
qualifiers | Record<string, any> | undefined | No | Additional qualifiers for transactions |
requireAuthorization | boolean | undefined | No | Whether to require pre-authorization (Default: true) |
SapiomSessionMetadata
Section titled “SapiomSessionMetadata”Trace metadata injected into RunnableConfig Used internally to propagate trace context through LangChain execution
| Property | Type | Required | Description |
|---|---|---|---|
__sapiomTraceId | string | Yes | Current 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. |
__sapiomAgentId | string | undefined | No | Agent 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 |
__sapiomAgentName | string | undefined | No | Agent 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 |
__sapiomClient | SapiomClient | undefined | No | Shared SapiomClient instance All wrappers use this for consistency |
__sapiomAgentTxId | string | undefined | No | Agent invocation transaction ID (when using agents) Root transaction for this trace |
__sapiomEnabled | boolean | undefined | No | Whether Sapiom tracking is enabled for this context Can be false to disable tracking |
__sapiomFailureMode | string | undefined | No | Failure mode for this context - ‘open’: Allow requests if Sapiom fails (prioritizes availability) - ‘closed’: Block requests if Sapiom fails (prioritizes security) |
SapiomWrapped
Section titled “SapiomWrapped”Marker interface for Sapiom-wrapped objects
| Property | Type | Required | Description |
|---|---|---|---|
__sapiomClient | SapiomClient | Yes | - |
__sapiomWrapped | true | Yes | - |
WrapSapiomAgentConfig
Section titled “WrapSapiomAgentConfig”Configuration for wrapping an agent with Sapiom tracking
Extends: BaseSapiomIntegrationConfig
| Property | Type | Required | Description |
|---|---|---|---|
traceId | string | undefined | No | Workflow trace identifier - Provided: Uses your ID to group all agent operations - Not provided: SDK auto-generates with “sdk-” prefix |
agentId | string | undefined | No | Agent identifier (UUID or numeric ID like AG-001) - Uses existing agent - Cannot be used with agentName |
agentName | string | undefined | No | Agent name for find-or-create behavior - Creates agent if doesn’t exist - Cannot be used with agentId |