Skip to content

Commit 157f731

Browse files
seanzhougooglecopybara-github
authored andcommitted
feat: Allow user to specify protocol for A2A RPC URL in to_a2a utility
this fixes google#2405 PiperOrigin-RevId: 797958771
1 parent ccab076 commit 157f731

2 files changed

Lines changed: 123 additions & 7 deletions

File tree

src/google/adk/a2a/utils/agent_to_a2a.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,25 +39,32 @@
3939
from ...runners import Runner
4040
from ...sessions.in_memory_session_service import InMemorySessionService
4141
from ..executor.a2a_agent_executor import A2aAgentExecutor
42+
from ..experimental import a2a_experimental
4243
from .agent_card_builder import AgentCardBuilder
4344

4445

46+
@a2a_experimental
4547
def to_a2a(
46-
agent: BaseAgent, *, host: str = "localhost", port: int = 8000
48+
agent: BaseAgent,
49+
*,
50+
host: str = "localhost",
51+
port: int = 8000,
52+
protocol: str = "http",
4753
) -> Starlette:
4854
"""Convert an ADK agent to a A2A Starlette application.
4955
5056
Args:
5157
agent: The ADK agent to convert
5258
host: The host for the A2A RPC URL (default: "localhost")
5359
port: The port for the A2A RPC URL (default: 8000)
60+
protocol: The protocol for the A2A RPC URL (default: "http")
5461
5562
Returns:
5663
A Starlette application that can be run with uvicorn
5764
5865
Example:
5966
agent = MyAgent()
60-
app = to_a2a(agent, host="localhost", port=8000)
67+
app = to_a2a(agent, host="localhost", port=8000, protocol="http")
6168
# Then run with: uvicorn module:app --host localhost --port 8000
6269
"""
6370
# Set up ADK logging to ensure logs are visible when using uvicorn directly
@@ -87,7 +94,7 @@ async def create_runner() -> Runner:
8794
)
8895

8996
# Build agent card
90-
rpc_url = f"http://{host}:{port}/"
97+
rpc_url = f"{protocol}://{host}:{port}/"
9198
card_builder = AgentCardBuilder(
9299
agent=agent,
93100
rpc_url=rpc_url,

tests/unittests/a2a/utils/test_agent_to_a2a.py

Lines changed: 113 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -123,15 +123,15 @@ def test_to_a2a_default_parameters(
123123
@patch("google.adk.a2a.utils.agent_to_a2a.InMemoryTaskStore")
124124
@patch("google.adk.a2a.utils.agent_to_a2a.AgentCardBuilder")
125125
@patch("google.adk.a2a.utils.agent_to_a2a.Starlette")
126-
def test_to_a2a_custom_host_port(
126+
def test_to_a2a_custom_host_port_protocol(
127127
self,
128128
mock_starlette_class,
129129
mock_card_builder_class,
130130
mock_task_store_class,
131131
mock_request_handler_class,
132132
mock_agent_executor_class,
133133
):
134-
"""Test to_a2a with custom host and port."""
134+
"""Test to_a2a with custom host, port, and protocol."""
135135
# Arrange
136136
mock_app = Mock(spec=Starlette)
137137
mock_starlette_class.return_value = mock_app
@@ -145,12 +145,14 @@ def test_to_a2a_custom_host_port(
145145
mock_card_builder_class.return_value = mock_card_builder
146146

147147
# Act
148-
result = to_a2a(self.mock_agent, host="example.com", port=9000)
148+
result = to_a2a(
149+
self.mock_agent, host="example.com", port=9000, protocol="https"
150+
)
149151

150152
# Assert
151153
assert result == mock_app
152154
mock_card_builder_class.assert_called_once_with(
153-
agent=self.mock_agent, rpc_url="http://example.com:9000/"
155+
agent=self.mock_agent, rpc_url="https://example.com:9000/"
154156
)
155157

156158
@patch("google.adk.a2a.utils.agent_to_a2a.A2aAgentExecutor")
@@ -704,3 +706,110 @@ def test_to_a2a_with_ip_address_host(
704706
mock_card_builder_class.assert_called_once_with(
705707
agent=self.mock_agent, rpc_url="http://192.168.1.1:8000/"
706708
)
709+
710+
@patch("google.adk.a2a.utils.agent_to_a2a.A2aAgentExecutor")
711+
@patch("google.adk.a2a.utils.agent_to_a2a.DefaultRequestHandler")
712+
@patch("google.adk.a2a.utils.agent_to_a2a.InMemoryTaskStore")
713+
@patch("google.adk.a2a.utils.agent_to_a2a.AgentCardBuilder")
714+
@patch("google.adk.a2a.utils.agent_to_a2a.Starlette")
715+
def test_to_a2a_with_https_protocol(
716+
self,
717+
mock_starlette_class,
718+
mock_card_builder_class,
719+
mock_task_store_class,
720+
mock_request_handler_class,
721+
mock_agent_executor_class,
722+
):
723+
"""Test to_a2a with HTTPS protocol."""
724+
# Arrange
725+
mock_app = Mock(spec=Starlette)
726+
mock_starlette_class.return_value = mock_app
727+
mock_task_store = Mock(spec=InMemoryTaskStore)
728+
mock_task_store_class.return_value = mock_task_store
729+
mock_agent_executor = Mock(spec=A2aAgentExecutor)
730+
mock_agent_executor_class.return_value = mock_agent_executor
731+
mock_request_handler = Mock(spec=DefaultRequestHandler)
732+
mock_request_handler_class.return_value = mock_request_handler
733+
mock_card_builder = Mock(spec=AgentCardBuilder)
734+
mock_card_builder_class.return_value = mock_card_builder
735+
736+
# Act
737+
result = to_a2a(self.mock_agent, protocol="https")
738+
739+
# Assert
740+
assert result == mock_app
741+
mock_card_builder_class.assert_called_once_with(
742+
agent=self.mock_agent, rpc_url="https://localhost:8000/"
743+
)
744+
745+
@patch("google.adk.a2a.utils.agent_to_a2a.A2aAgentExecutor")
746+
@patch("google.adk.a2a.utils.agent_to_a2a.DefaultRequestHandler")
747+
@patch("google.adk.a2a.utils.agent_to_a2a.InMemoryTaskStore")
748+
@patch("google.adk.a2a.utils.agent_to_a2a.AgentCardBuilder")
749+
@patch("google.adk.a2a.utils.agent_to_a2a.Starlette")
750+
def test_to_a2a_with_custom_protocol(
751+
self,
752+
mock_starlette_class,
753+
mock_card_builder_class,
754+
mock_task_store_class,
755+
mock_request_handler_class,
756+
mock_agent_executor_class,
757+
):
758+
"""Test to_a2a with custom protocol."""
759+
# Arrange
760+
mock_app = Mock(spec=Starlette)
761+
mock_starlette_class.return_value = mock_app
762+
mock_task_store = Mock(spec=InMemoryTaskStore)
763+
mock_task_store_class.return_value = mock_task_store
764+
mock_agent_executor = Mock(spec=A2aAgentExecutor)
765+
mock_agent_executor_class.return_value = mock_agent_executor
766+
mock_request_handler = Mock(spec=DefaultRequestHandler)
767+
mock_request_handler_class.return_value = mock_request_handler
768+
mock_card_builder = Mock(spec=AgentCardBuilder)
769+
mock_card_builder_class.return_value = mock_card_builder
770+
771+
# Act
772+
result = to_a2a(self.mock_agent, protocol="ws")
773+
774+
# Assert
775+
assert result == mock_app
776+
mock_card_builder_class.assert_called_once_with(
777+
agent=self.mock_agent, rpc_url="ws://localhost:8000/"
778+
)
779+
780+
@patch("google.adk.a2a.utils.agent_to_a2a.A2aAgentExecutor")
781+
@patch("google.adk.a2a.utils.agent_to_a2a.DefaultRequestHandler")
782+
@patch("google.adk.a2a.utils.agent_to_a2a.InMemoryTaskStore")
783+
@patch("google.adk.a2a.utils.agent_to_a2a.AgentCardBuilder")
784+
@patch("google.adk.a2a.utils.agent_to_a2a.Starlette")
785+
def test_to_a2a_with_all_custom_parameters(
786+
self,
787+
mock_starlette_class,
788+
mock_card_builder_class,
789+
mock_task_store_class,
790+
mock_request_handler_class,
791+
mock_agent_executor_class,
792+
):
793+
"""Test to_a2a with all custom parameters."""
794+
# Arrange
795+
mock_app = Mock(spec=Starlette)
796+
mock_starlette_class.return_value = mock_app
797+
mock_task_store = Mock(spec=InMemoryTaskStore)
798+
mock_task_store_class.return_value = mock_task_store
799+
mock_agent_executor = Mock(spec=A2aAgentExecutor)
800+
mock_agent_executor_class.return_value = mock_agent_executor
801+
mock_request_handler = Mock(spec=DefaultRequestHandler)
802+
mock_request_handler_class.return_value = mock_request_handler
803+
mock_card_builder = Mock(spec=AgentCardBuilder)
804+
mock_card_builder_class.return_value = mock_card_builder
805+
806+
# Act
807+
result = to_a2a(
808+
self.mock_agent, host="api.example.com", port=443, protocol="https"
809+
)
810+
811+
# Assert
812+
assert result == mock_app
813+
mock_card_builder_class.assert_called_once_with(
814+
agent=self.mock_agent, rpc_url="https://api.example.com:443/"
815+
)

0 commit comments

Comments
 (0)