chore: import upstream snapshot with attribution
Continuous Integration / Pre-commit Linter (push) Has been cancelled
Continuous Integration / Mypy Check (Python 3.10) (push) Has been cancelled
Continuous Integration / Mypy Check (Python 3.11) (push) Has been cancelled
Continuous Integration / Mypy Check (Python 3.12) (push) Has been cancelled
Continuous Integration / Mypy Check (Python 3.13) (push) Has been cancelled
Continuous Integration / Unit Tests (Python 3.10) (push) Has been cancelled
Continuous Integration / Unit Tests (Python 3.11) (push) Has been cancelled
Continuous Integration / Unit Tests (Python 3.12) (push) Has been cancelled
Continuous Integration / Unit Tests (Python 3.13) (push) Has been cancelled
Continuous Integration / Unit Tests (Python 3.14) (push) Has been cancelled
Continuous Integration / A2A v0.3 Tests (Python 3.10) (push) Has been cancelled
Continuous Integration / A2A v0.3 Tests (Python 3.11) (push) Has been cancelled
Continuous Integration / A2A v0.3 Tests (Python 3.12) (push) Has been cancelled
Copybara PR Handler / close-imported-pr (push) Has been cancelled
Continuous Integration / A2A v0.3 Tests (Python 3.13) (push) Has been cancelled
Continuous Integration / A2A v0.3 Tests (Python 3.14) (push) Has been cancelled

This commit is contained in:
wehub-resource-sync
2026-07-13 13:25:13 +08:00
commit ec2b666284
2231 changed files with 491535 additions and 0 deletions
@@ -0,0 +1,165 @@
# CrewAI Tool \*\*kwargs Parameter Handling
This sample demonstrates how `CrewaiTool` correctly handles tools with
`**kwargs` parameters, which is a common pattern in CrewAI tools.
## What This Sample Demonstrates
### Key Feature: \*\*kwargs Parameter Passing
CrewAI tools often accept arbitrary parameters via `**kwargs`:
```python
def _run(self, query: str, **kwargs) -> str:
# Extra parameters are passed through kwargs
category = kwargs.get('category')
date_range = kwargs.get('date_range')
limit = kwargs.get('limit')
```
The `CrewaiTool` wrapper detects this pattern and passes all parameters through
(except framework-managed ones like `self` and `tool_context`).
### Contrast with Regular Tools
For comparison, tools without `**kwargs` only accept explicitly declared
parameters:
```python
def _run(self, query: str, category: str) -> str:
```
## Prerequisites
### Required: CrewAI Tools (Python 3.10+)
```bash
pip install 'crewai-tools>=0.2.0'
```
### Required: API Key
```bash
export GOOGLE_API_KEY="your-api-key-here"
# OR
export GOOGLE_GENAI_API_KEY="your-api-key-here"
```
## Running the Sample
### Option 1: Run the Happy Path Test
```bash
cd contributing/samples/crewai_tool_kwargs
python main.py
```
**Expected output:**
```
============================================================
CrewAI Tool **kwargs Parameter Test
============================================================
🧪 Test 1: Basic search (no extra parameters)
User: Search for Python tutorials
Agent: [Uses tool and returns results]
🧪 Test 2: Search with filters (**kwargs test)
User: Search for machine learning articles, filtered by...
Agent: [Uses tool with category, date_range, and limit parameters]
============================================================
✅ Happy path test completed successfully!
============================================================
```
## What Gets Tested
**CrewAI tool integration** - Wrapping a CrewAI BaseTool with ADK
**Basic parameters** - Required `query` parameter passes correctly
✅ \*\***kwargs passing** - Extra parameters (category, date_range, limit) pass
through
**End-to-end execution** - Tool executes and returns results to agent
## Code Structure
```
crewai_tool_kwargs/
├── __init__.py # Module initialization
├── agent.py # Agent with CrewAI tool
├── main.py # Happy path test
└── README.md # This file
```
### Key Files
**agent.py:**
- Defines `CustomSearchTool` (CrewAI BaseTool with \*\*kwargs)
- Wraps it with `CrewaiTool`
- Creates agent with the wrapped tool
**main.py:**
- Test 1: Basic search (no extra params)
- Test 2: Search with filters (tests \*\*kwargs)
## How It Works
1. **CrewAI Tool Definition** (`agent.py`):
```python
class CustomSearchTool(BaseTool):
def _run(self, query: str, **kwargs) -> str:
# kwargs receives: category, date_range, limit, etc.
```
1. **ADK Wrapping** (`agent.py`):
```python
adk_search_tool = CrewaiTool(
crewai_search_tool,
name="search_with_filters",
description="..."
)
```
1. **LLM Function Calling** (`main.py`):
- LLM sees the tool in function calling format
- LLM calls with: `{query: "...", category: "...", date_range: "...", limit: 10}`
- CrewaiTool passes ALL parameters to `**kwargs`
1. **Tool Execution**:
- `query` → positional parameter
- `category`, `date_range`, `limit` → collected in `**kwargs`
- Tool logic uses all parameters
## Troubleshooting
### ImportError: No module named 'crewai'
```bash
pip install 'crewai-tools>=0.2.0'
```
### Python Version Error
CrewAI requires Python 3.10+:
```bash
python --version # Should be 3.10 or higher
```
### Missing API Key
```bash
export GOOGLE_API_KEY="your-key-here"
```
## Related
- Parent class: `FunctionTool` - Base class for all function-based tools
- Unit tests: `tests/unittests/tools/test_crewai_tool.py`
@@ -0,0 +1,15 @@
# Copyright 2026 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from . import agent
@@ -0,0 +1,111 @@
# Copyright 2026 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Sample demonstrating CrewAI tool with **kwargs parameter handling.
This sample shows how CrewaiTool correctly passes arbitrary parameters
through **kwargs, which is a common pattern in CrewAI tools.
"""
from typing import Optional
from crewai.tools import BaseTool
from google.adk import Agent
from google.adk.tools.crewai_tool import CrewaiTool
from pydantic import BaseModel
from pydantic import Field
class SearchInput(BaseModel):
"""Input schema for the search tool."""
query: str = Field(..., description="The search query string")
category: Optional[str] = Field(
None, description="Filter by category (e.g., 'technology', 'science')"
)
date_range: Optional[str] = Field(
None, description="Filter by date range (e.g., 'last_week', '2024')"
)
limit: Optional[int] = Field(
None, description="Limit the number of results (e.g., 10, 20)"
)
class CustomSearchTool(BaseTool):
"""A custom CrewAI tool that accepts arbitrary search parameters via **kwargs.
This demonstrates the key CrewAI tool pattern where tools accept
flexible parameters through **kwargs.
"""
name: str = "custom_search"
description: str = (
"Search for information with flexible filtering options. "
"Accepts a query and optional filter parameters like category, "
"date_range, limit, etc."
)
args_schema: type[BaseModel] = SearchInput
def _run(self, query: str, **kwargs) -> str:
"""Execute search with arbitrary filter parameters.
Args:
query: The search query string.
**kwargs: Additional filter parameters like category, date_range, limit.
Returns:
A formatted string showing the query and applied filters.
"""
result_parts = [f"Searching for: '{query}'"]
if kwargs:
result_parts.append("Applied filters:")
for key, value in kwargs.items():
result_parts.append(f" - {key}: {value}")
else:
result_parts.append("No additional filters applied.")
# Simulate search results
result_parts.append(f"\nFound 3 results matching your criteria.")
return "\n".join(result_parts)
crewai_search_tool = CustomSearchTool()
# Wrap it with ADK's CrewaiTool
adk_search_tool = CrewaiTool(
crewai_search_tool,
name="search_with_filters",
description=(
"Search for information with optional filters like category, "
"date_range, or limit"
),
)
root_agent = Agent(
name="search_agent",
description="An agent that can search with flexible filtering options",
instruction="""
You are a helpful search assistant.
When users ask you to search, use the search_with_filters tool.
You can pass additional parameters like:
- category: to filter by category (e.g., "technology", "science")
- date_range: to filter by date (e.g., "last_week", "2024")
- limit: to limit the number of results (e.g., 10, 20)
Always acknowledge what filters you're applying.
""",
tools=[adk_search_tool],
)
@@ -0,0 +1,105 @@
# Copyright 2026 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Happy path test for CrewAI tool with **kwargs parameter handling.
This demonstrates that CrewaiTool correctly passes arbitrary parameters
through **kwargs to the underlying CrewAI tool.
"""
import asyncio
import agent
from dotenv import load_dotenv
from google.adk.cli.utils import logs
from google.adk.runners import InMemoryRunner
from google.genai import types
load_dotenv(override=True)
logs.log_to_tmp_folder()
async def main():
"""Run happy path test demonstrating **kwargs parameter passing."""
app_name = "crewai_kwargs_test"
user_id = "test_user"
runner = InMemoryRunner(
agent=agent.root_agent,
app_name=app_name,
)
session = await runner.session_service.create_session(
app_name=app_name, user_id=user_id
)
print("=" * 60)
print("CrewAI Tool **kwargs Parameter Test")
print("=" * 60)
# Test 1: Simple search without extra parameters
print("\n🧪 Test 1: Basic search (no extra parameters)")
print("-" * 60)
content1 = types.Content(
role="user",
parts=[types.Part.from_text(text="Search for Python tutorials")],
)
print(f"User: {content1.parts[0].text}")
async for event in runner.run_async(
user_id=user_id,
session_id=session.id,
new_message=content1,
):
if event.content.parts and event.content.parts[0].text:
print(f"Agent: {event.content.parts[0].text}")
# Test 2: Search with extra parameters (testing **kwargs)
print("\n🧪 Test 2: Search with filters (**kwargs test)")
print("-" * 60)
content2 = types.Content(
role="user",
parts=[
types.Part.from_text(
text=(
"Search for machine learning articles, filtered by category"
" 'technology', date_range 'last_month', and limit to 10"
" results"
)
)
],
)
print(f"User: {content2.parts[0].text}")
async for event in runner.run_async(
user_id=user_id,
session_id=session.id,
new_message=content2,
):
if event.content.parts and event.content.parts[0].text:
print(f"Agent: {event.content.parts[0].text}")
# Verify success
print("\n" + "=" * 60)
print("✅ Happy path test completed successfully!")
print("=" * 60)
print("\nVerified behaviors:")
print(" ✅ CrewAI tool integrated with ADK agent")
print(" ✅ Basic parameters passed correctly")
print(" ✅ Extra parameters passed through **kwargs")
print(" ✅ Tool executed and returned results")
if __name__ == "__main__":
asyncio.run(main())