agento11y-langgraph 0.10.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,3 @@
1
+ SPDX-License-Identifier: Apache-2.0
2
+
3
+ See /LICENSE at repository root for full license text.
@@ -0,0 +1,194 @@
1
+ Metadata-Version: 2.4
2
+ Name: agento11y-langgraph
3
+ Version: 0.10.0
4
+ Summary: LangGraph callback handlers for the Grafana Agent Observability Python SDK
5
+ License: SPDX-License-Identifier: Apache-2.0
6
+
7
+ See /LICENSE at repository root for full license text.
8
+
9
+ Requires-Python: >=3.10
10
+ Description-Content-Type: text/markdown
11
+ License-File: LICENSE
12
+ Requires-Dist: agento11y>=0.10.0
13
+ Requires-Dist: langchain-core>=0.3.0
14
+ Requires-Dist: langgraph>=0.2.0
15
+ Provides-Extra: dev
16
+ Requires-Dist: pytest>=9.0.3; extra == "dev"
17
+ Dynamic: license-file
18
+
19
+ # Sigil Python Framework Module: LangGraph
20
+
21
+ `agento11y-langgraph` provides callback handlers that map LangGraph lifecycle events into Sigil generation recorder lifecycles.
22
+
23
+ ## Installation
24
+
25
+ ```bash
26
+ pip install agento11y agento11y-langgraph
27
+ pip install langgraph langchain-openai
28
+ ```
29
+
30
+ ## Usage
31
+
32
+ ```python
33
+ from agento11y import Client
34
+ from agento11y_langgraph import with_agento11y_langgraph_callbacks
35
+
36
+ client = Client()
37
+ config = with_agento11y_langgraph_callbacks(None, client=client, provider_resolver="auto")
38
+ ```
39
+
40
+ ## End-to-end example (graph invoke + stream)
41
+
42
+ ```python
43
+ from typing import TypedDict
44
+
45
+ from langchain_core.runnables import RunnableConfig
46
+ from langchain_openai import ChatOpenAI
47
+ from langgraph.graph import END, StateGraph
48
+ from agento11y import Client
49
+ from agento11y_langgraph import with_agento11y_langgraph_callbacks
50
+
51
+
52
+ class GraphState(TypedDict):
53
+ prompt: str
54
+ answer: str
55
+
56
+
57
+ client = Client()
58
+ llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)
59
+
60
+
61
+ def run_model(state: GraphState, config: RunnableConfig) -> GraphState:
62
+ response = llm.invoke(
63
+ state["prompt"],
64
+ config=config,
65
+ )
66
+ return {"prompt": state["prompt"], "answer": str(response.content).strip()}
67
+
68
+
69
+ workflow = StateGraph(GraphState)
70
+ workflow.add_node("model", run_model)
71
+ workflow.set_entry_point("model")
72
+ workflow.add_edge("model", END)
73
+ graph = workflow.compile()
74
+
75
+ agento11y_config = with_agento11y_langgraph_callbacks(
76
+ None,
77
+ client=client,
78
+ provider_resolver="auto",
79
+ agent_name="langgraph-example",
80
+ agent_version="1.0.0",
81
+ )
82
+
83
+ # Non-stream graph invocation.
84
+ out = graph.invoke(
85
+ {"prompt": "Explain SLO burn rate in one paragraph.", "answer": ""},
86
+ config=agento11y_config,
87
+ )
88
+ print(out["answer"])
89
+
90
+ # Streamed graph events.
91
+ for _event in graph.stream(
92
+ {"prompt": "List three practical alerting tips.", "answer": ""},
93
+ config=agento11y_config,
94
+ ):
95
+ pass
96
+
97
+ client.shutdown()
98
+ ```
99
+
100
+ ## Workflow step capture
101
+
102
+ Enable `capture_workflow_steps=True` to record each graph node as a Sigil workflow step.
103
+ This enables the **Workflow** tab in the conversation detail view, showing node execution order,
104
+ duration, input/output state, and which LLM generations ran inside each node. The **Dependencies**
105
+ tab remains available for the generation-level DAG built from `parent_generation_ids`.
106
+
107
+ Always set `conversation_title` to a short human-readable label — it appears as the conversation
108
+ name in the Sigil UI. Without it, the title falls back to an opaque auto-generated ID.
109
+
110
+ ```python
111
+ from agento11y import Client
112
+ from agento11y_langgraph import Agento11yLangGraphHandler
113
+
114
+ client = Client()
115
+ handler = Agento11yLangGraphHandler(
116
+ client=client,
117
+ agent_name="my-pipeline",
118
+ conversation_title="My Pipeline Run",
119
+ capture_workflow_steps=True,
120
+ )
121
+
122
+ # Reuse the `graph` from the end-to-end example above. The node must pass its
123
+ # received `config` into `llm.invoke(...)` so generations link to the workflow step.
124
+ result = graph.invoke(
125
+ {"prompt": "Explain why my dashboard is slow.", "answer": ""},
126
+ config={"callbacks": [handler]},
127
+ )
128
+ client.shutdown()
129
+ ```
130
+
131
+ The handler automatically:
132
+ - Detects graph root and direct-child nodes
133
+ - Creates a workflow step per node with `input_state`, `output_state`, and timestamps
134
+ - Links LLM generation IDs to their parent step via `linked_generation_ids`
135
+ - Tracks sequential `parent_step_ids` so the DAG edges are correct
136
+
137
+ ## Persistent thread example (LangGraph checkpointer)
138
+
139
+ ```python
140
+ from langgraph.checkpoint.memory import MemorySaver
141
+
142
+ checkpointer = MemorySaver()
143
+ graph = workflow.compile(checkpointer=checkpointer)
144
+
145
+ thread_config = {
146
+ **with_agento11y_langgraph_callbacks(None, client=client, provider_resolver="auto"),
147
+ "configurable": {"thread_id": "customer-42"},
148
+ }
149
+
150
+ graph.invoke({"prompt": "Remember that my timezone is UTC+1.", "answer": ""}, config=thread_config)
151
+ graph.invoke({"prompt": "What timezone did I just give you?", "answer": ""}, config=thread_config)
152
+
153
+ # Advanced usage: explicit handler wiring remains supported.
154
+ _ = graph.invoke(
155
+ {"prompt": "manual handler wiring", "answer": ""},
156
+ config={"callbacks": [handler]},
157
+ )
158
+ ```
159
+
160
+ When `thread_id` is present, the handler records:
161
+
162
+ - `conversation_id=<thread_id>`
163
+ - `metadata["agento11y.framework.run_id"]=<run id>`
164
+ - `metadata["agento11y.framework.thread_id"]=<thread id>`
165
+ - generation span attributes `agento11y.framework.run_id` and `agento11y.framework.thread_id`
166
+
167
+ ## Behavior
168
+
169
+ - Lifecycle mapping:
170
+ - `on_llm_start` / `on_chat_model_start` -> generation recorder
171
+ - `on_tool_start` / `on_tool_end` / `on_tool_error` -> `start_tool_execution`
172
+ - `on_chain_start` / `on_chain_end` / `on_chain_error` -> framework chain spans
173
+ - `on_retriever_start` / `on_retriever_end` / `on_retriever_error` -> framework retriever spans
174
+ - `on_llm_new_token` -> first-token timestamp for stream mode
175
+ - Mode mapping: non-stream -> `SYNC`, stream -> `STREAM`.
176
+ - Provider resolver parity:
177
+ - explicit provider metadata when available
178
+ - model-name inference (`gpt-`/`o1`/`o3`/`o4` -> `openai`, `claude-` -> `anthropic`, `gemini-` -> `gemini`)
179
+ - fallback -> `custom`
180
+ - Framework tags/metadata are always set:
181
+ - `agento11y.framework.name=langgraph`
182
+ - `agento11y.framework.source=handler`
183
+ - `agento11y.framework.language=python`
184
+ - `metadata["agento11y.framework.run_id"]=<run id>`
185
+ - `metadata["agento11y.framework.thread_id"]=<thread id>` (when present in callback metadata/config)
186
+ - `metadata["agento11y.framework.parent_run_id"]` (when available)
187
+ - `metadata["agento11y.framework.component_name"]` (serialized component identity)
188
+ - `metadata["agento11y.framework.run_type"]` (`llm`, `chat`, `tool`, `chain`, `retriever`)
189
+ - `metadata["agento11y.framework.tags"]` (normalized callback tags)
190
+ - `metadata["agento11y.framework.retry_attempt"]` (when available)
191
+ - `metadata["agento11y.framework.langgraph.node"]` (when callback context exposes node identity)
192
+ - generation span attributes mirror low-cardinality framework metadata keys
193
+
194
+ Call `client.shutdown()` during teardown to flush buffered telemetry.
@@ -0,0 +1,176 @@
1
+ # Sigil Python Framework Module: LangGraph
2
+
3
+ `agento11y-langgraph` provides callback handlers that map LangGraph lifecycle events into Sigil generation recorder lifecycles.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ pip install agento11y agento11y-langgraph
9
+ pip install langgraph langchain-openai
10
+ ```
11
+
12
+ ## Usage
13
+
14
+ ```python
15
+ from agento11y import Client
16
+ from agento11y_langgraph import with_agento11y_langgraph_callbacks
17
+
18
+ client = Client()
19
+ config = with_agento11y_langgraph_callbacks(None, client=client, provider_resolver="auto")
20
+ ```
21
+
22
+ ## End-to-end example (graph invoke + stream)
23
+
24
+ ```python
25
+ from typing import TypedDict
26
+
27
+ from langchain_core.runnables import RunnableConfig
28
+ from langchain_openai import ChatOpenAI
29
+ from langgraph.graph import END, StateGraph
30
+ from agento11y import Client
31
+ from agento11y_langgraph import with_agento11y_langgraph_callbacks
32
+
33
+
34
+ class GraphState(TypedDict):
35
+ prompt: str
36
+ answer: str
37
+
38
+
39
+ client = Client()
40
+ llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)
41
+
42
+
43
+ def run_model(state: GraphState, config: RunnableConfig) -> GraphState:
44
+ response = llm.invoke(
45
+ state["prompt"],
46
+ config=config,
47
+ )
48
+ return {"prompt": state["prompt"], "answer": str(response.content).strip()}
49
+
50
+
51
+ workflow = StateGraph(GraphState)
52
+ workflow.add_node("model", run_model)
53
+ workflow.set_entry_point("model")
54
+ workflow.add_edge("model", END)
55
+ graph = workflow.compile()
56
+
57
+ agento11y_config = with_agento11y_langgraph_callbacks(
58
+ None,
59
+ client=client,
60
+ provider_resolver="auto",
61
+ agent_name="langgraph-example",
62
+ agent_version="1.0.0",
63
+ )
64
+
65
+ # Non-stream graph invocation.
66
+ out = graph.invoke(
67
+ {"prompt": "Explain SLO burn rate in one paragraph.", "answer": ""},
68
+ config=agento11y_config,
69
+ )
70
+ print(out["answer"])
71
+
72
+ # Streamed graph events.
73
+ for _event in graph.stream(
74
+ {"prompt": "List three practical alerting tips.", "answer": ""},
75
+ config=agento11y_config,
76
+ ):
77
+ pass
78
+
79
+ client.shutdown()
80
+ ```
81
+
82
+ ## Workflow step capture
83
+
84
+ Enable `capture_workflow_steps=True` to record each graph node as a Sigil workflow step.
85
+ This enables the **Workflow** tab in the conversation detail view, showing node execution order,
86
+ duration, input/output state, and which LLM generations ran inside each node. The **Dependencies**
87
+ tab remains available for the generation-level DAG built from `parent_generation_ids`.
88
+
89
+ Always set `conversation_title` to a short human-readable label — it appears as the conversation
90
+ name in the Sigil UI. Without it, the title falls back to an opaque auto-generated ID.
91
+
92
+ ```python
93
+ from agento11y import Client
94
+ from agento11y_langgraph import Agento11yLangGraphHandler
95
+
96
+ client = Client()
97
+ handler = Agento11yLangGraphHandler(
98
+ client=client,
99
+ agent_name="my-pipeline",
100
+ conversation_title="My Pipeline Run",
101
+ capture_workflow_steps=True,
102
+ )
103
+
104
+ # Reuse the `graph` from the end-to-end example above. The node must pass its
105
+ # received `config` into `llm.invoke(...)` so generations link to the workflow step.
106
+ result = graph.invoke(
107
+ {"prompt": "Explain why my dashboard is slow.", "answer": ""},
108
+ config={"callbacks": [handler]},
109
+ )
110
+ client.shutdown()
111
+ ```
112
+
113
+ The handler automatically:
114
+ - Detects graph root and direct-child nodes
115
+ - Creates a workflow step per node with `input_state`, `output_state`, and timestamps
116
+ - Links LLM generation IDs to their parent step via `linked_generation_ids`
117
+ - Tracks sequential `parent_step_ids` so the DAG edges are correct
118
+
119
+ ## Persistent thread example (LangGraph checkpointer)
120
+
121
+ ```python
122
+ from langgraph.checkpoint.memory import MemorySaver
123
+
124
+ checkpointer = MemorySaver()
125
+ graph = workflow.compile(checkpointer=checkpointer)
126
+
127
+ thread_config = {
128
+ **with_agento11y_langgraph_callbacks(None, client=client, provider_resolver="auto"),
129
+ "configurable": {"thread_id": "customer-42"},
130
+ }
131
+
132
+ graph.invoke({"prompt": "Remember that my timezone is UTC+1.", "answer": ""}, config=thread_config)
133
+ graph.invoke({"prompt": "What timezone did I just give you?", "answer": ""}, config=thread_config)
134
+
135
+ # Advanced usage: explicit handler wiring remains supported.
136
+ _ = graph.invoke(
137
+ {"prompt": "manual handler wiring", "answer": ""},
138
+ config={"callbacks": [handler]},
139
+ )
140
+ ```
141
+
142
+ When `thread_id` is present, the handler records:
143
+
144
+ - `conversation_id=<thread_id>`
145
+ - `metadata["agento11y.framework.run_id"]=<run id>`
146
+ - `metadata["agento11y.framework.thread_id"]=<thread id>`
147
+ - generation span attributes `agento11y.framework.run_id` and `agento11y.framework.thread_id`
148
+
149
+ ## Behavior
150
+
151
+ - Lifecycle mapping:
152
+ - `on_llm_start` / `on_chat_model_start` -> generation recorder
153
+ - `on_tool_start` / `on_tool_end` / `on_tool_error` -> `start_tool_execution`
154
+ - `on_chain_start` / `on_chain_end` / `on_chain_error` -> framework chain spans
155
+ - `on_retriever_start` / `on_retriever_end` / `on_retriever_error` -> framework retriever spans
156
+ - `on_llm_new_token` -> first-token timestamp for stream mode
157
+ - Mode mapping: non-stream -> `SYNC`, stream -> `STREAM`.
158
+ - Provider resolver parity:
159
+ - explicit provider metadata when available
160
+ - model-name inference (`gpt-`/`o1`/`o3`/`o4` -> `openai`, `claude-` -> `anthropic`, `gemini-` -> `gemini`)
161
+ - fallback -> `custom`
162
+ - Framework tags/metadata are always set:
163
+ - `agento11y.framework.name=langgraph`
164
+ - `agento11y.framework.source=handler`
165
+ - `agento11y.framework.language=python`
166
+ - `metadata["agento11y.framework.run_id"]=<run id>`
167
+ - `metadata["agento11y.framework.thread_id"]=<thread id>` (when present in callback metadata/config)
168
+ - `metadata["agento11y.framework.parent_run_id"]` (when available)
169
+ - `metadata["agento11y.framework.component_name"]` (serialized component identity)
170
+ - `metadata["agento11y.framework.run_type"]` (`llm`, `chat`, `tool`, `chain`, `retriever`)
171
+ - `metadata["agento11y.framework.tags"]` (normalized callback tags)
172
+ - `metadata["agento11y.framework.retry_attempt"]` (when available)
173
+ - `metadata["agento11y.framework.langgraph.node"]` (when callback context exposes node identity)
174
+ - generation span attributes mirror low-cardinality framework metadata keys
175
+
176
+ Call `client.shutdown()` during teardown to flush buffered telemetry.
@@ -0,0 +1,51 @@
1
+ """Public exports for Sigil LangGraph callback handlers."""
2
+
3
+ from typing import Any
4
+
5
+ from agento11y import Client
6
+
7
+ from .handler import Agento11yAsyncLangGraphHandler, Agento11yLangGraphHandler
8
+
9
+
10
+ def create_agento11y_langgraph_handler(
11
+ *,
12
+ client: Client,
13
+ async_handler: bool = False,
14
+ **handler_kwargs: Any,
15
+ ) -> Agento11yLangGraphHandler | Agento11yAsyncLangGraphHandler:
16
+ """Create a LangGraph Sigil callback handler for sync or async flows."""
17
+ if async_handler:
18
+ return Agento11yAsyncLangGraphHandler(client=client, **handler_kwargs)
19
+ return Agento11yLangGraphHandler(client=client, **handler_kwargs)
20
+
21
+
22
+ def with_agento11y_langgraph_callbacks(
23
+ config: dict[str, Any] | None,
24
+ *,
25
+ client: Client,
26
+ async_handler: bool = False,
27
+ **handler_kwargs: Any,
28
+ ) -> dict[str, Any]:
29
+ """Append a Sigil callback handler to a LangGraph invocation config."""
30
+ merged = dict(config or {})
31
+ existing = merged.get("callbacks")
32
+ if isinstance(existing, list):
33
+ callbacks = list(existing)
34
+ elif existing is None:
35
+ callbacks = []
36
+ else:
37
+ callbacks = [existing]
38
+ if not any(isinstance(item, (Agento11yLangGraphHandler, Agento11yAsyncLangGraphHandler)) for item in callbacks):
39
+ callbacks.append(
40
+ create_agento11y_langgraph_handler(client=client, async_handler=async_handler, **handler_kwargs)
41
+ )
42
+ merged["callbacks"] = callbacks
43
+ return merged
44
+
45
+
46
+ __all__ = [
47
+ "Agento11yLangGraphHandler",
48
+ "Agento11yAsyncLangGraphHandler",
49
+ "create_agento11y_langgraph_handler",
50
+ "with_agento11y_langgraph_callbacks",
51
+ ]