Skip to content

Commit 0337d19

Browse files
authored
chore: sync Google internal changes to GitHub (google#6022)
1 parent 9670ce2 commit 0337d19

38 files changed

Lines changed: 1901 additions & 427 deletions

.github/workflows/release-update-adk-web.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
name: Release: Update ADk Web
1+
name: "Release: Update ADk Web"
22

33
on:
44
workflow_dispatch:
@@ -27,7 +27,7 @@ jobs:
2727
run: |
2828
TARGET_DIR="src/google/adk/cli/browser"
2929
REPO="${{ github.event.inputs.adk_web_repo }}"
30-
TAG="${{ github.event.inputs.adk_web_tag }}"
30+
TAG="${{ github.event.inputs.adk_web_tag }}"
3131
# Clean target directory
3232
rm -rf "$TARGET_DIR"/*
3333
mkdir -p "$TARGET_DIR"

.pre-commit-config.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
exclude: ^(src/google/adk/cli/browser/|src/google/adk/v1/|v1_tests/)
12
repos:
23
- repo: https://github.com/pre-commit/pre-commit-hooks
34
rev: v4.6.0

contributing/samples/adk_team/adk_triaging_agent/agent.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,16 @@
2929
"agent engine": "yeesian",
3030
"auth": "xuanyang15",
3131
"bq": "shobsi",
32+
"cli": "wyf7107",
3233
"core": "Jacksunwei",
3334
"documentation": "joefernandez",
3435
"eval": "ankursharmas",
36+
"integrations": "wukath",
3537
"live": "wuliang229",
3638
"mcp": "wukath",
3739
"models": "xuanyang15",
3840
"services": "DeanChensj",
41+
"skills": "wukath",
3942
"tools": "xuanyang15",
4043
"tracing": "mhenc",
4144
"web": "wyf7107",
@@ -63,8 +66,13 @@
6366
- "live": Streaming, bidi, audio, or Gemini Live configuration.
6467
- "models": Non-Gemini model adapters (LiteLLM, Ollama, OpenAI, etc.).
6568
- "tracing": Telemetry, observability, structured logs, or spans.
69+
- "cli": ADK CLI commands (e.g., create, deploy, eval) and CLI tools.
70+
- "skills": GCP Skills Registry (`GCPSkillRegistry`), skill prompt models,
71+
and dynamic skill toolsets.
72+
- "integrations": Third-party integrations (e.g., CrewAI, LangChain,
73+
Slack) excluding BigQuery.
6674
- "core": Core ADK runtime (Agent definitions, Runner, planners,
67-
thinking config, CLI commands, GlobalInstructionPlugin, CPU usage, or
75+
thinking config, GlobalInstructionPlugin, CPU usage, or
6876
general orchestration including agent transfer for multi-agents system).
6977
Default to "core" when the topic is about ADK behavior and no other
7078
label is a better fit.
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
# GCP Auth Sample
2+
3+
Demonstrates the use of Agent Identity auth manager with an agent that queries
4+
Spotify and Google Maps using auth providers.
5+
6+
Use `adk web` to run API key and 2-legged oauth flows, while use the included
7+
custom agent web client to run 3-legged oauth flows.
8+
9+
## Setup
10+
11+
### 1. Activate environment
12+
13+
```bash
14+
cd adk-python
15+
python3 -m venv .venv
16+
source .venv/bin/activate
17+
```
18+
19+
### 2. Install dependencies
20+
21+
```bash
22+
pip install "google-adk[agent-identity]"
23+
```
24+
25+
### 3. Authenticate your environment
26+
27+
```bash
28+
gcloud auth application-default login
29+
export GOOGLE_CLOUD_PROJECT="YOUR_GOOGLE_CLOUD_PROJECT"
30+
gcloud auth application-default set-quota-project $GOOGLE_CLOUD_PROJECT
31+
```
32+
33+
### 4. Create auth providers
34+
35+
Refer to the [public documentation](https://cloud.google.com/iam/docs/manage-auth-providers) to create the following Agent Identity auth providers.
36+
37+
> **Note:**
38+
> The identity running the agent (via Application Default Credentials) must have
39+
> the necessary [permissions](https://docs.cloud.google.com/iam/docs/roles-permissions/iamconnectors#iamconnectors.user)
40+
> to retrieve credentials from these connectors. Ensure your account has the
41+
> necessary role to access these resources.
42+
43+
```bash
44+
export GOOGLE_CLOUD_LOCATION="YOUR_GOOGLE_CLOUD_LOCATION"
45+
export MAPS_API_AUTH_PROVIDER_ID="YOUR_MAPS_API_AUTH_PROVIDER_ID"
46+
export SPOTIFY_2LO_AUTH_PROVIDER_ID="YOUR_SPOTIFY_2LO_AUTH_PROVIDER_ID"
47+
export SPOTIFY_3LO_AUTH_PROVIDER_ID="YOUR_SPOTIFY_3LO_AUTH_PROVIDER_ID"
48+
49+
gcloud alpha agent-identity connectors create $MAPS_API_AUTH_PROVIDER_ID \
50+
--project=$GOOGLE_CLOUD_PROJECT \
51+
--location=$GOOGLE_CLOUD_LOCATION \
52+
--api-key=YOUR_API_KEY
53+
54+
gcloud alpha agent-identity connectors create $SPOTIFY_2LO_AUTH_PROVIDER_ID \
55+
--project=$GOOGLE_CLOUD_PROJECT \
56+
--location=$GOOGLE_CLOUD_LOCATION \
57+
--two-legged-oauth-client-id=OAUTH_CLIENT_ID \
58+
--two-legged-oauth-client-secret=OAUTH_CLIENT_SECRET \
59+
--two-legged-oauth-token-endpoint=OAUTH_TOKEN_ENDPOINT
60+
61+
gcloud alpha agent-identity connectors create $SPOTIFY_3LO_AUTH_PROVIDER_ID \
62+
--project=$GOOGLE_CLOUD_PROJECT \
63+
--location=$GOOGLE_CLOUD_LOCATION \
64+
--three-legged-oauth-client-id=OAUTH_CLIENT_ID \
65+
--three-legged-oauth-client-secret=OAUTH_CLIENT_SECRET \
66+
--three-legged-oauth-authorization-url=AUTHORIZATION_URL \
67+
--three-legged-oauth-token-url=TOKEN_URL \
68+
--allowed-scopes=ALLOWED_SCOPES
69+
```
70+
71+
### 5. Test API key and 2LO auth provider using ADK web client
72+
73+
```bash
74+
adk web contributing/samples
75+
```
76+
77+
- On the ADK web UI, select the agent named `gcp_auth` from the dropdown.
78+
- Sample queries to try:
79+
- API key (Google Maps tool): "What is the current weather in New York?"
80+
- 2LO key (Spotify tool): "Tell me about the song: Waving Flag"
81+
82+
### 6. Test 3LO auth provider using custom web client
83+
84+
> **Note:** If the agent backend is running on a different port or host other
85+
> than `localhost:8000`, please set the `AGENT_BACKEND_URL` environment variable
86+
> before starting the client (e.g.,
87+
> `export AGENT_BACKEND_URL="http://localhost:9000"`).
88+
89+
- In a separate shell, activate environment
90+
91+
```bash
92+
cd adk-python
93+
python3 -m venv .venv
94+
source .venv/bin/activate
95+
```
96+
97+
- Navigate to the client directory and install dependencies
98+
99+
```bash
100+
cd contributing/samples/gcp_auth/client
101+
pip install -r requirements.txt
102+
```
103+
104+
- Start the client application
105+
106+
```bash
107+
uvicorn main:app --port 8080 --reload
108+
```
109+
110+
- Open `http://localhost:8080`. (**Note:** You must use `localhost` and not `127.0.0.1`, as the OAuth redirect URL specifically requires it.)
111+
- On the login screen, enter an arbitrary user ID (e.g. test_user123).
112+
- Sample queries to try:
113+
- 3LO key (Spotify tool): "What are my private Spotify playlists?"
Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
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 __future__ import annotations
16+
17+
import os
18+
19+
from google.adk.agents import Agent
20+
from google.adk.apps import App
21+
from google.adk.auth.auth_credential import AuthCredential
22+
from google.adk.auth.auth_tool import AuthConfig
23+
from google.adk.auth.credential_manager import CredentialManager
24+
from google.adk.integrations.agent_identity import GcpAuthProvider
25+
from google.adk.integrations.agent_identity import GcpAuthProviderScheme
26+
from google.adk.tools.authenticated_function_tool import AuthenticatedFunctionTool
27+
from google.adk.tools.mcp_tool.mcp_session_manager import StreamableHTTPConnectionParams
28+
from google.adk.tools.mcp_tool.mcp_toolset import McpToolset
29+
import httpx
30+
31+
PROJECT_ID = os.environ.get("GOOGLE_CLOUD_PROJECT")
32+
LOCATION = os.environ.get("GOOGLE_CLOUD_LOCATION")
33+
MAPS_API_AUTH_PROVIDER_ID = os.environ.get("MAPS_API_AUTH_PROVIDER_ID")
34+
SPOTIFY_2LO_AUTH_PROVIDER_ID = os.environ.get("SPOTIFY_2LO_AUTH_PROVIDER_ID")
35+
SPOTIFY_3LO_AUTH_PROVIDER_ID = os.environ.get("SPOTIFY_3LO_AUTH_PROVIDER_ID")
36+
37+
MAPS_API_AUTH_PROVIDER = f"projects/{PROJECT_ID}/locations/{LOCATION}/connectors/{MAPS_API_AUTH_PROVIDER_ID}"
38+
SPOTIFY_2LO_AUTH_PROVIDER = f"projects/{PROJECT_ID}/locations/{LOCATION}/connectors/{SPOTIFY_2LO_AUTH_PROVIDER_ID}"
39+
SPOTIFY_3LO_AUTH_PROVIDER = f"projects/{PROJECT_ID}/locations/{LOCATION}/connectors/{SPOTIFY_3LO_AUTH_PROVIDER_ID}"
40+
41+
MAPS_MCP_ENDPOINT = "https://mapstools.googleapis.com/mcp"
42+
CONTINUE_URI = "http://localhost:8080/commit"
43+
MODEL = "gemini-2.5-flash"
44+
45+
46+
async def spotify_search_track(
47+
credential: AuthCredential, query: str
48+
) -> str | list:
49+
"""Searches for a track on Spotify and returns its details."""
50+
headers = {}
51+
if http := credential.http:
52+
if http.scheme and http.credentials and (token := http.credentials.token):
53+
headers["Authorization"] = f"{http.scheme.title()} {token}"
54+
if http.additional_headers:
55+
headers.update(http.additional_headers)
56+
57+
if not headers:
58+
return "Error: No authentication token available."
59+
60+
async with httpx.AsyncClient() as client:
61+
response = await client.get(
62+
"https://api.spotify.com/v1/search",
63+
headers=headers,
64+
params={"q": query, "type": "track", "limit": 1},
65+
)
66+
67+
if response.status_code != 200:
68+
return f"Error from Spotify API: {response.status_code} - {response.text}"
69+
70+
data = response.json()
71+
items = data.get("tracks", {}).get("items", [])
72+
73+
if not items:
74+
return f"No track found for query '{query}'."
75+
76+
return items
77+
78+
79+
async def spotify_get_playlists(credential: AuthCredential) -> str | list:
80+
"""Fetches the current user's private playlists on Spotify."""
81+
headers = {}
82+
if http := credential.http:
83+
if http.scheme and http.credentials and (token := http.credentials.token):
84+
headers["Authorization"] = f"{http.scheme.title()} {token}"
85+
if http.additional_headers:
86+
headers.update(http.additional_headers)
87+
88+
if not headers:
89+
return "Error: No authentication token available."
90+
91+
async with httpx.AsyncClient() as client:
92+
response = await client.get(
93+
"https://api.spotify.com/v1/me/playlists",
94+
headers=headers,
95+
params={"limit": 10},
96+
)
97+
98+
if response.status_code != 200:
99+
return f"Error from Spotify API: {response.status_code} - {response.text}"
100+
101+
data = response.json()
102+
items = data.get("items", [])
103+
104+
if not items:
105+
return "No playlists found for the current user."
106+
107+
# Extract useful information
108+
return [
109+
{
110+
"name": item.get("name"),
111+
"public": item.get("public"),
112+
"total_tracks": item.get("tracks", {}).get("total"),
113+
}
114+
for item in items
115+
if item
116+
]
117+
118+
119+
spotify_auth_config_2lo = AuthConfig(
120+
auth_scheme=GcpAuthProviderScheme(name=SPOTIFY_2LO_AUTH_PROVIDER)
121+
)
122+
spotify_search_track_tool = AuthenticatedFunctionTool(
123+
func=spotify_search_track,
124+
auth_config=spotify_auth_config_2lo,
125+
)
126+
127+
spotify_auth_config_3lo = AuthConfig(
128+
auth_scheme=GcpAuthProviderScheme(
129+
name=SPOTIFY_3LO_AUTH_PROVIDER,
130+
scopes=["playlist-read-private"],
131+
continue_uri=CONTINUE_URI,
132+
)
133+
)
134+
spotify_get_playlist_tool = AuthenticatedFunctionTool(
135+
func=spotify_get_playlists,
136+
auth_config=spotify_auth_config_3lo,
137+
)
138+
139+
maps_tools = McpToolset(
140+
connection_params=StreamableHTTPConnectionParams(url=MAPS_MCP_ENDPOINT),
141+
auth_scheme=GcpAuthProviderScheme(name=MAPS_API_AUTH_PROVIDER),
142+
errlog=None, # Required for agent freezing (pickling)
143+
)
144+
145+
CredentialManager.register_auth_provider(GcpAuthProvider())
146+
147+
root_agent = Agent(
148+
name="gcp_auth_agent",
149+
model=MODEL,
150+
instruction=(
151+
"You are a Spotify and Google Maps assistant. Use your tools to "
152+
"search for track details, fetch the user's private playlists, "
153+
"and look up locations. Keep responses concise, friendly, and "
154+
"emoji-filled!"
155+
),
156+
tools=[
157+
spotify_search_track_tool,
158+
spotify_get_playlist_tool,
159+
maps_tools,
160+
],
161+
)
162+
163+
app = App(
164+
name="gcp_auth",
165+
root_agent=root_agent,
166+
)

0 commit comments

Comments
 (0)