Skip to content

Commit fa70a64

Browse files
Open Telemetry for A2A-python-sdk, enable trace
1 parent fd1f691 commit fa70a64

File tree

9 files changed

+35
-6
lines changed

9 files changed

+35
-6
lines changed

src/a2a/client/client.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
SetTaskPushNotificationConfigRequest,
2525
SetTaskPushNotificationConfigResponse,
2626
)
27+
from a2a.utils.telemetry import SpanKind, trace_class
2728

2829

2930
class A2ACardResolver:
@@ -59,6 +60,7 @@ async def get_agent_card(
5960
) from e
6061

6162

63+
@trace_class(kind=SpanKind.CLIENT)
6264
class A2AClient:
6365
"""A2A Client."""
6466

src/a2a/server/events/event_consumer.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,13 @@
1212
TaskStatusUpdateEvent,
1313
)
1414
from a2a.utils.errors import ServerError
15+
from a2a.utils.telemetry import SpanKind, trace_class
1516

1617

1718
logger = logging.getLogger(__name__)
1819

1920

21+
@trace_class(kind=SpanKind.SERVER)
2022
class EventConsumer:
2123
"""Consumer to read events from the agent event queue."""
2224

src/a2a/server/events/event_queue.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
TaskArtifactUpdateEvent,
1212
TaskStatusUpdateEvent,
1313
)
14+
from a2a.utils.telemetry import SpanKind, trace_class
1415

1516

1617
logger = logging.getLogger(__name__)
@@ -26,6 +27,7 @@
2627
)
2728

2829

30+
@trace_class(kind=SpanKind.SERVER)
2931
class EventQueue:
3032
"""Event queue for A2A responses from agent."""
3133

src/a2a/server/events/in_memory_queue_manager.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@
66
QueueManager,
77
TaskQueueExists,
88
)
9+
from a2a.utils.telemetry import SpanKind, trace_class
910

1011

12+
@trace_class(kind=SpanKind.SERVER)
1113
class InMemoryQueueManager(QueueManager):
1214
"""InMemoryQueueManager is used for a single binary management.
1315

src/a2a/server/request_handlers/default_request_handler.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,13 @@
2828
UnsupportedOperationError,
2929
)
3030
from a2a.utils.errors import ServerError
31+
from a2a.utils.telemetry import SpanKind, trace_class
3132

3233

3334
logger = logging.getLogger(__name__)
3435

3536

37+
@trace_class(kind=SpanKind.SERVER)
3638
class DefaultRequestHandler(RequestHandler):
3739
"""Default request handler for all incoming requests."""
3840

@@ -112,7 +114,7 @@ async def on_message_send(
112114
else:
113115
queue = EventQueue()
114116
result_aggregator = ResultAggregator(task_manager)
115-
# TODO to manage the non-blocking flows.
117+
# TODO: to manage the non-blocking flows.
116118
producer_task = asyncio.create_task(
117119
self._run_event_stream(
118120
RequestContext(
@@ -128,7 +130,8 @@ async def on_message_send(
128130
try:
129131
consumer = EventConsumer(queue)
130132

131-
# TODO - register the queue for the task upon the first sign it is a task.
133+
# TODO: - register the queue for the task upon
134+
# the first sign it is a task.
132135
result = await result_aggregator.consume_all(consumer)
133136
if not result:
134137
raise ServerError(error=InternalError())

src/a2a/server/request_handlers/jsonrpc_handler.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,15 @@
3636
)
3737
from a2a.utils.errors import ServerError
3838
from a2a.utils.helpers import validate
39+
from a2a.utils.telemetry import SpanKind, trace_class
3940

4041

4142
logger = logging.getLogger(__name__)
4243

4344

45+
@trace_class(kind=SpanKind.SERVER)
4446
class JSONRPCHandler:
45-
"""A handler that maps the JSONRPC Objects to the request handler and back."""
47+
"""Maps the JSONRPC Objects to the request handler and back."""
4648

4749
def __init__(
4850
self,
@@ -53,7 +55,7 @@ def __init__(
5355
5456
Args:
5557
agent_card: The AgentCard describing the agent's capabilities.
56-
request_handler: The handler instance responsible for processing A2A requests.
58+
request_handler: The handler instance to process A2A requests.
5759
"""
5860
self.agent_card = agent_card
5961
self.request_handler = request_handler

src/a2a/utils/__init__.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
# type: ignore
2+
from opentelemetry.trace import SpanKind as OTelSpanKind
3+
14
from a2a.utils.artifact import new_text_artifact
25
from a2a.utils.helpers import (
36
append_artifact_to_task,
@@ -12,7 +15,9 @@
1215
from a2a.utils.task import new_task
1316

1417

18+
SpanKind = OTelSpanKind
1519
__all__ = [
20+
'SpanKind',
1621
'append_artifact_to_task',
1722
'build_text_artifact',
1823
'create_task_obj',

src/a2a/utils/helpers.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,13 @@
1313
TextPart,
1414
)
1515
from a2a.utils.errors import ServerError, UnsupportedOperationError
16+
from a2a.utils.telemetry import trace_function
1617

1718

1819
logger = logging.getLogger(__name__)
1920

2021

22+
@trace_function()
2123
def create_task_obj(message_send_params: MessageSendParams) -> Task:
2224
"""Create a new task object from message send params."""
2325
if not message_send_params.message.contextId:
@@ -31,6 +33,7 @@ def create_task_obj(message_send_params: MessageSendParams) -> Task:
3133
)
3234

3335

36+
@trace_function()
3437
def append_artifact_to_task(task: Task, event: TaskArtifactUpdateEvent) -> None:
3538
"""Helper method for updating Task with new artifact data."""
3639
if not task.artifacts:

src/a2a/utils/telemetry.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,11 @@ def sync_wrapper(*args, **kwargs):
210210
return async_wrapper if is_async_func else sync_wrapper
211211

212212

213-
def trace_class(include_list: list[str] = None, exclude_list: list[str] = None):
213+
def trace_class(
214+
include_list: list[str] = None,
215+
exclude_list: list[str] = None,
216+
kind=SpanKind.INTERNAL,
217+
):
214218
"""A class decorator to automatically trace specified methods of a class.
215219
216220
This decorator iterates over the methods of a class and applies the
@@ -278,7 +282,11 @@ def decorator(cls):
278282
all_methods[name] = method
279283
span_name = f'{cls.__module__}.{cls.__name__}.{name}'
280284
# Set the decorator on the method.
281-
setattr(cls, name, trace_function(span_name=span_name)(method))
285+
setattr(
286+
cls,
287+
name,
288+
trace_function(span_name=span_name, kind=kind)(method),
289+
)
282290
return cls
283291

284292
return decorator

0 commit comments

Comments
 (0)