Skip to content

Commit 9163a19

Browse files
committed
Merge branch '1.0-dev' into ishymko/push-notifications
2 parents a79ac64 + 7998a26 commit 9163a19

41 files changed

Lines changed: 1283 additions & 759 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CHANGELOG.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,38 @@
11
# Changelog
22

3+
## [0.3.24](https://github.com/a2aproject/a2a-python/compare/v0.3.23...v0.3.24) (2026-02-20)
4+
5+
6+
### Bug Fixes
7+
8+
* **core:** preserve legitimate falsy values in _clean_empty ([#713](https://github.com/a2aproject/a2a-python/issues/713)) ([7632f55](https://github.com/a2aproject/a2a-python/commit/7632f55572641d8fbc353ee08ef2b1f6b75c38b6))
9+
* **deps:** `DeprecationWarning` on `HTTP_413_REQUEST_ENTITY_TOO_LARGE` ([#693](https://github.com/a2aproject/a2a-python/issues/693)) ([9968f9c](https://github.com/a2aproject/a2a-python/commit/9968f9c07f105bae8a6b296aeb6dea873b3b88b0))
10+
11+
## [0.3.23](https://github.com/a2aproject/a2a-python/compare/v0.3.22...v0.3.23) (2026-02-13)
12+
13+
14+
### Features
15+
16+
* add async context manager support to BaseClient ([#688](https://github.com/a2aproject/a2a-python/issues/688)) ([ae9dc88](https://github.com/a2aproject/a2a-python/commit/ae9dc8897885ad26461083682dd7ba008d5af3cb))
17+
* add async context manager support to ClientTransport ([#682](https://github.com/a2aproject/a2a-python/issues/682)) ([2e45c0d](https://github.com/a2aproject/a2a-python/commit/2e45c0d54e47f1725b13c67c8e509b0e6e61efb6))
18+
* support async card modifiers ([#654](https://github.com/a2aproject/a2a-python/issues/654)) ([a802500](https://github.com/a2aproject/a2a-python/commit/a802500b3ad82845c1a6fc155f80e75a20a1bcab))
19+
* support disabling OTel instrumentation via env var ([#611](https://github.com/a2aproject/a2a-python/issues/611)) ([72216b9](https://github.com/a2aproject/a2a-python/commit/72216b988c0681e07d26ea8d5489a619d1ad6dda))
20+
21+
22+
### Bug Fixes
23+
24+
* do not crash on SSE comment line ([#636](https://github.com/a2aproject/a2a-python/issues/636)) ([3dcb847](https://github.com/a2aproject/a2a-python/commit/3dcb84772fdc8a4d3b63b518ed491e5ed3d38d0a))
25+
* gRPC metadata header casing and invocation_metadata() call ([#676](https://github.com/a2aproject/a2a-python/issues/676)) ([390b763](https://github.com/a2aproject/a2a-python/commit/390b763d106eae3b2ca8ca78a2d0bfdc68f8fe2c))
26+
* Improve error handling for Timeout exceptions on REST and JSON-RPC clients ([#690](https://github.com/a2aproject/a2a-python/issues/690)) ([2acd838](https://github.com/a2aproject/a2a-python/commit/2acd838796d44ab9bfe6ba8c8b4ea0c2571a59dc))
27+
* map rejected task state in proto converters ([#668](https://github.com/a2aproject/a2a-python/issues/668)) ([957e92b](https://github.com/a2aproject/a2a-python/commit/957e92b9059792c44a40bbab18160996f5512145)), closes [#625](https://github.com/a2aproject/a2a-python/issues/625)
28+
* **server:** fix deadlocks on agent execution failure in non-streaming ([#614](https://github.com/a2aproject/a2a-python/issues/614)) ([d3c973f](https://github.com/a2aproject/a2a-python/commit/d3c973fe72afc0142f8a4c94d0c0fbe4ba2ddfe8))
29+
30+
31+
### Documentation
32+
33+
* explicitly mention supported spec version and transports in readme ([#681](https://github.com/a2aproject/a2a-python/issues/681)) ([c91d4fb](https://github.com/a2aproject/a2a-python/commit/c91d4fba517190d8f7c76b42ea26914a4275f1d5)), closes [#677](https://github.com/a2aproject/a2a-python/issues/677)
34+
* Update README to include Code Wiki badge ([2698cc0](https://github.com/a2aproject/a2a-python/commit/2698cc04f15282fb358018f06bd88ae159d987b4))
35+
336
## [0.3.22](https://github.com/a2aproject/a2a-python/compare/v0.3.21...v0.3.22) (2025-12-16)
437

538

Gemini.md renamed to GEMINI.md

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,25 +3,18 @@
33
## Project frameworks
44
- uv as package manager
55

6-
## How to run all tests
7-
1. If dependencies are not installed, install them using the following command
8-
```
9-
uv sync --all-extras
10-
```
11-
12-
2. Run tests
13-
```
14-
uv run pytest
15-
```
16-
17-
## Other instructions
6+
## Code style and mandatory checks
187
1. Whenever writing python code, write types as well.
198
2. After making the changes run ruff to check and fix the formatting issues
209
```
2110
uv run ruff check --fix
11+
uv run ruff format
2212
```
2313
3. Run mypy type checkers to check for type errors
2414
```
25-
uv run mypy
15+
uv run mypy src
2616
```
2717
4. Run the unit tests to make sure that none of the unit tests are broken.
18+
```
19+
uv run pytest
20+
```

buf.gen.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
version: v2
33
inputs:
44
- git_repo: https://github.com/a2aproject/A2A.git
5-
ref: v1.0.0-rc
5+
ref: main
66
subdir: specification
77
managed:
88
enabled: true

scripts/gen_proto.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
set -e
33

44
# Run buf generate to regenerate protobuf code and OpenAPI spec
5-
npx @bufbuild/buf generate
5+
npx --yes @bufbuild/buf generate
66

77
# The OpenAPI generator produces a file named like 'a2a.swagger.json' or similar.
88
# We need it to be 'a2a.json' for the A2A SDK.

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/jsonrpc.py

Lines changed: 4 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
from httpx_sse import SSEError, aconnect_sse
1212
from jsonrpc.jsonrpc2 import JSONRPC20Request, JSONRPC20Response
1313

14-
from a2a.client.card_resolver import A2ACardResolver
1514
from a2a.client.errors import (
1615
A2AClientHTTPError,
1716
A2AClientJSONError,
@@ -53,31 +52,18 @@ class JsonRpcTransport(ClientTransport):
5352
def __init__(
5453
self,
5554
httpx_client: httpx.AsyncClient,
56-
agent_card: AgentCard | None = None,
57-
url: str | None = None,
55+
agent_card: AgentCard,
56+
url: str,
5857
interceptors: list[ClientCallInterceptor] | None = None,
5958
extensions: list[str] | None = None,
6059
):
6160
"""Initializes the JsonRpcTransport."""
62-
if url:
63-
self.url = url
64-
elif agent_card:
65-
if agent_card.supported_interfaces:
66-
self.url = agent_card.supported_interfaces[0].url
67-
else:
68-
# Fallback or error if no interfaces?
69-
# For compatibility we might check if 'url' attr exists (it does not on proto anymore)
70-
raise ValueError('AgentCard has no supported interfaces')
71-
else:
72-
raise ValueError('Must provide either agent_card or url')
73-
61+
self.url = url
7462
self.httpx_client = httpx_client
7563
self.agent_card = agent_card
7664
self.interceptors = interceptors or []
7765
self.extensions = extensions
78-
self._needs_extended_card = (
79-
agent_card.capabilities.extended_agent_card if agent_card else True
80-
)
66+
self._needs_extended_card = agent_card.capabilities.extended_agent_card
8167

8268
async def _apply_interceptors(
8369
self,
@@ -513,15 +499,6 @@ async def get_extended_agent_card(
513499

514500
card = self.agent_card
515501

516-
if not card:
517-
resolver = A2ACardResolver(self.httpx_client, self.url)
518-
card = await resolver.get_agent_card(
519-
http_kwargs=modified_kwargs,
520-
signature_verifier=signature_verifier,
521-
)
522-
self.agent_card = card
523-
self._needs_extended_card = card.capabilities.extended_agent_card
524-
525502
if not card.capabilities.extended_agent_card:
526503
return card
527504

src/a2a/client/transports/rest.py

Lines changed: 4 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
from google.protobuf.message import Message
1111
from httpx_sse import SSEError, aconnect_sse
1212

13-
from a2a.client.card_resolver import A2ACardResolver
1413
from a2a.client.errors import (
1514
A2AClientHTTPError,
1615
A2AClientJSONError,
@@ -37,10 +36,6 @@
3736
Task,
3837
TaskPushNotificationConfig,
3938
)
40-
from a2a.utils.constants import (
41-
TRANSPORT_HTTP_JSON,
42-
TRANSPORT_JSONRPC,
43-
)
4439
from a2a.utils.telemetry import SpanKind, trace_class
4540

4641

@@ -54,37 +49,17 @@ class RestTransport(ClientTransport):
5449
def __init__(
5550
self,
5651
httpx_client: httpx.AsyncClient,
57-
agent_card: AgentCard | None = None,
58-
url: str | None = None,
52+
agent_card: AgentCard,
53+
url: str,
5954
interceptors: list[ClientCallInterceptor] | None = None,
6055
extensions: list[str] | None = None,
6156
):
6257
"""Initializes the RestTransport."""
63-
if url:
64-
self.url = url
65-
elif agent_card:
66-
for interface in agent_card.supported_interfaces:
67-
if interface.protocol_binding in (
68-
TRANSPORT_HTTP_JSON,
69-
TRANSPORT_JSONRPC,
70-
):
71-
self.url = interface.url
72-
break
73-
else:
74-
raise ValueError(
75-
f'AgentCard does not support {TRANSPORT_HTTP_JSON} '
76-
f'or {TRANSPORT_JSONRPC}'
77-
)
78-
else:
79-
raise ValueError('Must provide either agent_card or url')
80-
if self.url.endswith('/'):
81-
self.url = self.url[:-1]
58+
self.url = url.removesuffix('/')
8259
self.httpx_client = httpx_client
8360
self.agent_card = agent_card
8461
self.interceptors = interceptors or []
85-
self._needs_extended_card = (
86-
agent_card.capabilities.extended_agent_card if agent_card else True
87-
)
62+
self._needs_extended_card = agent_card.capabilities.extended_agent_card
8863
self.extensions = extensions
8964

9065
async def _apply_interceptors(
@@ -493,15 +468,6 @@ async def get_extended_agent_card(
493468

494469
card = self.agent_card
495470

496-
if not card:
497-
resolver = A2ACardResolver(self.httpx_client, self.url)
498-
card = await resolver.get_agent_card(
499-
http_kwargs=modified_kwargs,
500-
signature_verifier=signature_verifier,
501-
)
502-
self.agent_card = card
503-
self._needs_extended_card = card.capabilities.extended_agent_card
504-
505471
if not card.capabilities.extended_agent_card:
506472
return card
507473
_, modified_kwargs = await self._apply_interceptors(

src/a2a/server/apps/jsonrpc/jsonrpc_app.py

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939
GetExtendedAgentCardRequest,
4040
GetTaskPushNotificationConfigRequest,
4141
GetTaskRequest,
42-
ListTaskPushNotificationConfigRequest,
42+
ListTaskPushNotificationConfigsRequest,
4343
ListTasksRequest,
4444
SendMessageRequest,
4545
SubscribeToTaskRequest,
@@ -70,7 +70,14 @@
7070
from starlette.exceptions import HTTPException
7171
from starlette.requests import Request
7272
from starlette.responses import JSONResponse, Response
73-
from starlette.status import HTTP_413_REQUEST_ENTITY_TOO_LARGE
73+
74+
try:
75+
# Starlette v0.48.0
76+
from starlette.status import HTTP_413_CONTENT_TOO_LARGE
77+
except ImportError:
78+
from starlette.status import ( # type: ignore[no-redef]
79+
HTTP_413_REQUEST_ENTITY_TOO_LARGE as HTTP_413_CONTENT_TOO_LARGE,
80+
)
7481

7582
_package_starlette_installed = True
7683
else:
@@ -82,7 +89,14 @@
8289
from starlette.exceptions import HTTPException
8390
from starlette.requests import Request
8491
from starlette.responses import JSONResponse, Response
85-
from starlette.status import HTTP_413_REQUEST_ENTITY_TOO_LARGE
92+
93+
try:
94+
# Starlette v0.48.0
95+
from starlette.status import HTTP_413_CONTENT_TOO_LARGE
96+
except ImportError:
97+
from starlette.status import (
98+
HTTP_413_REQUEST_ENTITY_TOO_LARGE as HTTP_413_CONTENT_TOO_LARGE,
99+
)
86100

87101
_package_starlette_installed = True
88102
except ImportError:
@@ -96,7 +110,7 @@
96110
Request = Any
97111
JSONResponse = Any
98112
Response = Any
99-
HTTP_413_REQUEST_ENTITY_TOO_LARGE = Any
113+
HTTP_413_CONTENT_TOO_LARGE = Any
100114

101115

102116
class StarletteUserProxy(A2AUser):
@@ -171,7 +185,7 @@ class JSONRPCApplication(ABC):
171185
'CancelTask': CancelTaskRequest,
172186
'CreateTaskPushNotificationConfig': CreateTaskPushNotificationConfigRequest,
173187
'GetTaskPushNotificationConfig': GetTaskPushNotificationConfigRequest,
174-
'ListTaskPushNotificationConfig': ListTaskPushNotificationConfigRequest,
188+
'ListTaskPushNotificationConfigs': ListTaskPushNotificationConfigsRequest,
175189
'DeleteTaskPushNotificationConfig': DeleteTaskPushNotificationConfigRequest,
176190
'SubscribeToTask': SubscribeToTaskRequest,
177191
'GetExtendedAgentCard': GetExtendedAgentCardRequest,
@@ -394,7 +408,7 @@ async def _handle_requests(self, request: Request) -> Response: # noqa: PLR0911
394408
None, JSONParseError(message=str(e))
395409
)
396410
except HTTPException as e:
397-
if e.status_code == HTTP_413_REQUEST_ENTITY_TOO_LARGE:
411+
if e.status_code == HTTP_413_CONTENT_TOO_LARGE:
398412
return self._generate_error_response(
399413
request_id,
400414
InvalidRequestError(message='Payload too large'),
@@ -486,9 +500,9 @@ async def _process_non_streaming_request(
486500
context,
487501
)
488502
)
489-
case ListTaskPushNotificationConfigRequest():
503+
case ListTaskPushNotificationConfigsRequest():
490504
handler_result = (
491-
await self.handler.list_push_notification_config(
505+
await self.handler.list_push_notification_configs(
492506
request_obj,
493507
context,
494508
)

0 commit comments

Comments
 (0)