feat(a2a): add loongsuite-instrumentation-a2a (#28) - #274
Open
RichardoMrMu wants to merge 14 commits into
Open
RichardoMrMu wants to merge 14 commits into
RichardoMrMu wants to merge 14 commits into
Conversation
…est-requirements.txt
…rc/opentelemetry/instrumentation/a2a/package.py
…rc/opentelemetry/instrumentation/a2a/version.py
…rc/opentelemetry/instrumentation/a2a/__init__.py
…ests/test_instrumentor.py
…loongsuite_instrumentation_a2a.py
github-actions
Bot
requested review from
123liuziming,
Cirilla-zmh and
ralf0131
September 22, 2026 01:57
CI precommit failed on the new package: E402 in tests/conftest.py (imports intentionally after sys.path setup + pytest_configure), PLC0415 for lazy imports in src/tests, and I001/format. Follow the existing per-file-ignores convention used by every other loongsuite instrumentation package (PLC0415 for the package, E402+F811 for tests), and apply ruff import sorting + formatting. No behavior change; 8 tests still pass.
Contributor
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
Instrumentation lifecycle bugs, default sensitive-content capture, and missing CI registration must be resolved.
Get a fresh assessment by requesting another Copilot review.
Review effort: Balanced
Findings: 2
Open (6)
Require explicit consent before capturing prompt content · New Use delattr to restore inherited executor hooks · New Register A2A tests and lint in LoongSuite CI · New Use computed agent name in span naming · New Preserve singleton instrumentation state across constructions · New Delegate subclass initialization without suppressing errors · New
What changed in this PR
Adds automatic OpenTelemetry instrumentation for A2A server-side agent execution.
Changes:
- Creates AGENT spans around
AgentExecutor.execute. - Supports existing and late-defined executor subclasses.
- Adds package metadata, tests, documentation, and bootstrap registration.
| File | Description |
|---|---|
pyproject.toml |
Adds lint exceptions. |
loongsuite-distro/.../loongsuite_instrumentation_a2a.py |
Registers bootstrap metadata. |
.../tests/test_instrumentor.py |
Tests spans and lifecycle behavior. |
.../tests/conftest.py |
Configures tracing fixtures. |
.../tests/__init__.py |
Initializes the test package. |
.../test-requirements.txt |
Defines test dependencies. |
.../version.py |
Defines package version. |
.../package.py |
Declares instrumented dependency. |
.../a2a/__init__.py |
Implements instrumentation. |
.../README.md |
Documents installation and usage. |
.../pyproject.toml |
Defines package metadata and entry point. |
.../LICENSE |
Adds Apache 2.0 license. |
.../CHANGELOG.md |
Records the initial release. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Comment on lines
+93
to
+94
| def _capture_content() -> bool: | ||
| return os.getenv(_CAPTURE_CONTENT_ENV, "true").lower() != "false" |
Comment on lines
+285
to
+293
| try: | ||
| del base.__dict__["__init_subclass__"] | ||
| except (KeyError, TypeError): | ||
| try: | ||
| base.__init_subclass__ = classmethod( | ||
| lambda cls, **kw: None | ||
| ) | ||
| except Exception: # pragma: no cover - defensive | ||
| pass |
| build-backend = "hatchling.build" | ||
|
|
||
| [project] | ||
| name = "loongsuite-instrumentation-a2a" |
| ) | ||
|
|
||
| with self._tracer.start_as_current_span( | ||
| f"{_OP_INVOKE_AGENT} {_AGENT_NAME}", |
Comment on lines
+229
to
+234
| def __init__(self): | ||
| super().__init__() | ||
| self._wrapper = None | ||
| self._base = None | ||
| self._saved_init_subclass = None | ||
| self._wrapped_classes = [] |
Comment on lines
+261
to
+266
| try: | ||
| if saved is not None: | ||
| saved.__func__(cls, **kw) | ||
| except Exception: # pragma: no cover - defensive | ||
| pass | ||
| _wrap_execute(cls, wrapper) |
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.


What
Adds a new instrumentation package
loongsuite-instrumentation-a2a, providing automatic OpenTelemetry instrumentation for the official A2A (Agent2Agent) Python SDK (a2a-sdk).Closes #28 (roadmap #36 — "Add instrumentation for official A2A",
contribution welcome).How
Produces an ARMS gen-ai AGENT span (
gen_ai.span.kind=AGENT,gen_ai.operation.name=invoke_agent) around each server-side agent turn, bracketing the user'sAgentExecutor.executeinvocation so that all downstream work nests under a singleinvoke_agentspan with a sharedtrace_id. Recordsa2a.context_id,a2a.task_id, and the user input asgen_ai.input.messages(disable withOTEL_INSTRUMENTATION_A2A_CAPTURE_CONTENT=false).Complementary, not duplicative.
a2a-sdkalready ships an OTel tracing layer (a2a.utils.telemetry) that decorates its transports and request handlers with generic spans under the instrumenting modulea2a-python-sdk— those describe protocol plumbing, carry no gen-ai semconv, and do not wrap the user'sexecuteimplementation (which is an abstract method applications override). This package supplies exactly that missing gen-ai agent boundary, mirroring how siblingloongsuitepackages layer ARMS gen-ai spans over frameworks that already emit some telemetry.AgentExecutor.executeis an ABC method overridden by every concrete agent, so the instrumentor (1) walks the existingAgentExecutorsubclass tree atinstrument()time and wraps each subclass's ownexecuteviawrapt, and (2) installs an__init_subclass__hook onAgentExecutorso executors defined after instrumentation are wrapped as they are created. A sentinel prevents double-wrapping;uninstrument()unwraps every markedexecuteand restores__init_subclass__.Testing
tests/test_instrumentor.pydrives the reala2a-sdkAgentExecutorABC with in-process executor subclasses and asserts on spans exported to anInMemorySpanExporter(no network). Each GREEN assertion is paired with a RED baseline:gen_ai.framework=a2a,invoke_agent,a2a.context_id/a2a.task_id; inner agent work nests under the AGENT span (sametrace_id, correct parent); user input captured.instrument()still gets an AGENT span — proving the__init_subclass__hook.uninstrument(), executing a fresh executor produces no AGENT span.8 tests pass (
pytest), ona2a-sdk 1.1.5+opentelemetry-sdk.Notes
pyproject.toml, entry point, CHANGELOG and bootstrap-registry entry follow the existingloongsuite-instrumentation-*conventions (e.g. terminus2).