Skip to content

Commit 49580f6

Browse files
Merge branch 'main' into feat/firestore
2 parents 9d8bb5d + e63d991 commit 49580f6

123 files changed

Lines changed: 8733 additions & 712 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/triage.yml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,7 @@ jobs:
1717
if: >-
1818
github.repository == 'google/adk-python' && (
1919
github.event_name == 'schedule' ||
20-
github.event.action == 'opened' ||
21-
github.event.label.name == 'planned'
20+
github.event.action == 'opened'
2221
)
2322
permissions:
2423
issues: write

contributing/samples/adk_triaging_agent/agent.py

Lines changed: 29 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,14 @@
4242
"workflow": "DeanChensj",
4343
}
4444

45+
46+
LABEL_TO_GTECH = [
47+
"klateefa",
48+
"llalitkumarrr",
49+
"surajksharma07",
50+
"sanketpatil06",
51+
]
52+
4553
LABEL_GUIDELINES = """
4654
Label rubric and disambiguation rules:
4755
- "documentation": Tutorials, README content, reference docs, or samples.
@@ -121,15 +129,13 @@ def list_untriaged_issues(issue_count: int) -> dict[str, Any]:
121129

122130
existing_component_labels = issue_labels & component_labels
123131
has_component = bool(existing_component_labels)
124-
has_planned = "planned" in issue_labels
125132

126133
# Determine what actions are needed
127134
needs_component_label = not has_component
128-
needs_owner = has_planned and not assignees
135+
needs_owner = not assignees
129136

130137
# Include issue if it needs any action
131138
if needs_component_label or needs_owner:
132-
issue["has_planned_label"] = has_planned
133139
issue["has_component_label"] = has_component
134140
issue["existing_component_label"] = (
135141
list(existing_component_labels)[0]
@@ -146,7 +152,6 @@ def list_untriaged_issues(issue_count: int) -> dict[str, Any]:
146152

147153
def add_label_to_issue(issue_number: int, label: str) -> dict[str, Any]:
148154
"""Add the specified component label to the given issue number.
149-
150155
Args:
151156
issue_number: issue number of the GitHub issue.
152157
label: label to assign
@@ -177,37 +182,30 @@ def add_label_to_issue(issue_number: int, label: str) -> dict[str, Any]:
177182
}
178183

179184

180-
def add_owner_to_issue(issue_number: int, label: str) -> dict[str, Any]:
181-
"""Assign an owner to the issue based on the component label.
185+
def assign_gtech_owner_to_issue(issue_number: int) -> dict[str, Any]:
186+
"""Assign an owner from the GTech team to the given issue number.
182187
183-
This should only be called for issues that have the 'planned' label.
188+
This is go to option irrespective of component label or planned label,
189+
as long as the issue needs an owner.
190+
191+
All unassigned issues will be considered for GTech ownership. Unassigned
192+
issues will seperated in two categories: issues with type "Bug" and issues
193+
with type "Feature". Then bug issues and feature issues will be equally
194+
assigned to the Gtech members in such a way that every day all members get
195+
equal number of bug and feature issues.
184196
185197
Args:
186198
issue_number: issue number of the GitHub issue.
187-
label: component label that determines the owner to assign
188199
189200
Returns:
190201
The status of this request, with the assigned owner when successful.
191202
"""
192-
print(
193-
f"Attempting to assign owner for label '{label}' to issue #{issue_number}"
194-
)
195-
if label not in LABEL_TO_OWNER:
196-
return error_response(
197-
f"Error: Label '{label}' is not a valid component label."
198-
)
199-
200-
owner = LABEL_TO_OWNER.get(label, None)
201-
if not owner:
202-
return {
203-
"status": "warning",
204-
"message": f"Label '{label}' does not have an owner. Will not assign.",
205-
}
206-
203+
print(f"Attempting to assign GTech owner to issue #{issue_number}")
204+
gtech_assignee = LABEL_TO_GTECH[issue_number % len(LABEL_TO_GTECH)]
207205
assignee_url = (
208206
f"{GITHUB_BASE_URL}/repos/{OWNER}/{REPO}/issues/{issue_number}/assignees"
209207
)
210-
assignee_payload = {"assignees": [owner]}
208+
assignee_payload = {"assignees": [gtech_assignee]}
211209

212210
try:
213211
response = post_request(assignee_url, assignee_payload)
@@ -217,7 +215,7 @@ def add_owner_to_issue(issue_number: int, label: str) -> dict[str, Any]:
217215
return {
218216
"status": "success",
219217
"message": response,
220-
"assigned_owner": owner,
218+
"assigned_owner": gtech_assignee,
221219
}
222220

223221

@@ -259,7 +257,7 @@ def change_issue_type(issue_number: int, issue_type: str) -> dict[str, Any]:
259257
260258
Each issue will have flags indicating what actions are needed:
261259
- `needs_component_label`: true if the issue needs a component label
262-
- `needs_owner`: true if the issue needs an owner assigned (has 'planned' label but no assignee)
260+
- `needs_owner`: true if the issue needs an owner assigned
263261
264262
For each issue, perform ONLY the required actions based on the flags:
265263
@@ -271,8 +269,8 @@ def change_issue_type(issue_number: int, issue_type: str) -> dict[str, Any]:
271269
- Otherwise → do not change the issue type
272270
273271
2. **If `needs_owner` is true**:
274-
- Use `add_owner_to_issue` to assign an owner based on the component label
275-
- Note: If the issue already has a component label (`has_component_label: true`), use that existing label to determine the owner
272+
- Use `assign_gtech_owner_to_issue` to assign an owner.
273+
276274
277275
Do NOT add a component label if `needs_component_label` is false.
278276
Do NOT assign an owner if `needs_owner` is false.
@@ -282,19 +280,18 @@ def change_issue_type(issue_number: int, issue_type: str) -> dict[str, Any]:
282280
placeholders (never output text like "[fill in later]").
283281
- Justify the chosen label with a short explanation referencing the issue
284282
details.
285-
- Mention the assigned owner only when you actually assign one (i.e., when
286-
the issue has the 'planned' label).
283+
- Mention the assigned owner only when you actually assign one.
287284
- If no label is applied, clearly state why.
288285
289286
Present the following in an easy to read format highlighting issue number and your label.
290287
- the issue summary in a few sentence
291288
- your label recommendation and justification
292-
- the owner of the label if you assign the issue to an owner (only for planned issues)
289+
- the owner, if you assign the issue to an owner
293290
""",
294291
tools=[
295292
list_untriaged_issues,
296293
add_label_to_issue,
297-
add_owner_to_issue,
294+
assign_gtech_owner_to_issue,
298295
change_issue_type,
299296
],
300297
)

contributing/samples/adk_triaging_agent/main.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ async def fetch_specific_issue_details(issue_number: int):
5757

5858
# Determine what actions are needed
5959
needs_component_label = not has_component
60-
needs_owner = has_planned and not has_assignee
60+
needs_owner = not has_assignee
6161

6262
if needs_component_label or needs_owner:
6363
print(

contributing/samples/agent_registry_agent/agent.py

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
from google.adk.agents.llm_agent import LlmAgent
2020
from google.adk.integrations.agent_registry import AgentRegistry
21+
from google.adk.models.google_llm import Gemini
2122

2223
# Project and location can be set via environment variables:
2324
# GOOGLE_CLOUD_PROJECT and GOOGLE_CLOUD_LOCATION
@@ -27,6 +28,8 @@
2728
# Initialize Agent Registry client
2829
registry = AgentRegistry(project_id=project_id, location=location)
2930

31+
# List agents, MCP servers, and endpoints resource names from the registry.
32+
# They can be used to initialize the agent, toolset, and model below.
3033
print(f"Listing agents in {project_id}/{location}...")
3134
agents = registry.list_agents()
3235
for agent in agents.get("agents", []):
@@ -37,6 +40,11 @@
3740
for server in mcp_servers.get("mcpServers", []):
3841
print(f"- MCP Server: {server.get('displayName')} ({server.get('name')})")
3942

43+
print(f"\nListing endpoints in {project_id}/{location}...")
44+
endpoints = registry.list_endpoints()
45+
for endpoint in endpoints.get("endpoints", []):
46+
print(f"- Endpoint: {endpoint.get('displayName')} ({endpoint.get('name')})")
47+
4048
# Example of using a specific agent or MCP server from the registry:
4149
# (Note: These names should be full resource names as returned by list methods)
4250

@@ -52,8 +60,19 @@
5260
f"projects/{project_id}/locations/{location}/mcpServers/MCP_SERVER_NAME"
5361
)
5462

63+
# 3. Getting a specific model endpoint configuration
64+
# This returns a string like:
65+
# "projects/adk12345/locations/us-central1/publishers/google/models/gemini-2.5-flash"
66+
# TODO: Replace ENDPOINT_NAME with your endpoint name
67+
model_name = registry.get_model_name(
68+
f"projects/{project_id}/locations/{location}/endpoints/ENDPOINT_NAME"
69+
)
70+
71+
# Initialize the model using the resolved model name from registry.
72+
gemini_model = Gemini(model=model_name)
73+
5574
root_agent = LlmAgent(
56-
model="gemini-2.5-flash",
75+
model=gemini_model,
5776
name="discovery_agent",
5877
instruction=(
5978
"You have access to tools and sub-agents discovered via Registry."

contributing/samples/bigquery/agent.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
from google.adk.tools.bigquery.config import BigQueryToolConfig
2222
from google.adk.tools.bigquery.config import WriteMode
2323
import google.auth
24+
import google.auth.transport.requests
2425

2526
# Define the desired credential type.
2627
# By default use Application Default Credentials (ADC) from the local
@@ -57,6 +58,8 @@
5758
# service account key file
5859
# https://cloud.google.com/iam/docs/service-account-creds#user-managed-keys
5960
creds, _ = google.auth.load_credentials_from_file("service_account_key.json")
61+
if not creds.valid:
62+
creds.refresh(google.auth.transport.requests.Request())
6063
credentials_config = BigQueryCredentialsConfig(credentials=creds)
6164
elif CREDENTIALS_TYPE == AuthCredentialTypes.HTTP:
6265
# Initialize the tools to use the externally provided access token. One such
@@ -73,6 +76,10 @@
7376
# Initialize the tools to use the application default credentials.
7477
# https://cloud.google.com/docs/authentication/provide-credentials-adc
7578
application_default_credentials, _ = google.auth.default()
79+
if not application_default_credentials.valid:
80+
application_default_credentials.refresh(
81+
google.auth.transport.requests.Request()
82+
)
7683
credentials_config = BigQueryCredentialsConfig(
7784
credentials=application_default_credentials
7885
)

contributing/samples/data_agent/agent.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,10 @@
5151
# Initialize the tools to use the application default credentials.
5252
# https://cloud.google.com/docs/authentication/provide-credentials-adc
5353
application_default_credentials, _ = google.auth.default()
54+
if not application_default_credentials.valid:
55+
application_default_credentials.refresh(
56+
google.auth.transport.requests.Request()
57+
)
5458
credentials_config = DataAgentCredentialsConfig(
5559
credentials=application_default_credentials
5660
)

contributing/samples/hello_world/main.py

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,15 @@ async def run_prompt(session: Session, new_message: str):
4848
session_id=session.id,
4949
new_message=content,
5050
):
51-
if event.content.parts and event.content.parts[0].text:
52-
print(f'** {event.author}: {event.content.parts[0].text}')
51+
if event.content.parts:
52+
for part in event.content.parts:
53+
if part.text:
54+
print(f'** {event.author}: {part.text}')
55+
if part.function_call:
56+
print(
57+
f'** {event.author} calls tool: {part.function_call.name} with'
58+
f' args {part.function_call.args}'
59+
)
5360

5461
async def run_prompt_bytes(session: Session, new_message: str):
5562
content = types.Content(
@@ -74,6 +81,7 @@ async def check_rolls_in_state(rolls_size: int):
7481
session = await runner.session_service.get_session(
7582
app_name=app_name, user_id=user_id_1, session_id=session_11.id
7683
)
84+
print('** session.state:', session.state)
7785
assert len(session.state['rolls']) == rolls_size
7886
for roll in session.state['rolls']:
7987
assert roll > 0 and roll <= 100
@@ -82,9 +90,9 @@ async def check_rolls_in_state(rolls_size: int):
8290
print('Start time:', start_time)
8391
print('------------------------------------')
8492
await run_prompt(session_11, 'Hi')
85-
await run_prompt(session_11, 'Roll a die with 100 sides')
93+
await run_prompt(session_11, 'Roll a dice with 100 sides')
8694
await check_rolls_in_state(1)
87-
await run_prompt(session_11, 'Roll a die again with 100 sides.')
95+
await run_prompt(session_11, 'Roll a dice again with 100 sides.')
8896
await check_rolls_in_state(2)
8997
await run_prompt(session_11, 'What numbers did I got?')
9098
await run_prompt_bytes(session_11, 'Hi bytes')
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# Simple Live (Bidi-Streaming) Agent with Parallel Tools
2+
This project provides a basic example of a live, [bidirectional streaming](https://google.github.io/adk-docs/streaming/) agent that demonstrates parallel tool execution.
3+
4+
## Getting Started
5+
6+
Follow these steps to get the agent up and running:
7+
8+
1. **Start the ADK Web Server**
9+
Open your terminal, navigate to the root directory that contains the
10+
`live_bidi_streaming_parallel_tools_agent` folder, and execute the following
11+
command:
12+
```bash
13+
adk web
14+
```
15+
16+
2. **Access the ADK Web UI**
17+
Once the server is running, open your web browser and navigate to the URL
18+
provided in the terminal (it will typically be `http://localhost:8000`).
19+
20+
3. **Select the Agent**
21+
In the top-left corner of the ADK Web UI, use the dropdown menu to select
22+
this agent (`live_bidi_streaming_parallel_tools_agent`).
23+
24+
4. **Start Streaming**
25+
Click on the **Audio** icon located near the chat input
26+
box to begin the streaming session.
27+
28+
5. **Interact with the Agent**
29+
You can now begin talking to the agent, and it will respond in real-time.
30+
Try asking it to perform multiple actions at once, for example: "Turn on the
31+
lights and the TV at the same time." The agent will be able to invoke both
32+
`turn_on_lights` and `turn_on_tv` tools in parallel.
33+
34+
## Usage Notes
35+
36+
* You only need to click the **Audio** button once to initiate the
37+
stream. The current version does not support stopping and restarting the stream
38+
by clicking the button again during a session.
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# Copyright 2026 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
from . import agent
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# Copyright 2026 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
16+
from google.adk.agents.llm_agent import Agent
17+
18+
19+
def turn_on_lights():
20+
"""Turn on the lights."""
21+
print("turn_on_lights")
22+
return {"status": "OK"}
23+
24+
25+
def turn_on_tv():
26+
"""Turn on the tv."""
27+
print("turn_on_tv")
28+
return {"status": "OK"}
29+
30+
31+
root_agent = Agent(
32+
model="gemini-live-2.5-flash-native-audio",
33+
name="Home_helper",
34+
instruction="Be polite and answer all user's questions.",
35+
tools=[turn_on_lights, turn_on_tv],
36+
)

0 commit comments

Comments
 (0)