Skip to content

feat: Implement the core system of typed hooks & callbacks #304

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: main
Choose a base branch
from

Conversation

zastrowm
Copy link
Member

Description

Add the HookRegistry and a small subset of events (AgentInitializedEvent, StartRequestEvent, EndRequestEvent) as a POC for how hooks will work.

This is intentionally only a subset of events listed as called out in #231 (comment) because I wanted to focus on the overall idea rather than the specific events. Follow-up PRs will focus on adding/implementing additional events.

Hooks are not yet publicly exposed to prevent builders from starting to rely on it.

Related Issues

#231

Documentation PR

N/A - will work on docs after more events are added/plumbed through

Type of Change

New feature

Testing

How have you tested the change? Verify that the changes do not break functionality or introduce warnings in consuming repositories: agents-docs, agents-tools, agents-cli

  • I ran hatch run prepare

Checklist

  • I have read the CONTRIBUTING document
  • I have added any necessary tests that prove my fix is effective or my feature works
  • I have updated the documentation accordingly
  • I have added an appropriate example to the documentation to outline the feature, or no new docs are needed
  • My changes generate no new warnings
  • Any dependent changes have been merged and published

By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.

Relates to strands-agents#231

Add the HookRegistry and a small subset of events (AgentInitializedEvent, StartRequestEvent, EndRequestEvent) as a POC for how hooks will work.
```
"""

def __call__(self, event: Any) -> None:
Copy link
Member

Choose a reason for hiding this comment

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

nit: can we now have this as event: HookEvent

Copy link
Member Author

Choose a reason for hiding this comment

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

whelp - this should be TEvent.

Fixed in the latest commit

registry.invoke_callbacks(event)
```
"""
for callback in self.get_callbacks_for(event):
Copy link
Member

Choose a reason for hiding this comment

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

we have #306, is there a use case for async callbacks?

Should all callbacks be async? If they are, should they always or conditionally be blocking?

Use case I'm considering is a callback where a metric wants to be emitted. I expect a large number of telemetry sdks are async native. So customers would have the extra work of modeling them synchrnously.

Copy link
Member Author

Choose a reason for hiding this comment

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

Hmmm... probably.

I'm going to bring this up to the team as a double-check, as I think this makes sense.

I'm going to defer to a follow-up CR, however, as I need to think on this.

@@ -320,6 +322,10 @@ def __init__(
self.name = name
self.description = description

self._hooks = HookRegistry()
# Register built-in hook providers (like ConversationManager) here
Copy link
Member

Choose a reason for hiding this comment

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

should this comment be here? Or should this be more of a todo to refactor conversational manager? Seems like the comment is really linked to the line below

Copy link
Member Author

Choose a reason for hiding this comment

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

Yeah - it's intended to a be a placeholder that is "Add built-in hook providers here" before Initialize is called. That way the conversation manager gets the AgentInitializedEvent

@@ -320,6 +322,10 @@ def __init__(
self.name = name
self.description = description

self._hooks = HookRegistry()
Copy link
Member

Choose a reason for hiding this comment

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

Why _hooks and not hooks? Are you planning on exposing this in a later pr?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, exposing later after the the exact contracts are figured out. I figure as long as we keep it internal, we can tweak things before making it publicly accessible. LMK if you think a bigger "all-at-once" approach would be more ideal



@dataclass
class StartRequestEvent(HookEvent):
Copy link
Member

Choose a reason for hiding this comment

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

When specifically is this event triggered? Is it just agent("Hello!"), or are there other entry points like direct tool calling or structured_output?


def __init__(self) -> None:
"""Initialize an empty hook registry."""
self._registered_callbacks: dict[Type, list[HookCallback]] = {}
Copy link
Member

Choose a reason for hiding this comment

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

Should this be:

Suggested change
self._registered_callbacks: dict[Type, list[HookCallback]] = {}
self._registered_callbacks: dict[TEvent, list[HookCallback]] = {}

?

Copy link
Member Author

Choose a reason for hiding this comment

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

In this case it would be TYPE[TEvent] but it doesn't make sense as there's nothing that indicates what TEvent would be bound to (Python typechecker complains).

It's equivalent to Java where a method might be generic, but a map that stores data would type it as non-generic Object

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants