From ff8b93d5b300fd44f2acfed493181b2ebd6225a5 Mon Sep 17 00:00:00 2001 From: yolagil Date: Tue, 23 Sep 2025 20:55:07 -0400 Subject: [PATCH 01/13] Fix missing extensions from protobuf --- src/a2a/utils/proto_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/a2a/utils/proto_utils.py b/src/a2a/utils/proto_utils.py index e619cd72..8853621f 100644 --- a/src/a2a/utils/proto_utils.py +++ b/src/a2a/utils/proto_utils.py @@ -579,7 +579,7 @@ def message(cls, message: a2a_pb2.Message) -> types.Message: task_id=message.task_id or None, role=cls.role(message.role), metadata=cls.metadata(message.metadata), - extensions=list(message.extensions) or None, + extensions=list(message.extensions) if message.extensions else None, ) @classmethod From b7906f898157892747460d7bd26b46f3e6fc8bb5 Mon Sep 17 00:00:00 2001 From: "agil.yolchuyev" Date: Tue, 23 Sep 2025 21:00:01 -0400 Subject: [PATCH 02/13] Update src/a2a/utils/proto_utils.py Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --- src/a2a/utils/proto_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/a2a/utils/proto_utils.py b/src/a2a/utils/proto_utils.py index 8853621f..e619cd72 100644 --- a/src/a2a/utils/proto_utils.py +++ b/src/a2a/utils/proto_utils.py @@ -579,7 +579,7 @@ def message(cls, message: a2a_pb2.Message) -> types.Message: task_id=message.task_id or None, role=cls.role(message.role), metadata=cls.metadata(message.metadata), - extensions=list(message.extensions) if message.extensions else None, + extensions=list(message.extensions) or None, ) @classmethod From 8a0196ce3c7438a9078d00610fd1e7431d677474 Mon Sep 17 00:00:00 2001 From: yolagil Date: Wed, 22 Oct 2025 11:04:18 -0400 Subject: [PATCH 03/13] update create_text_message_object function to handle all inputs --- src/a2a/client/helpers.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/a2a/client/helpers.py b/src/a2a/client/helpers.py index 930c71e6..ff81475b 100644 --- a/src/a2a/client/helpers.py +++ b/src/a2a/client/helpers.py @@ -1,22 +1,22 @@ """Helper functions for the A2A client.""" +from typing import Any from uuid import uuid4 from a2a.types import Message, Part, Role, TextPart def create_text_message_object( - role: Role = Role.user, content: str = '' + role: Role = Role.user, + content: str = '', + extensions: list[Any] | None = None, + metadata: dict[Any, Any] | None = None, ) -> Message: - """Create a Message object containing a single TextPart. - - Args: - role: The role of the message sender (user or agent). Defaults to Role.user. - content: The text content of the message. Defaults to an empty string. - - Returns: - A `Message` object with a new UUID message_id. - """ + """Create a Message object containing a single TextPart.""" return Message( - role=role, parts=[Part(TextPart(text=content))], message_id=str(uuid4()) + role=role, + parts=[Part(TextPart(text=content or ''))], + message_id=str(uuid4()), + extensions=extensions or [], + metadata=metadata or {}, ) From b236530cc97bfacffa185d39f1de9be71cdaa7c9 Mon Sep 17 00:00:00 2001 From: yolagil Date: Wed, 22 Oct 2025 11:23:10 -0400 Subject: [PATCH 04/13] update docstrings --- src/a2a/client/helpers.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/a2a/client/helpers.py b/src/a2a/client/helpers.py index ff81475b..78dd30a0 100644 --- a/src/a2a/client/helpers.py +++ b/src/a2a/client/helpers.py @@ -12,7 +12,17 @@ def create_text_message_object( extensions: list[Any] | None = None, metadata: dict[Any, Any] | None = None, ) -> Message: - """Create a Message object containing a single TextPart.""" + """Create a Message object containing a single TextPart. + + Args: + role: The role of the message sender (user or agent). Defaults to Role.user. + content: The text content of the message. Defaults to an empty string. + extensions: The extensions of the message. Defaults to an empty list. + metadata: The metadata of the message. Defaults to an empty dictionary. + + Returns: + A `Message` object with a new UUID message_id. + """ return Message( role=role, parts=[Part(TextPart(text=content or ''))], From 4c2b78f56039c365caf1e102e4a3dd1e7f9c6c09 Mon Sep 17 00:00:00 2001 From: "agil.yolchuyev" Date: Wed, 22 Oct 2025 11:31:42 -0400 Subject: [PATCH 05/13] Update src/a2a/client/helpers.py Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --- src/a2a/client/helpers.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/a2a/client/helpers.py b/src/a2a/client/helpers.py index 78dd30a0..b97b7a3d 100644 --- a/src/a2a/client/helpers.py +++ b/src/a2a/client/helpers.py @@ -9,8 +9,8 @@ def create_text_message_object( role: Role = Role.user, content: str = '', - extensions: list[Any] | None = None, - metadata: dict[Any, Any] | None = None, + extensions: list[str] | None = None, + metadata: dict[str, Any] | None = None, ) -> Message: """Create a Message object containing a single TextPart. From e6d806ec4375a94592a9fdf5c68758dd616d5a04 Mon Sep 17 00:00:00 2001 From: "agil.yolchuyev" Date: Wed, 22 Oct 2025 11:31:55 -0400 Subject: [PATCH 06/13] Update src/a2a/client/helpers.py Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --- src/a2a/client/helpers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/a2a/client/helpers.py b/src/a2a/client/helpers.py index b97b7a3d..67a39ce7 100644 --- a/src/a2a/client/helpers.py +++ b/src/a2a/client/helpers.py @@ -25,7 +25,7 @@ def create_text_message_object( """ return Message( role=role, - parts=[Part(TextPart(text=content or ''))], + parts=[Part(TextPart(text=content))], message_id=str(uuid4()), extensions=extensions or [], metadata=metadata or {}, From 1211d73590e08d56008cf6acf7555de9f67bbd00 Mon Sep 17 00:00:00 2001 From: yolagil Date: Wed, 22 Oct 2025 11:40:44 -0400 Subject: [PATCH 07/13] add test --- tests/client/test_jsonrpc_client.py | 54 +++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/tests/client/test_jsonrpc_client.py b/tests/client/test_jsonrpc_client.py index 58feec25..b19660c1 100644 --- a/tests/client/test_jsonrpc_client.py +++ b/tests/client/test_jsonrpc_client.py @@ -378,6 +378,60 @@ async def test_send_message_success( assert isinstance(response, Message) assert response.model_dump() == success_response.model_dump() + @pytest.mark.asyncio + async def test_send_message_success_with_extensions( + self, mock_httpx_client: AsyncMock, mock_agent_card: MagicMock + ): + client = JsonRpcTransport( + httpx_client=mock_httpx_client, agent_card=mock_agent_card + ) + params = MessageSendParams( + message=create_text_message_object(content='Hello', extensions=['test']) + ) + success_response = create_text_message_object( + role=Role.agent, content='Hi there!', extensions=['test'] + ) + rpc_response = SendMessageSuccessResponse( + id='123', jsonrpc='2.0', result=success_response + ) + response = httpx.Response( + 200, json=rpc_response.model_dump(mode='json') + ) + response.request = httpx.Request('POST', 'http://agent.example.com/api') + mock_httpx_client.post.return_value = response + + response = await client.send_message(request=params) + + assert isinstance(response, Message) + assert response.model_dump() == success_response.model_dump() + + @pytest.mark.asyncio + async def test_send_message_success_with_metadata( + self, mock_httpx_client: AsyncMock, mock_agent_card: MagicMock + ): + client = JsonRpcTransport( + httpx_client=mock_httpx_client, agent_card=mock_agent_card + ) + params = MessageSendParams( + message=create_text_message_object(content='Hello', metadata={'test': 'test'}) + ) + success_response = create_text_message_object( + role=Role.agent, content='Hi there!', metadata={'test': 'test'} + ) + rpc_response = SendMessageSuccessResponse( + id='123', jsonrpc='2.0', result=success_response + ) + response = httpx.Response( + 200, json=rpc_response.model_dump(mode='json') + ) + response.request = httpx.Request('POST', 'http://agent.example.com/api') + mock_httpx_client.post.return_value = response + + response = await client.send_message(request=params) + + assert isinstance(response, Message) + assert response.model_dump() == success_response.model_dump() + @pytest.mark.asyncio async def test_send_message_error_response( self, mock_httpx_client: AsyncMock, mock_agent_card: MagicMock From 275f1247177d725cefff53a151120329fd358f1d Mon Sep 17 00:00:00 2001 From: yolagil Date: Wed, 22 Oct 2025 12:05:24 -0400 Subject: [PATCH 08/13] fix linter --- src/a2a/utils/proto_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/a2a/utils/proto_utils.py b/src/a2a/utils/proto_utils.py index e619cd72..f8aa04ba 100644 --- a/src/a2a/utils/proto_utils.py +++ b/src/a2a/utils/proto_utils.py @@ -57,7 +57,7 @@ def make_dict_serializable(value: Any) -> Any: Returns: A serializable value. """ - if isinstance(value, (str, int, float, bool)) or value is None: + if isinstance(value, str | int | float | bool) or value is None: return value if isinstance(value, dict): return {k: make_dict_serializable(v) for k, v in value.items()} From 54f3af903278d9883a5a7b48495c58e334bd5953 Mon Sep 17 00:00:00 2001 From: yolagil Date: Wed, 22 Oct 2025 12:14:32 -0400 Subject: [PATCH 09/13] fix linter --- tests/client/test_jsonrpc_client.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tests/client/test_jsonrpc_client.py b/tests/client/test_jsonrpc_client.py index b19660c1..57600cf5 100644 --- a/tests/client/test_jsonrpc_client.py +++ b/tests/client/test_jsonrpc_client.py @@ -386,7 +386,9 @@ async def test_send_message_success_with_extensions( httpx_client=mock_httpx_client, agent_card=mock_agent_card ) params = MessageSendParams( - message=create_text_message_object(content='Hello', extensions=['test']) + message=create_text_message_object( + content='Hello', extensions=['test'] + ) ) success_response = create_text_message_object( role=Role.agent, content='Hi there!', extensions=['test'] @@ -413,7 +415,9 @@ async def test_send_message_success_with_metadata( httpx_client=mock_httpx_client, agent_card=mock_agent_card ) params = MessageSendParams( - message=create_text_message_object(content='Hello', metadata={'test': 'test'}) + message=create_text_message_object( + content='Hello', metadata={'test': 'test'} + ) ) success_response = create_text_message_object( role=Role.agent, content='Hi there!', metadata={'test': 'test'} From 85d0c9eddbffe5ac5cbbb47c86ca76b2678f02a8 Mon Sep 17 00:00:00 2001 From: yolagil Date: Wed, 22 Oct 2025 16:09:48 -0400 Subject: [PATCH 10/13] fix extension links --- .github/actions/spelling/allow.txt | 42 +++++++++++++++--------------- src/a2a/utils/proto_utils.py | 1 + 2 files changed, 22 insertions(+), 21 deletions(-) diff --git a/.github/actions/spelling/allow.txt b/.github/actions/spelling/allow.txt index a016962c..da68a546 100644 --- a/.github/actions/spelling/allow.txt +++ b/.github/actions/spelling/allow.txt @@ -1,17 +1,10 @@ AAgent +ACMRTUXB ACard AClient -ACMRTUXB -aconnect -adk AError AFast -agentic AGrpc -aio -aiomysql -amannn -aproject ARequest ARun AServer @@ -19,6 +12,26 @@ AServers AService AStarlette AUser +DSNs +EUR +GBP +GVsb +INR +JPY +JSONRPCt +JWS +Llm +POSTGRES +RUF +SLF +Tful +aconnect +adk +agentic +aio +aiomysql +amannn +aproject autouse backticks cla @@ -29,32 +42,23 @@ coro datamodel deepwiki drivername -DSNs dunders euo -EUR excinfo fernet fetchrow fetchval -GBP genai getkwargs gle -GVsb ietf initdb inmemory -INR isready -JPY -JSONRPCt -JWS kwarg langgraph lifecycles linting -Llm lstrips mikeas mockurl @@ -64,7 +68,6 @@ oidc opensource otherurl postgres -POSTGRES postgresql protoc pyi @@ -74,14 +77,11 @@ pyversions redef respx resub -RUF -SLF socio sse tagwords taskupdate testuuid -Tful tiangolo typeerror vulnz diff --git a/src/a2a/utils/proto_utils.py b/src/a2a/utils/proto_utils.py index f8aa04ba..68a57f1f 100644 --- a/src/a2a/utils/proto_utils.py +++ b/src/a2a/utils/proto_utils.py @@ -140,6 +140,7 @@ def message(cls, message: types.Message | None) -> a2a_pb2.Message | None: task_id=message.task_id or '', role=cls.role(message.role), metadata=cls.metadata(message.metadata), + extensions=message.extensions or [], ) @classmethod From 3d0288d8c601c767d87e883f2faccd978dbe7ff9 Mon Sep 17 00:00:00 2001 From: yolagil Date: Fri, 24 Oct 2025 23:14:07 -0400 Subject: [PATCH 11/13] update --- src/a2a/utils/proto_utils.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/a2a/utils/proto_utils.py b/src/a2a/utils/proto_utils.py index 68a57f1f..f8aa04ba 100644 --- a/src/a2a/utils/proto_utils.py +++ b/src/a2a/utils/proto_utils.py @@ -140,7 +140,6 @@ def message(cls, message: types.Message | None) -> a2a_pb2.Message | None: task_id=message.task_id or '', role=cls.role(message.role), metadata=cls.metadata(message.metadata), - extensions=message.extensions or [], ) @classmethod From 1a35a1659775e1e83240e09e33290317e66df17d Mon Sep 17 00:00:00 2001 From: yolagil Date: Fri, 24 Oct 2025 23:17:51 -0400 Subject: [PATCH 12/13] revert all proto proto_utils changes --- src/a2a/utils/proto_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/a2a/utils/proto_utils.py b/src/a2a/utils/proto_utils.py index f8aa04ba..e619cd72 100644 --- a/src/a2a/utils/proto_utils.py +++ b/src/a2a/utils/proto_utils.py @@ -57,7 +57,7 @@ def make_dict_serializable(value: Any) -> Any: Returns: A serializable value. """ - if isinstance(value, str | int | float | bool) or value is None: + if isinstance(value, (str, int, float, bool)) or value is None: return value if isinstance(value, dict): return {k: make_dict_serializable(v) for k, v in value.items()} From 5d60e93ff26bff2886a78056c6bac0440152de43 Mon Sep 17 00:00:00 2001 From: Holt Skinner <13262395+holtskinner@users.noreply.github.com> Date: Mon, 27 Oct 2025 10:57:08 -0500 Subject: [PATCH 13/13] Formatting --- .github/actions/spelling/allow.txt | 42 +++++++++++++++--------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/.github/actions/spelling/allow.txt b/.github/actions/spelling/allow.txt index da68a546..a016962c 100644 --- a/.github/actions/spelling/allow.txt +++ b/.github/actions/spelling/allow.txt @@ -1,10 +1,17 @@ AAgent -ACMRTUXB ACard AClient +ACMRTUXB +aconnect +adk AError AFast +agentic AGrpc +aio +aiomysql +amannn +aproject ARequest ARun AServer @@ -12,26 +19,6 @@ AServers AService AStarlette AUser -DSNs -EUR -GBP -GVsb -INR -JPY -JSONRPCt -JWS -Llm -POSTGRES -RUF -SLF -Tful -aconnect -adk -agentic -aio -aiomysql -amannn -aproject autouse backticks cla @@ -42,23 +29,32 @@ coro datamodel deepwiki drivername +DSNs dunders euo +EUR excinfo fernet fetchrow fetchval +GBP genai getkwargs gle +GVsb ietf initdb inmemory +INR isready +JPY +JSONRPCt +JWS kwarg langgraph lifecycles linting +Llm lstrips mikeas mockurl @@ -68,6 +64,7 @@ oidc opensource otherurl postgres +POSTGRES postgresql protoc pyi @@ -77,11 +74,14 @@ pyversions redef respx resub +RUF +SLF socio sse tagwords taskupdate testuuid +Tful tiangolo typeerror vulnz