chore: import upstream snapshot with attribution
Continuous Integration / Pre-commit Linter (push) Waiting to run
Continuous Integration / Mypy Check (Python 3.10) (push) Waiting to run
Continuous Integration / Mypy Check (Python 3.11) (push) Waiting to run
Continuous Integration / Mypy Check (Python 3.12) (push) Waiting to run
Continuous Integration / Mypy Check (Python 3.13) (push) Waiting to run
Continuous Integration / Unit Tests (Python 3.10) (push) Waiting to run
Continuous Integration / Unit Tests (Python 3.11) (push) Waiting to run
Continuous Integration / Unit Tests (Python 3.12) (push) Waiting to run
Continuous Integration / Unit Tests (Python 3.13) (push) Waiting to run
Continuous Integration / Unit Tests (Python 3.14) (push) Waiting to run
Continuous Integration / A2A v0.3 Tests (Python 3.10) (push) Waiting to run
Continuous Integration / A2A v0.3 Tests (Python 3.11) (push) Waiting to run
Continuous Integration / A2A v0.3 Tests (Python 3.12) (push) Waiting to run
Continuous Integration / A2A v0.3 Tests (Python 3.13) (push) Waiting to run
Continuous Integration / A2A v0.3 Tests (Python 3.14) (push) Waiting to run
Copybara PR Handler / close-imported-pr (push) Waiting to run
Continuous Integration / Pre-commit Linter (push) Waiting to run
Continuous Integration / Mypy Check (Python 3.10) (push) Waiting to run
Continuous Integration / Mypy Check (Python 3.11) (push) Waiting to run
Continuous Integration / Mypy Check (Python 3.12) (push) Waiting to run
Continuous Integration / Mypy Check (Python 3.13) (push) Waiting to run
Continuous Integration / Unit Tests (Python 3.10) (push) Waiting to run
Continuous Integration / Unit Tests (Python 3.11) (push) Waiting to run
Continuous Integration / Unit Tests (Python 3.12) (push) Waiting to run
Continuous Integration / Unit Tests (Python 3.13) (push) Waiting to run
Continuous Integration / Unit Tests (Python 3.14) (push) Waiting to run
Continuous Integration / A2A v0.3 Tests (Python 3.10) (push) Waiting to run
Continuous Integration / A2A v0.3 Tests (Python 3.11) (push) Waiting to run
Continuous Integration / A2A v0.3 Tests (Python 3.12) (push) Waiting to run
Continuous Integration / A2A v0.3 Tests (Python 3.13) (push) Waiting to run
Continuous Integration / A2A v0.3 Tests (Python 3.14) (push) Waiting to run
Copybara PR Handler / close-imported-pr (push) Waiting to run
This commit is contained in:
@@ -0,0 +1,58 @@
|
||||
# ADK Agent with Plugin
|
||||
|
||||
### What is ADK Plugin?
|
||||
|
||||
At its core, ADK extensibility is built on
|
||||
[**callbacks**](https://google.github.io/adk-docs/callbacks/): functions you
|
||||
write that ADK automatically executes at key stages of an agent's lifecycle.
|
||||
**A Plugin is simply a class that packages these individual callback functions
|
||||
together for a broader purpose.**
|
||||
|
||||
While a standard Agent Callback is configured on a *single agent, a single tool*
|
||||
for a *specific task*, a Plugin is registered *once* on the `Runner` and its
|
||||
callbacks apply *globally* to every agent, tool, and LLM call managed by that
|
||||
runner. This makes Plugins the ideal solution for implementing horizontal
|
||||
features that cut across your entire application.
|
||||
|
||||
### What can plugins do?
|
||||
|
||||
Plugins are incredibly versatile. By implementing different callback methods, you
|
||||
can achieve a wide range of functionalities.
|
||||
|
||||
- **Logging & Tracing**: Create detailed logs of agent, tool, and LLM activity
|
||||
for debugging and performance analysis.
|
||||
- **Policy Enforcement**: Implement security guardrails. For example, a
|
||||
before_tool_callback can check if a user is authorized to use a specific
|
||||
tool and prevent its execution by returning a value.
|
||||
- **Monitoring & Metrics**: Collect and export metrics on token usage,
|
||||
execution times, and invocation counts to monitoring systems like Prometheus
|
||||
or Stackdriver.
|
||||
- **Caching**: In before_model_callback or before_tool_callback, you can
|
||||
check if a request has been made before. If so, you can return a cached
|
||||
response, skipping the expensive LLM or tool call entirely.
|
||||
- **Request/Response Modification**: Dynamically add information to LLM prompts
|
||||
(e.g., in before_model_callback) or standardize tool outputs (e.g., in
|
||||
after_tool_callback).
|
||||
|
||||
### Run the agent
|
||||
|
||||
**Note: Plugin is NOT supported in `adk web`yet.**
|
||||
|
||||
Use following command to run the main.py
|
||||
|
||||
```bash
|
||||
python3 -m contributing.samples.plugin_basic.main
|
||||
```
|
||||
|
||||
It should output the following content. Note that the outputs from plugin are
|
||||
printed.
|
||||
|
||||
```bash
|
||||
[Plugin] Agent run count: 1
|
||||
[Plugin] LLM request count: 1
|
||||
** Got event from hello_world
|
||||
Hello world: query is [hello world]
|
||||
** Got event from hello_world
|
||||
[Plugin] LLM request count: 2
|
||||
** Got event from hello_world
|
||||
```
|
||||
@@ -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 .main import root_agent
|
||||
@@ -0,0 +1,43 @@
|
||||
# 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 google.adk.agents.base_agent import BaseAgent
|
||||
from google.adk.agents.callback_context import CallbackContext
|
||||
from google.adk.models.llm_request import LlmRequest
|
||||
from google.adk.plugins.base_plugin import BasePlugin
|
||||
|
||||
|
||||
class CountInvocationPlugin(BasePlugin):
|
||||
"""A custom plugin that counts agent and tool invocations."""
|
||||
|
||||
def __init__(self) -> None:
|
||||
"""Initialize the plugin with counters."""
|
||||
super().__init__(name="count_invocation")
|
||||
self.agent_count: int = 0
|
||||
self.tool_count: int = 0
|
||||
self.llm_request_count: int = 0
|
||||
|
||||
async def before_agent_callback(
|
||||
self, *, agent: BaseAgent, callback_context: CallbackContext
|
||||
) -> None:
|
||||
"""Count agent runs."""
|
||||
self.agent_count += 1
|
||||
print(f"[Plugin] Agent run count: {self.agent_count}")
|
||||
|
||||
async def before_model_callback(
|
||||
self, *, callback_context: CallbackContext, llm_request: LlmRequest
|
||||
) -> None:
|
||||
"""Count LLM requests."""
|
||||
self.llm_request_count += 1
|
||||
print(f"[Plugin] LLM request count: {self.llm_request_count}")
|
||||
@@ -0,0 +1,64 @@
|
||||
# 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.
|
||||
|
||||
import asyncio
|
||||
|
||||
from google.adk import Agent
|
||||
from google.adk.runners import InMemoryRunner
|
||||
from google.adk.tools.tool_context import ToolContext
|
||||
from google.genai import types
|
||||
|
||||
# [Step 2] Import the plugin.
|
||||
from .count_plugin import CountInvocationPlugin
|
||||
|
||||
|
||||
async def hello_world(tool_context: ToolContext, query: str):
|
||||
print(f'Hello world: query is [{query}]')
|
||||
|
||||
|
||||
root_agent = Agent(
|
||||
name='hello_world',
|
||||
description='Prints hello world with user query.',
|
||||
instruction="""Use hello_world tool to print hello world and user query.
|
||||
""",
|
||||
tools=[hello_world],
|
||||
)
|
||||
|
||||
|
||||
async def main():
|
||||
"""Main entry point for the agent."""
|
||||
prompt = 'hello world'
|
||||
runner = InMemoryRunner(
|
||||
agent=root_agent,
|
||||
app_name='test_app_with_plugin',
|
||||
# [Step 2] Add your plugin here. You can add multiple plugins.
|
||||
plugins=[CountInvocationPlugin()],
|
||||
)
|
||||
session = await runner.session_service.create_session(
|
||||
user_id='user',
|
||||
app_name='test_app_with_plugin',
|
||||
)
|
||||
|
||||
async for event in runner.run_async(
|
||||
user_id='user',
|
||||
session_id=session.id,
|
||||
new_message=types.Content(
|
||||
role='user', parts=[types.Part.from_text(text=prompt)]
|
||||
),
|
||||
):
|
||||
print(f'** Got event from {event.author}')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
asyncio.run(main())
|
||||
Reference in New Issue
Block a user