Skip to content

Commit 2b7aafa

Browse files
committed
fix(cli): resolve Dev UI graph broken and FunctionTool complex type crash
- Re-adds missing /dev/build_graph_image/ endpoint to fix broken dev UI graph visualization (#5430). - Strips unsupported prefixItems/unevaluatedItems from JSON schema to fix FunctionTool crash on complex types (#5428). - Fixes circular import NameError by moving common decorators to the top of cli_tools_click.py. - Resolves TypeErrors in cli_run and tests by correctly propagating new options (max_llm_calls, avatar_config, trigger_sources). - Updates test_adk_run to correctly ignore the handled 'verbose' option.
1 parent 6e6e124 commit 2b7aafa

9 files changed

Lines changed: 304 additions & 254 deletions

File tree

contributing/samples/gepa/experiment.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,6 @@
4343
from tau_bench.types import EnvRunResult
4444
from tau_bench.types import RunConfig
4545
import tau_bench_agent as tau_bench_agent_lib
46-
4746
import utils
4847

4948

contributing/samples/gepa/run_experiment.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@
2525
from absl import flags
2626
import experiment
2727
from google.genai import types
28-
2928
import utils
3029

3130
_OUTPUT_DIR = flags.DEFINE_string(

repro_5428.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import asyncio
2+
from google.adk.tools.function_tool import FunctionTool
3+
4+
async def generate_image(
5+
prompt: str,
6+
input_bytes: list[tuple[bytes, str]] | None = None,
7+
) -> dict:
8+
"""Generate an image from a prompt."""
9+
return {"status": "success"}
10+
11+
async def main():
12+
try:
13+
generate_image_tool = FunctionTool(func=generate_image)
14+
generate_image_tool._get_declaration()
15+
print("SUCCESS! No validation error.")
16+
except Exception as e:
17+
print(f"FAILED! Error: {type(e).__name__}: {e}")
18+
19+
if __name__ == "__main__":
20+
asyncio.run(main())

src/google/adk/cli/adk_web_server.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2013,6 +2013,26 @@ async def event_generator():
20132013
media_type="text/event-stream",
20142014
)
20152015

2016+
@app.get("/dev/build_graph_image/{app_name}", tags=[TAG_DEBUG])
2017+
async def get_app_graph_image(
2018+
app_name: str, dark_mode: bool = False
2019+
) -> Response:
2020+
"""Returns the agent graph as an SVG image for the dev UI."""
2021+
agent_or_app = self.agent_loader.load_agent(app_name)
2022+
root_agent = self._get_root_agent(agent_or_app)
2023+
2024+
# Get graph with NO highlights (empty list) and specified theme
2025+
dot_graph = await agent_graph.get_agent_graph(
2026+
root_agent, [], dark_mode=dark_mode
2027+
)
2028+
2029+
if dot_graph and isinstance(dot_graph, graphviz.Digraph):
2030+
# Render the graph as SVG
2031+
svg_image = dot_graph.pipe(format="svg")
2032+
return Response(content=svg_image, media_type="image/svg+xml")
2033+
else:
2034+
raise HTTPException(status_code=404, detail="Graph not found")
2035+
20162036
@app.get(
20172037
"/dev/{app_name}/graph",
20182038
response_model_exclude_none=True,

src/google/adk/cli/cli.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
from pydantic import BaseModel
2525

2626
from ..agents.llm_agent import LlmAgent
27+
from ..agents.run_config import RunConfig
2728
from ..apps.app import App
2829
from ..artifacts.base_artifact_service import BaseArtifactService
2930
from ..auth.credential_service.base_credential_service import BaseCredentialService
@@ -189,13 +190,13 @@ async def run_cli(
189190
if avatar_config:
190191
try:
191192
if Path(avatar_config).is_file():
192-
with open(avatar_config, "r", encoding="utf-8") as f:
193+
with open(avatar_config, 'r', encoding='utf-8') as f:
193194
config_dict = json.load(f)
194195
else:
195196
config_dict = json.loads(avatar_config)
196197
avatar_config_obj = types.AvatarConfig.model_validate(config_dict)
197198
except Exception as e:
198-
click.secho(f"Warning: Failed to parse avatar_config: {e}", fg="yellow")
199+
click.secho(f'Warning: Failed to parse avatar_config: {e}', fg='yellow')
199200

200201
agent_loader = AgentLoader(agents_dir=agents_dir)
201202
agent_or_app = agent_loader.load_agent(agent_folder_name)

0 commit comments

Comments
 (0)