Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
217 changes: 217 additions & 0 deletions contributing/samples/runner_debug_example/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,217 @@
# Runner Debug Helper Example

This example demonstrates the `run_debug()` helper method that simplifies agent interaction for debugging and experimentation in ADK.

## Overview

The `run_debug()` method reduces agent interaction boilerplate from 7-8 lines to just 2 lines, making it ideal for:

- Quick debugging sessions
- Jupyter notebooks
- REPL experimentation
- Writing examples
- Initial agent development

## Files Included

- `agent.py` - Agent with 2 tools: weather and calculate
- `main.py` - 8 examples demonstrating all features
- `README.md` - This documentation

## Setup

### Prerequisites

Set your Google API key:

```bash
export GOOGLE_API_KEY="your-api-key"
```

### Running the Example

```bash
python -m contributing.samples.runner_debug_example.main
```

## Features Demonstrated

1. **Minimal Usage**: Simple 2-line agent interaction
2. **Multiple Queries**: Processing multiple queries in sequence
3. **Session Persistence**: Maintaining conversation context
4. **Separate Sessions**: Managing multiple user sessions
5. **Tool Calls**: Displaying tool invocations and results
6. **Event Capture**: Collecting events for programmatic inspection
7. **Advanced Configuration**: Using RunConfig for custom settings
8. **Comparison**: Before/after boilerplate reduction

## Part Types Supported

The `run_debug()` method properly displays all ADK part types:

| Part Type | Display Format | Use Case |
|-----------|---------------|----------|
| `text` | `agent > {text}` | Regular text responses |
| `function_call` | `agent > [Calling tool: {name}({args})]` | Tool invocations |
| `function_response` | `agent > [Tool result: {response}]` | Tool results |
| `executable_code` | `agent > [Executing {language} code...]` | Code blocks |
| `code_execution_result` | `agent > [Code output: {output}]` | Code execution results |
| `inline_data` | `agent > [Inline data: {mime_type}]` | Images, files, etc. |
| `file_data` | `agent > [File: {uri}]` | File references |

## Tools Available in Example

The example agent includes 2 tools to demonstrate tool handling:

1. **`get_weather(city)`** - Returns mock weather data for major cities
2. **`calculate(expression)`** - Evaluates mathematical expressions safely

## Key Benefits

### Before (7-8 lines)

```python
from google.adk.sessions import InMemorySessionService
from google.genai import types

APP_NAME = "default"
USER_ID = "default"
session_service = InMemorySessionService()
runner = Runner(agent=agent, app_name=APP_NAME, session_service=session_service)
session = await session_service.create_session(
app_name=APP_NAME, user_id=USER_ID, session_id="default"
)
content = types.Content(role="user", parts=[types.Part.from_text("Hi")])
async for event in runner.run_async(
user_id=USER_ID, session_id=session.id, new_message=content
):
if event.content and event.content.parts:
print(event.content.parts[0].text)
```

### After (2 lines)

```python
runner = InMemoryRunner(agent=agent)
await runner.run_debug("Hi")
```

## API Reference

```python
async def run_debug(
self,
user_queries: str | list[str] | None = None,
*,
user_id: str = 'default',
session_name: str = 'default',
print_output: bool = True,
return_events: bool = False,
verbose: bool = False,
run_config: Optional[RunConfig] = None,
) -> Optional[List[Event]]:
```

### Parameters

- `user_queries`: Single query string or list of queries
- `user_id`: User identifier for session tracking
- `session_name`: Session identifier for conversation continuity
- `print_output`: Whether to print responses to console
- `return_events`: Whether to return event list
- `verbose`: Whether to show detailed tool calls and responses (default: False)
- `run_config`: Optional advanced configuration

### Usage Examples

```python
# Minimal usage
runner = InMemoryRunner(agent=agent)
await runner.run_debug("What's the weather?")

# Multiple queries
await runner.run_debug(["Query 1", "Query 2", "Query 3"])

# Custom session
await runner.run_debug(
"Hello",
user_id="alice",
session_name="debug_session"
)

# Capture events without printing
events = await runner.run_debug(
"Process this",
print_output=False,
return_events=True
)

# Show tool calls with verbose mode
await runner.run_debug(
"What's the weather?",
verbose=True # Shows [Calling tool: ...] and [Tool result: ...]
)

# With custom configuration
from google.adk.agents.run_config import RunConfig
config = RunConfig(support_cfc=False)
await runner.run_debug("Query", run_config=config)
```

## Troubleshooting

### Common Issues and Solutions

1. **Tool calls not showing in output**
- **Issue**: Tool invocations and responses are not displayed
- **Solution**: Set `verbose=True` to see detailed tool interactions:

```python
await runner.run_debug("Query", verbose=True)
```

2. **Import errors when running tests**
- **Issue**: `ModuleNotFoundError: No module named 'google.adk'`
- **Solution**: Ensure you're using the virtual environment:

```bash
source .venv/bin/activate
python -m pytest tests/
```

3. **Session state not persisting between calls**
- **Issue**: Agent doesn't remember previous interactions
- **Solution**: Use the same `user_id` and `session_name` across calls:

```python
await runner.run_debug("First query", user_id="alice", session_name="debug")
await runner.run_debug("Follow-up", user_id="alice", session_name="debug")
```

4. **Output truncation issues**
- **Issue**: Long tool responses are truncated with "..."
- **Solution**: This is by design to keep debug output readable. For full responses, use:

```python
events = await runner.run_debug("Query", print_output=False, return_events=True)
# Process events programmatically for full content
```

5. **API key errors**
- **Issue**: Authentication failures or missing API key
- **Solution**: Ensure your Google API key is set:

```bash
export GOOGLE_API_KEY="your-api-key"
```

## Important Notes

`run_debug()` is designed for debugging and experimentation only. For production use requiring:

- Custom session/memory services (Spanner, Cloud SQL)
- Fine-grained event processing
- Error recovery and resumability
- Performance optimization

Use the standard `run_async()` method instead.
17 changes: 17 additions & 0 deletions contributing/samples/runner_debug_example/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Runner debug example demonstrating simplified agent interaction."""

from . import agent
124 changes: 124 additions & 0 deletions contributing/samples/runner_debug_example/agent.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Example agent for demonstrating run_debug helper method."""

from google.adk import Agent
from google.adk.tools.tool_context import ToolContext


def get_weather(city: str, tool_context: ToolContext) -> str:
"""Get weather information for a city.

Args:
city: Name of the city to get weather for.
tool_context: Tool context for session state.

Returns:
Weather information as a string.
"""
# Store query history in session state
if "weather_queries" not in tool_context.state:
tool_context.state["weather_queries"] = []
tool_context.state["weather_queries"].append(city)

# Mock weather data for demonstration
weather_data = {
"San Francisco": "Foggy, 15°C (59°F)",
"New York": "Sunny, 22°C (72°F)",
"London": "Rainy, 12°C (54°F)",
"Tokyo": "Clear, 25°C (77°F)",
"Paris": "Cloudy, 18°C (64°F)",
}

return weather_data.get(
city, f"Weather data not available for {city}. Try a major city."
)


def calculate(expression: str) -> str:
"""Safely evaluate a mathematical expression.

This tool demonstrates how function calls are displayed in run_debug().

Args:
expression: Mathematical expression to evaluate.

Returns:
Result of the calculation as a string.
"""
import ast
import operator

# Supported operators for safe evaluation
operators = {
ast.Add: operator.add,
ast.Sub: operator.sub,
ast.Mult: operator.mul,
ast.Div: operator.truediv,
ast.Pow: operator.pow,
ast.USub: operator.neg,
}

def _eval(node):
"""Recursively evaluate an AST node."""
if isinstance(node, ast.Expression):
return _eval(node.body)
elif isinstance(node, ast.Constant): # Python 3.8+
return node.value
elif isinstance(node, ast.Num): # For older Python versions
return node.n
elif isinstance(node, ast.BinOp):
op = operators.get(type(node.op))
if op:
return op(_eval(node.left), _eval(node.right))
else:
raise ValueError(f"Unsupported operation: {type(node.op).__name__}")
elif isinstance(node, ast.UnaryOp):
op = operators.get(type(node.op))
if op:
return op(_eval(node.operand))
else:
raise ValueError(f"Unsupported operation: {type(node.op).__name__}")
else:
raise ValueError(f"Unsupported expression type: {type(node).__name__}")

try:
# Parse the expression into an AST
tree = ast.parse(expression, mode="eval")
# Safely evaluate the AST
result = _eval(tree)
return f"Result: {result}"
except (SyntaxError, ValueError) as e:
return f"Error: {str(e)}"
except ZeroDivisionError:
return "Error: Division by zero"
except Exception as e:
return f"Error: {str(e)}"


root_agent = Agent(
model="gemini-2.5-flash-lite",
name="agent",
description="A helpful assistant demonstrating run_debug() helper method",
instruction="""You are a helpful assistant that can:
1. Provide weather information for major cities
2. Perform mathematical calculations
3. Remember previous queries in the conversation

When users ask about weather, use the get_weather tool.
When users ask for calculations, use the calculate tool.
Be friendly and conversational.""",
tools=[get_weather, calculate],
)
Loading