Agent Observability Python Framework Module: LangGraph
agento11y-langgraph provides callback handlers that map LangGraph lifecycle events into agento11y generation recorder lifecycles.
Installation
pip install agento11y agento11y-langgraph
pip install langgraph langchain-openai
Usage
from agento11y import Client
from agento11y_langgraph import with_agento11y_langgraph_callbacks
client = Client()
config = with_agento11y_langgraph_callbacks(None, client=client, provider_resolver="auto")
End-to-end example (graph invoke + stream)
from typing import TypedDict
from langchain_core.runnables import RunnableConfig
from langchain_openai import ChatOpenAI
from langgraph.graph import END, StateGraph
from agento11y import Client
from agento11y_langgraph import with_agento11y_langgraph_callbacks
class GraphState(TypedDict):
prompt: str
answer: str
client = Client()
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)
def run_model(state: GraphState, config: RunnableConfig) -> GraphState:
response = llm.invoke(
state["prompt"],
config=config,
)
return {"prompt": state["prompt"], "answer": str(response.content).strip()}
workflow = StateGraph(GraphState)
workflow.add_node("model", run_model)
workflow.set_entry_point("model")
workflow.add_edge("model", END)
graph = workflow.compile()
agento11y_config = with_agento11y_langgraph_callbacks(
None,
client=client,
provider_resolver="auto",
agent_name="langgraph-example",
agent_version="1.0.0",
)
# Non-stream graph invocation.
out = graph.invoke(
{"prompt": "Explain SLO burn rate in one paragraph.", "answer": ""},
config=agento11y_config,
)
print(out["answer"])
# Streamed graph events.
for _event in graph.stream(
{"prompt": "List three practical alerting tips.", "answer": ""},
config=agento11y_config,
):
pass
client.shutdown()
Workflow step capture
Enable capture_workflow_steps=True to record each graph node as a workflow step.
This enables the Workflow tab in the conversation detail view, showing node execution order,
duration, input/output state, and which LLM generations ran inside each node. The Dependencies
tab remains available for the generation-level DAG built from parent_generation_ids.
Always set conversation_title to a short human-readable label — it appears as the conversation
name in the Agent Observability UI. Without it, the title falls back to an opaque auto-generated ID.
from agento11y import Client
from agento11y_langgraph import Agento11yLangGraphHandler
client = Client()
handler = Agento11yLangGraphHandler(
client=client,
agent_name="my-pipeline",
conversation_title="My Pipeline Run",
capture_workflow_steps=True,
)
# Reuse the `graph` from the end-to-end example above. The node must pass its
# received `config` into `llm.invoke(...)` so generations link to the workflow step.
result = graph.invoke(
{"prompt": "Explain why my dashboard is slow.", "answer": ""},
config={"callbacks": [handler]},
)
client.shutdown()
The handler automatically:
- Detects graph root and direct-child nodes
- Creates a workflow step per node with
input_state,output_state, and timestamps - Links LLM generation IDs to their parent step via
linked_generation_ids - Tracks sequential
parent_step_idsso the DAG edges are correct
Two things silently break the linkage:
- The node's
configparameter must be annotatedRunnableConfig. LangGraph inspects the annotation to decide whether to inject the config. Annotate itdict[str, Any]and LangGraph will not pass it, so the node's generations land outside the workflow step. - A custom generation exporter must implement
export_workflow_steps. Withcapture_workflow_steps=Truethe client calls it on every flush; an exporter without the method logs a warning per batch and drops the steps.
Conversation grouping
The handler resolves the conversation id per invocation, in this order:
conversation_id/session_id/group_idin the callback metadata, invocation params, orconfigurable- The LangGraph
thread_id - The handler's
conversation_idconstructor argument - A synthetic per-run id
Pass conversation_id on the constructor when your application owns the conversation identity and
does not use a LangGraph checkpointer. Per-invocation identity still wins, so a handler built once
per process cannot override a checkpointed thread_id.
handler = Agento11yLangGraphHandler(
client=client,
agent_name="my-pipeline",
conversation_id=request.conversation_id,
conversation_title="My Pipeline Run",
)
Without any of these, each run becomes its own conversation.
Persistent thread example (LangGraph checkpointer)
from langgraph.checkpoint.memory import MemorySaver
checkpointer = MemorySaver()
graph = workflow.compile(checkpointer=checkpointer)
thread_config = {
**with_agento11y_langgraph_callbacks(None, client=client, provider_resolver="auto"),
"configurable": {"thread_id": "customer-42"},
}
graph.invoke({"prompt": "Remember that my timezone is UTC+1.", "answer": ""}, config=thread_config)
graph.invoke({"prompt": "What timezone did I just give you?", "answer": ""}, config=thread_config)
# Advanced usage: explicit handler wiring remains supported.
_ = graph.invoke(
{"prompt": "manual handler wiring", "answer": ""},
config={"callbacks": [handler]},
)
When thread_id is present, the handler records:
conversation_id=<thread_id>metadata["agento11y.framework.run_id"]=<run id>metadata["agento11y.framework.thread_id"]=<thread id>- generation span attributes
agento11y.framework.run_idandagento11y.framework.thread_id
Behavior
- Lifecycle mapping:
on_llm_start/on_chat_model_start-> generation recorder- System and developer messages passed to
on_chat_model_startare lifted out of the input message list intosystem_prompt(joined with a blank line when there are several), since the wire format has no system role. An explicitinvocation_params["system_prompt"]wins. on_tool_start/on_tool_end/on_tool_error->start_tool_executionon_chain_start/on_chain_end/on_chain_error-> framework chain spanson_retriever_start/on_retriever_end/on_retriever_error-> framework retriever spanson_llm_new_token-> first-token timestamp for stream mode
- Mode mapping: non-stream ->
SYNC, stream ->STREAM. - Provider resolver parity:
- explicit provider metadata when available
- model-name inference (
gpt-/o1/o3/o4->openai,claude-->anthropic,gemini-->gemini) - fallback ->
custom
- Framework tags/metadata are always set:
agento11y.framework.name=langgraphagento11y.framework.source=handleragento11y.framework.language=pythonmetadata["agento11y.framework.run_id"]=<run id>metadata["agento11y.framework.thread_id"]=<thread id>(when present in callback metadata/config)metadata["agento11y.framework.parent_run_id"](when available)metadata["agento11y.framework.component_name"](serialized component identity)metadata["agento11y.framework.run_type"](llm,chat,tool,chain,retriever)metadata["agento11y.framework.tags"](normalized callback tags)metadata["agento11y.framework.retry_attempt"](when available)metadata["agento11y.framework.langgraph.node"](when callback context exposes node identity)- generation span attributes mirror low-cardinality framework metadata keys
Call client.shutdown() during teardown to flush buffered telemetry.
Metadata
Release files for agento11y-langgraph 0.14.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| agento11y_langgraph-0.14.0.tar.gz | 11.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| agento11y_langgraph-0.14.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 18.7 kB
Release files / agento11y_langgraph-0.14.0.tar.gz
| Download URL | agento11y_langgraph-0.14.0.tar.gz |
|---|---|
| Size | 11.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
d3ad27e05ffd238f70fe471492db9eba569ee9dab658a2f98857dc76c515b581
|
|
BLAKE2b-256 checksum How to use checksums |
458d4a8ab7e5bb9a795f48de9fd3713d7ac788c2e058de9afaf578939de488c4
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.13
|
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 Aug 11, 2026.
Transparency logRelease files / agento11y_langgraph-0.14.0-py3-none-any.whl
| Download URL | agento11y_langgraph-0.14.0-py3-none-any.whl |
|---|---|
| Size | 7.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
129424e406fa0e974d0ae957c0f64bf79ce46ebdfa9fe11ea09d605f69cff49a
|
|
BLAKE2b-256 checksum How to use checksums |
defd00579b6ad52af9b6b688b58003a02d1af8e3ec13faf7cf7b283d3015e036
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.13
|
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 Aug 11, 2026.
Transparency log