Skip to content

Commit 7425481

Browse files
authored
Merge branch 'main' into fix/parallel-state-delta-list-merge
2 parents a4ddc64 + cb4dd42 commit 7425481

68 files changed

Lines changed: 5306 additions & 487 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/toolbox_agent/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Toolbox Agent
22

3-
This agent utilizes [MCP toolbox for database](https://googleapis.github.io/genai-toolbox/getting-started/introduction/) to assist end users based on information stored in a database.
3+
This agent utilizes [MCP toolbox for database](https://mcp-toolbox.dev) to assist end users based on information stored in a database.
44

55
Follow the steps below to run this agent.
66

pyproject.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ test = [
123123
"a2a-sdk>=0.3.0,<0.4.0",
124124
"anthropic>=0.43.0", # For anthropic model tests
125125
"crewai[tools];python_version>='3.11' and python_version<'3.12'", # For CrewaiTool tests; chromadb/pypika fail on 3.12+
126+
"google-cloud-parametermanager>=0.4.0, <1.0.0",
126127
"kubernetes>=29.0.0", # For GkeCodeExecutor
127128
"langchain-community>=0.3.17",
128129
"langgraph>=0.2.60, <0.4.8", # For LangGraphAgent
@@ -157,6 +158,7 @@ extensions = [
157158
"beautifulsoup4>=3.2.2", # For load_web_page tool.
158159
"crewai[tools];python_version>='3.11' and python_version<'3.12'", # For CrewaiTool; chromadb/pypika fail on 3.12+
159160
"docker>=7.0.0", # For ContainerCodeExecutor
161+
"google-cloud-parametermanager>=0.4.0, <1.0.0",
160162
"kubernetes>=29.0.0", # For GkeCodeExecutor
161163
"k8s-agent-sandbox>=0.1.1.post3", # For GkeCodeExecutor sandbox mode
162164
"langgraph>=0.2.60, <0.4.8", # For LangGraphAgent

src/google/adk/a2a/converters/from_adk_event.py

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,23 @@ def convert_event_to_a2a_events(
218218
),
219219
)
220220
)
221+
elif _serialize_value(event.actions) is not None:
222+
a2a_events.append(
223+
TaskStatusUpdateEvent(
224+
task_id=task_id,
225+
context_id=context_id,
226+
status=TaskStatus(
227+
state=TaskState.working,
228+
message=Message(
229+
message_id=str(uuid.uuid4()),
230+
role=Role.agent,
231+
parts=[],
232+
),
233+
timestamp=datetime.now(timezone.utc).isoformat(),
234+
),
235+
final=False,
236+
)
237+
)
221238

222239
a2a_events = _add_event_metadata(event, a2a_events)
223240
return a2a_events
@@ -280,7 +297,10 @@ def _add_event_metadata(
280297
metadata[_get_adk_metadata_key(field_name)] = value
281298

282299
for a2a_event in a2a_events:
283-
if isinstance(a2a_event, TaskStatusUpdateEvent):
300+
if (
301+
isinstance(a2a_event, TaskStatusUpdateEvent)
302+
and a2a_event.status.message
303+
):
284304
a2a_event.status.message.metadata = metadata.copy()
285305
elif isinstance(a2a_event, TaskArtifactUpdateEvent):
286306
a2a_event.artifact.metadata = metadata.copy()

src/google/adk/a2a/executor/a2a_agent_executor.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -260,17 +260,15 @@ async def _handle_request(
260260
context.context_id,
261261
self._config.gen_ai_part_converter,
262262
):
263-
a2a_event = await execute_after_event_interceptors(
263+
a2a_events = await execute_after_event_interceptors(
264264
a2a_event,
265265
executor_context,
266266
adk_event,
267267
self._config.execute_interceptors,
268268
)
269-
if a2a_event is None:
270-
continue
271-
272-
task_result_aggregator.process_event(a2a_event)
273-
await event_queue.enqueue_event(a2a_event)
269+
for e in a2a_events:
270+
task_result_aggregator.process_event(e)
271+
await event_queue.enqueue_event(e)
274272

275273
# publish the task result event - this is final
276274
if (

src/google/adk/a2a/executor/a2a_agent_executor_impl.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
from ..experimental import a2a_experimental
5050
from .config import A2aAgentExecutorConfig
5151
from .executor_context import ExecutorContext
52+
from .interceptors.include_artifacts_in_a2a_event import include_artifacts_in_a2a_event_interceptor
5253
from .utils import execute_after_agent_interceptors
5354
from .utils import execute_after_event_interceptors
5455
from .utils import execute_before_agent_interceptors
@@ -221,15 +222,14 @@ async def _handle_request(
221222
self._config.gen_ai_part_converter,
222223
):
223224
a2a_event.metadata = self._get_invocation_metadata(executor_context)
224-
a2a_event = await execute_after_event_interceptors(
225+
a2a_events = await execute_after_event_interceptors(
225226
a2a_event,
226227
executor_context,
227228
adk_event,
228229
self._config.execute_interceptors,
229230
)
230-
if not a2a_event:
231-
continue
232-
await event_queue.enqueue_event(a2a_event)
231+
for e in a2a_events:
232+
await event_queue.enqueue_event(e)
233233

234234
if error_event:
235235
final_event = error_event

src/google/adk/a2a/executor/config.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ class ExecuteInterceptor:
5757
after_event: Optional[
5858
Callable[
5959
[ExecutorContext, A2AEvent, Event],
60-
Awaitable[Union[A2AEvent, None]],
60+
Awaitable[Union[A2AEvent, list[A2AEvent], None]],
6161
]
6262
] = None
6363
"""Hook executed after an ADK event is converted to an A2A event.
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
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 .include_artifacts_in_a2a_event import include_artifacts_in_a2a_event_interceptor
16+
17+
__all__ = [
18+
"include_artifacts_in_a2a_event_interceptor",
19+
]

0 commit comments

Comments
 (0)