Skip to content
Merged
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
153 changes: 153 additions & 0 deletions contributing/samples/interactions_api/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
# Interactions API Sample Agent

This sample agent demonstrates the Interactions API integration in ADK. The
Interactions API provides stateful conversation capabilities, allowing chained
interactions using `previous_interaction_id` instead of sending full
conversation history.

## Features Tested

1. **Basic Text Generation** - Simple conversation without tools
2. **Google Search Tool** - Web search using `GoogleSearchTool` with
`bypass_multi_tools_limit=True`
3. **Multi-Turn Conversations** - Stateful interactions with context retention
via `previous_interaction_id`
4. **Custom Function Tool** - Weather lookup using `get_current_weather`

## Important: Tool Compatibility

The Interactions API does **NOT** support mixing custom function calling tools
with built-in tools (like `google_search`) in the same agent. To work around
this limitation:

```python
# Use bypass_multi_tools_limit=True to convert google_search to a function tool
GoogleSearchTool(bypass_multi_tools_limit=True)
```

This converts the built-in `google_search` to a function calling tool (via
`GoogleSearchAgentTool`), which allows it to work alongside custom function
tools.

## How to Run

### Prerequisites

```bash
# From the adk-python root directory
uv sync --all-extras
source .venv/bin/activate

# Set up authentication (choose one):
# Option 1: Using Google Cloud credentials
export GOOGLE_CLOUD_PROJECT=your-project-id

# Option 2: Using API Key
export GOOGLE_API_KEY=your-api-key
```

### Running Tests

```bash
cd contributing/samples

# Run automated tests with Interactions API
python -m interactions_api.main
```

## Key Differences: Interactions API vs Standard API

### Interactions API (`use_interactions_api=True`)
- Uses stateful interactions via `previous_interaction_id`
- Only sends current turn contents when chaining interactions
- Returns `interaction_id` in responses for chaining
- Ideal for long conversations with many turns
- Context caching is not used (state maintained via interaction chaining)

### Standard API (`use_interactions_api=False`)
- Uses stateless `generate_content` calls
- Sends full conversation history with each request
- No interaction IDs in responses
- Context caching can be used

## Code Structure

```
interactions_api/
├── __init__.py # Package initialization
├── agent.py # Agent definition with Interactions API
├── main.py # Test runner
├── test_interactions_curl.sh # cURL-based API tests
├── test_interactions_direct.py # Direct API tests
└── README.md # This file
```

## Agent Configuration

```python
from google.adk.agents.llm_agent import Agent
from google.adk.models.google_llm import Gemini
from google.adk.tools.google_search_tool import GoogleSearchTool

root_agent = Agent(
model=Gemini(
model="gemini-2.5-flash",
use_interactions_api=True, # Enable Interactions API
),
name="interactions_test_agent",
tools=[
GoogleSearchTool(bypass_multi_tools_limit=True), # Converted to function tool
get_current_weather, # Custom function tool
],
)
```

## Example Output

```
============================================================
TEST 1: Basic Text Generation
============================================================

>> User: Hello! What can you help me with?
<< Agent: Hello! I can help you with: 1) Search the web...
[Interaction ID: v1_abc123...]
PASSED: Basic text generation works

============================================================
TEST 2: Function Calling (Google Search Tool)
============================================================

>> User: Search for the capital of France.
[Tool Call] google_search_agent({'request': 'capital of France'})
[Tool Result] google_search_agent: {'result': 'The capital of France is Paris...'}
<< Agent: The capital of France is Paris.
[Interaction ID: v1_def456...]
PASSED: Google search tool works

============================================================
TEST 3: Multi-Turn Conversation (Stateful)
============================================================

>> User: Remember the number 42.
<< Agent: I'll remember that number - 42.
[Interaction ID: v1_ghi789...]

>> User: What number did I ask you to remember?
<< Agent: You asked me to remember the number 42.
[Interaction ID: v1_jkl012...]
PASSED: Multi-turn conversation works with context retention

============================================================
TEST 5: Custom Function Tool (get_current_weather)
============================================================

>> User: What's the weather like in Tokyo?
[Tool Call] get_current_weather({'city': 'Tokyo'})
[Tool Result] get_current_weather: {'city': 'Tokyo', 'temperature_f': 68, ...}
<< Agent: The weather in Tokyo is 68F and Partly Cloudy.
[Interaction ID: v1_mno345...]
PASSED: Custom function tool works with bypass_multi_tools_limit

ALL TESTS PASSED (Interactions API)
```
17 changes: 17 additions & 0 deletions contributing/samples/interactions_api/__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.

"""Sample agent for testing the Interactions API integration."""

from . import agent
105 changes: 105 additions & 0 deletions contributing/samples/interactions_api/agent.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
# 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.

"""Agent definition for testing the Interactions API integration.

NOTE: The Interactions API does NOT support mixing custom function calling tools
with built-in tools in the same agent. To work around this limitation, we use
bypass_multi_tools_limit=True on GoogleSearchTool, which converts the built-in
google_search to a function calling tool (via GoogleSearchAgentTool).

The bypass is only triggered when len(agent.tools) > 1, so we include multiple
tools in the agent (GoogleSearchTool + get_current_weather).

With bypass_multi_tools_limit=True and multiple tools, all tools become function
calling tools, which allows mixing google_search with custom function tools.
"""

from google.adk.agents.llm_agent import Agent
from google.adk.models.google_llm import Gemini
from google.adk.tools.google_search_tool import GoogleSearchTool


def get_current_weather(city: str) -> dict:
"""Get the current weather for a city.

This is a mock implementation for testing purposes.

Args:
city: The name of the city to get weather for.

Returns:
A dictionary containing weather information.
"""
# Mock weather data for testing
weather_data = {
"new york": {"temperature": 72, "condition": "Sunny", "humidity": 45},
"london": {"temperature": 59, "condition": "Cloudy", "humidity": 78},
"tokyo": {
"temperature": 68,
"condition": "Partly Cloudy",
"humidity": 60,
},
"paris": {"temperature": 64, "condition": "Rainy", "humidity": 85},
"sydney": {"temperature": 77, "condition": "Clear", "humidity": 55},
}

city_lower = city.lower()
if city_lower in weather_data:
data = weather_data[city_lower]
return {
"city": city,
"temperature_f": data["temperature"],
"condition": data["condition"],
"humidity": data["humidity"],
}
else:
return {
"city": city,
"temperature_f": 70,
"condition": "Unknown",
"humidity": 50,
"note": "Weather data not available, using defaults",
}


# Main agent with google_search (via bypass) and custom function tools
# Using bypass_multi_tools_limit=True converts google_search to a function calling tool.
# We need len(tools) > 1 to trigger the bypass, so we include get_current_weather directly.
# This allows mixing google_search with custom function tools via the Interactions API.
#
# NOTE: code_executor is not compatible with function calling mode because the model
# tries to call a function (e.g., run_code) instead of outputting code in markdown.
root_agent = Agent(
model=Gemini(
model="gemini-2.5-flash",
use_interactions_api=True,
),
name="interactions_test_agent",
description="An agent for testing the Interactions API integration",
instruction="""You are a helpful assistant that can:

1. Search the web for information using google_search
2. Get weather information using get_current_weather

When users ask for information that requires searching, use google_search.
When users ask about weather, use get_current_weather.

Be concise and helpful in your responses. Always confirm what you did.
""",
tools=[
GoogleSearchTool(bypass_multi_tools_limit=True),
get_current_weather,
],
)
Loading
Loading