11import pytest
22from mesa .discrete_space import OrthogonalMooreGrid
3+
34from mesa_llm .llm_agent import LLMAgent
45from mesa_llm .reasoning .reasoning import Reasoning
56from mesa_llm .tools .tool_decorator import _python_to_json_type , tool
67
8+
79class DummyReasoning (Reasoning ):
8- def plan (self , * args , ** kwargs ): pass
9- async def aplan (self , * args , ** kwargs ): pass
10+ def plan (self , * args , ** kwargs ):
11+ pass
12+
13+ async def aplan (self , * args , ** kwargs ):
14+ pass
15+
1016
1117class AsyncAgent (LLMAgent ):
1218 def __init__ (self , model ):
@@ -18,20 +24,27 @@ async def astep(self):
1824 self .astep_called = True
1925 return await super ().astep ()
2026
27+
2128@pytest .mark .asyncio
2229async def test_llm_agent_async_features (mocker ):
2330 model = mocker .Mock ()
2431 model .steps = 1
2532 # Mocking memory methods to avoid actual LLM calls
26- mocker .patch ("mesa_llm.memory.st_lt_memory.STLTMemory.aadd_to_memory" , side_effect = lambda ** kwargs : None )
27- mocker .patch ("mesa_llm.memory.st_lt_memory.STLTMemory.aprocess_step" , side_effect = lambda ** kwargs : None )
28-
33+ mocker .patch (
34+ "mesa_llm.memory.st_lt_memory.STLTMemory.aadd_to_memory" ,
35+ side_effect = lambda ** kwargs : None ,
36+ )
37+ mocker .patch (
38+ "mesa_llm.memory.st_lt_memory.STLTMemory.aprocess_step" ,
39+ side_effect = lambda ** kwargs : None ,
40+ )
41+
2942 agent = AsyncAgent (model )
30-
43+
3144 # Test astep wrapper
3245 await agent .astep ()
3346 assert agent .astep_called is True
34-
47+
3548 # Test apre_step and apost_step directly
3649 await agent .apre_step ()
3750 await agent .apost_step ()
@@ -42,50 +55,52 @@ async def test_llm_agent_async_features(mocker):
4255 for r in recipients :
4356 r .memory .aadd_to_memory .assert_called ()
4457
58+
4559def test_llm_agent_pos_none_clears_cell (mocker ):
4660 grid = OrthogonalMooreGrid (dimensions = (5 , 5 ), torus = False )
4761 model = mocker .Mock (grid = grid )
4862 agent = LLMAgent (model , reasoning = DummyReasoning )
49-
63+
5064 # Place in a cell
5165 cell = grid ._cells [(2 , 2 )]
5266 agent .cell = cell
5367 assert agent .pos == (2 , 2 )
54-
68+
5569 # Set pos to None
5670 agent .pos = None
5771 assert agent .pos is None
5872 assert agent .cell is None
5973
74+
6075def test_llm_agent_move_to_all_spaces (mocker ):
6176 from mesa .discrete_space import OrthogonalMooreGrid
6277 from mesa .space import ContinuousSpace , SingleGrid
63-
78+
6479 # Test Orthogonal Grid
6580 grid = OrthogonalMooreGrid (dimensions = (5 , 5 ), torus = False )
6681 model = mocker .Mock (grid = grid , space = None )
6782 agent = LLMAgent (model , reasoning = DummyReasoning )
6883 agent .cell = grid ._cells [(0 , 0 )]
69-
84+
7085 agent .move_to ((1 , 1 ))
7186 assert agent .pos == (1 , 1 )
7287 assert agent .cell is grid ._cells [(1 , 1 )]
73-
88+
7489 # Test Continuous Space
7590 space = ContinuousSpace (10 , 10 , False )
7691 model = mocker .Mock (space = space , grid = None )
7792 agent = LLMAgent (model , reasoning = DummyReasoning )
7893 space .place_agent (agent , (1 , 1 ))
79-
94+
8095 agent .move_to ((5.5 , 6.6 ))
8196 assert agent .pos == (5.5 , 6.6 )
82-
97+
8398 # Test Unsupported
8499 model = mocker .Mock (grid = None , space = None )
85100 agent = LLMAgent (model , reasoning = DummyReasoning )
86101 with pytest .raises (ValueError , match = "Unsupported environment" ):
87102 agent .move_to ((1 , 1 ))
88-
103+
89104 # Test SingleGrid (Line 112)
90105 grid = SingleGrid (5 , 5 , False )
91106 model = mocker .Mock (grid = grid , space = None )
@@ -94,55 +109,79 @@ def test_llm_agent_move_to_all_spaces(mocker):
94109 agent .move_to ((1 , 1 ))
95110 assert agent .pos == (1 , 1 )
96111
112+
97113def test_llm_agent_step_wrapper (mocker ):
98114 # Test sync step wrapper (Lines 381-390)
99115 class SyncSubAgent (LLMAgent ):
100116 def __init__ (self , model ):
101117 super ().__init__ (model , reasoning = DummyReasoning )
102118 self .step_called = False
119+
103120 def step (self ):
104121 self .step_called = True
105-
122+
106123 model = mocker .Mock ()
107124 model .steps = 1
108- mocker .patch ("mesa_llm.memory.st_lt_memory.STLTMemory.process_step" , side_effect = lambda ** kwargs : None )
109-
125+ mocker .patch (
126+ "mesa_llm.memory.st_lt_memory.STLTMemory.process_step" ,
127+ side_effect = lambda ** kwargs : None ,
128+ )
129+
110130 agent = SyncSubAgent (model )
111131 agent .step ()
112132 assert agent .step_called is True
113-
133+
114134 # Test astep fallback to step (Line 366)
115- mocker .patch ("mesa_llm.memory.st_lt_memory.STLTMemory.aprocess_step" , side_effect = lambda ** kwargs : None )
135+ mocker .patch (
136+ "mesa_llm.memory.st_lt_memory.STLTMemory.aprocess_step" ,
137+ side_effect = lambda ** kwargs : None ,
138+ )
139+
116140 async def run_astep ():
117141 await agent .astep ()
142+
118143 import asyncio
144+
119145 asyncio .run (run_astep ())
120146 assert agent .step_called is True
121147
148+
122149def test_tool_decorator_string_annotations ():
123150 # Test _python_to_json_type with string literals
124151 assert _python_to_json_type ("int" ) == {"type" : "integer" }
125- assert _python_to_json_type ("list[int]" ) == {"type" : "array" , "items" : {"type" : "integer" }}
126- assert _python_to_json_type ("list[str]" ) == {"type" : "array" , "items" : {"type" : "string" }}
127- assert _python_to_json_type ("dict[str, int]" ) == {"type" : "object" , "additionalProperties" : {"type" : "integer" }}
128-
152+ assert _python_to_json_type ("list[int]" ) == {
153+ "type" : "array" ,
154+ "items" : {"type" : "integer" },
155+ }
156+ assert _python_to_json_type ("list[str]" ) == {
157+ "type" : "array" ,
158+ "items" : {"type" : "string" },
159+ }
160+ assert _python_to_json_type ("dict[str, int]" ) == {
161+ "type" : "object" ,
162+ "additionalProperties" : {"type" : "integer" },
163+ }
164+
129165 # Test invalid string fallback
130166 assert _python_to_json_type ("SomethingUnknown" ) == {"type" : "string" }
131-
167+
132168 # Test None type
133169 assert _python_to_json_type (type (None )) == {"type" : "null" }
134170
171+
135172def test_optional_union_edge_cases ():
136173 from typing import Union
174+
137175 # Test Union with more than 2 types
138176 schema = _python_to_json_type (Union [int , str , float ])
139177 assert "anyOf" in schema
140178 assert len (schema ["anyOf" ]) == 3
141-
179+
142180 # Test Optional with None only (unusual but possible)
143181 schema = _python_to_json_type (Union [None , None ])
144182 assert schema == {"type" : "null" }
145183
184+
146185@tool
147186def string_annotated_tool (agent , data : "list[int]" ) -> str :
148187 """A tool with string annotations.
@@ -151,6 +190,7 @@ def string_annotated_tool(agent, data: "list[int]") -> str:
151190 """
152191 return str (data )
153192
193+
154194def test_string_annotated_tool_schema ():
155195 schema = string_annotated_tool .__tool_schema__
156196 props = schema ["function" ]["parameters" ]["properties" ]
0 commit comments