Skip to content

Commit d09a6a2

Browse files
committed
refactor: unify transport constants usage
Fixes #705
1 parent 72a1007 commit d09a6a2

10 files changed

Lines changed: 53 additions & 70 deletions

File tree

src/a2a/client/client_factory.py

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,7 @@
2020
AgentInterface,
2121
)
2222
from a2a.utils.constants import (
23-
TRANSPORT_GRPC,
24-
TRANSPORT_HTTP_JSON,
25-
TRANSPORT_JSONRPC,
23+
TransportProtocol,
2624
)
2725

2826

@@ -74,9 +72,9 @@ def __init__(
7472

7573
def _register_defaults(self, supported: list[str]) -> None:
7674
# Empty support list implies JSON-RPC only.
77-
if TRANSPORT_JSONRPC in supported or not supported:
75+
if TransportProtocol.JSONRPC in supported or not supported:
7876
self.register(
79-
TRANSPORT_JSONRPC,
77+
TransportProtocol.JSONRPC,
8078
lambda card, url, config, interceptors: JsonRpcTransport(
8179
config.httpx_client or httpx.AsyncClient(),
8280
card,
@@ -85,9 +83,9 @@ def _register_defaults(self, supported: list[str]) -> None:
8583
config.extensions or None,
8684
),
8785
)
88-
if TRANSPORT_HTTP_JSON in supported:
86+
if TransportProtocol.HTTP_JSON in supported:
8987
self.register(
90-
TRANSPORT_HTTP_JSON,
88+
TransportProtocol.HTTP_JSON,
9189
lambda card, url, config, interceptors: RestTransport(
9290
config.httpx_client or httpx.AsyncClient(),
9391
card,
@@ -96,14 +94,14 @@ def _register_defaults(self, supported: list[str]) -> None:
9694
config.extensions or None,
9795
),
9896
)
99-
if TRANSPORT_GRPC in supported:
97+
if TransportProtocol.GRPC in supported:
10098
if GrpcTransport is None:
10199
raise ImportError(
102100
'To use GrpcClient, its dependencies must be installed. '
103101
'You can install them with \'pip install "a2a-sdk[grpc]"\''
104102
)
105103
self.register(
106-
TRANSPORT_GRPC,
104+
TransportProtocol.GRPC,
107105
GrpcTransport.create,
108106
)
109107

@@ -207,7 +205,7 @@ def create(
207205
server configuration, a `ValueError` is raised.
208206
"""
209207
client_set = self._config.supported_protocol_bindings or [
210-
TRANSPORT_JSONRPC
208+
TransportProtocol.JSONRPC
211209
]
212210
transport_protocol = None
213211
transport_url = None

src/a2a/client/transports/rest.py

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,7 @@
3535
TaskPushNotificationConfig,
3636
)
3737
from a2a.utils.constants import (
38-
TRANSPORT_HTTP_JSON,
39-
TRANSPORT_JSONRPC,
38+
TransportProtocol,
4039
)
4140
from a2a.utils.telemetry import SpanKind, trace_class
4241

@@ -62,15 +61,15 @@ def __init__(
6261
elif agent_card:
6362
for interface in agent_card.supported_interfaces:
6463
if interface.protocol_binding in (
65-
TRANSPORT_HTTP_JSON,
66-
TRANSPORT_JSONRPC,
64+
TransportProtocol.HTTP_JSON,
65+
TransportProtocol.JSONRPC,
6766
):
6867
self.url = interface.url
6968
break
7069
else:
7170
raise ValueError(
72-
f'AgentCard does not support {TRANSPORT_HTTP_JSON} '
73-
f'or {TRANSPORT_JSONRPC}'
71+
f'AgentCard does not support {TransportProtocol.HTTP_JSON} '
72+
f'or {TransportProtocol.JSONRPC}'
7473
)
7574
else:
7675
raise ValueError('Must provide either agent_card or url')

src/a2a/utils/__init__.py

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,6 @@
1212
DEFAULT_RPC_URL,
1313
EXTENDED_AGENT_CARD_PATH,
1414
PREV_AGENT_CARD_WELL_KNOWN_PATH,
15-
TRANSPORT_GRPC,
16-
TRANSPORT_HTTP_JSON,
17-
TRANSPORT_JSONRPC,
1815
TransportProtocol,
1916
)
2017
from a2a.utils.helpers import (
@@ -45,9 +42,6 @@
4542
'DEFAULT_RPC_URL',
4643
'EXTENDED_AGENT_CARD_PATH',
4744
'PREV_AGENT_CARD_WELL_KNOWN_PATH',
48-
'TRANSPORT_GRPC',
49-
'TRANSPORT_HTTP_JSON',
50-
'TRANSPORT_JSONRPC',
5145
'TransportProtocol',
5246
'append_artifact_to_task',
5347
'are_modalities_compatible',

src/a2a/utils/constants.py

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
"""Constants for well-known URIs used throughout the A2A Python SDK."""
22

3+
from enum import Enum
4+
5+
36
AGENT_CARD_WELL_KNOWN_PATH = '/.well-known/agent-card.json'
47
PREV_AGENT_CARD_WELL_KNOWN_PATH = '/.well-known/agent.json'
58
EXTENDED_AGENT_CARD_PATH = '/agent/authenticatedExtendedCard'
@@ -8,19 +11,12 @@
811
"""Default page size for the `tasks/list` method."""
912

1013

11-
# Transport protocol constants
12-
# These match the protocol binding values used in AgentCard
13-
TRANSPORT_JSONRPC = 'JSONRPC'
14-
TRANSPORT_HTTP_JSON = 'HTTP+JSON'
15-
TRANSPORT_GRPC = 'GRPC'
16-
17-
18-
class TransportProtocol:
14+
class TransportProtocol(str, Enum):
1915
"""Transport protocol string constants."""
2016

21-
jsonrpc = TRANSPORT_JSONRPC
22-
http_json = TRANSPORT_HTTP_JSON
23-
grpc = TRANSPORT_GRPC
17+
JSONRPC = 'JSONRPC'
18+
HTTP_JSON = 'HTTP+JSON'
19+
GRPC = 'GRPC'
2420

2521

2622
DEFAULT_MAX_CONTENT_LENGTH = 10 * 1024 * 1024 # 10MB

tests/client/test_auth_middleware.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ async def test_client_with_simple_interceptor() -> None:
178178
interceptor = HeaderInterceptor('X-Test-Header', 'Test-Value-123')
179179
card = AgentCard(
180180
supported_interfaces=[
181-
AgentInterface(url=url, protocol_binding=TransportProtocol.jsonrpc)
181+
AgentInterface(url=url, protocol_binding=TransportProtocol.JSONRPC)
182182
],
183183
name='testbot',
184184
description='test bot',
@@ -192,7 +192,7 @@ async def test_client_with_simple_interceptor() -> None:
192192
async with httpx.AsyncClient() as http_client:
193193
config = ClientConfig(
194194
httpx_client=http_client,
195-
supported_protocol_bindings=[TransportProtocol.jsonrpc],
195+
supported_protocol_bindings=[TransportProtocol.JSONRPC],
196196
)
197197
factory = ClientFactory(config)
198198
client = factory.create(card, interceptors=[interceptor])
@@ -310,7 +310,7 @@ async def test_auth_interceptor_variants(
310310
agent_card = AgentCard(
311311
supported_interfaces=[
312312
AgentInterface(
313-
url=test_case.url, protocol_binding=TransportProtocol.jsonrpc
313+
url=test_case.url, protocol_binding=TransportProtocol.JSONRPC
314314
)
315315
],
316316
name=f'{test_case.scheme_name}bot',
@@ -333,7 +333,7 @@ async def test_auth_interceptor_variants(
333333
async with httpx.AsyncClient() as http_client:
334334
config = ClientConfig(
335335
httpx_client=http_client,
336-
supported_protocol_bindings=[TransportProtocol.jsonrpc],
336+
supported_protocol_bindings=[TransportProtocol.JSONRPC],
337337
)
338338
factory = ClientFactory(config)
339339
client = factory.create(agent_card, interceptors=[auth_interceptor])
@@ -362,7 +362,7 @@ async def test_auth_interceptor_skips_when_scheme_not_in_security_schemes(
362362
supported_interfaces=[
363363
AgentInterface(
364364
url='http://agent.com/rpc',
365-
protocol_binding=TransportProtocol.jsonrpc,
365+
protocol_binding=TransportProtocol.JSONRPC,
366366
)
367367
],
368368
name='missingbot',

tests/client/test_client_factory.py

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ def base_agent_card() -> AgentCard:
2525
description='An agent for testing.',
2626
supported_interfaces=[
2727
AgentInterface(
28-
protocol_binding=TransportProtocol.jsonrpc,
28+
protocol_binding=TransportProtocol.JSONRPC,
2929
url='http://primary-url.com',
3030
)
3131
],
@@ -42,8 +42,8 @@ def test_client_factory_selects_preferred_transport(base_agent_card: AgentCard):
4242
config = ClientConfig(
4343
httpx_client=httpx.AsyncClient(),
4444
supported_protocol_bindings=[
45-
TransportProtocol.jsonrpc,
46-
TransportProtocol.http_json,
45+
TransportProtocol.JSONRPC,
46+
TransportProtocol.HTTP_JSON,
4747
],
4848
extensions=['https://example.com/test-ext/v0'],
4949
)
@@ -61,16 +61,16 @@ def test_client_factory_selects_secondary_transport_url(
6161
"""Verify that the factory selects the correct URL for a secondary transport."""
6262
base_agent_card.supported_interfaces.append(
6363
AgentInterface(
64-
protocol_binding=TransportProtocol.http_json,
64+
protocol_binding=TransportProtocol.HTTP_JSON,
6565
url='http://secondary-url.com',
6666
)
6767
)
6868
# Client prefers REST, which is available as a secondary transport
6969
config = ClientConfig(
7070
httpx_client=httpx.AsyncClient(),
7171
supported_protocol_bindings=[
72-
TransportProtocol.http_json,
73-
TransportProtocol.jsonrpc,
72+
TransportProtocol.HTTP_JSON,
73+
TransportProtocol.JSONRPC,
7474
],
7575
use_client_preference=True,
7676
extensions=['https://example.com/test-ext/v0'],
@@ -89,22 +89,22 @@ def test_client_factory_server_preference(base_agent_card: AgentCard):
8989
base_agent_card.supported_interfaces.insert(
9090
0,
9191
AgentInterface(
92-
protocol_binding=TransportProtocol.http_json,
92+
protocol_binding=TransportProtocol.HTTP_JSON,
9393
url='http://primary-url.com',
9494
),
9595
)
9696
base_agent_card.supported_interfaces.append(
9797
AgentInterface(
98-
protocol_binding=TransportProtocol.jsonrpc,
98+
protocol_binding=TransportProtocol.JSONRPC,
9999
url='http://secondary-url.com',
100100
)
101101
)
102102
# Client supports both, but server prefers REST
103103
config = ClientConfig(
104104
httpx_client=httpx.AsyncClient(),
105105
supported_protocol_bindings=[
106-
TransportProtocol.jsonrpc,
107-
TransportProtocol.http_json,
106+
TransportProtocol.JSONRPC,
107+
TransportProtocol.HTTP_JSON,
108108
],
109109
)
110110
factory = ClientFactory(config)
@@ -258,7 +258,8 @@ def custom_transport_producer(*args, **kwargs):
258258
base_agent_card,
259259
client_config=config,
260260
extra_transports=typing.cast(
261-
dict[str, TransportProducer], {'custom': custom_transport_producer}
261+
'dict[str, TransportProducer]',
262+
{'custom': custom_transport_producer},
262263
),
263264
)
264265

tests/client/transports/test_rest_client.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33

44
import httpx
55
import pytest
6-
from google.protobuf import json_format
76

7+
from google.protobuf import json_format
88
from httpx_sse import EventSource, ServerSentEvent
99

1010
from a2a.client import create_text_message_object
@@ -15,10 +15,9 @@
1515
AgentCapabilities,
1616
AgentCard,
1717
AgentInterface,
18-
Role,
1918
SendMessageRequest,
2019
)
21-
from a2a.utils.constants import TRANSPORT_HTTP_JSON
20+
from a2a.utils.constants import TransportProtocol
2221

2322

2423
@pytest.fixture
@@ -31,7 +30,7 @@ def mock_agent_card() -> MagicMock:
3130
mock = MagicMock(spec=AgentCard, url='http://agent.example.com/api')
3231
mock.supported_interfaces = [
3332
AgentInterface(
34-
protocol_binding=TRANSPORT_HTTP_JSON,
33+
protocol_binding=TransportProtocol.HTTP_JSON,
3534
url='http://agent.example.com/api',
3635
)
3736
]
@@ -276,7 +275,7 @@ async def test_get_card_with_extended_card_support_with_extensions(
276275
capabilities=AgentCapabilities(extended_agent_card=True),
277276
)
278277
interface = agent_card.supported_interfaces.add()
279-
interface.protocol_binding = TRANSPORT_HTTP_JSON
278+
interface.protocol_binding = TransportProtocol.HTTP_JSON
280279
interface.url = 'http://agent.example.com/api'
281280

282281
client = RestTransport(

tests/e2e/push_notifications/test_default_push_notification_support.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ async def test_notification_triggering_with_in_message_config_e2e(
105105
token = uuid.uuid4().hex
106106
a2a_client = ClientFactory(
107107
ClientConfig(
108-
supported_protocol_bindings=[TransportProtocol.http_json],
108+
supported_protocol_bindings=[TransportProtocol.HTTP_JSON],
109109
push_notification_configs=[
110110
PushNotificationConfig(
111111
id='in-message-config',
@@ -114,7 +114,7 @@ async def test_notification_triggering_with_in_message_config_e2e(
114114
)
115115
],
116116
)
117-
).create(minimal_agent_card(agent_server, [TransportProtocol.http_json]))
117+
).create(minimal_agent_card(agent_server, [TransportProtocol.HTTP_JSON]))
118118

119119
# Send a message and extract the returned task.
120120
responses = [
@@ -157,9 +157,9 @@ async def test_notification_triggering_after_config_change_e2e(
157157
# Configure an A2A client without a push notification config.
158158
a2a_client = ClientFactory(
159159
ClientConfig(
160-
supported_protocol_bindings=[TransportProtocol.http_json],
160+
supported_protocol_bindings=[TransportProtocol.HTTP_JSON],
161161
)
162-
).create(minimal_agent_card(agent_server, [TransportProtocol.http_json]))
162+
).create(minimal_agent_card(agent_server, [TransportProtocol.HTTP_JSON]))
163163

164164
# Send a message and extract the returned task.
165165
responses = [

tests/integration/test_client_server_integration.py

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,7 @@
1919
from a2a.types import a2a_pb2_grpc
2020
from a2a.server.apps import A2AFastAPIApplication, A2ARESTFastAPIApplication
2121
from a2a.server.request_handlers import GrpcHandler, RequestHandler
22-
from a2a.utils.constants import (
23-
TRANSPORT_HTTP_JSON,
24-
TRANSPORT_GRPC,
25-
TRANSPORT_JSONRPC,
26-
)
22+
from a2a.utils.constants import TransportProtocol
2723
from a2a.utils.signing import (
2824
create_agent_card_signer,
2925
create_signature_verifier,
@@ -156,10 +152,10 @@ def agent_card() -> AgentCard:
156152
default_output_modes=['text/plain'],
157153
supported_interfaces=[
158154
AgentInterface(
159-
protocol_binding=TRANSPORT_HTTP_JSON,
155+
protocol_binding=TransportProtocol.HTTP_JSON,
160156
url='http://testserver',
161157
),
162-
AgentInterface(protocol_binding='grpc', url='localhost:50051'),
158+
AgentInterface(protocol_binding=TransportProtocol.GRPC, url='localhost:50051'),
163159
],
164160
)
165161

tests/integration/test_end_to_end.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
TaskState,
3434
a2a_pb2_grpc,
3535
)
36-
from a2a.utils import TRANSPORT_GRPC, TRANSPORT_HTTP_JSON, TRANSPORT_JSONRPC
36+
from a2a.utils import TransportProtocol
3737

3838

3939
class MockAgentExecutor(AgentExecutor):
@@ -68,15 +68,15 @@ def agent_card() -> AgentCard:
6868
default_output_modes=['text/plain'],
6969
supported_interfaces=[
7070
AgentInterface(
71-
protocol_binding=TRANSPORT_HTTP_JSON,
71+
protocol_binding=TransportProtocol.HTTP_JSON,
7272
url='http://testserver',
7373
),
7474
AgentInterface(
75-
protocol_binding=TRANSPORT_JSONRPC,
75+
protocol_binding=TransportProtocol.JSONRPC,
7676
url='http://testserver',
7777
),
7878
AgentInterface(
79-
protocol_binding=TRANSPORT_GRPC,
79+
protocol_binding=TransportProtocol.GRPC,
8080
url='localhost:50051',
8181
),
8282
],
@@ -149,7 +149,7 @@ async def grpc_setup(
149149

150150
# Update the gRPC interface dynamically based on the assigned port
151151
for interface in grpc_agent_card.supported_interfaces:
152-
if interface.protocol_binding == TRANSPORT_GRPC:
152+
if interface.protocol_binding == TransportProtocol.GRPC:
153153
interface.url = server_address
154154
break
155155
else:

0 commit comments

Comments
 (0)