Skip to content

Commit 50fdf3f

Browse files
authored
feat: traceability extension - more follow up changes - docs update, agent card (#330)
* feat: traceability extension - more follow up changes - docs update, agent card * some small fixes * removed unused import * small linter fix
1 parent dc9cbeb commit 50fdf3f

8 files changed

Lines changed: 7656 additions & 8 deletions

File tree

extensions/traceability/v1/spec.md

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
# Traceability Extension
2+
3+
## Overview
4+
5+
This extension defines how to add traceability information to `Message` and `Artifact`
6+
objects.
7+
8+
## Extension URI
9+
10+
The URI of this extension is `https://github.com/a2aproject/a2a-samples/extensions/traceability/v1`.
11+
12+
This is the only URI accepted for this extension.
13+
14+
## Traceability Format
15+
16+
17+
18+
## Message/Artifact Metadata Field
19+
20+
Traceability information MUST be stored in the metadata for a Message or Artifact, under a
21+
field with the key `github.com/a2aproject/a2a-samples/extensions/traceability/v1/traceability`,
22+
or an addtional artifact in the returned completed response.
23+
24+
```proto
25+
// A Trace message that contains a collection of spans.
26+
message ResponseTrace {
27+
// A unique identifier for the trace.
28+
string trace_id = 1;
29+
30+
31+
// The list of steps that make up this trace.
32+
repeated Step steps = 2;
33+
}
34+
35+
36+
enum CallTypeEnum {
37+
AGENT = 1;
38+
TOOL = 2;
39+
}
40+
41+
42+
message StepAction {
43+
oneof action {
44+
ToolInvocation tool_invocation = 1;
45+
AgentInvocation agent_invocation = 2;
46+
}
47+
}
48+
49+
50+
message ToolInvocation {
51+
string tool_name = 1;
52+
google.protobuf.Struct parameters = 2;
53+
}
54+
55+
56+
message AgentInvocation {
57+
// The URL of the agent that was invoked.
58+
string agent_url = 1;
59+
// the agent name
60+
string agent_name = 2;
61+
// The request message sent to the agent.
62+
google.protobuf.Struct requests = 3;
63+
// intenral response trace for this specific steps, if the callee also
64+
// supports the traceability extension and the caller requests traceability.
65+
ResponseTrace response_trace = 4;
66+
}
67+
68+
69+
// A Step message that represents a single operation within a trace.
70+
message Step {
71+
// A unique identifier for this step.
72+
string step_id = 1;
73+
74+
75+
// The trace_id of the trace this step belongs to.
76+
string trace_id = 2;
77+
78+
79+
// The step_id of the parent step. Empty if this is a root step.
80+
string parent_step_id = 3;
81+
82+
83+
// The type of the operation this step represents.
84+
CallTypeEnum call_type = 4;
85+
86+
87+
// Detailed invocation about the step
88+
StepAction step_action = 5;
89+
90+
91+
// The cost of the operation this step represents.
92+
int64 cost = 6;
93+
94+
95+
// The token of the operation this step represents.
96+
int64 total_tokens = 7;
97+
98+
99+
// A set of key-value attributes with additional details about the step.
100+
map<string, string> additional_attributes = 8;
101+
102+
103+
// The latency of the operation this step represents.
104+
int64 latency = 9;
105+
106+
107+
// The start time of the operation.
108+
google.protobuf.Timestamp start_time = 10;
109+
110+
111+
// The end time of the operation.
112+
google.protobuf.Timestamp end_time = 11;
113+
}
114+
115+
```
116+
117+
## Extension Activation
118+
119+
Clients indicate their desire to receive traceability on response by specifying
120+
the [Extension URI](#extension-uri) via the transport-defined extension
121+
activation mechanism. For JSON-RPC and HTTP transports, this is indicated via
122+
the `X-A2A-Extensions` HTTP header. For gRPC, this is indicated via the
123+
`X-A2A-Extensions` metadata value.

samples/python/extensions/traceability/src/traceability_ext/__init__.py

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
from enum import Enum
66
from typing import Any
77

8+
from a2a.types import AgentExtension
9+
810

911
_CORE_PATH = 'github.com/a2aproject/a2a-samples/extensions/traceability/v1'
1012
TRACEABILITY_EXTENSION_URI = f'https://{_CORE_PATH}'
@@ -238,7 +240,7 @@ def __exit__(
238240
self,
239241
exc_type: type[BaseException] | None,
240242
exc_val: BaseException | None,
241-
traceback: types.TracebackType | None,
243+
exc_traceback: types.TracebackType | None,
242244
) -> bool:
243245
"""Context manager exit point that finalizes the trace step.
244246
@@ -253,10 +255,25 @@ def __exit__(
253255
error_msg = None
254256
if exc_type:
255257
error_msg = ''.join(
256-
traceback.format_exception(exc_type, exc_val, traceback)
258+
exc_traceback.format_exception(exc_type, exc_val, exc_traceback)
257259
)
258260
self.step.end_step(error=error_msg)
259261
if self.response_trace:
260262
self.response_trace.add_step(self.step)
261263
# Do not suppress exceptions
262264
return False
265+
266+
267+
class TraceabilityExtension:
268+
"""An implementation of the Traceability extension.
269+
270+
This extension implementation illustrates a simple way for an extension to
271+
provide functionality to agent developers.
272+
"""
273+
274+
def agent_extension(self) -> AgentExtension:
275+
"""Get the AgentExtension representing this extension."""
276+
return AgentExtension(
277+
uri=TRACEABILITY_EXTENSION_URI,
278+
description='Adds traceability information to artifacts.',
279+
)

samples/python/hosts/a2a_multiagent_host/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ The application utilizes a multi-agent architecture where a host A2A server dele
1414

1515
![architecture](assets/A2A_multi_agents.jpg)
1616

17-
### App UI
17+
### screenshot for CLI tool run with traceability information returned
1818

1919
![screenshot](assets/cli_trace_screenshot.png)
2020

samples/python/hosts/a2a_multiagent_host/__main__.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
from routing_agent import (
2424
root_agent,
2525
)
26+
from traceability_ext import TraceabilityExtension
2627

2728

2829
load_dotenv()
@@ -54,14 +55,22 @@ def main(host: str = DEFAULT_HOST, port: int = DEFAULT_PORT):
5455

5556
app_url = os.environ.get('APP_URL', f'http://{host}:{port}')
5657

58+
traceability_ext = TraceabilityExtension()
59+
capabilities = AgentCapabilities(
60+
streaming=True,
61+
extensions=[
62+
traceability_ext.agent_extension(),
63+
],
64+
)
65+
5766
agent_card = AgentCard(
5867
name='Host A2A Agent',
5968
description='A2A server that helps with weather and airbnb',
6069
url=app_url,
6170
version='1.0.0',
6271
default_input_modes=['text'],
6372
default_output_modes=['text'],
64-
capabilities=AgentCapabilities(streaming=True),
73+
capabilities=capabilities,
6574
skills=[skill],
6675
)
6776

samples/python/hosts/a2a_multiagent_host/host_agent_executor.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import json
22
import logging
33

4-
from collections.abc import AsyncIterator
54
from typing import TYPE_CHECKING
65

76
from a2a.server.agent_execution import AgentExecutor
@@ -65,12 +64,11 @@ async def _process_request(
6564
self._active_sessions.add(session_id)
6665

6766
try:
68-
event_iterator: AsyncIterator[Event] = self.runner.run_async(
67+
async for event in self.runner.run_async(
6968
user_id=DEFAULT_USER_ID,
7069
session_id=session_id,
7170
new_message=new_message,
72-
)
73-
async for event in event_iterator:
71+
):
7472
logger.debug(
7573
'### Event received: %s',
7674
event.model_dump_json(exclude_none=True, indent=2),

0 commit comments

Comments
 (0)