Skip to content

Commit e6d015f

Browse files
authored
Merge branch 'main' into add-gemma-via-api
2 parents 0aa6767 + f1abdb1 commit e6d015f

Some content is hidden

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

42 files changed

+2730
-105
lines changed

contributing/samples/adk_agent_builder_assistant/agent_builder_assistant.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
from google.adk.tools import FunctionTool
2727
from google.genai import types
2828

29+
from .sub_agents.adk_knowledge_agent import create_adk_knowledge_agent
2930
from .sub_agents.google_search_agent import create_google_search_agent
3031
from .sub_agents.url_context_agent import create_url_context_agent
3132
from .tools.cleanup_unused_files import cleanup_unused_files
@@ -71,9 +72,14 @@ def create_agent(
7172
# - Maintains compatibility with existing ADK tool ecosystem
7273

7374
# Built-in ADK tools wrapped as sub-agents
75+
adk_knowledge_agent = create_adk_knowledge_agent()
7476
google_search_agent = create_google_search_agent()
7577
url_context_agent = create_url_context_agent()
76-
agent_tools = [AgentTool(google_search_agent), AgentTool(url_context_agent)]
78+
agent_tools = [
79+
AgentTool(adk_knowledge_agent),
80+
AgentTool(google_search_agent),
81+
AgentTool(url_context_agent),
82+
]
7783

7884
# CUSTOM FUNCTION TOOLS: Agent Builder specific capabilities
7985
#

contributing/samples/adk_agent_builder_assistant/instruction_embedded.template

Lines changed: 87 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ Always reference this schema when creating configurations to ensure compliance.
4242
- Questions about ADK capabilities, concepts, or existing implementations
4343
- **CRITICAL**: For informational questions, provide the requested information and STOP. Do NOT offer to create, build, or generate anything unless explicitly asked.
4444
* **CREATION/BUILDING INTENT** (Only then ask for root directory):
45-
- "Create a new agent..." / "Build me an agent..."
45+
- "Create a new agent..." / "Build me an agent..."
4646
- "Generate an agent..." / "Implement an agent..."
4747
- "Update my agent..." / "Modify my agent..." / "Change my agent..."
4848
- "I want to create..." / "Help me build..." / "Help me update..."
@@ -71,7 +71,7 @@ Always reference this schema when creating configurations to ensure compliance.
7171
- Explore existing project structure using the RESOLVED ABSOLUTE PATH
7272
- Identify integration needs (APIs, databases, external services)
7373

74-
### 2. Design Phase
74+
### 2. Design Phase
7575
- **MANDATORY HIGH-LEVEL DESIGN CONFIRMATION**: Present complete architecture design BEFORE any implementation
7676
- **ASK FOR EXPLICIT CONFIRMATION**: "Does this design approach work for you? Should I proceed with implementation?"
7777
- **INCLUDE IN DESIGN PRESENTATION**:
@@ -194,6 +194,9 @@ Always reference this schema when creating configurations to ensure compliance.
194194

195195
### ADK Knowledge and Research Tools
196196

197+
#### Remote Semantic Search
198+
- **adk_knowledge_agent**: Search ADK knowledge base for ADK examples, patterns, and documentation
199+
197200
#### Web-based Research
198201
- **google_search_agent**: Search web for ADK examples, patterns, and documentation (returns full page content as results)
199202
- **url_context_agent**: Fetch content from specific URLs when mentioned in search results or user queries (use only when specific URLs need additional fetching)
@@ -207,8 +210,10 @@ Always reference this schema when creating configurations to ensure compliance.
207210
* Follow up with **read_files** to get complete file contents
208211

209212
**Research Workflow for ADK Questions:**
213+
Mainly rely on **adk_knowledge_agent** for ADK questions. Use other tools only when the knowledge agent doesn't have enough information.
214+
210215
1. **search_adk_source** - Find specific code patterns with regex
211-
2. **read_files** - Read complete source files for detailed analysis
216+
2. **read_files** - Read complete source files for detailed analysis
212217
3. **google_search_agent** - Find external examples and documentation
213218
4. **url_context_agent** - Fetch specific GitHub files or documentation pages
214219

@@ -224,6 +229,10 @@ Always reference this schema when creating configurations to ensure compliance.
224229

225230
**Research Tool Usage Patterns:**
226231

232+
**Default Research Tool:**
233+
Use **adk_knowledge_agent** as the primary research tool for ADK questions.
234+
Use other tools only when the knowledge agent doesn't have enough information.
235+
227236
**For ADK Code Questions (NEW - Preferred Method):**
228237
1. **search_adk_source** - Find exact code patterns:
229238
* Class definitions: `"class FunctionTool"` or `"class.*Agent"`
@@ -265,6 +274,77 @@ Always reference this schema when creating configurations to ensure compliance.
265274
6. **Implement simple functions**: For obvious functions like `is_prime`, `roll_dice`, replace TODO with actual implementation
266275
7. **Keep TODO for complex**: For complex business logic, leave TODO comments
267276
8. **Follow current ADK patterns**: Always search for and reference the latest examples from contributing/samples
277+
9. **Gemini API Usage**: If generating Python code that interacts with Gemini models, use `import google.genai as genai`, not `google.generativeai`.
278+
279+
### 🚨 CRITICAL: Callback Correct Signatures
280+
ADK supports different callback types with DIFFERENT signatures. Use FUNCTION-based callbacks (never classes):
281+
282+
## 1. Agent Callbacks (before_agent_callbacks / after_agent_callbacks)
283+
284+
**✅ CORRECT Agent Callback:**
285+
```python
286+
from typing import Optional
287+
from google.genai import types
288+
from google.adk.agents.callback_context import CallbackContext
289+
290+
def content_filter_callback(context: CallbackContext) -> Optional[types.Content]:
291+
"""After agent callback to filter sensitive content."""
292+
# Access the response content through context
293+
if hasattr(context, 'response') and context.response:
294+
response_text = str(context.response)
295+
if "confidential" in response_text.lower():
296+
filtered_text = response_text.replace("confidential", "[FILTERED]")
297+
return types.Content(parts=[types.Part(text=filtered_text)])
298+
return None # Return None to keep original response
299+
```
300+
301+
## 2. Model Callbacks (before_model_callbacks / after_model_callbacks)
302+
303+
**✅ CORRECT Model Callback:**
304+
```python
305+
from typing import Optional
306+
from google.adk.models.llm_request import LlmRequest
307+
from google.adk.models.llm_response import LlmResponse
308+
from google.adk.agents.callback_context import CallbackContext
309+
310+
def log_model_request(context: CallbackContext, request: LlmRequest) -> Optional[LlmResponse]:
311+
"""Before model callback to log requests."""
312+
print(f"Model request: {{request.contents}}")
313+
return None # Return None to proceed with original request
314+
315+
def modify_model_response(context: CallbackContext, response: LlmResponse) -> Optional[LlmResponse]:
316+
"""After model callback to modify response."""
317+
# Modify response if needed
318+
return response # Return modified response or None for original
319+
```
320+
321+
## 3. Tool Callbacks (before_tool_callbacks / after_tool_callbacks)
322+
323+
**✅ CORRECT Tool Callback:**
324+
```python
325+
from typing import Any, Dict, Optional
326+
from google.adk.tools.base_tool import BaseTool
327+
from google.adk.tools.tool_context import ToolContext
328+
329+
def validate_tool_input(tool: BaseTool, args: Dict[str, Any], context: ToolContext) -> Optional[Dict]:
330+
"""Before tool callback to validate input."""
331+
# Validate or modify tool arguments
332+
if "unsafe_param" in args:
333+
del args["unsafe_param"]
334+
return args # Return modified args or None for original
335+
336+
def log_tool_result(tool: BaseTool, args: Dict[str, Any], context: ToolContext, result: Dict) -> Optional[Dict]:
337+
"""After tool callback to log results."""
338+
print(f"Tool {{tool.name}} executed with result: {{result}}")
339+
return None # Return None to keep original result
340+
```
341+
342+
## Callback Signature Summary:
343+
- **Agent Callbacks**: `(CallbackContext) -> Optional[types.Content]`
344+
- **Before Model**: `(CallbackContext, LlmRequest) -> Optional[LlmResponse]`
345+
- **After Model**: `(CallbackContext, LlmResponse) -> Optional[LlmResponse]`
346+
- **Before Tool**: `(BaseTool, Dict[str, Any], ToolContext) -> Optional[Dict]`
347+
- **After Tool**: `(BaseTool, Dict[str, Any], ToolContext, Dict) -> Optional[Dict]`
268348

269349
## Important ADK Requirements
270350

@@ -305,8 +385,8 @@ Always reference this schema when creating configurations to ensure compliance.
305385

306386
### Examples:
307387
- **User input**: `./config_agents/roll_and_check`
308-
- **WRONG approach**: Create files at `/config_agents/roll_and_check`
309-
- **CORRECT approach**:
388+
- **WRONG approach**: Create files at `/config_agents/roll_and_check`
389+
- **CORRECT approach**:
310390
1. Call `resolve_root_directory("./config_agents/roll_and_check")`
311391
2. Get resolved path: `/Users/user/Projects/adk-python/config_agents/roll_and_check`
312392
3. Use the resolved absolute path for all operations
@@ -334,7 +414,7 @@ Always reference this schema when creating configurations to ensure compliance.
334414
**Your primary role is to be a collaborative architecture consultant that follows an efficient, user-centric workflow:**
335415

336416
1. **Always ask for root folder first** - Know where to create the project
337-
2. **Design with specific paths** - Include exact file locations in proposals
417+
2. **Design with specific paths** - Include exact file locations in proposals
338418
3. **Provide high-level architecture overview** - When confirming design, always include:
339419
* Overall system architecture and component relationships
340420
* Agent types and their responsibilities
@@ -355,4 +435,4 @@ Always reference this schema when creating configurations to ensure compliance.
355435
**Incorrect Commands to Avoid:**
356436
- `adk run [root_directory]/root_agent.yaml` - Do NOT specify the YAML file directly
357437
- `adk web` without parent directory - Must specify the parent folder containing the agent projects
358-
- Always use the project directory for `adk run`, and parent directory for `adk web`
438+
- Always use the project directory for `adk run`, and parent directory for `adk web`

contributing/samples/adk_agent_builder_assistant/sub_agents/__init__.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,12 @@
1414

1515
"""Sub-agents for Agent Builder Assistant."""
1616

17+
from .adk_knowledge_agent import create_adk_knowledge_agent
1718
from .google_search_agent import create_google_search_agent
1819
from .url_context_agent import create_url_context_agent
1920

20-
__all__ = ['create_google_search_agent', 'create_url_context_agent']
21+
__all__ = [
22+
'create_adk_knowledge_agent',
23+
'create_google_search_agent',
24+
'create_url_context_agent',
25+
]
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# Copyright 2025 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+
"""Sub-agent for ADK Knowledge."""
16+
17+
from google.adk.agents.llm_agent import Agent
18+
from google.adk.agents.remote_a2a_agent import AGENT_CARD_WELL_KNOWN_PATH
19+
from google.adk.agents.remote_a2a_agent import RemoteA2aAgent
20+
21+
22+
def create_adk_knowledge_agent() -> Agent:
23+
"""Create a sub-agent that only uses google_search tool."""
24+
return RemoteA2aAgent(
25+
name="adk_knowledge_agent",
26+
description=(
27+
"Agent for performing Vertex AI Search to find ADK knowledge and"
28+
" documentation"
29+
),
30+
agent_card=(
31+
f"https://adk-agent-builder-knowledge-service-654646711756.us-central1.run.app/a2a/adk_knowledge_agent{AGENT_CARD_WELL_KNOWN_PATH}"
32+
),
33+
)
File renamed without changes.
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
# OAuth2 Client Credentials Weather Agent
2+
3+
This sample demonstrates OAuth2 client credentials flow with ADK's `AuthenticatedFunctionTool` using a practical weather assistant agent.
4+
5+
## Overview
6+
7+
The OAuth2 client credentials grant type is used for server-to-server authentication where no user interaction is required. This demo shows:
8+
9+
- How to configure OAuth2 client credentials in ADK
10+
- Using `AuthenticatedFunctionTool` for automatic token management
11+
- Transparent authentication in a practical weather assistant
12+
- Testing the OAuth2 client credentials implementation
13+
14+
## Architecture
15+
16+
```
17+
[WeatherAssistant] -> [AuthenticatedFunctionTool] -> [OAuth2CredentialExchanger] -> [OAuth2 Server] -> [Weather API]
18+
```
19+
20+
1. **WeatherAssistant** calls weather tool when user asks for weather data
21+
2. **AuthenticatedFunctionTool** automatically handles OAuth2 flow
22+
3. **OAuth2CredentialExchanger** exchanges client credentials for access token
23+
4. **Authenticated requests** are made to weather API
24+
25+
## Files
26+
27+
### `agent.py` - WeatherAssistant Agent
28+
29+
Weather assistant agent that demonstrates OAuth2 client credentials flow transparently:
30+
31+
- **OAuth2 Configuration**: Client credentials setup with token URL and scopes
32+
- **Weather Tool**: Single `get_weather_data` tool for fetching weather information
33+
- **Agent Definition**: ADK LLM agent focused on providing weather information
34+
35+
**Key Features:**
36+
- Automatic token exchange using client ID and secret
37+
- Bearer token authentication
38+
- Transparent OAuth2 handling (invisible to the model)
39+
- Practical use case demonstrating machine-to-machine authentication
40+
41+
### `main.py` - CLI Interface
42+
43+
Command-line interface for running the WeatherAssistant agent:
44+
45+
```bash
46+
# Ask for weather
47+
python contributing/samples/oauth2_client_credentials/main.py "What's the weather in Tokyo?"
48+
```
49+
50+
**Requirements:**
51+
- LLM API key (Google AI or Vertex AI)
52+
- OAuth2 test server running
53+
54+
### `oauth2_test_server.py` - Local OAuth2 Server
55+
56+
Mock OAuth2 server for testing the client credentials flow:
57+
58+
```bash
59+
python contributing/samples/oauth2_client_credentials/oauth2_test_server.py
60+
```
61+
62+
**Features:**
63+
- OIDC discovery endpoint (`/.well-known/openid_configuration`)
64+
- Client credentials token exchange (`/token`)
65+
- Protected weather API (`/api/weather`)
66+
- Supports both `authorization_code` and `client_credentials` grant types
67+
- Test credentials: `client_id="test_client"`, `client_secret="test_secret"`
68+
69+
**Endpoints:**
70+
- `GET /.well-known/openid_configuration` - OIDC discovery
71+
- `POST /token` - Token exchange
72+
- `GET /api/weather` - Weather API (requires Bearer token)
73+
- `GET /` - Server info
74+
75+
## Quick Start
76+
77+
1. **Start the OAuth2 server:**
78+
```bash
79+
python contributing/samples/oauth2_client_credentials/oauth2_test_server.py &
80+
```
81+
2. Create a `.env` file in the project root with your API credentials:
82+
83+
```bash
84+
# Choose Model Backend: 0 -> ML Dev, 1 -> Vertex
85+
GOOGLE_GENAI_USE_VERTEXAI=1
86+
87+
# ML Dev backend config
88+
GOOGLE_API_KEY=your_google_api_key_here
89+
90+
# Vertex backend config
91+
GOOGLE_CLOUD_PROJECT=your_project_id
92+
GOOGLE_CLOUD_LOCATION=us-central1
93+
```
94+
95+
3. **Run the agent:**
96+
```bash
97+
# Ask for weather
98+
python contributing/samples/oauth2_client_credentials/main.py "What's the weather in Tokyo?"
99+
```
100+
101+
3. **Interactive demo (use ADK commands):**
102+
```bash
103+
# Interactive CLI
104+
adk run contributing/samples/oauth2_client_credentials
105+
106+
# Interactive web UI
107+
adk web contributing/samples
108+
```
109+
110+
## OAuth2 Configuration
111+
112+
The agent uses these OAuth2 settings (configured in `agent.py`):
113+
114+
```python
115+
flows = OAuthFlows(
116+
clientCredentials=OAuthFlowClientCredentials(
117+
tokenUrl="http://localhost:8000/token",
118+
scopes={
119+
"read": "Read access to weather data",
120+
"write": "Write access for data updates",
121+
"admin": "Administrative access",
122+
},
123+
)
124+
)
125+
126+
raw_credential = AuthCredential(
127+
auth_type=AuthCredentialTypes.OAUTH2,
128+
oauth2=OAuth2Auth(
129+
client_id="test_client",
130+
client_secret="test_secret",
131+
),
132+
)
133+
```
134+
135+
## Authentication Flow
136+
137+
1. **Weather Request**: User asks WeatherAssistant for weather information
138+
2. **Tool Invocation**: Agent calls `get_weather_data` authenticated function tool
139+
3. **Credential Loading**: CredentialManager loads OAuth2 configuration
140+
4. **Token Exchange**: OAuth2CredentialExchanger uses client credentials to get access token
141+
5. **Request Enhancement**: AuthenticatedFunctionTool adds `Authorization: Bearer <token>` header
142+
6. **API Call**: Weather API accessed with valid token
143+
7. **Response**: Weather data returned to user

0 commit comments

Comments
 (0)