Skip to content

Commit 9db22cc

Browse files
authored
feat(loongsuite-instrumentation-langgraph): Add langgraph instrumentation to identify react agent (#143)
1 parent 460097d commit 9db22cc

File tree

19 files changed

+1393
-0
lines changed

19 files changed

+1393
-0
lines changed

.github/workflows/loongsuite_lint_0.yml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,25 @@ jobs:
127127
- name: Run tests
128128
run: tox -c tox-loongsuite.ini -e lint-loongsuite-instrumentation-langchain
129129

130+
lint-loongsuite-instrumentation-langgraph:
131+
name: LoongSuite loongsuite-instrumentation-langgraph
132+
runs-on: ubuntu-latest
133+
timeout-minutes: 30
134+
steps:
135+
- name: Checkout repo @ SHA - ${{ github.sha }}
136+
uses: actions/checkout@v4
137+
138+
- name: Set up Python 3.13
139+
uses: actions/setup-python@v5
140+
with:
141+
python-version: "3.13"
142+
143+
- name: Install tox
144+
run: pip install tox-uv
145+
146+
- name: Run tests
147+
run: tox -c tox-loongsuite.ini -e lint-loongsuite-instrumentation-langgraph
148+
130149
lint-loongsuite-instrumentation-mem0:
131150
name: LoongSuite loongsuite-instrumentation-mem0
132151
runs-on: ubuntu-latest

.github/workflows/loongsuite_test_0.yml

Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -906,6 +906,196 @@ jobs:
906906
- name: Run tests
907907
run: tox -c tox-loongsuite.ini -e py313-test-loongsuite-instrumentation-langchain-latest -- -ra
908908

909+
py39-test-loongsuite-instrumentation-langgraph-oldest_ubuntu-latest:
910+
name: LoongSuite loongsuite-instrumentation-langgraph-oldest 3.9 Ubuntu
911+
runs-on: ubuntu-latest
912+
timeout-minutes: 30
913+
steps:
914+
- name: Checkout repo @ SHA - ${{ github.sha }}
915+
uses: actions/checkout@v4
916+
917+
- name: Set up Python 3.9
918+
uses: actions/setup-python@v5
919+
with:
920+
python-version: "3.9"
921+
922+
- name: Install tox
923+
run: pip install tox-uv
924+
925+
- name: Run tests
926+
run: tox -c tox-loongsuite.ini -e py39-test-loongsuite-instrumentation-langgraph-oldest -- -ra
927+
928+
py39-test-loongsuite-instrumentation-langgraph-latest_ubuntu-latest:
929+
name: LoongSuite loongsuite-instrumentation-langgraph-latest 3.9 Ubuntu
930+
runs-on: ubuntu-latest
931+
timeout-minutes: 30
932+
steps:
933+
- name: Checkout repo @ SHA - ${{ github.sha }}
934+
uses: actions/checkout@v4
935+
936+
- name: Set up Python 3.9
937+
uses: actions/setup-python@v5
938+
with:
939+
python-version: "3.9"
940+
941+
- name: Install tox
942+
run: pip install tox-uv
943+
944+
- name: Run tests
945+
run: tox -c tox-loongsuite.ini -e py39-test-loongsuite-instrumentation-langgraph-latest -- -ra
946+
947+
py310-test-loongsuite-instrumentation-langgraph-oldest_ubuntu-latest:
948+
name: LoongSuite loongsuite-instrumentation-langgraph-oldest 3.10 Ubuntu
949+
runs-on: ubuntu-latest
950+
timeout-minutes: 30
951+
steps:
952+
- name: Checkout repo @ SHA - ${{ github.sha }}
953+
uses: actions/checkout@v4
954+
955+
- name: Set up Python 3.10
956+
uses: actions/setup-python@v5
957+
with:
958+
python-version: "3.10"
959+
960+
- name: Install tox
961+
run: pip install tox-uv
962+
963+
- name: Run tests
964+
run: tox -c tox-loongsuite.ini -e py310-test-loongsuite-instrumentation-langgraph-oldest -- -ra
965+
966+
py310-test-loongsuite-instrumentation-langgraph-latest_ubuntu-latest:
967+
name: LoongSuite loongsuite-instrumentation-langgraph-latest 3.10 Ubuntu
968+
runs-on: ubuntu-latest
969+
timeout-minutes: 30
970+
steps:
971+
- name: Checkout repo @ SHA - ${{ github.sha }}
972+
uses: actions/checkout@v4
973+
974+
- name: Set up Python 3.10
975+
uses: actions/setup-python@v5
976+
with:
977+
python-version: "3.10"
978+
979+
- name: Install tox
980+
run: pip install tox-uv
981+
982+
- name: Run tests
983+
run: tox -c tox-loongsuite.ini -e py310-test-loongsuite-instrumentation-langgraph-latest -- -ra
984+
985+
py311-test-loongsuite-instrumentation-langgraph-oldest_ubuntu-latest:
986+
name: LoongSuite loongsuite-instrumentation-langgraph-oldest 3.11 Ubuntu
987+
runs-on: ubuntu-latest
988+
timeout-minutes: 30
989+
steps:
990+
- name: Checkout repo @ SHA - ${{ github.sha }}
991+
uses: actions/checkout@v4
992+
993+
- name: Set up Python 3.11
994+
uses: actions/setup-python@v5
995+
with:
996+
python-version: "3.11"
997+
998+
- name: Install tox
999+
run: pip install tox-uv
1000+
1001+
- name: Run tests
1002+
run: tox -c tox-loongsuite.ini -e py311-test-loongsuite-instrumentation-langgraph-oldest -- -ra
1003+
1004+
py311-test-loongsuite-instrumentation-langgraph-latest_ubuntu-latest:
1005+
name: LoongSuite loongsuite-instrumentation-langgraph-latest 3.11 Ubuntu
1006+
runs-on: ubuntu-latest
1007+
timeout-minutes: 30
1008+
steps:
1009+
- name: Checkout repo @ SHA - ${{ github.sha }}
1010+
uses: actions/checkout@v4
1011+
1012+
- name: Set up Python 3.11
1013+
uses: actions/setup-python@v5
1014+
with:
1015+
python-version: "3.11"
1016+
1017+
- name: Install tox
1018+
run: pip install tox-uv
1019+
1020+
- name: Run tests
1021+
run: tox -c tox-loongsuite.ini -e py311-test-loongsuite-instrumentation-langgraph-latest -- -ra
1022+
1023+
py312-test-loongsuite-instrumentation-langgraph-oldest_ubuntu-latest:
1024+
name: LoongSuite loongsuite-instrumentation-langgraph-oldest 3.12 Ubuntu
1025+
runs-on: ubuntu-latest
1026+
timeout-minutes: 30
1027+
steps:
1028+
- name: Checkout repo @ SHA - ${{ github.sha }}
1029+
uses: actions/checkout@v4
1030+
1031+
- name: Set up Python 3.12
1032+
uses: actions/setup-python@v5
1033+
with:
1034+
python-version: "3.12"
1035+
1036+
- name: Install tox
1037+
run: pip install tox-uv
1038+
1039+
- name: Run tests
1040+
run: tox -c tox-loongsuite.ini -e py312-test-loongsuite-instrumentation-langgraph-oldest -- -ra
1041+
1042+
py312-test-loongsuite-instrumentation-langgraph-latest_ubuntu-latest:
1043+
name: LoongSuite loongsuite-instrumentation-langgraph-latest 3.12 Ubuntu
1044+
runs-on: ubuntu-latest
1045+
timeout-minutes: 30
1046+
steps:
1047+
- name: Checkout repo @ SHA - ${{ github.sha }}
1048+
uses: actions/checkout@v4
1049+
1050+
- name: Set up Python 3.12
1051+
uses: actions/setup-python@v5
1052+
with:
1053+
python-version: "3.12"
1054+
1055+
- name: Install tox
1056+
run: pip install tox-uv
1057+
1058+
- name: Run tests
1059+
run: tox -c tox-loongsuite.ini -e py312-test-loongsuite-instrumentation-langgraph-latest -- -ra
1060+
1061+
py313-test-loongsuite-instrumentation-langgraph-oldest_ubuntu-latest:
1062+
name: LoongSuite loongsuite-instrumentation-langgraph-oldest 3.13 Ubuntu
1063+
runs-on: ubuntu-latest
1064+
timeout-minutes: 30
1065+
steps:
1066+
- name: Checkout repo @ SHA - ${{ github.sha }}
1067+
uses: actions/checkout@v4
1068+
1069+
- name: Set up Python 3.13
1070+
uses: actions/setup-python@v5
1071+
with:
1072+
python-version: "3.13"
1073+
1074+
- name: Install tox
1075+
run: pip install tox-uv
1076+
1077+
- name: Run tests
1078+
run: tox -c tox-loongsuite.ini -e py313-test-loongsuite-instrumentation-langgraph-oldest -- -ra
1079+
1080+
py313-test-loongsuite-instrumentation-langgraph-latest_ubuntu-latest:
1081+
name: LoongSuite loongsuite-instrumentation-langgraph-latest 3.13 Ubuntu
1082+
runs-on: ubuntu-latest
1083+
timeout-minutes: 30
1084+
steps:
1085+
- name: Checkout repo @ SHA - ${{ github.sha }}
1086+
uses: actions/checkout@v4
1087+
1088+
- name: Set up Python 3.13
1089+
uses: actions/setup-python@v5
1090+
with:
1091+
python-version: "3.13"
1092+
1093+
- name: Install tox
1094+
run: pip install tox-uv
1095+
1096+
- name: Run tests
1097+
run: tox -c tox-loongsuite.ini -e py313-test-loongsuite-instrumentation-langgraph-latest -- -ra
1098+
9091099
py310-test-loongsuite-instrumentation-mem0-oldest_ubuntu-latest:
9101100
name: LoongSuite loongsuite-instrumentation-mem0-oldest 3.10 Ubuntu
9111101
runs-on: ubuntu-latest

copilot-instructions.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,8 @@ except Exception as e:
163163

164164
各组之间用空行分隔。
165165

166+
按照 PLC 0415 的规范,如非必要,导入语句原则上应该放在文件的开头。如果由于某些原因不得不放在代码中间,必须添加注释说明,并使用行内注释`# noqa: PLC0415`来避免校验失败。
167+
166168
### 11.2 避免重复导入
167169

168170
同一个模块只应导入一次,检查文件中是否存在重复的 import 语句。
@@ -251,3 +253,12 @@ except Exception as e:
251253

252254
* PR 的标题需要符合 conventional commits 规范
253255
* PR 中需要关联工作项
256+
257+
---
258+
259+
## 22. 中英文命名约定
260+
261+
所有注释以及说明文档中都应遵循本约定。
262+
263+
* "探针"(名词)对应英文命名应使用 instrumentation,而非 probe
264+
* "埋点"(动词)对应英文命名应使用 instrument
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Changelog
2+
3+
All notable changes to this project will be documented in this file.
4+
5+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7+
8+
## Unreleased
9+
10+
### Added
11+
12+
- Initial instrumentation framework for LangGraph
13+
([#143](https://github.com/alibaba/loongsuite-python-agent/pull/143))
14+
- Patch `create_react_agent` to set `_loongsuite_react_agent = True` flag
15+
on `CompiledStateGraph`
16+
- Patch `Pregel.stream` / `Pregel.astream` to inject
17+
`metadata["_loongsuite_react_agent"]` into `RunnableConfig`, enabling
18+
LangChain instrumentation to detect ReAct agents via callback metadata
19+
- All patches use `wrapt.wrap_function_wrapper` /
20+
`opentelemetry.instrumentation.utils.unwrap` (consistent with
21+
`loongsuite-instrumentation-langchain`)
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
# LongSuite LangGraph Instrumentation
2+
3+
OpenTelemetry instrumentation for [LangGraph](https://github.com/langchain-ai/langgraph).
4+
5+
## Installation
6+
7+
```bash
8+
pip install loongsuite-instrumentation-langgraph
9+
```
10+
11+
## Usage
12+
13+
```python
14+
from opentelemetry.instrumentation.langgraph import LangGraphInstrumentor
15+
16+
LangGraphInstrumentor().instrument()
17+
```
18+
19+
## What it does
20+
21+
This instrumentation patches two targets to enable LangChain instrumentation
22+
(`loongsuite-instrumentation-langchain`) to recognise LangGraph ReAct agents
23+
and create proper Agent / ReAct Step spans.
24+
25+
All patches use `wrapt.wrap_function_wrapper` (consistent with
26+
`loongsuite-instrumentation-langchain`).
27+
28+
### 1. `create_react_agent` patch
29+
30+
Wraps `langgraph.prebuilt.create_react_agent` to set a boolean flag
31+
`_loongsuite_react_agent = True` on the compiled `CompiledStateGraph`.
32+
The flag itself is not accessible inside LangChain's callback system — it
33+
only serves as the trigger for the second patch below.
34+
35+
### 2. `Pregel.stream` / `Pregel.astream` patch
36+
37+
Wraps the graph execution entry points so that when a graph carrying
38+
`_loongsuite_react_agent = True` is invoked, the metadata
39+
`{"_loongsuite_react_agent": True}` is injected into the `RunnableConfig`
40+
**before** execution begins.
41+
42+
The data flow:
43+
44+
```
45+
graph._loongsuite_react_agent = True # set by patch 1
46+
47+
48+
Pregel.stream() wrapper intercepts call # patch 2
49+
50+
51+
config["metadata"]["_loongsuite_react_agent"] = True
52+
53+
54+
LangChain callback manager reads metadata
55+
56+
57+
Run.metadata["_loongsuite_react_agent"] # LoongsuiteTracer reads this
58+
```
59+
60+
LangChain's callback system automatically propagates `config["metadata"]`
61+
to all child callbacks, so every sub-node within the graph also carries the
62+
flag. The `LoongsuiteTracer` disambiguates the top-level graph (Agent span)
63+
from child nodes (chain spans) by tracking an internal
64+
`inside_langgraph_react` flag that propagates through the run hierarchy.
65+
66+
`Pregel.invoke()` / `ainvoke()` internally delegate to `stream` / `astream`,
67+
so only the latter two need to be patched.
68+
69+
## How it works with LangChain instrumentation
70+
71+
When both instrumentors are active, the `LoongsuiteTracer` in the LangChain
72+
instrumentation:
73+
74+
1. **Detects the agent**`_has_langgraph_react_metadata(run)` checks
75+
`Run.metadata` for the flag. If the parent is not already inside a
76+
LangGraph agent, this run becomes an Agent span.
77+
78+
2. **Resolves agent name** — when the ReAct agent is invoked inside an
79+
outer graph node (e.g. `product_agent`), the agent span inherits the
80+
node's name (`invoke_agent product_agent`) instead of the generic
81+
default (`invoke_agent LangGraph`).
82+
83+
3. **Tracks ReAct steps** — each time the `"agent"` node fires inside
84+
the graph, a new ReAct Step span is created, with the hierarchy:
85+
`Agent > ReAct Step > LLM / Tool`.
86+
87+
## Compatibility
88+
89+
- `langgraph >= 0.2`
90+
- `langchain_core >= 0.1.0`
91+
- Python 3.9+

0 commit comments

Comments
 (0)