fix: Handle App objects in eval and graph endpoints

Merge https://github.com/google/adk-python/pull/3060

## Description

Fixes #3059

This PR fixes two endpoints in `adk web` that fail when using App objects instead of bare agents.

## Changes

- **Eval execution endpoint** (line ~969): Extract root_agent from App objects before passing to LocalEvalService
- **Graph visualization endpoint** (line ~1308): Extract root_agent from App objects before graph operations

Both endpoints now properly handle both BaseAgent and App objects by checking the type and extracting `.root_agent` when needed.

## Testing Plan

### Manual E2E Testing with ADK Web

Tested with an App object that includes context caching:

```python
from google.adk.apps import App
from google.adk.agents import LlmAgent

root_agent = LlmAgent(name="MyAgent", model="gemini-1.5-pro-002")
app = App(
    name="my_agent",
    root_agent=root_agent,
    context_cache_config=ContextCacheConfig(...)
)
```

**Before fix:**
- Graph visualization failed (tried to call agent methods on App object)
- Eval execution failed (LocalEvalService received App instead of agent)

**After fix:**
- Graph visualization works correctly
- Eval execution works correctly
- Both endpoints properly extract root_agent from App objects

## Checklist

- [x] Code follows project style (autoformat.sh passed)
- [x] Changes are focused and minimal
- [x] Issue #3059 created and referenced
- [x] Manual E2E testing completed

COPYBARA_INTEGRATE_REVIEW=https://github.com/google/adk-python/pull/3060 from ejfn:ejfn/bugfix-app-object-endpoints 01c30191bfd9487a8c8463ccf24b297cb9a4ce37
PiperOrigin-RevId: 821746910
This commit is contained in:
ejfn
2025-10-20 12:10:50 -07:00
committed by Copybara-Service
parent ee39a89110
commit 0b73a6937b
+11 -11
View File
@@ -486,6 +486,12 @@ class AdkWebServer:
self.runner_dict[app_name] = runner
return runner
def _get_root_agent(self, agent_or_app: BaseAgent | App) -> BaseAgent:
"""Extract root agent from either a BaseAgent or App object."""
if isinstance(agent_or_app, App):
return agent_or_app.root_agent
return agent_or_app
def _create_runner(self, agentic_app: App) -> Runner:
"""Create a runner with common services."""
return Runner(
@@ -933,9 +939,8 @@ class AdkWebServer:
# Populate the session with initial session state.
agent_or_app = self.agent_loader.load_agent(app_name)
if isinstance(agent_or_app, App):
agent_or_app = agent_or_app.root_agent
initial_session_state = create_empty_state(agent_or_app)
root_agent = self._get_root_agent(agent_or_app)
initial_session_state = create_empty_state(root_agent)
new_eval_case = EvalCase(
eval_id=req.eval_id,
@@ -1096,7 +1101,8 @@ class AdkWebServer:
status_code=400, detail=f"Eval set `{eval_set_id}` not found."
)
root_agent = self.agent_loader.load_agent(app_name)
agent_or_app = self.agent_loader.load_agent(app_name)
root_agent = self._get_root_agent(agent_or_app)
eval_case_results = []
@@ -1437,13 +1443,7 @@ class AdkWebServer:
function_calls = event.get_function_calls()
function_responses = event.get_function_responses()
agent_or_app = self.agent_loader.load_agent(app_name)
# The loader may return an App; unwrap to its root agent so the graph builder
# receives a BaseAgent instance.
root_agent = (
agent_or_app.root_agent
if isinstance(agent_or_app, App)
else agent_or_app
)
root_agent = self._get_root_agent(agent_or_app)
dot_graph = None
if function_calls:
function_call_highlights = []