Skip to content

Commit d4719ca

Browse files
committed
update
1 parent f8431c6 commit d4719ca

File tree

3 files changed

+172
-162
lines changed

3 files changed

+172
-162
lines changed

libs/agno/tests/integration/agent/test_hooks.py

Lines changed: 78 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,15 @@
22
Tests for Agent hooks functionality.
33
"""
44

5-
from typing import Optional
5+
from typing import Any, AsyncIterator, Iterator, Optional
66
from unittest.mock import AsyncMock, Mock
77

88
import pytest
99

1010
from agno.agent import Agent
1111
from agno.exceptions import CheckTrigger, InputCheckError, OutputCheckError
12+
from agno.models.base import Model
13+
from agno.models.response import ModelResponse
1214
from agno.run.agent import RunInput, RunOutput
1315
from agno.session.agent import AgentSession
1416

@@ -103,46 +105,83 @@ async def async_tracking_post_hook(run_output: RunOutput, agent: Agent) -> None:
103105
)
104106

105107

108+
class MockTestModel(Model):
109+
"""Test model class that inherits from Model for testing purposes."""
110+
111+
def __init__(self, model_response_content: Optional[str] = None):
112+
super().__init__(id="test-model", name="test-model", provider="test")
113+
self.instructions = None
114+
self._model_response_content = model_response_content or "Test response from mock model"
115+
116+
# Mock the response object
117+
self._mock_response = Mock()
118+
self._mock_response.content = self._model_response_content
119+
self._mock_response.role = "assistant"
120+
self._mock_response.reasoning_content = None
121+
self._mock_response.tool_executions = None
122+
self._mock_response.images = None
123+
self._mock_response.videos = None
124+
self._mock_response.audios = None
125+
self._mock_response.files = None
126+
self._mock_response.citations = None
127+
self._mock_response.references = None
128+
self._mock_response.metadata = None
129+
130+
# Create Mock objects for response methods to track call_args
131+
self.response = Mock(return_value=self._mock_response)
132+
self.aresponse = AsyncMock(return_value=self._mock_response)
133+
134+
def get_instructions_for_model(self, *args, **kwargs):
135+
"""Mock get_instructions_for_model."""
136+
return None
137+
138+
def get_system_message_for_model(self, *args, **kwargs):
139+
"""Mock get_system_message_for_model."""
140+
return None
141+
142+
async def aget_instructions_for_model(self, *args, **kwargs):
143+
"""Mock async get_instructions_for_model."""
144+
return None
145+
146+
async def aget_system_message_for_model(self, *args, **kwargs):
147+
"""Mock async get_system_message_for_model."""
148+
return None
149+
150+
def parse_args(self, *args, **kwargs):
151+
"""Mock parse_args."""
152+
return {}
153+
154+
# Implement abstract methods required by Model base class
155+
def invoke(self, *args, **kwargs) -> ModelResponse:
156+
"""Mock invoke method."""
157+
return self._mock_response
158+
159+
async def ainvoke(self, *args, **kwargs) -> ModelResponse:
160+
"""Mock async invoke method."""
161+
return await self.aresponse(*args, **kwargs)
162+
163+
def invoke_stream(self, *args, **kwargs) -> Iterator[ModelResponse]:
164+
"""Mock invoke_stream method."""
165+
yield self._mock_response
166+
167+
async def ainvoke_stream(self, *args, **kwargs) -> AsyncIterator[ModelResponse]:
168+
"""Mock async invoke_stream method."""
169+
yield self._mock_response
170+
return
171+
172+
def _parse_provider_response(self, response: Any, **kwargs) -> ModelResponse:
173+
"""Mock _parse_provider_response method."""
174+
return self._mock_response
175+
176+
def _parse_provider_response_delta(self, response: Any) -> ModelResponse:
177+
"""Mock _parse_provider_response_delta method."""
178+
return self._mock_response
179+
180+
106181
def create_test_agent(pre_hooks=None, post_hooks=None, model_response_content=None) -> Agent:
107182
"""Create a test agent with mock model that supports both sync and async operations."""
108-
# Mock the model to avoid needing real API keys
109-
mock_model = Mock()
110-
mock_model.id = "test-model"
111-
mock_model.provider = "test"
112-
mock_model.instructions = None
113-
mock_model.name = "test-model"
114-
115-
# Mock the response method to return a proper mock response
116-
mock_response = Mock()
117-
mock_response.content = model_response_content or "Test response from mock model"
118-
mock_response.role = "assistant"
119-
mock_response.reasoning_content = None
120-
mock_response.tool_executions = None
121-
mock_response.images = None
122-
mock_response.videos = None
123-
mock_response.audios = None
124-
mock_response.files = None
125-
mock_response.citations = None
126-
mock_response.references = None
127-
mock_response.metadata = None
128-
129-
# Set up both sync and async response methods
130-
mock_model.response.return_value = mock_response
131-
132-
# For async operations, we need to mock the async methods
133-
# Create an async mock that returns the same mock_response
134-
async_response_mock = AsyncMock(return_value=mock_response)
135-
mock_model.aresponse = async_response_mock
136-
137-
# Mock other methods that might be called
138-
mock_model.get_instructions_for_model.return_value = None
139-
mock_model.get_system_message_for_model.return_value = None
140-
mock_model.structured_outputs = False
141-
mock_model.parse_args = Mock(return_value={})
142-
143-
# Add async versions if they exist
144-
mock_model.aget_instructions_for_model = AsyncMock(return_value=None)
145-
mock_model.aget_system_message_for_model = AsyncMock(return_value=None)
183+
# Create a test model that inherits from Model
184+
mock_model = MockTestModel(model_response_content=model_response_content)
146185

147186
return Agent(
148187
name="Test Agent",

libs/agno/tests/integration/teams/test_hooks.py

Lines changed: 90 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,15 @@
55
and configured according to their expected behavior.
66
"""
77

8-
from typing import Any, Dict, Optional
8+
from typing import Any, AsyncIterator, Dict, Iterator, Optional
99
from unittest.mock import AsyncMock, Mock
1010

1111
import pytest
1212

1313
from agno.agent import Agent
1414
from agno.exceptions import CheckTrigger, InputCheckError, OutputCheckError
15+
from agno.models.base import Model
16+
from agno.models.response import ModelResponse
1517
from agno.run.team import TeamRunInput, TeamRunOutput
1618
from agno.session.team import TeamSession
1719
from agno.team import Team
@@ -109,28 +111,88 @@ def clear_hook_tracker():
109111
hook_execution_tracker["post_hooks"].clear()
110112

111113

114+
class MockTestModel(Model):
115+
"""Test model class that inherits from Model for testing purposes."""
116+
117+
def __init__(self, model_id: str, model_response_content: Optional[str] = None):
118+
super().__init__(id=model_id, name=f"{model_id}-model", provider="test")
119+
self.instructions = None
120+
self._model_response_content = model_response_content or f"Response from {model_id}"
121+
122+
# Mock the response object
123+
self._mock_response = Mock()
124+
self._mock_response.content = self._model_response_content
125+
self._mock_response.role = "assistant"
126+
self._mock_response.reasoning_content = None
127+
self._mock_response.tool_executions = None
128+
self._mock_response.images = None
129+
self._mock_response.videos = None
130+
self._mock_response.audios = None
131+
self._mock_response.files = None
132+
self._mock_response.citations = None
133+
self._mock_response.references = None
134+
self._mock_response.metadata = None
135+
self._mock_response.tool_calls = []
136+
self._mock_response.updated_session_state = None
137+
# Set event to assistant_response by default (matches ModelResponse default)
138+
from agno.models.response import ModelResponseEvent
139+
self._mock_response.event = ModelResponseEvent.assistant_response.value
140+
141+
# Create Mock objects for response methods
142+
self.response = Mock(return_value=self._mock_response)
143+
self.aresponse = AsyncMock(return_value=self._mock_response)
144+
145+
def get_instructions_for_model(self, *args, **kwargs):
146+
"""Mock get_instructions_for_model."""
147+
return None
148+
149+
def get_system_message_for_model(self, *args, **kwargs):
150+
"""Mock get_system_message_for_model."""
151+
return None
152+
153+
async def aget_instructions_for_model(self, *args, **kwargs):
154+
"""Mock async get_instructions_for_model."""
155+
return None
156+
157+
async def aget_system_message_for_model(self, *args, **kwargs):
158+
"""Mock async get_system_message_for_model."""
159+
return None
160+
161+
def parse_args(self, *args, **kwargs):
162+
"""Mock parse_args."""
163+
return {}
164+
165+
# Implement abstract methods required by Model base class
166+
def invoke(self, *args, **kwargs) -> ModelResponse:
167+
"""Mock invoke method."""
168+
return self._mock_response
169+
170+
async def ainvoke(self, *args, **kwargs) -> ModelResponse:
171+
"""Mock async invoke method."""
172+
return await self.aresponse(*args, **kwargs)
173+
174+
def invoke_stream(self, *args, **kwargs) -> Iterator[ModelResponse]:
175+
"""Mock invoke_stream method."""
176+
yield self._mock_response
177+
178+
async def ainvoke_stream(self, *args, **kwargs) -> AsyncIterator[ModelResponse]:
179+
"""Mock async invoke_stream method."""
180+
yield self._mock_response
181+
return
182+
183+
def _parse_provider_response(self, response: Any, **kwargs) -> ModelResponse:
184+
"""Mock _parse_provider_response method."""
185+
return self._mock_response
186+
187+
def _parse_provider_response_delta(self, response: Any) -> ModelResponse:
188+
"""Mock _parse_provider_response_delta method."""
189+
return self._mock_response
190+
191+
112192
def create_mock_agent(name: str) -> Agent:
113193
"""Create a mock agent for team testing."""
114-
mock_model = Mock()
115-
mock_model.id = f"mock-model-{name.lower()}"
116-
mock_model.provider = "mock"
117-
mock_model.instructions = None
118-
mock_model.response.return_value = Mock(
119-
content=f"Response from {name}",
120-
role="assistant",
121-
reasoning_content=None,
122-
tool_executions=None,
123-
images=None,
124-
videos=None,
125-
audios=None,
126-
files=None,
127-
citations=None,
128-
references=None,
129-
metadata=None,
130-
tool_calls=None,
131-
)
132-
mock_model.get_instructions_for_model.return_value = None
133-
mock_model.get_system_message_for_model.return_value = None
194+
model_id = f"mock-model-{name.lower()}"
195+
mock_model = MockTestModel(model_id=model_id, model_response_content=f"Response from {name}")
134196

135197
return Agent(name=name, model=mock_model, description=f"Mock {name} for testing")
136198

@@ -141,43 +203,10 @@ def create_test_team(pre_hooks=None, post_hooks=None, model_response_content=Non
141203
agent1 = create_mock_agent("Agent1")
142204
agent2 = create_mock_agent("Agent2")
143205

144-
# Mock the team model to avoid needing real API keys
145-
mock_model = Mock()
146-
mock_model.id = "test-team-model"
147-
mock_model.provider = "test"
148-
mock_model.instructions = None
149-
mock_model.name = "test-team-model"
150-
151-
# Mock response object
152-
mock_response = Mock()
153-
mock_response.content = model_response_content or "Test team response from mock model"
154-
mock_response.role = "assistant"
155-
mock_response.reasoning_content = None
156-
mock_response.tool_executions = None
157-
mock_response.images = None
158-
mock_response.videos = None
159-
mock_response.audios = None
160-
mock_response.files = None
161-
mock_response.citations = None
162-
mock_response.references = None
163-
mock_response.metadata = None
164-
mock_response.tool_calls = []
165-
166-
# Set up both sync and async response methods
167-
mock_model.response.return_value = mock_response
168-
169-
# For async operations, we need to mock the async methods
170-
async_response_mock = AsyncMock(return_value=mock_response)
171-
mock_model.aresponse = async_response_mock
172-
173-
mock_model.get_instructions_for_model.return_value = None
174-
mock_model.get_system_message_for_model.return_value = None
175-
mock_model.structured_outputs = False
176-
mock_model.parse_args = Mock(return_value={})
177-
178-
# Add async versions
179-
mock_model.aget_instructions_for_model = AsyncMock(return_value=None)
180-
mock_model.aget_system_message_for_model = AsyncMock(return_value=None)
206+
# Create a test model that inherits from Model
207+
mock_model = MockTestModel(
208+
model_id="test-team-model", model_response_content=model_response_content or "Test team response from mock model"
209+
)
181210

182211
return Team(
183212
name="Test Team",
@@ -560,24 +589,10 @@ def output_validator(run_output: TeamRunOutput) -> None:
560589
agent2 = create_mock_agent("Agent2")
561590

562591
# Create mock team model with long response
563-
mock_model = Mock()
564-
mock_model.id = "test-team-model"
565-
mock_model.provider = "test"
566-
mock_model.response.return_value = Mock(
567-
content="A" * 150, # Long output to trigger post-hook
568-
reasoning_content=None,
569-
tool_executions=None,
570-
images=None,
571-
videos=None,
572-
audios=None,
573-
files=None,
574-
citations=None,
575-
references=None,
576-
metadata=None,
577-
role="assistant",
592+
mock_model = MockTestModel(
593+
model_id="test-team-model",
594+
model_response_content="A" * 150, # Long output to trigger post-hook
578595
)
579-
mock_model.get_instructions_for_model.return_value = None
580-
mock_model.get_system_message_for_model.return_value = None
581596

582597
team = Team(
583598
name="Validated Team",

0 commit comments

Comments
 (0)