Skip to content

Commit d17d5d8

Browse files
authored
Merge branch 'main' into firestore
2 parents eb5a94b + eed9bd3 commit d17d5d8

23 files changed

Lines changed: 895 additions & 112 deletions

File tree

.github/workflows/gemini-dispatch.yml

Lines changed: 35 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,6 @@ on:
77
pull_request_review:
88
types:
99
- 'submitted'
10-
pull_request:
11-
types:
12-
- 'opened'
13-
- 'ready_for_review'
14-
issues:
15-
types:
16-
- 'opened'
17-
- 'reopened'
1810
issue_comment:
1911
types:
2012
- 'created'
@@ -44,19 +36,11 @@ jobs:
4436
env | grep '^DEBUG_'
4537
4638
dispatch:
47-
# For PRs: only if not from a fork
48-
# For issues: only on open/reopen
49-
# For comments: only if user types @gemini-cli and is OWNER/MEMBER/COLLABORATOR
39+
# Only trigger if user types @gemini-cli and author association is OWNER, MEMBER, or COLLABORATOR
5040
if: |-
51-
(
52-
github.event_name == 'pull_request' &&
53-
github.event.pull_request.head.repo.fork == false &&
54-
github.event.pull_request.draft == false
55-
) || (
56-
github.event.sender.type == 'User' &&
57-
startsWith(github.event.comment.body || github.event.review.body || github.event.issue.body, '@gemini-cli') &&
58-
contains(fromJSON('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.comment.author_association || github.event.review.author_association || github.event.issue.author_association)
59-
)
41+
github.event.sender.type == 'User' &&
42+
startsWith(github.event.comment.body || github.event.review.body, '@gemini-cli') &&
43+
contains(fromJSON('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.comment.author_association || github.event.review.author_association)
6044
runs-on: 'ubuntu-latest'
6145
permissions:
6246
contents: 'read'
@@ -82,22 +66,43 @@ jobs:
8266

8367
- name: 'Extract command'
8468
id: 'extract_command'
85-
uses: 'actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea' # ratchet:actions/github-script@v7
69+
uses: 'actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd' # ratchet:actions/github-script@v8.0.0
8670
env:
87-
EVENT_TYPE: '${{ github.event_name }}.${{ github.event.action }}'
88-
REQUEST: '${{ github.event.comment.body || github.event.review.body || github.event.issue.body }}'
71+
REQUEST: '${{ github.event.comment.body || github.event.review.body }}'
72+
IS_PR: '${{ !!(github.event.pull_request || github.event.issue.pull_request) }}'
8973
with:
9074
script: |
91-
const eventType = process.env.EVENT_TYPE;
9275
const request = process.env.REQUEST;
76+
const isPr = process.env.IS_PR === 'true';
9377
core.setOutput('request', request);
9478
95-
if (eventType === 'pull_request.opened' || eventType === 'pull_request.ready_for_review') {
96-
core.setOutput('command', 'review');
97-
} else if (request.startsWith("@gemini-cli /review")) {
98-
core.setOutput('command', 'review');
99-
const additionalContext = request.replace(/^@gemini-cli \/review/, '').trim();
100-
core.setOutput('additional_context', additionalContext);
79+
// Ensure request is on a PR targeting the main branch
80+
let baseRef = '';
81+
if (context.eventName === 'pull_request_review' || context.eventName === 'pull_request_review_comment') {
82+
baseRef = context.payload.pull_request.base.ref;
83+
} else if (context.eventName === 'issue_comment' && context.payload.issue.pull_request) {
84+
const pr = await github.rest.pulls.get({
85+
owner: context.repo.owner,
86+
repo: context.repo.repo,
87+
pull_number: context.payload.issue.number
88+
});
89+
baseRef = pr.data.base.ref;
90+
}
91+
92+
if (isPr && baseRef !== 'main') {
93+
console.log(`Skipping: PR targets '${baseRef}', but only 'main' is allowed.`);
94+
core.setOutput('command', 'fallthrough');
95+
return;
96+
}
97+
98+
if (request.startsWith("@gemini-cli /review")) {
99+
if (isPr) {
100+
core.setOutput('command', 'review');
101+
const additionalContext = request.replace(/^@gemini-cli \/review/, '').trim();
102+
core.setOutput('additional_context', additionalContext);
103+
} else {
104+
core.setOutput('command', 'fallthrough');
105+
}
101106
} else if (request.startsWith("@gemini-cli")) {
102107
const additionalContext = request.replace(/^@gemini-cli/, '').trim();
103108
core.setOutput('command', 'invoke');

.github/workflows/gemini-invoke.yml

Lines changed: 6 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -52,12 +52,14 @@ jobs:
5252
ISSUE_NUMBER: '${{ github.event.pull_request.number || github.event.issue.number }}'
5353
REPOSITORY: '${{ github.repository }}'
5454
ADDITIONAL_CONTEXT: '${{ inputs.additional_context }}'
55+
# Required to allow the Gemini CLI to process files in the ephemeral GitHub Actions runner
56+
GEMINI_CLI_TRUST_WORKSPACE: 'true'
5557
with:
5658
gcp_location: '${{ vars.GOOGLE_CLOUD_LOCATION }}'
5759
gcp_project_id: '${{ vars.GOOGLE_CLOUD_PROJECT }}'
5860
gcp_service_account: '${{ vars.SERVICE_ACCOUNT_EMAIL }}'
5961
gcp_workload_identity_provider: '${{ vars.GCP_WIF_PROVIDER }}'
60-
gemini_api_key: '${{ secrets.GEMINI_API_KEY }}'
62+
gemini_api_key: '${{ secrets.GOOGLE_API_KEY }}'
6163
gemini_cli_version: '${{ vars.GEMINI_CLI_VERSION }}'
6264
gemini_debug: '${{ fromJSON(vars.GEMINI_DEBUG || vars.ACTIONS_STEP_DEBUG || false) }}'
6365
gemini_model: '${{ vars.GEMINI_MODEL }}'
@@ -91,32 +93,12 @@ jobs:
9193
"GITHUB_PERSONAL_ACCESS_TOKEN",
9294
"ghcr.io/github/github-mcp-server:v0.27.0"
9395
],
94-
"includeTools": [
95-
"add_issue_comment",
96-
"issue_read",
97-
"list_issues",
98-
"search_issues",
99-
"pull_request_read",
100-
"list_pull_requests",
101-
"search_pull_requests",
102-
"get_commit",
103-
"get_file_contents",
104-
"list_commits",
105-
"search_code"
106-
],
10796
"env": {
10897
"GITHUB_PERSONAL_ACCESS_TOKEN": "${{ steps.mint_identity_token.outputs.token || secrets.GITHUB_TOKEN || github.token }}"
10998
}
11099
}
111-
},
112-
"tools": {
113-
"core": [
114-
"run_shell_command(cat)",
115-
"run_shell_command(echo)",
116-
"run_shell_command(grep)",
117-
"run_shell_command(head)",
118-
"run_shell_command(tail)"
119-
]
120100
}
121101
}
122-
prompt: '/gemini-invoke'
102+
prompt: |-
103+
/gemini-invoke
104+
[IMPORTANT] Do not generate execution plans and do not ask for approval (such as suggesting `@gemini-cli /approve`). Perform the requested task or answer the question directly and immediately.

.github/workflows/gemini-review.yml

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -51,14 +51,15 @@ jobs:
5151
PULL_REQUEST_NUMBER: '${{ github.event.pull_request.number || github.event.issue.number }}'
5252
REPOSITORY: '${{ github.repository }}'
5353
ADDITIONAL_CONTEXT: '${{ inputs.additional_context }}'
54-
GEMINI_API_KEY: '${{ secrets.GEMINI_API_KEY }}'
54+
GEMINI_API_KEY: '${{ secrets.GOOGLE_API_KEY }}'
55+
# Required to allow the Gemini CLI to process files in the ephemeral GitHub Actions runner
5556
GEMINI_CLI_TRUST_WORKSPACE: 'true'
5657
with:
5758
gcp_location: '${{ vars.GOOGLE_CLOUD_LOCATION }}'
5859
gcp_project_id: '${{ vars.GOOGLE_CLOUD_PROJECT }}'
5960
gcp_service_account: '${{ vars.SERVICE_ACCOUNT_EMAIL }}'
6061
gcp_workload_identity_provider: '${{ vars.GCP_WIF_PROVIDER }}'
61-
gemini_api_key: '${{ secrets.GEMINI_API_KEY }}'
62+
gemini_api_key: '${{ secrets.GOOGLE_API_KEY }}'
6263
gemini_cli_version: '${{ vars.GEMINI_CLI_VERSION }}'
6364
gemini_debug: '${{ fromJSON(vars.GEMINI_DEBUG || vars.ACTIONS_STEP_DEBUG || false) }}'
6465
gemini_model: '${{ vars.GEMINI_MODEL }}'
@@ -90,11 +91,6 @@ jobs:
9091
"GITHUB_PERSONAL_ACCESS_TOKEN",
9192
"ghcr.io/github/github-mcp-server:v0.27.0"
9293
],
93-
"includeTools": [
94-
"pull_request_read",
95-
"add_comment_to_pending_review",
96-
"pull_request_review_write"
97-
],
9894
"env": {
9995
"GITHUB_PERSONAL_ACCESS_TOKEN": "${{ steps.mint_identity_token.outputs.token || secrets.GITHUB_TOKEN || github.token }}"
10096
}
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: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
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+
"""Sample agent demonstrating the use of GCPSkillRegistry."""
16+
17+
from google.adk import Agent
18+
from google.adk.integrations.skill_registry import GCPSkillRegistry
19+
from google.adk.tools.skill_toolset import SkillToolset
20+
21+
# Initialize GCP Skill Registry
22+
registry = GCPSkillRegistry(
23+
project_id="your-project-id", location="us-central1"
24+
)
25+
26+
# Initialize SkillToolset with registry
27+
skill_toolset = SkillToolset(skills=[], registry=registry)
28+
29+
root_agent = Agent(
30+
model="gemini-2.5-flash",
31+
name="skill_registry_agent",
32+
description=(
33+
"An agent that can discover and load skills from GCP Skill Registry."
34+
),
35+
instruction=(
36+
"Use search_skills to find skills and load_skill to load them if"
37+
" needed."
38+
),
39+
tools=[skill_toolset],
40+
)

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ scripts.adk = "google.adk.cli:main"
166166

167167
[tool.flit.sdist]
168168
include = [ 'src/**/*', 'README.md', 'pyproject.toml', 'LICENSE' ]
169-
exclude = [ 'src/**/*.sh' ]
169+
exclude = [ 'src/**/*.sh', 'src/**/README.md' ]
170170

171171
[tool.flit.module]
172172
name = "google.adk"

src/google/adk/a2a/utils/agent_to_a2a.py

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
from ...runners import Runner
3636
from ...sessions.in_memory_session_service import InMemorySessionService
3737
from ..executor.a2a_agent_executor import A2aAgentExecutor
38+
from ..executor.config import A2aAgentExecutorConfig
3839
from ..experimental import a2a_experimental
3940
from .agent_card_builder import AgentCardBuilder
4041

@@ -86,6 +87,7 @@ def to_a2a(
8687
task_store: TaskStore | None = None,
8788
runner: Runner | None = None,
8889
lifespan: Callable[[Starlette], AsyncIterator[None]] | None = None,
90+
agent_executor_factory: Callable[[Runner], A2aAgentExecutor] | None = None,
8991
) -> Starlette:
9092
"""Convert an ADK agent to a A2A Starlette application.
9193
@@ -95,20 +97,21 @@ def to_a2a(
9597
port: The port for the A2A RPC URL (default: 8000)
9698
protocol: The protocol for the A2A RPC URL (default: "http")
9799
agent_card: Optional pre-built AgentCard object or path to agent card
98-
JSON. If not provided, will be built automatically from the
99-
agent.
100+
JSON. If not provided, will be built automatically from the agent.
100101
push_config_store: Optional A2A push notification config store. If not
101-
provided, an in-memory store will be created so push-notification
102-
config RPC methods are supported.
102+
provided, an in-memory store will be created so push-notification config
103+
RPC methods are supported.
103104
task_store: Optional A2A task store for persisting task state. If not
104105
provided, an in-memory store will be created.
105106
runner: Optional pre-built Runner object. If not provided, a default
106-
runner will be created using in-memory services.
107-
lifespan: Optional async context manager for Starlette lifespan
108-
events. Use this to run startup/shutdown logic (e.g. initializing
109-
database connections or loading resources). The context manager
110-
receives the Starlette app instance and can set state on
111-
``app.state``.
107+
runner will be created using in-memory services.
108+
lifespan: Optional async context manager for Starlette lifespan events.
109+
Use this to run startup/shutdown logic (e.g. initializing database
110+
connections or loading resources). The context manager receives the
111+
Starlette app instance and can set state on ``app.state``.
112+
agent_executor_factory: Optional factory function that creates an instance
113+
of A2aAgentExecutor. If not provided, a default A2aAgentExecutor will be
114+
created.
112115
113116
Returns:
114117
A Starlette application that can be run with uvicorn
@@ -148,7 +151,7 @@ async def lifespan(app):
148151
adk_logger = logging.getLogger("google_adk")
149152
adk_logger.setLevel(logging.INFO)
150153

151-
async def create_runner() -> Runner:
154+
def create_runner() -> Runner:
152155
"""Create a runner for the agent."""
153156
return Runner(
154157
app_name=agent.name or "adk_agent",
@@ -164,8 +167,10 @@ async def create_runner() -> Runner:
164167
if task_store is None:
165168
task_store = InMemoryTaskStore()
166169

167-
agent_executor = A2aAgentExecutor(
168-
runner=runner or create_runner,
170+
agent_executor = (
171+
agent_executor_factory(runner or create_runner())
172+
if agent_executor_factory is not None
173+
else A2aAgentExecutor(runner=runner or create_runner)
169174
)
170175

171176
if push_config_store is None:

src/google/adk/evaluation/simulation/per_turn_user_simulator_quality_v1.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,10 @@ async def _evaluate_intermediate_turn(
325325
previous_invocations=invocation_history,
326326
)
327327

328+
config = (
329+
self._llm_options.judge_model_config
330+
or genai_types.GenerateContentConfig()
331+
)
328332
llm_request = LlmRequest(
329333
model=self._llm_options.judge_model,
330334
contents=[
@@ -333,7 +337,7 @@ async def _evaluate_intermediate_turn(
333337
role="user",
334338
)
335339
],
336-
config=self._llm_options.judge_model_config,
340+
config=config,
337341
)
338342
add_default_retry_options_if_not_present(llm_request)
339343
num_samples = self._llm_options.num_samples
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+
"""Skill Registry integrations."""
16+
17+
from .gcp_skill_registry import GCPSkillRegistry
18+
19+
__all__ = ["GCPSkillRegistry"]

0 commit comments

Comments
 (0)