Documentation Index
Fetch the complete documentation index at: https://docs.pandaprobe.com/llms.txt
Use this file to discover all available pages before exploring further.
Installation
pip install "pandaprobe[langchain]"
uv add "pandaprobe[langchain]"
Setup
from pandaprobe.integrations.langchain import LangChainCallbackHandler
handler = LangChainCallbackHandler(
session_id="conversation-123",
user_id="user-abc",
tags=["production"],
)
We recommend using UUIDs for session_id and user_id so traces can be grouped reliably across runs.
Usage
The handler works with both langchain.agents.create_agent and plain LCEL pipelines (prompt | model | parser). Pass it via LangChain’s config["callbacks"]:
from langchain.agents import create_agent
agent = create_agent(
model="openai:gpt-5.4-nano",
tools=[...],
)
result = agent.invoke(
{"messages": [{"role": "user", "content": "Hello!"}]},
config={"callbacks": [handler]},
)
The handler must be passed in config["callbacks"] for each invocation. There is no global instrument() step.
What gets traced
| LangChain Callback | Span Kind | Description |
|---|
on_chain_start / on_chain_end | CHAIN (root) or AGENT (nested) | Root chain creates the trace boundary |
on_llm_start / on_chat_model_start / on_llm_end | LLM | Model, parameters, token usage, reasoning |
on_tool_start / on_tool_end | TOOL | Tool name, input, output |
on_retriever_start / on_retriever_end | RETRIEVER | Retrieval queries and results |
Trace name remapping
create_agent compiles to a LangGraph graph internally, so its root run reports name="LangGraph". LCEL pipelines report name="RunnableSequence". The handler rewrites both to "LangChain" so the trace is named consistently — custom user-given chain names are preserved.
Token usage
Token usage is extracted from LangChain’s usage_metadata (primary) or legacy llm_output.token_usage (fallback). The mapping is: input_tokens → prompt_tokens, output_tokens → completion_tokens. Reasoning tokens are subtracted from output_tokens when present.
This example builds a LangChain agent via create_agent with one tool and traces it via LangChainCallbackHandler:
from langchain.agents import create_agent
from langchain.tools import tool
import pandaprobe
from pandaprobe.integrations.langchain import LangChainCallbackHandler
@tool
def get_weather(city: str) -> str:
"""Get the current weather for a given city."""
weather_data = {
"london": "Cloudy, 15°C, 70% humidity",
"tokyo": "Sunny, 28°C, 45% humidity",
"new york": "Partly cloudy, 22°C, 55% humidity",
"paris": "Rainy, 12°C, 85% humidity",
}
return weather_data.get(city.lower(), f"Weather data not available for {city}")
@tool
def get_population(city: str) -> str:
"""Get the approximate population of a city."""
populations = {
"london": "8.8 million",
"tokyo": "13.9 million",
"new york": "8.3 million",
"paris": "2.2 million",
}
return populations.get(city.lower(), f"Population data not available for {city}")
agent = create_agent(
model="openai:gpt-5.4-nano",
tools=[get_weather, get_population],
system_prompt="You are a helpful assistant with access to weather and population tools.",
)
handler = LangChainCallbackHandler(tags=["tool-agent", "example"])
result = agent.invoke(
{"messages": [{"role": "user", "content": "What's the weather like in London and what's its population?"}]},
config={"callbacks": [handler]},
)
final_message = result["messages"][-1]
print(f"Agent: {final_message.content}")
pandaprobe.flush()
pandaprobe.shutdown()
This produces a trace with: CHAIN (root, named “LangChain”) → AGENT (tools node) → LLM (model call) → TOOL (get_weather) → TOOL (get_population) → LLM (final response).
LCEL pipelines
The handler also works for plain LCEL chains. The trace is named “LangChain” by default (the underlying RunnableSequence name is rewritten) so LCEL runs are labeled consistently with agent runs.
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI
from pandaprobe.integrations.langchain import LangChainCallbackHandler
prompt = ChatPromptTemplate.from_template("Tell me a joke about {topic}")
llm = ChatOpenAI(model="gpt-5.4-nano")
chain = prompt | llm
handler = LangChainCallbackHandler()
result = chain.invoke(
{"topic": "pandas"},
config={"callbacks": [handler]},
)