feat(a2a): add loongsuite-instrumentation-a2a (#28) - #274
RichardoMrMu wants to merge 23 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
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.
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.
| build-backend = "hatchling.build" | ||
|
|
||
| [project] | ||
| name = "loongsuite-instrumentation-a2a" |
|
Thanks for contributing this integration! The server-side agent execution tracing is a useful addition. Two suggestions before merging:
Happy to collaborate on the utility changes! |
…telemetry, align a2a.task.* to semconv-genai#195 (alibaba#28)
…l-safe, uninstrument restore (alibaba#28)
|
Thanks @sipercai — both suggestions are addressed, plus the Copilot findings, and I pushed the changes with tests. 1. Use
2. Align with A2A semconv (#195); keep agent execution distinct from A2A protocol
Copilot findings picked up in the same pass:
11 unit tests pass locally against the real |
|
Thanks for the update! All 11 tests passed locally. One clarification: we'd like Please build an Three remaining fixes:
|
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
The ineffective privacy opt-out and multiple telemetry correctness issues must be resolved before approval.
Get a fresh assessment by requesting another Copilot review.
Review effort: Balanced
Findings: 1
Open (5)
| ``NO_CONTENT`` (no capture), matching the rest of loongsuite. | ||
| """ | ||
| try: | ||
| return get_content_capturing_mode() in _CONTENT_ON_SPAN_MODES |
| # left to a dedicated protocol instrumentation. | ||
| _A2A_TASK_ID = "a2a.task.id" | ||
| _A2A_TASK_STATE = "a2a.task.state" | ||
| _A2A_CONTEXT_ID = "a2a.context.id" # a2a-sdk RequestContext grouping id |
| task_state = getattr( | ||
| getattr(current_task, "status", None), "state", None | ||
| ) | ||
| state_value = getattr(task_state, "value", task_state) | ||
| if state_value: | ||
| span.set_attribute(_A2A_TASK_STATE, str(state_value)) |
| async def test_green_agent_span_existing_subclass(instrument, span_exporter): | ||
| ExecCls = _make_executor_cls() |



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).