Skip to content
Draft
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
4 changes: 4 additions & 0 deletions src/strands/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,19 @@
from .agent.agent import Agent
from .agent.base import AgentBase
from .event_loop._retry import ModelRetryStrategy
from .hooks.decorator import hook
from .plugins.plugin import Plugin
from .tools.decorator import tool
from .types.tools import ToolContext

__all__ = [
"Agent",
"AgentBase",
"agent",
"hook",
"models",
"ModelRetryStrategy",
"Plugin",
"tool",
"ToolContext",
"types",
Expand Down
18 changes: 18 additions & 0 deletions src/strands/agent/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
from ..tools._tool_helpers import generate_missing_tool_result_content

if TYPE_CHECKING:
from ..plugins.plugin import Plugin
from ..tools import ToolProvider
from ..handlers.callback_handler import PrintingCallbackHandler, null_callback_handler
from ..hooks import (
Expand Down Expand Up @@ -129,6 +130,7 @@ def __init__(
structured_output_prompt: str | None = None,
tool_executor: ToolExecutor | None = None,
retry_strategy: ModelRetryStrategy | _DefaultRetryStrategySentinel | None = _DEFAULT_RETRY_STRATEGY,
plugins: "list[Plugin] | None" = None,
):
"""Initialize the Agent with the specified configuration.

Expand Down Expand Up @@ -186,6 +188,12 @@ def __init__(
retry_strategy: Strategy for retrying model calls on throttling or other transient errors.
Defaults to ModelRetryStrategy with max_attempts=6, initial_delay=4s, max_delay=240s.
Implement a custom HookProvider for custom retry logic, or pass None to disable retries.
plugins: List of Plugin instances to register with the agent.
Each plugin can contribute tools and hooks via ``@tool`` and ``@hook``
decorated methods, which are auto-discovered and registered during
agent initialization. An optional ``init_plugin(agent)`` callback is
invoked after registration for any additional setup.
Defaults to None.

Raises:
ValueError: If agent id contains path separators.
Expand Down Expand Up @@ -302,6 +310,16 @@ def __init__(
if hooks:
for hook in hooks:
self.hooks.add_hook(hook)

# Process plugins: register discovered tools, hooks, and call init_plugin
if plugins:
for plugin in plugins:
for plugin_tool in plugin.tools:
self.tool_registry.register_tool(plugin_tool)
for plugin_hook in plugin.hooks:
self.hooks.add_hook(plugin_hook)
plugin.init_plugin(self)

self.hooks.invoke_callbacks(AgentInitializedEvent(agent=self))

@property
Expand Down
27 changes: 23 additions & 4 deletions src/strands/hooks/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
built-in SDK components and user code to react to or modify agent behavior
through strongly-typed event callbacks.

Example Usage:
Example Usage with Class-Based Hooks:
```python
from strands.hooks import HookProvider, HookRegistry
from strands.hooks.events import BeforeInvocationEvent, AfterInvocationEvent
Expand All @@ -25,10 +25,24 @@ def log_end(self, event: AfterInvocationEvent) -> None:
agent = Agent(hooks=[LoggingHooks()])
```

This replaces the older callback_handler approach with a more composable,
type-safe system that supports multiple subscribers per event type.
Example Usage with Decorator-Based Hooks:
```python
from strands import Agent, hook
from strands.hooks import BeforeToolCallEvent

@hook
def log_tool_calls(event: BeforeToolCallEvent) -> None:
'''Log all tool calls before execution.'''
print(f"Tool: {event.tool_use}")

agent = Agent(hooks=[log_tool_calls])
```

This module supports both the class-based HookProvider approach and the newer
decorator-based @hook approach for maximum flexibility.
"""

from .decorator import DecoratedFunctionHook, hook
from .events import (
AfterInvocationEvent,
AfterModelCallEvent,
Expand All @@ -48,6 +62,10 @@ def log_end(self, event: AfterInvocationEvent) -> None:
from .registry import BaseHookEvent, HookCallback, HookEvent, HookProvider, HookRegistry

__all__ = [
# Decorator
"hook",
"DecoratedFunctionHook",
# Events
"AgentInitializedEvent",
"BeforeInvocationEvent",
"BeforeToolCallEvent",
Expand All @@ -56,12 +74,13 @@ def log_end(self, event: AfterInvocationEvent) -> None:
"AfterModelCallEvent",
"AfterInvocationEvent",
"MessageAddedEvent",
# Registry
"HookEvent",
"HookProvider",
"HookCallback",
"HookRegistry",
"HookEvent",
"BaseHookEvent",

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Issue: The __all__ list isn't alphabetically sorted, making it harder to maintain.

Suggestion: Sort alphabetically for consistency with other modules in the codebase. The Events and Registry sections are good, but entries within each section could be sorted.

# Multi-agent events
"AfterMultiAgentInvocationEvent",
"AfterNodeCallEvent",
"BeforeMultiAgentInvocationEvent",
Expand Down
Loading
Loading