Skip to content

Commit 1b25fcc

Browse files
authored
feat(deepagents): support DeepAgents instrumentation (#228)
2 parents e4232fd + 66ac6cd commit 1b25fcc

21 files changed

Lines changed: 1742 additions & 20 deletions

File tree

.github/workflows/loongsuite_lint_0.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ jobs:
8484
shell: bash
8585
env:
8686
LOONGSUITE_ALL_JOBS: >-
87-
[{"name": "lint-loongsuite-instrumentation-agentscope", "package": "loongsuite-instrumentation-agentscope", "tox_env": "lint-loongsuite-instrumentation-agentscope", "ui_name": "loongsuite-instrumentation-agentscope"}, {"name": "lint-loongsuite-instrumentation-dashscope", "package": "loongsuite-instrumentation-dashscope", "tox_env": "lint-loongsuite-instrumentation-dashscope", "ui_name": "loongsuite-instrumentation-dashscope"}, {"name": "lint-loongsuite-instrumentation-claude-agent-sdk", "package": "loongsuite-instrumentation-claude-agent-sdk", "tox_env": "lint-loongsuite-instrumentation-claude-agent-sdk", "ui_name": "loongsuite-instrumentation-claude-agent-sdk"}, {"name": "lint-loongsuite-instrumentation-google-adk", "package": "loongsuite-instrumentation-google-adk", "tox_env": "lint-loongsuite-instrumentation-google-adk", "ui_name": "loongsuite-instrumentation-google-adk"}, {"name": "lint-loongsuite-instrumentation-agno", "package": "loongsuite-instrumentation-agno", "tox_env": "lint-loongsuite-instrumentation-agno", "ui_name": "loongsuite-instrumentation-agno"}, {"name": "lint-loongsuite-instrumentation-langchain", "package": "loongsuite-instrumentation-langchain", "tox_env": "lint-loongsuite-instrumentation-langchain", "ui_name": "loongsuite-instrumentation-langchain"}, {"name": "lint-loongsuite-instrumentation-langgraph", "package": "loongsuite-instrumentation-langgraph", "tox_env": "lint-loongsuite-instrumentation-langgraph", "ui_name": "loongsuite-instrumentation-langgraph"}, {"name": "lint-loongsuite-instrumentation-qwen-agent", "package": "loongsuite-instrumentation-qwen-agent", "tox_env": "lint-loongsuite-instrumentation-qwen-agent", "ui_name": "loongsuite-instrumentation-qwen-agent"}, {"name": "lint-loongsuite-instrumentation-hermes-agent", "package": "loongsuite-instrumentation-hermes-agent", "tox_env": "lint-loongsuite-instrumentation-hermes-agent", "ui_name": "loongsuite-instrumentation-hermes-agent"}, {"name": "lint-loongsuite-instrumentation-mem0", "package": "loongsuite-instrumentation-mem0", "tox_env": "lint-loongsuite-instrumentation-mem0", "ui_name": "loongsuite-instrumentation-mem0"}, {"name": "lint-util-genai", "package": "util-genai", "tox_env": "lint-util-genai", "ui_name": "util-genai"}, {"name": "lint-loongsuite-instrumentation-litellm", "package": "loongsuite-instrumentation-litellm", "tox_env": "lint-loongsuite-instrumentation-litellm", "ui_name": "loongsuite-instrumentation-litellm"}, {"name": "lint-loongsuite-instrumentation-crewai", "package": "loongsuite-instrumentation-crewai", "tox_env": "lint-loongsuite-instrumentation-crewai", "ui_name": "loongsuite-instrumentation-crewai"}, {"name": "lint-loongsuite-instrumentation-qwenpaw", "package": "loongsuite-instrumentation-qwenpaw", "tox_env": "lint-loongsuite-instrumentation-qwenpaw", "ui_name": "loongsuite-instrumentation-qwenpaw"}, {"name": "lint-loongsuite-instrumentation-algotune", "package": "loongsuite-instrumentation-algotune", "tox_env": "lint-loongsuite-instrumentation-algotune", "ui_name": "loongsuite-instrumentation-algotune"}, {"name": "lint-loongsuite-instrumentation-bfclv4", "package": "loongsuite-instrumentation-bfclv4", "tox_env": "lint-loongsuite-instrumentation-bfclv4", "ui_name": "loongsuite-instrumentation-bfclv4"}, {"name": "lint-loongsuite-instrumentation-claw-eval", "package": "loongsuite-instrumentation-claw-eval", "tox_env": "lint-loongsuite-instrumentation-claw-eval", "ui_name": "loongsuite-instrumentation-claw-eval"}, {"name": "lint-loongsuite-instrumentation-minisweagent", "package": "loongsuite-instrumentation-minisweagent", "tox_env": "lint-loongsuite-instrumentation-minisweagent", "ui_name": "loongsuite-instrumentation-minisweagent"}, {"name": "lint-loongsuite-instrumentation-openhands", "package": "loongsuite-instrumentation-openhands", "tox_env": "lint-loongsuite-instrumentation-openhands", "ui_name": "loongsuite-instrumentation-openhands"}, {"name": "lint-loongsuite-instrumentation-slop-code", "package": "loongsuite-instrumentation-slop-code", "tox_env": "lint-loongsuite-instrumentation-slop-code", "ui_name": "loongsuite-instrumentation-slop-code"}, {"name": "lint-loongsuite-instrumentation-terminus2", "package": "loongsuite-instrumentation-terminus2", "tox_env": "lint-loongsuite-instrumentation-terminus2", "ui_name": "loongsuite-instrumentation-terminus2"}, {"name": "lint-loongsuite-instrumentation-vita", "package": "loongsuite-instrumentation-vita", "tox_env": "lint-loongsuite-instrumentation-vita", "ui_name": "loongsuite-instrumentation-vita"}, {"name": "lint-loongsuite-instrumentation-webarena", "package": "loongsuite-instrumentation-webarena", "tox_env": "lint-loongsuite-instrumentation-webarena", "ui_name": "loongsuite-instrumentation-webarena"}, {"name": "lint-loongsuite-instrumentation-widesearch", "package": "loongsuite-instrumentation-widesearch", "tox_env": "lint-loongsuite-instrumentation-widesearch", "ui_name": "loongsuite-instrumentation-widesearch"}, {"name": "lint-loongsuite-instrumentation-wildtool", "package": "loongsuite-instrumentation-wildtool", "tox_env": "lint-loongsuite-instrumentation-wildtool", "ui_name": "loongsuite-instrumentation-wildtool"}]
87+
[{"name": "lint-loongsuite-instrumentation-agentscope", "package": "loongsuite-instrumentation-agentscope", "tox_env": "lint-loongsuite-instrumentation-agentscope", "ui_name": "loongsuite-instrumentation-agentscope"}, {"name": "lint-loongsuite-instrumentation-dashscope", "package": "loongsuite-instrumentation-dashscope", "tox_env": "lint-loongsuite-instrumentation-dashscope", "ui_name": "loongsuite-instrumentation-dashscope"}, {"name": "lint-loongsuite-instrumentation-claude-agent-sdk", "package": "loongsuite-instrumentation-claude-agent-sdk", "tox_env": "lint-loongsuite-instrumentation-claude-agent-sdk", "ui_name": "loongsuite-instrumentation-claude-agent-sdk"}, {"name": "lint-loongsuite-instrumentation-google-adk", "package": "loongsuite-instrumentation-google-adk", "tox_env": "lint-loongsuite-instrumentation-google-adk", "ui_name": "loongsuite-instrumentation-google-adk"}, {"name": "lint-loongsuite-instrumentation-agno", "package": "loongsuite-instrumentation-agno", "tox_env": "lint-loongsuite-instrumentation-agno", "ui_name": "loongsuite-instrumentation-agno"}, {"name": "lint-loongsuite-instrumentation-langchain", "package": "loongsuite-instrumentation-langchain", "tox_env": "lint-loongsuite-instrumentation-langchain", "ui_name": "loongsuite-instrumentation-langchain"}, {"name": "lint-loongsuite-instrumentation-langgraph", "package": "loongsuite-instrumentation-langgraph", "tox_env": "lint-loongsuite-instrumentation-langgraph", "ui_name": "loongsuite-instrumentation-langgraph"}, {"name": "lint-loongsuite-instrumentation-deepagents", "package": "loongsuite-instrumentation-deepagents", "tox_env": "lint-loongsuite-instrumentation-deepagents", "ui_name": "loongsuite-instrumentation-deepagents"}, {"name": "lint-loongsuite-instrumentation-qwen-agent", "package": "loongsuite-instrumentation-qwen-agent", "tox_env": "lint-loongsuite-instrumentation-qwen-agent", "ui_name": "loongsuite-instrumentation-qwen-agent"}, {"name": "lint-loongsuite-instrumentation-hermes-agent", "package": "loongsuite-instrumentation-hermes-agent", "tox_env": "lint-loongsuite-instrumentation-hermes-agent", "ui_name": "loongsuite-instrumentation-hermes-agent"}, {"name": "lint-loongsuite-instrumentation-mem0", "package": "loongsuite-instrumentation-mem0", "tox_env": "lint-loongsuite-instrumentation-mem0", "ui_name": "loongsuite-instrumentation-mem0"}, {"name": "lint-util-genai", "package": "util-genai", "tox_env": "lint-util-genai", "ui_name": "util-genai"}, {"name": "lint-loongsuite-instrumentation-litellm", "package": "loongsuite-instrumentation-litellm", "tox_env": "lint-loongsuite-instrumentation-litellm", "ui_name": "loongsuite-instrumentation-litellm"}, {"name": "lint-loongsuite-instrumentation-crewai", "package": "loongsuite-instrumentation-crewai", "tox_env": "lint-loongsuite-instrumentation-crewai", "ui_name": "loongsuite-instrumentation-crewai"}, {"name": "lint-loongsuite-instrumentation-qwenpaw", "package": "loongsuite-instrumentation-qwenpaw", "tox_env": "lint-loongsuite-instrumentation-qwenpaw", "ui_name": "loongsuite-instrumentation-qwenpaw"}, {"name": "lint-loongsuite-instrumentation-algotune", "package": "loongsuite-instrumentation-algotune", "tox_env": "lint-loongsuite-instrumentation-algotune", "ui_name": "loongsuite-instrumentation-algotune"}, {"name": "lint-loongsuite-instrumentation-bfclv4", "package": "loongsuite-instrumentation-bfclv4", "tox_env": "lint-loongsuite-instrumentation-bfclv4", "ui_name": "loongsuite-instrumentation-bfclv4"}, {"name": "lint-loongsuite-instrumentation-claw-eval", "package": "loongsuite-instrumentation-claw-eval", "tox_env": "lint-loongsuite-instrumentation-claw-eval", "ui_name": "loongsuite-instrumentation-claw-eval"}, {"name": "lint-loongsuite-instrumentation-minisweagent", "package": "loongsuite-instrumentation-minisweagent", "tox_env": "lint-loongsuite-instrumentation-minisweagent", "ui_name": "loongsuite-instrumentation-minisweagent"}, {"name": "lint-loongsuite-instrumentation-openhands", "package": "loongsuite-instrumentation-openhands", "tox_env": "lint-loongsuite-instrumentation-openhands", "ui_name": "loongsuite-instrumentation-openhands"}, {"name": "lint-loongsuite-instrumentation-slop-code", "package": "loongsuite-instrumentation-slop-code", "tox_env": "lint-loongsuite-instrumentation-slop-code", "ui_name": "loongsuite-instrumentation-slop-code"}, {"name": "lint-loongsuite-instrumentation-terminus2", "package": "loongsuite-instrumentation-terminus2", "tox_env": "lint-loongsuite-instrumentation-terminus2", "ui_name": "loongsuite-instrumentation-terminus2"}, {"name": "lint-loongsuite-instrumentation-vita", "package": "loongsuite-instrumentation-vita", "tox_env": "lint-loongsuite-instrumentation-vita", "ui_name": "loongsuite-instrumentation-vita"}, {"name": "lint-loongsuite-instrumentation-webarena", "package": "loongsuite-instrumentation-webarena", "tox_env": "lint-loongsuite-instrumentation-webarena", "ui_name": "loongsuite-instrumentation-webarena"}, {"name": "lint-loongsuite-instrumentation-widesearch", "package": "loongsuite-instrumentation-widesearch", "tox_env": "lint-loongsuite-instrumentation-widesearch", "ui_name": "loongsuite-instrumentation-widesearch"}, {"name": "lint-loongsuite-instrumentation-wildtool", "package": "loongsuite-instrumentation-wildtool", "tox_env": "lint-loongsuite-instrumentation-wildtool", "ui_name": "loongsuite-instrumentation-wildtool"}]
8888
LOONGSUITE_FULL: ${{ steps.detect.outputs.full }}
8989
LOONGSUITE_PACKAGES: ${{ steps.detect.outputs.packages }}
9090
run: |

.github/workflows/loongsuite_test_0.yml

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.
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 DeepAgents instrumentation that marks `create_deep_agent` graphs so
13+
LangChain instrumentation emits an `AGENT` span for the DeepAgents root.
14+
- DeepAgents skill-load telemetry: when an agent reads a registered skill's
15+
top-level `SKILL.md` through the built-in `read_file` tool, the tool span
16+
carries `gen_ai.skill.name`, `gen_ai.skill.id`,
17+
`gen_ai.skill.description`, and `gen_ai.skill.version`.
18+
19+
## Version 0.6.0 (2026-06-03)
20+
21+
There are no changelog entries for this release.
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# LoongSuite DeepAgents Instrumentation
2+
3+
LoongSuite instrumentation for [DeepAgents](https://github.com/langchain-ai/deepagents).
4+
5+
## Installation
6+
7+
```bash
8+
pip install loongsuite-instrumentation-deepagents
9+
```
10+
11+
## Usage
12+
13+
```python
14+
from opentelemetry.instrumentation.deepagents import DeepAgentsInstrumentor
15+
16+
DeepAgentsInstrumentor().instrument()
17+
18+
from deepagents import create_deep_agent
19+
```
20+
21+
When instrumenting manually, call `DeepAgentsInstrumentor().instrument()` before
22+
importing or binding `deepagents.create_deep_agent`. Auto-instrumentation runs
23+
before application imports and does not need this extra ordering step.
24+
25+
## What it does
26+
27+
This instrumentation patches `deepagents.graph.create_deep_agent` so the final
28+
graph returned by DeepAgents is marked with `_loongsuite_react_agent = True`.
29+
It also marks the graph as a DeepAgents agent and injects the same flags into
30+
call-time `RunnableConfig` metadata for `invoke`, `ainvoke`, `stream`, and
31+
`astream`.
32+
33+
The marker is consumed by `loongsuite-instrumentation-langchain`, which routes
34+
the DeepAgents root graph span as an `AGENT` span instead of a generic `CHAIN`
35+
span.
36+
37+
The LangChain tracer also maps each DeepAgents `model` decision node to a
38+
`react step` span. Multi-round tool flows end the previous step with
39+
`gen_ai.react.finish_reason = "tool_calls"` and the final step with `"stop"`.
40+
This package intentionally does not create separate ENTRY spans or GenAI
41+
metrics.
42+
43+
For DeepAgents skills, the framework first loads skill metadata through
44+
`SkillsMiddleware.before_agent` and exposes the available skills in the system
45+
prompt. Loading the full skill instructions happens when the agent calls the
46+
built-in filesystem tool to read the skill file, for example
47+
`read_file(file_path="/skills/foo/SKILL.md")`. That `execute_tool read_file`
48+
span is annotated with `gen_ai.skill.name`, `gen_ai.skill.id`,
49+
`gen_ai.skill.description`, and `gen_ai.skill.version` when the file path
50+
matches a registered top-level `SKILL.md`. Reading helper files under the same
51+
skill directory is recorded as a normal file read, not as a skill load.
52+
53+
## Compatibility
54+
55+
- `deepagents >= 0.6.0, < 0.7.0`
56+
- Python 3.11+
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
[build-system]
2+
requires = ["hatchling"]
3+
build-backend = "hatchling.build"
4+
5+
[project]
6+
name = "loongsuite-instrumentation-deepagents"
7+
dynamic = ["version"]
8+
description = "LoongSuite DeepAgents Instrumentation"
9+
readme = "README.md"
10+
license = "Apache-2.0"
11+
requires-python = ">=3.11"
12+
authors = [
13+
{ name = "OpenTelemetry Authors", email = "cncf-opentelemetry-contributors@lists.cncf.io" },
14+
]
15+
classifiers = [
16+
"Development Status :: 4 - Beta",
17+
"Intended Audience :: Developers",
18+
"License :: OSI Approved :: Apache Software License",
19+
"Programming Language :: Python",
20+
"Programming Language :: Python :: 3",
21+
"Programming Language :: Python :: 3.11",
22+
"Programming Language :: Python :: 3.12",
23+
"Programming Language :: Python :: 3.13",
24+
]
25+
dependencies = [
26+
"loongsuite-instrumentation-langchain",
27+
"loongsuite-instrumentation-langgraph",
28+
"opentelemetry-api ~= 1.37",
29+
"opentelemetry-instrumentation >= 0.58b0",
30+
"opentelemetry-semantic-conventions >= 0.58b0",
31+
"wrapt >= 1.0.0, < 2.0.0",
32+
]
33+
34+
[project.optional-dependencies]
35+
instruments = [
36+
"deepagents >= 0.6.0, < 0.7.0",
37+
]
38+
39+
[project.entry-points.opentelemetry_instrumentor]
40+
deepagents = "opentelemetry.instrumentation.deepagents:DeepAgentsInstrumentor"
41+
42+
[project.urls]
43+
Homepage = "https://github.com/alibaba/loongsuite-python-agent/tree/main/instrumentation-loongsuite/loongsuite-instrumentation-deepagents"
44+
Repository = "https://github.com/alibaba/loongsuite-python-agent"
45+
46+
[tool.hatch.version]
47+
path = "src/opentelemetry/instrumentation/deepagents/version.py"
48+
49+
[tool.hatch.build.targets.sdist]
50+
include = [
51+
"src",
52+
"tests",
53+
]
54+
55+
[tool.hatch.build.targets.wheel]
56+
packages = ["src/opentelemetry"]
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
# Copyright The OpenTelemetry Authors
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
"""LoongSuite instrumentation for langchain-ai DeepAgents."""
16+
17+
from __future__ import annotations
18+
19+
import importlib
20+
import logging
21+
from typing import Any, Collection
22+
23+
from opentelemetry.instrumentation.deepagents.internal.patch import (
24+
instrument_create_deep_agent,
25+
uninstrument_create_deep_agent,
26+
)
27+
from opentelemetry.instrumentation.deepagents.package import _instruments
28+
from opentelemetry.instrumentation.instrumentor import BaseInstrumentor
29+
30+
__all__ = ["DeepAgentsInstrumentor"]
31+
32+
logger = logging.getLogger(__name__)
33+
34+
35+
def _instrument_dependency(
36+
module_name: str,
37+
class_name: str,
38+
**kwargs: Any,
39+
) -> BaseInstrumentor | None:
40+
try:
41+
module = importlib.import_module(module_name)
42+
except ModuleNotFoundError as exc:
43+
if exc.name == module_name or (
44+
exc.name is not None and module_name.startswith(f"{exc.name}.")
45+
):
46+
logger.warning(
47+
"deepagents instrumentation requires %s; continuing without it.",
48+
module_name,
49+
)
50+
return None
51+
raise
52+
53+
instrumentor_type = getattr(module, class_name, None)
54+
if instrumentor_type is None:
55+
logger.warning(
56+
"deepagents instrumentation could not find %s.%s",
57+
module_name,
58+
class_name,
59+
)
60+
return None
61+
62+
instrumentor = instrumentor_type()
63+
if instrumentor.is_instrumented_by_opentelemetry:
64+
return None
65+
instrumentor.instrument(**kwargs)
66+
if instrumentor.is_instrumented_by_opentelemetry:
67+
return instrumentor
68+
return None
69+
70+
71+
class DeepAgentsInstrumentor(BaseInstrumentor):
72+
"""Instrument DeepAgents root graphs as LangChain agent spans."""
73+
74+
def instrumentation_dependencies(self) -> Collection[str]:
75+
return _instruments
76+
77+
def _instrument(self, **kwargs: Any) -> None:
78+
self._dependency_instrumentors = []
79+
langchain_instrumentor = _instrument_dependency(
80+
"opentelemetry.instrumentation.langchain",
81+
"LangChainInstrumentor",
82+
**kwargs,
83+
)
84+
if langchain_instrumentor is not None:
85+
self._dependency_instrumentors.append(langchain_instrumentor)
86+
87+
langgraph_instrumentor = _instrument_dependency(
88+
"opentelemetry.instrumentation.langgraph",
89+
"LangGraphInstrumentor",
90+
**kwargs,
91+
)
92+
if langgraph_instrumentor is not None:
93+
self._dependency_instrumentors.append(langgraph_instrumentor)
94+
95+
instrument_create_deep_agent()
96+
97+
def _uninstrument(self, **kwargs: Any) -> None:
98+
uninstrument_create_deep_agent()
99+
for instrumentor in reversed(
100+
getattr(self, "_dependency_instrumentors", [])
101+
):
102+
try:
103+
instrumentor.uninstrument()
104+
except Exception as exc: # noqa: BLE001
105+
logger.debug(
106+
"Failed to uninstrument deepagents dependency %s: %s",
107+
instrumentor.__class__.__name__,
108+
exc,
109+
)
110+
self._dependency_instrumentors = []
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Copyright The OpenTelemetry Authors
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.

0 commit comments

Comments
 (0)