Skip to content

Commit 6d64415

Browse files
committed
fix(web): restore build_graph_image endpoint
1 parent 0582867 commit 6d64415

2 files changed

Lines changed: 66 additions & 0 deletions

File tree

src/google/adk/cli/adk_web_server.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
from fastapi import Response
3838
from fastapi.middleware.cors import CORSMiddleware
3939
from fastapi.responses import RedirectResponse
40+
from fastapi.responses import Response
4041
from fastapi.responses import StreamingResponse
4142
from fastapi.staticfiles import StaticFiles
4243
from fastapi.websockets import WebSocket
@@ -1033,6 +1034,24 @@ async def get_trace_dict(event_id: str) -> Any:
10331034

10341035
if web_assets_dir:
10351036

1037+
@app.get("/dev/build_graph_image/{app_name}")
1038+
async def get_app_graph_image(
1039+
app_name: str, dark_mode: bool = False
1040+
) -> Response:
1041+
agent_or_app = self.agent_loader.load_agent(app_name)
1042+
root_agent = self._get_root_agent(agent_or_app)
1043+
1044+
graph_image = await agent_graph.get_agent_graph(
1045+
root_agent, [], image=True, dark_mode=dark_mode
1046+
)
1047+
1048+
if isinstance(graph_image, bytes):
1049+
return Response(content=graph_image, media_type="image/png")
1050+
1051+
raise HTTPException(
1052+
status_code=500, detail="Failed to render app graph image"
1053+
)
1054+
10361055
@app.get("/dev/build_graph/{app_name}")
10371056
async def get_app_info(app_name: str) -> Any:
10381057
runner = await self.get_runner_async(app_name)

tests/unittests/cli/test_fast_api.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1769,6 +1769,53 @@ def list_agents(self):
17691769
assert "dotSrc" in response.json()
17701770

17711771

1772+
def test_build_graph_image_returns_png_bytes():
1773+
"""Ensure legacy graph-image endpoint still returns a PNG for the dev UI."""
1774+
from google.adk.cli.adk_web_server import AdkWebServer
1775+
1776+
root_agent = DummyAgent(name="dummy_agent")
1777+
app_agent = App(name="test_app", root_agent=root_agent)
1778+
1779+
class Loader:
1780+
1781+
def load_agent(self, app_name):
1782+
return app_agent
1783+
1784+
def list_agents(self):
1785+
return [app_agent.name]
1786+
1787+
adk_web_server = AdkWebServer(
1788+
agent_loader=Loader(),
1789+
session_service=AsyncMock(),
1790+
memory_service=MagicMock(),
1791+
artifact_service=MagicMock(),
1792+
credential_service=MagicMock(),
1793+
eval_sets_manager=MagicMock(),
1794+
eval_set_results_manager=MagicMock(),
1795+
agents_dir=".",
1796+
)
1797+
1798+
fast_api_app = adk_web_server.get_fast_api_app(
1799+
setup_observer=lambda _observer, _server: None,
1800+
tear_down_observer=lambda _observer, _server: None,
1801+
)
1802+
1803+
client = TestClient(fast_api_app)
1804+
1805+
with patch(
1806+
"google.adk.cli.agent_graph.get_agent_graph",
1807+
new=AsyncMock(return_value=b"png-bytes"),
1808+
) as mock_get_agent_graph:
1809+
response = client.get("/dev/build_graph_image/test_app?dark_mode=true")
1810+
1811+
assert response.status_code == 200
1812+
assert response.content == b"png-bytes"
1813+
assert response.headers["content-type"] == "image/png"
1814+
mock_get_agent_graph.assert_awaited_once_with(
1815+
root_agent, [], image=True, dark_mode=True
1816+
)
1817+
1818+
17721819
def test_a2a_agent_discovery(test_app_with_a2a):
17731820
"""Test that A2A agents are properly discovered and configured."""
17741821
# This test mainly verifies that the A2A setup doesn't break the app

0 commit comments

Comments
 (0)