LangChain Integration
Use this integration when your LangChain application is implemented in Python or TypeScript.
Setup
- Install SDK
pip install progress-observability
Install the
langchainpackage, not onlylangchain-core. Framework instrumentation activates only when thelangchain(orlanggraph) distribution is installed. On-corealone the app runs and LLM spans arrive, but chain structure is never emitted — silently.
- Instrument your app
Observability.instrument(
app_name=os.getenv("OBSERVABILITY_APP_NAME"),
api_key=os.getenv("OBSERVABILITY_API_KEY")
)
- Complete example
import os
from dotenv import load_dotenv
from langchain_openai import ChatOpenAI
from langchain_core.tools import tool
from langchain.agents import create_agent
from progress.observability import Observability
load_dotenv()
# LLM INSTRUMENTATION
Observability.instrument(
app_name=os.getenv("OBSERVABILITY_APP_NAME"),
api_key=os.getenv("OBSERVABILITY_API_KEY")
)
model = ChatOpenAI(
api_key=os.getenv("OPENAI_API_KEY"),
model="gpt-4o-mini"
)
@tool
def get_weather(location: str) -> str:
"""Get the weather for a given location."""
return f"The weather in {location} is cloudy with a high of 15°C."
agent = create_agent(
model=model,
tools=[get_weather],
system_prompt="You are a helpful AI assistant."
)
try:
result = agent.invoke({
"messages": [
{"role": "user", "content": "What's the weather in Paris?"}
]
})
finally:
Observability.shutdown()
- Install SDK
npm install @progress/observability
- Instrument your app
import '@progress/observability/register/hooks';
import { Observability } from '@progress/observability';
await Observability.instrument({
appName: process.env.OBSERVABILITY_APP_NAME,
apiKey: process.env.OBSERVABILITY_API_KEY
});
- Complete example
In ESM, import LangChain modules dynamically, after instrument() — static imports load LangChain before instrumentation is ready and can bypass it.
// bootstrap.ts — run with: tsx bootstrap.ts
import '@progress/observability/register/hooks';
import 'dotenv/config';
import { z } from 'zod';
import { Observability } from '@progress/observability';
await Observability.instrument({
appName: process.env.OBSERVABILITY_APP_NAME ?? 'my-langchain-app',
apiKey: process.env.OBSERVABILITY_API_KEY,
});
try {
// LangChain imports AFTER instrument()
const { ChatOpenAI } = await import('@langchain/openai');
const { tool } = await import('@langchain/core/tools');
const { createReactAgent } = await import('@langchain/langgraph/prebuilt');
const { HumanMessage } = await import('@langchain/core/messages');
const model = new ChatOpenAI({
apiKey: process.env.OPENAI_API_KEY,
model: process.env.OPENAI_MODEL ?? 'gpt-4o-mini',
});
const getWeather = tool(
async ({ location }: { location: string }) =>
`The weather in ${location} is cloudy with a high of 15°C.`,
{
name: 'get_weather',
description: 'Get the weather for a given location.',
schema: z.object({ location: z.string() }),
}
);
const agent = createReactAgent({
llm: model,
tools: [getWeather],
prompt: 'You are a helpful AI assistant.',
});
const result = await agent.invoke({
messages: [new HumanMessage("What's the weather in Paris?")],
});
console.log(result.messages[result.messages.length - 1].content);
} finally {
await Observability.shutdown();
}
Troubleshooting
Separate spans instead of a unified trace tree
We recommend using LangChain version 1.0.0 or later. With older versions, traces may appear as separate spans rather than a unified tree.
If upgrading is not an option, you can work around this by blocking the built-in OpenAI/Azure OpenAI auto-instrumentation via blockInstruments. The LangChain callback handler already captures LLM calls with the correct parent context, model name, and provider, so blocking these instrumentations prevents duplicate orphaned spans:
import { Observability, ObservabilityInstruments } from '@progress/observability';
await Observability.instrument({
appName: process.env.OBSERVABILITY_APP_NAME,
apiKey: process.env.OBSERVABILITY_API_KEY,
blockInstruments: new Set([
ObservabilityInstruments.AZURE_OPENAI,
ObservabilityInstruments.OPENAI,
]),
});