Skip to content

Commit 7a9152a

Browse files
authored
feat(interactions): support for GenAI SDK upgraded to 2.9+ (#6191)
1 parent 4f3f350 commit 7a9152a

6 files changed

Lines changed: 1098 additions & 681 deletions

File tree

contributing/samples/interactions_api/agent.py

Lines changed: 3 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -12,19 +12,7 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15-
"""Agent definition for testing the Interactions API integration.
16-
17-
NOTE: The Interactions API does NOT support mixing custom function calling tools
18-
with built-in tools in the same agent. To work around this limitation, we use
19-
bypass_multi_tools_limit=True on GoogleSearchTool, which converts the built-in
20-
google_search to a function calling tool (via GoogleSearchAgentTool).
21-
22-
The bypass is only triggered when len(agent.tools) > 1, so we include multiple
23-
tools in the agent (GoogleSearchTool + get_current_weather).
24-
25-
With bypass_multi_tools_limit=True and multiple tools, all tools become function
26-
calling tools, which allows mixing google_search with custom function tools.
27-
"""
15+
"""Agent definition for testing the Interactions API integration."""
2816

2917
from google.adk.agents.llm_agent import Agent
3018
from google.adk.models.google_llm import Gemini
@@ -74,10 +62,7 @@ def get_current_weather(city: str) -> dict:
7462
}
7563

7664

77-
# Main agent with google_search (via bypass) and custom function tools
78-
# Using bypass_multi_tools_limit=True converts google_search to a function calling tool.
79-
# We need len(tools) > 1 to trigger the bypass, so we include get_current_weather directly.
80-
# This allows mixing google_search with custom function tools via the Interactions API.
65+
# Main agent with google_search built-in tool and custom function tools
8166
#
8267
# NOTE: code_executor is not compatible with function calling mode because the model
8368
# tries to call a function (e.g., run_code) instead of outputting code in markdown.
@@ -99,7 +84,7 @@ def get_current_weather(city: str) -> dict:
9984
Be concise and helpful in your responses. Always confirm what you did.
10085
""",
10186
tools=[
102-
GoogleSearchTool(bypass_multi_tools_limit=True),
87+
GoogleSearchTool(),
10388
get_current_weather,
10489
],
10590
)

contributing/samples/interactions_api/main.py

Lines changed: 44 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,11 @@
1616
1717
This script tests the following features:
1818
1. Basic text generation
19-
2. Google Search tool (via bypass_multi_tools_limit)
19+
2. Google Search tool
2020
3. Multi-turn conversations with stateful interactions
2121
4. Google Search tool (additional coverage)
2222
5. Custom function tool (get_current_weather)
2323
24-
NOTE: The Interactions API does NOT support mixing custom function calling tools
25-
with built-in tools. To work around this, we use bypass_multi_tools_limit=True
26-
on GoogleSearchTool, which converts it to a function calling tool (via
27-
GoogleSearchAgentTool). The bypass only triggers when len(agent.tools) > 1,
28-
so we include both GoogleSearchTool and get_current_weather in the agent.
29-
3024
NOTE: Code execution via UnsafeLocalCodeExecutor is not compatible with function
3125
calling mode because the model tries to call a function instead of outputting
3226
code in markdown.
@@ -41,14 +35,14 @@
4135
import logging
4236
from pathlib import Path
4337
import time
44-
from typing import Optional
4538

4639
from dotenv import load_dotenv
4740
from google.adk.agents.run_config import RunConfig
4841
from google.adk.cli.utils import logs
4942
from google.adk.runners import InMemoryRunner
5043
from google.adk.runners import Runner
5144
from google.genai import types
45+
import httpx
5246

5347
from .agent import root_agent
5448

@@ -67,7 +61,8 @@ async def call_agent_async(
6761
prompt: str,
6862
agent_name: str = "",
6963
show_interaction_id: bool = True,
70-
) -> tuple[str, Optional[str]]:
64+
additional_parts: list[types.Part] | None = None,
65+
) -> tuple[str, str | None]:
7166
"""Call the agent asynchronously with the user's prompt.
7267
7368
Args:
@@ -77,13 +72,16 @@ async def call_agent_async(
7772
prompt: The prompt to send
7873
agent_name: The expected agent name for filtering responses
7974
show_interaction_id: Whether to show interaction IDs in output
75+
additional_parts: Optional list of additional content parts (e.g. files)
8076
8177
Returns:
8278
A tuple of (response_text, interaction_id)
8379
"""
84-
content = types.Content(
85-
role="user", parts=[types.Part.from_text(text=prompt)]
86-
)
80+
parts = [types.Part.from_text(text=prompt)]
81+
if additional_parts:
82+
parts.extend(additional_parts)
83+
84+
content = types.Content(role="user", parts=parts)
8785

8886
final_response_text = ""
8987
last_interaction_id = None
@@ -264,6 +262,39 @@ async def test_custom_function_tool(runner: Runner, session_id: str):
264262
return interaction_id
265263

266264

265+
async def test_pdf_summarization(runner: Runner, session_id: str) -> str | None:
266+
"""Test PDF summarization using the Interactions API."""
267+
print("\n" + "=" * 60)
268+
print("TEST 6: PDF Summarization")
269+
print("=" * 60)
270+
271+
url = "https://storage.googleapis.com/cloud-samples-data/generative-ai/pdf/2403.05530.pdf"
272+
print(f"Downloading {url}...")
273+
async with httpx.AsyncClient() as client:
274+
response = await client.get(
275+
url, headers={"User-Agent": "Mozilla/5.0"}, follow_redirects=True
276+
)
277+
response.raise_for_status()
278+
pdf_bytes = response.content
279+
280+
pdf_part = types.Part.from_bytes(data=pdf_bytes, mime_type="application/pdf")
281+
response, interaction_id = await call_agent_async(
282+
runner,
283+
USER_ID,
284+
session_id,
285+
"Please summarize the attached PDF document.",
286+
additional_parts=[pdf_part],
287+
)
288+
289+
assert response, "Expected a non-empty response"
290+
assert len(response) > 0, f"Expected summary in response: {response}"
291+
assert (
292+
"gemini" in response.lower() or "multimodal" in response.lower()
293+
), f"Expected summary of PDF in response: {response}"
294+
print("PASSED: PDF Summarization works")
295+
return interaction_id
296+
297+
267298
def check_interactions_api_available() -> bool:
268299
"""Check if the interactions API is available in the SDK."""
269300
try:
@@ -311,6 +342,7 @@ async def run_all_tests():
311342
await test_multi_turn_conversation(runner, session.id)
312343
await test_google_search_tool(runner, session.id)
313344
await test_custom_function_tool(runner, session.id)
345+
await test_pdf_summarization(runner, session.id)
314346

315347
print("\n" + "=" * 60)
316348
print("ALL TESTS PASSED (Interactions API)")

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ dependencies = [
5151
"google-cloud-spanner>=3.56,<4", # For Spanner database
5252
"google-cloud-speech>=2.30,<3", # For Audio Transcription
5353
"google-cloud-storage>=2.18,<4", # For GCS Artifact service
54-
"google-genai>=1.72,<2", # Google GenAI SDK
54+
"google-genai>=2.9,<3", # Google GenAI SDK
5555
"graphviz>=0.20.2,<1", # Graphviz for graph rendering
5656
"httpx>=0.27,<1", # HTTP client library
5757
"jsonschema>=4.23,<5", # Agent Builder config validation

0 commit comments

Comments
 (0)