LangChain & LangGraph OpenTelemetry Integration
Overview
This integration provides comprehensive OpenTelemetry instrumentation for both LangChain and LangGraph frameworks. It enables detailed tracing and monitoring of applications built with these frameworks.
Installation
Install traceAI LangChain
pip install traceAI-langchain
For LangGraph support (optional)
pip install traceAI-langchain[langgraph]
Install LangChain OpenAI
pip install langchain-openai
Environment Variables
Set up your environment variables to authenticate with FutureAGI.
import os
os.environ["FI_API_KEY"] = FI_API_KEY
os.environ["FI_SECRET_KEY"] = FI_SECRET_KEY
os.environ["OPENAI_API_KEY"] = OPENAI_API_KEY
LangChain Quickstart
Register Tracer Provider
Set up the trace provider to establish the observability pipeline:
from fi_instrumentation import register
from fi_instrumentation.fi_types import ProjectType
trace_provider = register(
project_type=ProjectType.OBSERVE,
project_name="langchain_app",
session_name="chat-bot"
)
Configure LangChain Instrumentation
Instrument the LangChain client to enable telemetry collection:
from traceai_langchain import LangChainInstrumentor
LangChainInstrumentor().instrument(tracer_provider=trace_provider)
Create LangChain Components
Set up your LangChain client with built-in observability:
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
prompt = ChatPromptTemplate.from_template("{x} {y} {z}?").partial(x="why is", z="blue")
chain = prompt | ChatOpenAI(model_name="gpt-3.5-turbo")
def run_chain():
try:
result = chain.invoke({"y": "sky"})
print(f"Response: {result}")
except Exception as e:
print(f"Error executing chain: {e}")
if __name__ == "__main__":
run_chain()
LangGraph Instrumentation
LangGraph tracing is automatic: LangGraph runs each node, tool and LLM through
LangChain's callback system, so LangChainInstrumentor captures them with no
LangGraph-specific setup. You get:
- Per-node spans named after the graph node (
agent,tools,ask_human, …) - Tool and LLM spans nested under their node
- Graph-node enrichment — the node's own span carries
gen_ai.agent.graph.node_nameandgen_ai.agent.graph.node_id - Session grouping — LangGraph's
configurable.thread_idbecomessession.idon every span (an explicitusing_session(...)ormetadata={"session_id": ...}wins). New in 0.2.1; with 0.2.0 or earlier, wrap the call inusing_session(thread_id). - HITL interrupts traced correctly — an interrupted node/tool span stays
OK(notERROR) and is marked withlanggraph.interrupt
LangGraphInstrumentoris a deprecated no-op kept for backwards compatibility. You can remove anyLangGraphInstrumentor().instrument()call — tracing happens throughLangChainInstrumentor.
LangGraph Quickstart
from typing import Annotated, TypedDict
from fi_instrumentation import register
from fi_instrumentation.fi_types import ProjectType
from fi_instrumentation.instrumentation.context_attributes import using_session
from langgraph.graph import StateGraph, START, END
from langgraph.graph.message import add_messages
from traceai_langchain import LangChainInstrumentor
class MyState(TypedDict):
messages: Annotated[list, add_messages]
trace_provider = register(
project_type=ProjectType.OBSERVE,
project_name="langgraph_app",
)
# Instrument LangChain — LangGraph is traced through it automatically.
LangChainInstrumentor().instrument(tracer_provider=trace_provider)
workflow = StateGraph(MyState)
workflow.add_node("agent", agent_node) # sync or async nodes both work
workflow.add_edge(START, "agent")
workflow.add_edge("agent", END)
app = workflow.compile()
# Group every span of a conversation under one session:
with using_session("thread-123"):
result = app.invoke(
{"messages": []}, {"configurable": {"thread_id": "thread-123"}}
)
LangGraph Span Attributes
Node, tool and LLM runs are standard LangChain spans (gen_ai.span.kind =
CHAIN / TOOL / LLM / AGENT) with these LangGraph additions:
gen_ai.agent.graph.node_name,gen_ai.agent.graph.node_id— the graph node (on the node's own span)langgraph_node,langgraph_step,langgraph_triggers,langgraph_path,langgraph_checkpoint_ns— LangGraph's raw callback metadatasession.id— fromconfig={"metadata": {"session_id": ...}}, elseusing_session(...), else LangGraph'sconfigurable.thread_id(0.2.1 and later; a blankthread_idsets nothing). The choice is made per run: set an explicit session around the wholeinvoke/stream, because a session set for only part of a run (inside a node, or in an innerwith_config) covers only the runs inside it, and the outer spans fall back tothread_id.langgraph.interrupt(attribute + event) on a HITL pause; alanggraph.resumeevent on resume
Deep Agents
Deep Agents builds a LangGraph graph
with create_deep_agent. There is no Deep Agents instrumentor and no separate
package: LangChainInstrumentor traces the graph, its model calls, its built-in
tools and its task subagents. The cookbook is
examples/deep_agents.py; the compatibility test
(tests/test_deepagents_compat.py) runs that same script offline against a
scripted fake model and a loopback collector.
Tested versions, as printed by the compatibility test:
| Package | Version |
|---|---|
deepagents |
0.7.21 (PyPI wheel); 0.7.23 and 0.7.28 (latest on 2026-10-16) also pass |
langchain |
1.4.3; 1.4.4 with deepagents 0.7.23 |
langchain-core |
1.6.6 (the floor deepagents 0.7.21 requires); 1.6.9 and 1.6.12 (latest) also pass |
langgraph |
1.2.12; 1.2.14 and 1.2.16 (latest) also pass |
langgraph-prebuilt |
as resolved with langgraph 1.2.12 (printed by the test), and 1.1.0 with langgraph 1.2.14; its ToolNode re-raises tool errors |
traceAI-langchain |
0.2.1 (tested from this repository before release): the thread_id → session.id change is not in 0.2.0 |
| Python | 3.11.12 and 3.13.7; 3.13.15 with deepagents 0.7.28; 3.12 with deepagents 0.7.23 |
traceAI-langchain's own lower bound (langchain-core>=0.2.43) is unchanged.
Deep Agents itself needs langchain-core>=1.6.6 and langgraph.
pip install "traceAI-langchain>=0.2.1" "deepagents==0.7.21" langgraph
With traceAI-langchain 0.2.0 or earlier, thread_id is not copied to session.id: upgrade, or wrap the call in using_session(thread_id).
from deepagents import create_deep_agent
from deepagents.backends import StateBackend
from fi_instrumentation import TraceConfig, register
from fi_instrumentation.fi_types import ProjectType
from traceai_langchain import LangChainInstrumentor
# 1. register(), 2. instrument(), 3. build the agent.
trace_provider = register(project_type=ProjectType.OBSERVE, project_name="deep-agents-app")
LangChainInstrumentor().instrument(
tracer_provider=trace_provider,
# Filesystem tool arguments and results can contain file contents.
config=TraceConfig(hide_inputs=True, hide_outputs=True),
)
# Pass a model explicitly: model=None selects the deprecated claude-sonnet-4-6 default.
agent = create_deep_agent(model=model, tools=[my_tool], backend=StateBackend())
result = agent.invoke(
{"messages": [{"role": "user", "content": "..."}]},
config={"configurable": {"thread_id": "t-1"}}, # becomes session.id (0.2.1+; with 0.2.0 use using_session)
)
trace_provider.force_flush()
What you get (all from traceAI-langchain; Deep Agents adds no keys):
- LLM spans with
gen_ai.request.modelandgen_ai.usage.*from the model'susage_metadata. Token counts are only on LLM spans, so a trace's token total is the sum of its model calls. - TOOL spans named after the tool that ran. With the default
StateBackend, 0.7.21 bindsls,read_file,write_file,edit_file,delete,glob,grepandtask, plus your tools.executeis bound only for a sandbox backend.write_todosis not a default tool at this version; harness profiles can add tools (the OpenAI Codex profile addswrite_todosforopenai:gpt-5.x-codexmodels). - Subagents: the
taskTOOL span is the parent of the subagent's span (named after the subagent, e.g.general-purpose, kindCHAIN) and of its model and tool spans, all in one trace. Subagent spans also carrylc_agent_name. - Graph nodes (
model,tools, middleware nodes) areCHAINspans; a node whose name contains "agent" (e.g.PatchToolCallsMiddleware.before_agent) is reported asAGENTby the existing name heuristic. - Session:
configurable.thread_id→session.idon every span, subagents included (0.2.1 and later; see the session note above). - Errors: a tool that raises ends its TOOL span
ERRORwith anexceptionevent (LangGraph's default tool error handler re-raises, soinvokeraises too). The status description is the exception message, without a traceback. Stacktraces remain in exception events only when error details are visible. With core 1.2.0+,TraceConfig.hide_error_detailscontrols this: content-hide flags derive it by default, and an explicitFalsekeeps error details. With older core versions,hide_inputs=Trueorhide_outputs=Truealso hides error details. Hidden failures keep their exception type, but no status description, message, stacktrace or provider-error output (body, response headers or request ids). Cancelling anastreamearly closes every span; the root span endsERRORwith aGeneratorExitexception event (asserted by the compatibility test). - CI: the Deep Agents job installs the real
deepagents==0.7.21wheel and runs the compatibility, session and error-policy tests with the current core. It also checks input/output privacy and sessions against published core 1.1.0.
Not emitted: cost, user id (set using_attributes(user_id=...) yourself),
retrieval (Deep Agents has no retriever tool), and a gen_ai.provider.name
(LangChain's raw ls_provider metadata is passed through as-is).
Examples
LangChain Examples
examples/chat_prompt_template.py- Basic chat prompt usageexamples/rag.py- Retrieval-augmented generationexamples/tool_calling_agent.py- Agent with toolsexamples/openai_chat_stream.py- Streaming responses
LangGraph Examples
examples/langgraph_simple_workflow.py- Simple state machine workflowexamples/langgraph_agent_supervisor.py- Multi-agent supervisor patternexamples/langgraph_human_in_the_loop.py- Human-in-the-loop interrupt workflow
Deep Agents Example
examples/deep_agents.py-create_deep_agentwith a custom tool, built-in file tools and atasksubagent (runs offline)
API Reference
LangChainInstrumentor
from traceai_langchain import LangChainInstrumentor
# Initialize and instrument
instrumentor = LangChainInstrumentor()
instrumentor.instrument(tracer_provider=trace_provider)
# Get current span
span = instrumentor.get_span(run_id)
# Get ancestor spans
ancestors = instrumentor.get_ancestors(run_id)
LangGraphInstrumentor
from traceai_langchain import LangGraphInstrumentor
# Deprecated no-op — LangGraph is traced automatically by LangChainInstrumentor.
# instrument() only logs a one-time deprecation notice; you can remove this call.
LangGraphInstrumentor().instrument(tracer_provider=trace_provider)
Troubleshooting
LangGraph not being traced
- Make sure you instrumented
LangChainInstrumentor— LangGraph is traced through it (LangGraphInstrumentoris a no-op). - Install the langgraph extra if you use LangGraph:
pip install traceAI-langchain[langgraph]. - Check that
langgraphis installed:pip show langgraph.
Metadata
Release files for traceAI-langchain 0.2.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| traceai_langchain-0.2.2.tar.gz | 26.4 kB | With provenance | Details for traceai_langchain-0.2.2.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| traceai_langchain-0.2.2-py3-none-any.whl | Python 3 | none | any | Details for traceai_langchain-0.2.2-py3-none-any.whl |
Total release size: 51.0 kB
Release files / traceai_langchain-0.2.2.tar.gz
| Download URL | traceai_langchain-0.2.2.tar.gz |
|---|---|
| Size | 26.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
ed2e172ca5eac9e4cf4ffe0d233eb74858d42a4acb5af37e0b4661931bccc581
|
|
BLAKE2b-256 checksum How to use checksums |
ffd8fff7c1194115d93b6a50e497b66774e93e3a41fce2934c69222db4c2c941
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 10, 2026.
Transparency logRelease files / traceai_langchain-0.2.2-py3-none-any.whl
| Download URL | traceai_langchain-0.2.2-py3-none-any.whl |
|---|---|
| Size | 24.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
c75c9a3a8fd36045d68f08fcd5b671fc64fb9506c2be2dbe43617d0e7e45ce47
|
|
BLAKE2b-256 checksum How to use checksums |
d6b15fc28eb47221b52171f4f05b1a124cd5cd4f30db28420d99570331e2c4ae
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 10, 2026.
Transparency logSecurity for traceAI-langchain 0.2.2
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
Both files in this release have been uploaded with provenance metadata.
Attestation for traceai_langchain-0.2.2.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 10, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.