Skip to content

feat(function-calling): Enable concurrent function calls #1146

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
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
15 changes: 15 additions & 0 deletions contributing/samples/concurrent_tool_calls/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# 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.

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

from datetime import datetime
import random
from google.adk import Agent
from google.adk.planners import BuiltInPlanner
from google.adk.planners import PlanReActPlanner
from google.adk.tools.tool_context import ToolContext
from google.genai import types


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

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

Returns:
A string containing weather information for the city.
"""
print('@get_weather is starting', datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f'))
import asyncio
# Use async sleep for non-blocking delay to simulate a real-world weather API call.
await asyncio.sleep(3)

# Mock weather data for demonstration
weather_conditions = ["sunny", "cloudy", "rainy", "snowy", "partly cloudy", "stormy"]
temperature = random.randint(-10, 35) # Temperature in Celsius
condition = random.choice(weather_conditions)
humidity = random.randint(30, 90)

weather_info = f"Weather in {city}: {condition}, {temperature}°C, humidity {humidity}%"

if not 'weather_queries' in tool_context.state:
tool_context.state['weather_queries'] = []

tool_context.state['weather_queries'] = tool_context.state['weather_queries'] + [{"city": city, "weather": weather_info}]
return weather_info

def before_tool_cb(tool, args, tool_context):
print('@before_tool_cb1', datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f'))

def after_tool_cb(tool, args, tool_context, tool_response):
print('@after_tool_cb1', datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f'))


root_agent = Agent(
model='gemini-2.0-flash',
name='weather_agent',
description=(
'Weather information agent that can provide current weather conditions'
' for different cities around the world.'
),
instruction="""
You provide weather information for cities and answer questions about weather conditions.
You can check weather for different cities around the world.
You can use multiple tools in parallel by calling functions in parallel (in one request and in one round).
It is ok to discuss previous weather queries and compare weather conditions between cities.
When you are asked to check weather for a city, you must call the get_weather tool with the city name. Be sure to pass in a string with the city name.

IMPORTANT: When you are asked to check weather for multiple cities (e.g., "check weather in New York, London, and Tokyo"), you MUST make ALL the get_weather function calls in parallel (simultaneously in one turn) to provide a faster response. Do NOT wait between weather queries.
""",
tools=[
get_weather,
],
before_tool_callback=[before_tool_cb],
after_tool_callback=[after_tool_cb],
)
78 changes: 78 additions & 0 deletions contributing/samples/concurrent_tool_calls/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# 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.

import asyncio
import time
import warnings

import agent
from dotenv import load_dotenv
from google.adk import Runner
from google.adk.artifacts import InMemoryArtifactService
from google.adk.cli.utils import logs
from google.adk.sessions import InMemorySessionService
from google.adk.sessions import Session
from google.genai import types

load_dotenv(override=True)
warnings.filterwarnings('ignore', category=UserWarning)
logs.log_to_tmp_folder()


async def main():
app_name = 'my_app'
user_id_1 = 'user1'
session_service = InMemorySessionService()
artifact_service = InMemoryArtifactService()
runner = Runner(
app_name=app_name,
agent=agent.root_agent,
artifact_service=artifact_service,
session_service=session_service,
)
session_11 = await session_service.create_session(
app_name=app_name, user_id=user_id_1
)

async def run_prompt(session: Session, new_message: str):
content = types.Content(
role='user', parts=[types.Part.from_text(text=new_message)]
)
print('** User says:', content.model_dump(exclude_none=True))
async for event in runner.run_async(
user_id=user_id_1,
session_id=session.id,
new_message=content,
):
if event.content.parts and event.content.parts[0].text:
print(f'** {event.author}: {event.content.parts[0].text}')

start_time = time.time()
print('Start time:', start_time)
print('------------------------------------')
await run_prompt(session_11, 'Hi')
await run_prompt(session_11, 'I have an urgent meeting. I need to check weather in New York, London, and Tokyo as soon as possible.')
print(
await artifact_service.list_artifact_keys(
app_name=app_name, user_id=user_id_1, session_id=session_11.id
)
)
end_time = time.time()
print('------------------------------------')
print('End time:', end_time)
print('Total time:', end_time - start_time)


if __name__ == '__main__':
asyncio.run(main())
Loading