Skip to content

Commit dd9149b

Browse files
committed
feat: more generic url
1 parent 9291daa commit dd9149b

File tree

9 files changed

+86
-47
lines changed

9 files changed

+86
-47
lines changed

contributing/samples/a2a_auth/README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -185,15 +185,16 @@ When deploying the remote BigQuery A2A agent to different environments (e.g., Cl
185185
}
186186
```
187187

188-
**Important:** The `url` field in `remote_a2a/bigquery_agent/agent.json` must point to the actual RPC endpoint where your remote BigQuery A2A agent is deployed and accessible.
188+
**Important:** The `url` field in `remote_a2a/bigquery_agent/agent.json` must point to the actual RPC endpoint where your remote BigQuery A2A agent is deployed and accessible.
189+
If the `url` field is an empty string, it will be automatically filled by the `--host` and `--port`, or `--base_url` provided to `adk api_server`.
189190

190191
## Troubleshooting
191192

192193
**Connection Issues:**
193194
- Ensure the local ADK web server is running on port 8000
194195
- Ensure the remote A2A server is running on port 8001
195196
- Check that no firewall is blocking localhost connections
196-
- **Verify the `url` field in `remote_a2a/bigquery_agent/agent.json` matches the actual deployed location of your remote A2A server**
197+
- **Verify the `url` field in `remote_a2a/bigquery_agent/agent.json` matches the actual deployed location of your remote A2A server, or if it's empty, make sure the `--host` and `--port`, or `--base_url` provided to `adk api_server` match the actual deployed location of your remote A2A server**
197198
- Verify the agent card URL passed to RemoteA2AAgent constructor matches the running A2A server
198199

199200

contributing/samples/a2a_basic/README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,14 +136,15 @@ When deploying the remote A2A agent to different environments (e.g., Cloud Run,
136136
```
137137

138138
**Important:** The `url` field in `remote_a2a/check_prime_agent/agent.json` must point to the actual RPC endpoint where your remote A2A agent is deployed and accessible.
139+
If the `url` field is an empty string, it will be automatically filled by the `--host` and `--port`, or `--base_url` provided to `adk api_server`.
139140

140141
## Troubleshooting
141142

142143
**Connection Issues:**
143144
- Ensure the local ADK web server is running on port 8000
144145
- Ensure the remote A2A server is running on port 8001
145146
- Check that no firewall is blocking localhost connections
146-
- **Verify the `url` field in `remote_a2a/check_prime_agent/agent.json` matches the actual deployed location of your remote A2A server**
147+
- **Verify the `url` field in `remote_a2a/check_prime_agent/agent.json` matches the actual deployed location of your remote A2A server, or if it's empty, make sure the `--host` and `--port`, or `--base_url` provided to `adk api_server` match the actual deployed location of your remote A2A server**
147148
- Verify the agent card URL passed to RemoteA2AAgent constructor matches the running A2A server
148149

149150

contributing/samples/a2a_human_in_loop/README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,14 +145,15 @@ When deploying the remote approval A2A agent to different environments (e.g., Cl
145145
```
146146

147147
**Important:** The `url` field in `remote_a2a/human_in_loop/agent.json` must point to the actual RPC endpoint where your remote approval A2A agent is deployed and accessible.
148+
If the `url` field is an empty string, it will be automatically filled by the `--host` and `--port`, or `--base_url` provided to `adk api_server`.
148149

149150
## Troubleshooting
150151

151152
**Connection Issues:**
152153
- Ensure the local ADK web server is running on port 8000
153154
- Ensure the remote A2A server is running on port 8001
154155
- Check that no firewall is blocking localhost connections
155-
- **Verify the `url` field in `remote_a2a/human_in_loop/agent.json` matches the actual deployed location of your remote A2A server**
156+
- **Verify the `url` field in `remote_a2a/human_in_loop/agent.json` matches the actual deployed location of your remote A2A server, or if it's empty, make sure the `--host` and `--port`, or `--base_url` provided to `adk api_server` match the actual deployed location of your remote A2A server**
156157
- Verify the agent card URL passed to RemoteA2AAgent constructor matches the running A2A server
157158

158159
**Agent Not Responding:**

contributing/samples/a2a_root/remote_a2a/hello_world/agent.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,4 +108,4 @@ async def check_prime(nums: list[int]) -> str:
108108
),
109109
)
110110

111-
a2a_app = to_a2a(root_agent, port=8001)
111+
a2a_app = to_a2a(root_agent, base_url='http://localhost:8001/')

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

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -47,17 +47,13 @@
4747
def to_a2a(
4848
agent: BaseAgent,
4949
*,
50-
host: str = "localhost",
51-
port: int = 8000,
52-
protocol: str = "http",
50+
base_url: str = "http://localhost:8000/",
5351
) -> Starlette:
5452
"""Convert an ADK agent to a A2A Starlette application.
5553
5654
Args:
5755
agent: The ADK agent to convert
58-
host: The host for the A2A RPC URL (default: "localhost")
59-
port: The port for the A2A RPC URL (default: 8000)
60-
protocol: The protocol for the A2A RPC URL (default: "http")
56+
base_url: The base URL for the A2A RPC URL (default: "http://localhost:8000/")
6157
6258
Returns:
6359
A Starlette application that can be run with uvicorn
@@ -94,10 +90,9 @@ async def create_runner() -> Runner:
9490
)
9591

9692
# Build agent card
97-
rpc_url = f"{protocol}://{host}:{port}/"
9893
card_builder = AgentCardBuilder(
9994
agent=agent,
100-
rpc_url=rpc_url,
95+
rpc_url=base_url,
10196
)
10297

10398
# Create a Starlette app that will be configured during startup

src/google/adk/cli/cli_tools_click.py

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import os
2424
import tempfile
2525
from typing import Optional
26+
from urllib.parse import urlparse
2627

2728
import click
2829
from click.core import ParameterSource
@@ -628,6 +629,16 @@ def decorator(func):
628629
help="Optional. The port of the server",
629630
default=8000,
630631
)
632+
@click.option(
633+
"--base_url",
634+
type=str,
635+
help=(
636+
"Optional. The base URL of the server. "
637+
"Mutually exclusive with --host and --port. "
638+
"Overrides the values of --host and --port if specified."
639+
),
640+
show_default=True,
641+
)
631642
@click.option(
632643
"--allow_origins",
633644
help="Optional. Any additional origins to allow for CORS.",
@@ -721,6 +732,7 @@ def cli_web(
721732
allow_origins: Optional[list[str]] = None,
722733
host: str = "127.0.0.1",
723734
port: int = 8000,
735+
base_url: Optional[str] = None,
724736
trace_to_cloud: bool = False,
725737
reload: bool = True,
726738
session_service_uri: Optional[str] = None,
@@ -741,6 +753,16 @@ def cli_web(
741753
adk web --session_service_uri=[uri] --port=[port] path/to/agents_dir
742754
"""
743755
logs.setup_adk_logger(getattr(logging, log_level.upper()))
756+
if base_url is None:
757+
base_url = f"http://{host}:{port}"
758+
else:
759+
parsed_url = urlparse(base_url)
760+
host = parsed_url.hostname
761+
port = parsed_url.port
762+
logging.debug(
763+
f"Ignoring --host and --port parameters, because --base-url is"
764+
f" specified."
765+
)
744766

745767
@asynccontextmanager
746768
async def _lifespan(app: FastAPI):
@@ -749,7 +771,7 @@ async def _lifespan(app: FastAPI):
749771
+-----------------------------------------------------------------------------+
750772
| ADK Web Server started |
751773
| |
752-
| For local testing, access at http://{host}:{port}.{" "*(29 - len(str(port)))}|
774+
| For local testing, access at {base_url}.{" "*(29 - len(str(port)))}|
753775
+-----------------------------------------------------------------------------+
754776
""",
755777
fg="green",
@@ -777,8 +799,7 @@ async def _lifespan(app: FastAPI):
777799
trace_to_cloud=trace_to_cloud,
778800
lifespan=_lifespan,
779801
a2a=a2a,
780-
host=host,
781-
port=port,
802+
base_url=base_url,
782803
reload_agents=reload_agents,
783804
)
784805
config = uvicorn.Config(
@@ -812,6 +833,7 @@ def cli_api_server(
812833
allow_origins: Optional[list[str]] = None,
813834
host: str = "127.0.0.1",
814835
port: int = 8000,
836+
base_url: Optional[str] = None,
815837
trace_to_cloud: bool = False,
816838
reload: bool = True,
817839
session_service_uri: Optional[str] = None,
@@ -833,6 +855,16 @@ def cli_api_server(
833855
"""
834856
logs.setup_adk_logger(getattr(logging, log_level.upper()))
835857

858+
if base_url is None:
859+
base_url = f"http://{host}:{port}"
860+
else:
861+
parsed_url = urlparse(base_url)
862+
host = parsed_url.hostname
863+
port = parsed_url.port
864+
logging.debug(
865+
f"Ignoring --host and --port parameters, because --base-url is"
866+
f" specified."
867+
)
836868
session_service_uri = session_service_uri or session_db_url
837869
artifact_service_uri = artifact_service_uri or artifact_storage_uri
838870
config = uvicorn.Config(
@@ -846,8 +878,7 @@ def cli_api_server(
846878
web=False,
847879
trace_to_cloud=trace_to_cloud,
848880
a2a=a2a,
849-
host=host,
850-
port=port,
881+
base_url=base_url,
851882
reload_agents=reload_agents,
852883
),
853884
host=host,

src/google/adk/cli/fast_api.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,7 @@ def get_fast_api_app(
6464
allow_origins: Optional[list[str]] = None,
6565
web: bool,
6666
a2a: bool = False,
67-
host: str = "127.0.0.1",
68-
port: int = 8000,
67+
base_url: str = "http://127.0.0.1:8000",
6968
trace_to_cloud: bool = False,
7069
reload_agents: bool = False,
7170
lifespan: Optional[Lifespan[FastAPI]] = None,
@@ -352,6 +351,8 @@ async def _get_a2a_runner_async() -> Runner:
352351
logger.info("Setting up A2A agent: %s", app_name)
353352

354353
try:
354+
a2a_rpc_path = f"{base_url}/a2a/{app_name}"
355+
355356
agent_executor = A2aAgentExecutor(
356357
runner=create_a2a_runner_loader(app_name),
357358
)
@@ -363,6 +364,10 @@ async def _get_a2a_runner_async() -> Runner:
363364
with (p / "agent.json").open("r", encoding="utf-8") as f:
364365
data = json.load(f)
365366
agent_card = AgentCard(**data)
367+
if (
368+
agent_card.url == ""
369+
): # empty url is a placeholder to be filled with the provided url
370+
agent_card.url = a2a_rpc_path
366371

367372
a2a_app = A2AStarletteApplication(
368373
agent_card=agent_card,

tests/unittests/a2a/utils/test_agent_to_a2a.py

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -145,9 +145,7 @@ def test_to_a2a_custom_host_port_protocol(
145145
mock_card_builder_class.return_value = mock_card_builder
146146

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

152150
# Assert
153151
assert result == mock_app
@@ -524,7 +522,7 @@ def test_to_a2a_with_custom_port_zero(
524522
mock_card_builder_class.return_value = mock_card_builder
525523

526524
# Act
527-
result = to_a2a(self.mock_agent, port=0)
525+
result = to_a2a(self.mock_agent, base_url="http://localhost:0/")
528526

529527
# Assert
530528
assert result == mock_app
@@ -559,7 +557,7 @@ def test_to_a2a_with_empty_string_host(
559557
mock_card_builder_class.return_value = mock_card_builder
560558

561559
# Act
562-
result = to_a2a(self.mock_agent, host="")
560+
result = to_a2a(self.mock_agent, base_url="http://:8000/")
563561

564562
# Assert
565563
assert result == mock_app
@@ -594,7 +592,7 @@ def test_to_a2a_with_negative_port(
594592
mock_card_builder_class.return_value = mock_card_builder
595593

596594
# Act
597-
result = to_a2a(self.mock_agent, port=-1)
595+
result = to_a2a(self.mock_agent, base_url="http://localhost:-1/")
598596

599597
# Assert
600598
assert result == mock_app
@@ -664,7 +662,9 @@ def test_to_a2a_with_special_characters_in_host(
664662
mock_card_builder_class.return_value = mock_card_builder
665663

666664
# Act
667-
result = to_a2a(self.mock_agent, host="test-host.example.com")
665+
result = to_a2a(
666+
self.mock_agent, base_url="http://test-host.example.com:8000/"
667+
)
668668

669669
# Assert
670670
assert result == mock_app
@@ -699,7 +699,7 @@ def test_to_a2a_with_ip_address_host(
699699
mock_card_builder_class.return_value = mock_card_builder
700700

701701
# Act
702-
result = to_a2a(self.mock_agent, host="192.168.1.1")
702+
result = to_a2a(self.mock_agent, base_url="http://192.168.1.1:8000/")
703703

704704
# Assert
705705
assert result == mock_app
@@ -734,7 +734,7 @@ def test_to_a2a_with_https_protocol(
734734
mock_card_builder_class.return_value = mock_card_builder
735735

736736
# Act
737-
result = to_a2a(self.mock_agent, protocol="https")
737+
result = to_a2a(self.mock_agent, base_url="https://localhost:8000/")
738738

739739
# Assert
740740
assert result == mock_app
@@ -769,7 +769,7 @@ def test_to_a2a_with_custom_protocol(
769769
mock_card_builder_class.return_value = mock_card_builder
770770

771771
# Act
772-
result = to_a2a(self.mock_agent, protocol="ws")
772+
result = to_a2a(self.mock_agent, base_url="ws://localhost:8000/")
773773

774774
# Assert
775775
assert result == mock_app
@@ -804,9 +804,7 @@ def test_to_a2a_with_all_custom_parameters(
804804
mock_card_builder_class.return_value = mock_card_builder
805805

806806
# Act
807-
result = to_a2a(
808-
self.mock_agent, host="api.example.com", port=443, protocol="https"
809-
)
807+
result = to_a2a(self.mock_agent, base_url="https://api.example.com")
810808

811809
# Assert
812810
assert result == mock_app

0 commit comments

Comments
 (0)