Skip to content

feat(agent): add add_hook convenience method for hook callback registration#1706

Merged
Unshure merged 6 commits intomainfrom
agent-tasks/1686
Feb 18, 2026
Merged

feat(agent): add add_hook convenience method for hook callback registration#1706
Unshure merged 6 commits intomainfrom
agent-tasks/1686

Conversation

@Unshure
Copy link
Member

@Unshure Unshure commented Feb 16, 2026

Motivation

Currently, registering a hook requires accessing the internal hooks registry:

agent.hooks.add_callback(BeforeModelCallEvent, my_callback)

This exposes implementation details and is less discoverable for users. Plugin authors and users who want to add hooks directly need a cleaner, more intuitive API.

Resolves #1686

Public API Changes

The Agent class now has an add_hook method that provides a cleaner API for registering hook callbacks:

# Before - accessing internal registry
agent.hooks.add_callback(BeforeModelCallEvent, my_callback)

# After - cleaner public API with explicit event type
agent.add_hook(my_callback, BeforeModelCallEvent)

# After - event type inferred from callback type hint
def my_callback(event: BeforeModelCallEvent) -> None:
    print(f"Model call for: {event.agent.name}")

agent.add_hook(my_callback)  # event_type inferred automatically

The add_callback method on HookRegistry also supports type inference:

# With explicit event type
registry.add_callback(BeforeModelCallEvent, my_handler)

# With event type inferred from type hint
registry.add_callback(None, my_typed_handler)

When event_type is not provided, it's inferred from the callback's first parameter type hint. A ValueError is raised with a helpful message if inference fails.

Naming Justification

I went back and forth between add_hook and add_callback. I decided to go with add_hook, as I think its the most representative name (callback is generic, but hook specifically means running code at a specific lifecycle event).

As for helping users who may not be familiar with hooks, I added a link to our docs to help them understand this concept.

@codecov
Copy link

codecov bot commented Feb 16, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@github-actions
Copy link

Assessment: Comment

This PR adds a useful convenience method for hook registration that improves the developer experience. The implementation is functional and well-documented.

Review Categories
  • API Design: The argument order inconsistency between Agent.add_hook(callback, event_type) and HookRegistry.add_callback(event_type, callback) may confuse users. Consider aligning these signatures.
  • Type Safety: The complex union type in add_callback could benefit from @overload decorators for better IDE support.
  • Testing: One test case should be strengthened to actually verify the callback was invoked.

The feature addresses a real need per issue #1686 and the type inference capability is a nice touch. Consider the API consistency feedback before merging.

@Unshure
Copy link
Member Author

Unshure commented Feb 16, 2026

/strands review the current pull request and add the @ overload changes proposed in the review

@github-actions
Copy link

Implementation of Review Feedback

I've implemented the @overload suggestion from the review. Here's a summary of changes:

Changes Made

1. src/strands/hooks/registry.py

  • Added overload to imports
  • Added two @overload signatures for add_callback():
    • add_callback(callback) - infers event type from type hint
    • add_callback(event_type, callback) - explicit event type
  • Updated docstring to document both call patterns

2. src/strands/agent/agent.py

  • Added overload to imports
  • Added two @overload signatures for add_hook():
    • add_hook(callback) - infers event type from type hint
    • add_hook(callback, event_type) - explicit event type
  • Updated docstring to document both call patterns

Verification

  • ✅ All 13 related tests pass
  • ✅ Ruff linting passes

Diff

# src/strands/hooks/registry.py
+from typing import (..., overload, ...)

+    @overload
+    def add_callback(self, callback: HookCallback[TEvent]) -> None: ...
+
+    @overload
+    def add_callback(self, event_type: type[TEvent], callback: HookCallback[TEvent]) -> None: ...

# src/strands/agent/agent.py
+from typing import (..., overload, ...)

+    @overload
+    def add_hook(self, callback: HookCallback[TEvent]) -> None: ...
+
+    @overload
+    def add_hook(self, callback: HookCallback[TEvent], event_type: type[TEvent]) -> None: ...

Note: I don't have push access to apply these changes directly. The PR author will need to apply them.

@Unshure
Copy link
Member Author

Unshure commented Feb 16, 2026

/strands review

@github-actions
Copy link

Assessment: Approve

The @overload changes have been applied and the PR is looking good.

Review Status
  • @overload decorators - Applied to both add_callback() and add_hook()
  • All tests pass - 13/13 tests passing
  • Linting clean - No ruff errors
  • ⚠️ Minor: One test (test_agent_add_hook_infers_event_type_from_callback) could be strengthened to verify callback invocation

The API design with type inference is a nice convenience feature. The @overload decorators provide clear IDE support for the two call patterns.

@Unshure Unshure self-assigned this Feb 16, 2026
@github-actions github-actions bot added size/m and removed size/m labels Feb 16, 2026
@github-actions github-actions bot added size/m and removed size/m labels Feb 17, 2026
@Unshure Unshure changed the title feat(agent): add add_hook convenience method for hook registration feat(agent): add add_hook convenience method for hook callback registration Feb 17, 2026
@github-actions
Copy link

Assessment: Approve ✅

All review feedback has been addressed in the latest commit. The PR is ready to merge.

Final Review Summary
Item Status
Test callback verification ✅ Fixed - now asserts callback invocation
Test coverage ✅ 100% (Codecov confirmed)
New edge case tests ✅ 6 additional tests for registry.py
All tests passing ✅ 16/16 tests pass
Documentation ✅ Good docstrings with examples
Naming justification ✅ Added to PR description

The implementation is clean, well-tested, and provides a nice convenience API for hook registration with type inference support.

zastrowm
zastrowm previously approved these changes Feb 17, 2026
@github-actions
Copy link

Assessment: Approve ✅

The latest commit significantly simplifies the implementation by removing the complex union type from add_callback.

Review Summary
Item Status
add_callback signature ✅ Simplified: `(event_type: type[TEvent]
add_hook implementation ✅ Clean pass-through to add_callback
All tests ✅ 13/13 passing
Coverage ✅ 100%
Documentation ✅ Good with docs link

Note: PR includes unrelated MCP version bump (1.11.0 → 1.23.0) in pyproject.toml. Consider splitting if this causes issues.

The implementation is now much cleaner and addresses the method overloading concerns raised by other reviewers.

@Unshure Unshure merged commit 0a31848 into main Feb 18, 2026
23 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEATURE] Add add_hook Convenience Method to Agent

5 participants

Comments