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
26 changes: 12 additions & 14 deletions libs/agno/agno/agent/_run.py
Original file line number Diff line number Diff line change
Expand Up @@ -1268,13 +1268,12 @@ def run_dispatch(
# Validate input against input_schema if provided
validated_input = validate_input(input, agent.input_schema)

# Normalise hook & guardails
if not agent._hooks_normalised:
if agent.pre_hooks:
agent.pre_hooks = normalize_pre_hooks(agent.pre_hooks) # type: ignore
if agent.post_hooks:
agent.post_hooks = normalize_post_hooks(agent.post_hooks) # type: ignore
agent._hooks_normalised = True
# Normalise hooks & guardrails on every run. Hooks may be reassigned between
# runs, so always normalise rather than caching behind a flag.
if agent.pre_hooks:
agent.pre_hooks = normalize_pre_hooks(agent.pre_hooks) # type: ignore
if agent.post_hooks:
agent.post_hooks = normalize_post_hooks(agent.post_hooks) # type: ignore

# Initialize session
session_id, user_id = initialize_session(agent, session_id=session_id, user_id=user_id)
Expand Down Expand Up @@ -2641,13 +2640,12 @@ def arun_dispatch( # type: ignore
# 2. Validate input against input_schema if provided
validated_input = validate_input(input, agent.input_schema)

# Normalise hooks & guardails
if not agent._hooks_normalised:
if agent.pre_hooks:
agent.pre_hooks = normalize_pre_hooks(agent.pre_hooks, async_mode=True) # type: ignore
if agent.post_hooks:
agent.post_hooks = normalize_post_hooks(agent.post_hooks, async_mode=True) # type: ignore
agent._hooks_normalised = True
# Normalise hooks & guardrails on every run. Hooks may be reassigned between
# runs, so always normalise rather than caching behind a flag.
if agent.pre_hooks:
agent.pre_hooks = normalize_pre_hooks(agent.pre_hooks, async_mode=True) # type: ignore
if agent.post_hooks:
agent.post_hooks = normalize_post_hooks(agent.post_hooks, async_mode=True) # type: ignore

# Initialize session
session_id, user_id = initialize_session(agent, session_id=session_id, user_id=user_id)
Expand Down
2 changes: 0 additions & 2 deletions libs/agno/agno/agent/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -689,8 +689,6 @@ def __init__(

self._formatter: Optional[SafeFormatter] = None

self._hooks_normalised = False

self._mcp_tools_initialized_on_run: List[Any] = []
self._connectable_tools_initialized_on_run: List[Any] = []

Expand Down
2 changes: 0 additions & 2 deletions libs/agno/agno/team/_init.py
Original file line number Diff line number Diff line change
Expand Up @@ -408,8 +408,6 @@ def __init__(

team._formatter = None

team._hooks_normalised = False

# List of MCP tools that were initialized on the last run
team._mcp_tools_initialized_on_run = []
# List of connectable tools that were initialized on the last run
Expand Down
26 changes: 12 additions & 14 deletions libs/agno/agno/team/_run.py
Original file line number Diff line number Diff line change
Expand Up @@ -1815,13 +1815,12 @@ def run_dispatch(
# Register run for cancellation tracking (after validation succeeds)
register_run(run_id) # type: ignore

# Normalise hook & guardails
if not team._hooks_normalised:
if team.pre_hooks:
team.pre_hooks = normalize_pre_hooks(team.pre_hooks) # type: ignore
if team.post_hooks:
team.post_hooks = normalize_post_hooks(team.post_hooks) # type: ignore
team._hooks_normalised = True
# Normalise hooks & guardrails on every run. Hooks may be reassigned between
# runs, so always normalise rather than caching behind a flag.
if team.pre_hooks:
team.pre_hooks = normalize_pre_hooks(team.pre_hooks) # type: ignore
if team.post_hooks:
team.post_hooks = normalize_post_hooks(team.post_hooks) # type: ignore

session_id, user_id = _initialize_session(team, session_id=session_id, user_id=user_id)

Expand Down Expand Up @@ -3948,13 +3947,12 @@ def arun_dispatch( # type: ignore
# Validate input against input_schema if provided
validated_input = validate_input(input, team.input_schema)

# Normalise hook & guardails
if not team._hooks_normalised:
if team.pre_hooks:
team.pre_hooks = normalize_pre_hooks(team.pre_hooks, async_mode=True) # type: ignore
if team.post_hooks:
team.post_hooks = normalize_post_hooks(team.post_hooks, async_mode=True) # type: ignore
team._hooks_normalised = True
# Normalise hooks & guardrails on every run. Hooks may be reassigned between
# runs, so always normalise rather than caching behind a flag.
if team.pre_hooks:
team.pre_hooks = normalize_pre_hooks(team.pre_hooks, async_mode=True) # type: ignore
if team.post_hooks:
team.post_hooks = normalize_post_hooks(team.post_hooks, async_mode=True) # type: ignore

session_id, user_id = _initialize_session(team, session_id=session_id, user_id=user_id)

Expand Down
2 changes: 0 additions & 2 deletions libs/agno/agno/team/team.py
Original file line number Diff line number Diff line change
Expand Up @@ -409,8 +409,6 @@ class Team:
_member_response_model: Optional[Union[Type[BaseModel], Dict[str, Any]]] = None
# Safe formatter for template resolution
_formatter: Optional[Any] = None
# Hooks normalised flag
_hooks_normalised: bool = False
# MCP tools initialized on the last run
_mcp_tools_initialized_on_run: Optional[List[Any]] = None
# Connectable tools initialized on the last run
Expand Down
33 changes: 33 additions & 0 deletions libs/agno/tests/unit/agent/test_hooks_normalised.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
from agno.guardrails.base import BaseGuardrail
from agno.run.agent import RunInput
from agno.utils.hooks import normalize_pre_hooks


class _StubGuardrail(BaseGuardrail):
name: str = "stub"

def check(self, run_input: RunInput) -> None:
pass

async def async_check(self, run_input: RunInput) -> None:
pass


def test_pre_hooks_normalised_on_first_run():
guardrail = _StubGuardrail()
hooks = normalize_pre_hooks([guardrail], async_mode=True)
assert hooks is not None
assert hooks[0] == guardrail.async_check


def test_pre_hooks_normalised_after_reassignment():
# Re-normalising with a new guardrail instance must return its bound method
guardrail = _StubGuardrail()
hooks = normalize_pre_hooks([guardrail], async_mode=True)
assert hooks is not None
assert hooks[0] == guardrail.async_check

guardrail2 = _StubGuardrail()
hooks2 = normalize_pre_hooks([guardrail2], async_mode=True)
assert hooks2 is not None
assert hooks2[0] == guardrail2.async_check
33 changes: 33 additions & 0 deletions libs/agno/tests/unit/team/test_hooks_normalised.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
from agno.guardrails.base import BaseGuardrail
from agno.run.agent import RunInput
from agno.utils.hooks import normalize_pre_hooks


class _StubGuardrail(BaseGuardrail):
name: str = "stub"

def check(self, run_input: RunInput) -> None:
pass

async def async_check(self, run_input: RunInput) -> None:
pass


def test_pre_hooks_normalised_on_first_run():
guardrail = _StubGuardrail()
hooks = normalize_pre_hooks([guardrail], async_mode=True)
assert hooks is not None
assert hooks[0] == guardrail.async_check


def test_pre_hooks_normalised_after_reassignment():
# Re-normalising with a new guardrail instance must return its bound method
guardrail = _StubGuardrail()
hooks = normalize_pre_hooks([guardrail], async_mode=True)
assert hooks is not None
assert hooks[0] == guardrail.async_check

guardrail2 = _StubGuardrail()
hooks2 = normalize_pre_hooks([guardrail2], async_mode=True)
assert hooks2 is not None
assert hooks2[0] == guardrail2.async_check
Loading