Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
141 changes: 88 additions & 53 deletions docs/plugins/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ immediately:
Saves files included in user messages as Artifacts.
* [**Logging**](https://github.com/google/adk-python/blame/main/src/google/adk/plugins/logging_plugin.py):
Log important information at each agent workflow callback point.
* [**Debug Logging**](https://github.com/google/adk-python/blob/main/src/google/adk/plugins/debug_logging_plugin.py):
Captures complete debug information for each invocation to a YAML file.

## Define and register Plugins

Expand All @@ -92,28 +94,28 @@ methods, as shown in the following code example:
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}")
"""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}")
```

=== "Typescript"
Expand Down Expand Up @@ -261,16 +263,25 @@ of the agent.
### Register Plugin class

Integrate your Plugin class by registering it during your agent initialization
as part of your `Runner` class, using the `plugins` parameter. You can specify
multiple Plugins with this parameter. The following code example shows how to
register the `CountInvocationPlugin` plugin defined in the previous section with
a simple ADK agent.
as part of your `Runner` class (in Python, your `App` object), using the
`plugins` parameter. You can specify multiple Plugins with this parameter. The
following code example shows how to register the `CountInvocationPlugin` plugin
defined in the previous section with a simple ADK agent.

!!! note "Python: prefer `App(plugins=...)` over `Runner(plugins=...)`"

In Python, the `plugins` parameter of `Runner` and `InMemoryRunner` is
deprecated and raises a `DeprecationWarning`. Set `plugins` on an
[`App`](/apps/) instead and pass that `App` to the runner as
`InMemoryRunner(app=app)`. Passing both `plugins` and `app` raises a
`ValueError`.

=== "Python"

```py
from google.adk.runners import InMemoryRunner
from google.adk import Agent
from google.adk.apps import App
from google.adk.tools.tool_context import ToolContext
from google.genai import types
import asyncio
Expand All @@ -281,25 +292,27 @@ a simple ADK agent.
async def hello_world(tool_context: ToolContext, query: str):
print(f'Hello world: query is [{query}]')

root_agent = Agent(
model='gemini-flash-latest',
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],
)
root_agent = Agent(
model='gemini-flash-latest',
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],
)

app = App(
name='test_app_with_plugin',
root_agent=root_agent,

# Add your plugin here. You can add multiple plugins.
plugins=[CountInvocationPlugin()],
)

async def main():
"""Main entry point for the agent."""
prompt = 'hello world'
runner = InMemoryRunner(
agent=root_agent,
app_name='test_app_with_plugin',

# Add your plugin here. You can add multiple plugins.
plugins=[CountInvocationPlugin()],
)
runner = InMemoryRunner(app=app)

# The rest is the same as starting a regular ADK runner.
session = await runner.session_service.create_session(
Expand Down Expand Up @@ -711,9 +724,10 @@ state of a single agent.</td>
You define when a Plugin is called with the callback functions to define in
your Plugin class. Callbacks are available when a user message is received,
before and after an `Runner`, `Agent`, `Model`, or `Tool` is called, for
`Events`, and when a `Model`, or `Tool` error occurs. These callbacks include,
and take precedence over, the any callbacks defined within your Agent, Model,
and Tool classes.
`Events`, and when a `Model`, or `Tool` error occurs. In Python, error
callbacks also run when an `Agent` raises an exception and when the run itself
fails. These callbacks include, and take precedence over, the any callbacks
defined within your Agent, Model, and Tool classes.

The following diagram illustrates callback points where you can attach and run
Plugin functionality during your agents workflow:
Expand All @@ -730,6 +744,7 @@ more detail.
- [Agent execution callbacks](#agent-execution-callbacks)
- [Model callbacks](#model-callbacks)
- [Tool callbacks](#tool-callbacks)
- [Event callbacks](#event-callbacks)
- [Runner end callbacks](#runner-end-callbacks)

### User Message callbacks
Expand Down Expand Up @@ -801,7 +816,12 @@ logic begins.
- **Purpose:** Global setup or initialization before the invocation runs.
- **Flow Control:** Return a `types.Content` object to **halt execution**:
the `Runner` exits early and ends the run with that content as the result.
Return `None` to proceed normally.
Return `None` to proceed normally. In Python, `run_async()` honors this
return value only when the root agent is a `BaseAgent` that is not an
`LlmAgent`. When the root agent is an `LlmAgent` or a `Workflow`, the
returned content is ignored and the run proceeds as if you had returned
`None`; use `before_agent_callback` or `before_model_callback` to
short-circuit those runs instead.

The following code example shows the basic syntax of this callback:

Expand Down Expand Up @@ -847,7 +867,10 @@ The following code example shows the basic syntax of this callback:
before the agent's main work begins. The main work encompasses the agent's
entire process for handling the request, which could involve calling models or
tools. After the agent has finished all its steps and prepared a result, the
`after_agent_callback` runs.
`after_agent_callback` runs. In Python, if the agent's run raises an exception,
`on_agent_error_callback(*, agent, callback_context, error)` runs instead of
`after_agent_callback`. That callback only observes the failure: its return
value is ignored and the original exception is still raised.

**Caution:** Plugins that implement these callbacks are executed *before* the
Agent-level callbacks are executed. Furthermore, if a Plugin-level agent
Expand Down Expand Up @@ -876,8 +899,7 @@ Furthermore, if a Plugin-level model callback returns anything other than a

#### Model on error callback details

The on error callback for Model objects is only supported by the Plugins
feature works as follows:
The on error callback for Model objects works as follows:

- **When It Runs:** When an exception is raised during the model call.
- **Common Use Cases:** Graceful error handling, logging the specific
Expand Down Expand Up @@ -959,8 +981,7 @@ is *not executed* (skipped).

#### Tool on error callback details

The on error callback for Tool objects is only supported by the Plugins feature
works as follows:
The on error callback for Tool objects works as follows:

- **When It Runs:** When an exception is raised during the execution of a
tool's `run` method.
Expand Down Expand Up @@ -1031,8 +1052,10 @@ before it's streamed to the client.
to the user. An agent's run may produce multiple events.
- **Purpose:** Useful for modifying or enriching events (e.g., adding
metadata) or for triggering side effects based on specific events.
- **Flow Control:** Return an `Event` object to **replace** the original
event.
- **Flow Control:** Return an `Event` object to **override** the original
event. In Python, ADK merges your event onto the original: only the fields
you set are applied, and `id`, `invocation_id`, and `timestamp` always come
from the original event.

The following code example shows the basic syntax of this callback:

Expand Down Expand Up @@ -1095,7 +1118,7 @@ The following code example shows the basic syntax of this callback:
```py
async def after_run_callback(
self, *, invocation_context: InvocationContext
) -> Optional[None]:
) -> None:
```

=== "Typescript"
Expand Down Expand Up @@ -1124,6 +1147,18 @@ The following code example shows the basic syntax of this callback:
}
```

In Python, ADK notifies your Plugin of two more end-of-life events:

- **`on_run_error_callback(*, invocation_context, error)`**: Runs instead of
`after_run_callback` when the run fails with an unhandled exception. This
callback only observes the failure: its return value is ignored and the
original exception is still raised.
- **`close()`**: Runs once per Plugin when you close the `Runner` with
`await runner.close()`, not once per run. Use it to release resources the
Plugin owns, such as an HTTP client or a metrics exporter. Each `close()`
call is bounded by the runner's `plugin_close_timeout`, five seconds by
default.

## Next steps

Check out these resources for developing and applying Plugins to your ADK
Expand Down
16 changes: 13 additions & 3 deletions docs/skills/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,16 @@ You can define [skills in code](#inline-skills) or load
from google.adk.tools import skill_toolset

weather_skill = load_skill_from_dir(
pathlib.Path(__file__).parent / "skills" / "weather_skill"
pathlib.Path(__file__).parent / "skills" / "weather-skill"
)

def get_weather_tool(city: str) -> dict:
"""Retrieves the current weather report for a specified city."""
return {
"status": "success",
"report": f"The weather in {city} is sunny with a temperature of 25°C.",
}

my_skill_toolset = skill_toolset.SkillToolset(
skills=[weather_skill],
additional_tools=[get_weather_tool],
Expand Down Expand Up @@ -101,9 +108,9 @@ You can define [skills in code](#inline-skills) or load
For a complete example, see the code sample in
[skills](https://github.com/google/adk-go/tree/main/examples/skills).

!!! note "Check your working directory"
!!! note "Check where `skills/` is resolved from"

Ensure that 'skills/' directory exist in your current working directory and contains the sub-directories for the Skills you want to use in your agent.
The Python and TypeScript examples above resolve `skills/` relative to the directory holding the agent source file, so place `skills/` next to that file. The Go example uses `os.DirFS("./skills")`, which resolves relative to the current working directory instead. Either way, the `skills/` directory must contain the sub-directories for the Skills you want to use in your agent.

## Skill structure

Expand Down Expand Up @@ -155,6 +162,9 @@ meets the following requirements:
* Must not be empty.
* Must be 1024 characters or less.

When loading a Skill from the filesystem, the directory name must match the
**name** in the frontmatter, or loading fails.

### Skills directory structure

The following directory structure shows the recommended way to include Skills in
Expand Down
Loading