diff --git a/src/google/adk/cli/adk_web_server.py b/src/google/adk/cli/adk_web_server.py index b073f1a7..f0cae1ae 100644 --- a/src/google/adk/cli/adk_web_server.py +++ b/src/google/adk/cli/adk_web_server.py @@ -436,6 +436,7 @@ class AdkWebServer: extra_plugins: Optional[list[str]] = None, logo_text: Optional[str] = None, logo_image_url: Optional[str] = None, + url_prefix: Optional[str] = None, ): self.agent_loader = agent_loader self.session_service = session_service @@ -452,6 +453,7 @@ class AdkWebServer: self.runners_to_clean: set[str] = set() self.current_app_name_ref: SharedValue[str] = SharedValue(value="") self.runner_dict = {} + self.url_prefix = url_prefix async def get_runner_async(self, app_name: str) -> Runner: """Returns the cached runner for the given app.""" @@ -559,6 +561,7 @@ class AdkWebServer: " overwritten.", runtime_config_path, ) + runtime_config["backendUrl"] = self.url_prefix if self.url_prefix else "" # Set custom logo config. if self.logo_text or self.logo_image_url: @@ -1562,6 +1565,10 @@ class AdkWebServer: mimetypes.add_type("application/javascript", ".js", True) mimetypes.add_type("text/javascript", ".js", True) + redirect_dev_ui_url = ( + self.url_prefix + "/dev-ui/" if self.url_prefix else "/dev-ui/" + ) + @app.get("/dev-ui/config") async def get_ui_config(): return { @@ -1571,11 +1578,11 @@ class AdkWebServer: @app.get("/") async def redirect_root_to_dev_ui(): - return RedirectResponse("/dev-ui/") + return RedirectResponse(redirect_dev_ui_url) @app.get("/dev-ui") async def redirect_dev_ui_add_slash(): - return RedirectResponse("/dev-ui/") + return RedirectResponse(redirect_dev_ui_url) app.mount( "/dev-ui/", diff --git a/src/google/adk/cli/cli_tools_click.py b/src/google/adk/cli/cli_tools_click.py index b841e017..009b96fe 100644 --- a/src/google/adk/cli/cli_tools_click.py +++ b/src/google/adk/cli/cli_tools_click.py @@ -1028,6 +1028,17 @@ def fast_api_common_options(): ), multiple=True, ) + @click.option( + "--url_prefix", + type=str, + help=( + "Optional. URL path prefix when the application is mounted behind a" + " reverse proxy or API gateway (e.g., '/api/v1', '/adk'). This" + " ensures generated URLs and redirects work correctly when the app" + " is not served at the root path. Must start with '/' if provided." + ), + default=None, + ) @functools.wraps(func) @click.pass_context def wrapper(ctx, *args, **kwargs): @@ -1065,6 +1076,7 @@ def cli_web( allow_origins: Optional[list[str]] = None, host: str = "127.0.0.1", port: int = 8000, + url_prefix: Optional[str] = None, trace_to_cloud: bool = False, otel_to_cloud: bool = False, reload: bool = True, @@ -1128,6 +1140,7 @@ def cli_web( a2a=a2a, host=host, port=port, + url_prefix=url_prefix, reload_agents=reload_agents, extra_plugins=extra_plugins, logo_text=logo_text, @@ -1164,6 +1177,7 @@ def cli_api_server( allow_origins: Optional[list[str]] = None, host: str = "127.0.0.1", port: int = 8000, + url_prefix: Optional[str] = None, trace_to_cloud: bool = False, otel_to_cloud: bool = False, reload: bool = True, @@ -1203,6 +1217,7 @@ def cli_api_server( a2a=a2a, host=host, port=port, + url_prefix=url_prefix, reload_agents=reload_agents, extra_plugins=extra_plugins, ), diff --git a/src/google/adk/cli/fast_api.py b/src/google/adk/cli/fast_api.py index 7e1e18be..58dcfc15 100644 --- a/src/google/adk/cli/fast_api.py +++ b/src/google/adk/cli/fast_api.py @@ -64,6 +64,7 @@ def get_fast_api_app( a2a: bool = False, host: str = "127.0.0.1", port: int = 8000, + url_prefix: Optional[str] = None, trace_to_cloud: bool = False, otel_to_cloud: bool = False, reload_agents: bool = False, @@ -144,6 +145,7 @@ def get_fast_api_app( extra_plugins=extra_plugins, logo_text=logo_text, logo_image_url=logo_image_url, + url_prefix=url_prefix, ) # Callbacks & other optional args for when constructing the FastAPI instance