1- import sys
2- import types
3- import pytest
41import asyncio
52import importlib
3+ import sys
4+ import types
65from typing import Any , Callable , List , Optional , Tuple
76
7+ import pytest
88
9- MODULE_PATH : str = "SelfhealingAgents.self_healing_system.agents.locator_agent.base_locator_agent"
9+ MODULE_PATH : str = (
10+ "SelfhealingAgents.self_healing_system.agents.locator_agent.base_locator_agent"
11+ )
1012
1113
1214def _run (coro ):
@@ -16,6 +18,7 @@ def _run(coro):
1618class _LoggerStub :
1719 def __init__ (self ) -> None :
1820 self .infos : List [str ] = []
21+
1922 def info (self , msg : str , * args : Any , ** kwargs : Any ) -> None :
2023 self .infos .append (str (msg ))
2124
@@ -32,20 +35,25 @@ def __init__(self, request_limit: int, total_tokens_limit: int) -> None:
3235
3336
3437class _FakeAgent :
35- def __init__ (self , * , model : Any , system_prompt : str , deps_type : Any , output_type : Any ) -> None :
38+ def __init__ (
39+ self , * , model : Any , system_prompt : str , deps_type : Any , output_type : Any
40+ ) -> None :
3641 self .model = model
3742 self .system_prompt = system_prompt
3843 self .deps_type = deps_type
3944 self .output_type = output_type
4045 self .run_result : _FakeAgentRunResult = _FakeAgentRunResult (None )
4146 self .validator : Optional [Callable [..., Any ]] = None
4247 self .run_calls : int = 0
48+
4349 @classmethod
4450 def __class_getitem__ (cls , item : Any ) -> "_FakeAgent" :
4551 return cls
52+
4653 def output_validator (self , func : Callable [..., Any ]) -> Callable [..., Any ]:
4754 self .validator = func
4855 return func
56+
4957 async def run (self , * args : Any , ** kwargs : Any ) -> _FakeAgentRunResult :
5058 self .run_calls += 1
5159 return self .run_result
@@ -74,7 +82,9 @@ def _install_stubs() -> _LoggerStub:
7482 robot_api_mod = types .ModuleType ("robot.api" )
7583 robot_api_mod .__path__ = []
7684 robot_api_interfaces_mod = types .ModuleType ("robot.api.interfaces" )
85+
7786 class ListenerV3 : ...
87+
7888 robot_api_interfaces_mod .ListenerV3 = ListenerV3
7989 logger = _LoggerStub ()
8090 robot_api_mod .logger = logger
@@ -85,7 +95,9 @@ class ListenerV3: ...
8595 _force_module ("robot.running" , types .ModuleType ("robot.running" ))
8696
8797 listener_stub = types .ModuleType ("SelfhealingAgents.listener" )
98+
8899 class SelfhealingAgents : ...
100+
89101 listener_stub .SelfhealingAgents = SelfhealingAgents
90102 _force_module ("SelfhealingAgents.listener" , listener_stub )
91103
@@ -105,55 +117,84 @@ class SelfhealingAgents: ...
105117
106118 aid_utils = types .ModuleType ("SelfhealingAgents.utils" )
107119 logging_mod = types .ModuleType ("SelfhealingAgents.utils.logging" )
120+
108121 def log (f : Callable [..., Any ]) -> Callable [..., Any ]:
109122 return f
123+
110124 logging_mod .log = log
111125 cfg_mod = types .ModuleType ("SelfhealingAgents.utils.cfg" )
126+
112127 class Cfg :
113128 def __init__ (self ) -> None :
114129 self .request_limit : int = 10
115130 self .total_tokens_limit : int = 1000
116131 self .use_llm_for_locator_generation : bool = True
117132 self .locator_agent_provider : str = "prov"
118133 self .locator_agent_model : str = "mod"
134+
119135 cfg_mod .Cfg = Cfg
120136 _force_module ("SelfhealingAgents.utils" , aid_utils )
121137 _force_module ("SelfhealingAgents.utils.logging" , logging_mod )
122138 _force_module ("SelfhealingAgents.utils.cfg" , cfg_mod )
123139
124- prompts_pkg = types .ModuleType ("SelfhealingAgents.self_healing_system.agents.prompts.locator.prompts_locator" )
140+ prompts_pkg = types .ModuleType (
141+ "SelfhealingAgents.self_healing_system.agents.prompts.locator.prompts_locator"
142+ )
143+
125144 class PromptsLocatorGenerationAgent :
126145 @staticmethod
127146 def get_system_msg (dom : Any ) -> str :
128147 return "GEN_SYS"
148+
129149 @staticmethod
130150 def get_user_msg (ctx : Any ) -> str :
131151 return "GEN_USER"
152+
132153 class PromptsLocatorSelectionAgent :
133154 @staticmethod
134155 def get_system_msg () -> str :
135156 return "SEL_SYS"
157+
136158 @staticmethod
137159 def get_user_msg (ctx : Any , suggestions : List [str ], metadata : List [dict ]) -> str :
138160 return "SEL_USER"
161+
139162 prompts_pkg .PromptsLocatorGenerationAgent = PromptsLocatorGenerationAgent
140163 prompts_pkg .PromptsLocatorSelectionAgent = PromptsLocatorSelectionAgent
141- _force_module ("SelfhealingAgents.self_healing_system.agents.prompts.locator.prompts_locator" , prompts_pkg )
164+ _force_module (
165+ "SelfhealingAgents.self_healing_system.agents.prompts.locator.prompts_locator" ,
166+ prompts_pkg ,
167+ )
168+
169+ client_model = types .ModuleType (
170+ "SelfhealingAgents.self_healing_system.llm.client_model"
171+ )
142172
143- client_model = types .ModuleType ("SelfhealingAgents.self_healing_system.llm.client_model" )
144173 def get_client_model (provider : str , model : str , cfg : Any ) -> str :
145174 return f"{ provider } :{ model } "
175+
146176 client_model .get_client_model = get_client_model
147- _force_module ("SelfhealingAgents.self_healing_system.llm.client_model" , client_model )
177+ _force_module (
178+ "SelfhealingAgents.self_healing_system.llm.client_model" , client_model
179+ )
180+
181+ loc_schema = types .ModuleType (
182+ "SelfhealingAgents.self_healing_system.schemas.api.locator_healing"
183+ )
148184
149- loc_schema = types .ModuleType ("SelfhealingAgents.self_healing_system.schemas.api.locator_healing" )
150185 class LocatorHealingResponse :
151186 def __init__ (self , suggestions : List [str ]) -> None :
152187 self .suggestions : List [str ] = suggestions
188+
153189 loc_schema .LocatorHealingResponse = LocatorHealingResponse
154- _force_module ("SelfhealingAgents.self_healing_system.schemas.api.locator_healing" , loc_schema )
190+ _force_module (
191+ "SelfhealingAgents.self_healing_system.schemas.api.locator_healing" , loc_schema
192+ )
193+
194+ payload_mod = types .ModuleType (
195+ "SelfhealingAgents.self_healing_system.schemas.internal_state.prompt_payload"
196+ )
155197
156- payload_mod = types .ModuleType ("SelfhealingAgents.self_healing_system.schemas.internal_state.prompt_payload" )
157198 class PromptPayload :
158199 def __init__ (
159200 self ,
@@ -172,13 +213,24 @@ def __init__(
172213 self .keyword_args = keyword_args
173214 self .failed_locator = failed_locator
174215 self .tried_locator_memory = tried_locator_memory
216+
175217 payload_mod .PromptPayload = PromptPayload
176- _force_module ("SelfhealingAgents.self_healing_system.schemas.internal_state.prompt_payload" , payload_mod )
218+ _force_module (
219+ "SelfhealingAgents.self_healing_system.schemas.internal_state.prompt_payload" ,
220+ payload_mod ,
221+ )
222+
223+ base_dom = types .ModuleType (
224+ "SelfhealingAgents.self_healing_system.context_retrieving.library_dom_utils.base_dom_utils"
225+ )
177226
178- base_dom = types .ModuleType ("SelfhealingAgents.self_healing_system.context_retrieving.library_dom_utils.base_dom_utils" )
179227 class BaseDomUtils : ...
228+
180229 base_dom .BaseDomUtils = BaseDomUtils
181- _force_module ("SelfhealingAgents.self_healing_system.context_retrieving.library_dom_utils.base_dom_utils" , base_dom )
230+ _force_module (
231+ "SelfhealingAgents.self_healing_system.context_retrieving.library_dom_utils.base_dom_utils" ,
232+ base_dom ,
233+ )
182234
183235 return logger
184236
@@ -224,18 +276,23 @@ def __init__(
224276 self ._raise_valid = raise_valid
225277 self ._raise_unique = raise_unique
226278 self ._raise_clickable = raise_clickable
279+
227280 def get_locator_proposals (self , failed : str , keyword : str ) -> List [str ]:
228281 return list (self ._proposals )
282+
229283 def get_locator_metadata (self , locator : str ) -> List [dict ]:
230284 return [self ._metadata_map .get (locator , {"id" : locator })]
285+
231286 def is_locator_valid (self , locator : str ) -> bool :
232287 if self ._raise_valid :
233288 raise RuntimeError ("valid err" )
234289 return self ._valid
290+
235291 def is_locator_unique (self , locator : str ) -> bool :
236292 if self ._raise_unique :
237293 raise RuntimeError ("unique err" )
238294 return bool (self ._unique_map .get (locator , False ))
295+
239296 def is_element_clickable (self , locator : str ) -> bool :
240297 if self ._raise_clickable :
241298 raise RuntimeError ("click err" )
@@ -264,15 +321,18 @@ def make(BaseLocatorAgent: Any, dom: _DomStub, *, use_llm: bool) -> Any:
264321 class Impl (BaseLocatorAgent ):
265322 def _process_locator (self , locator : str ) -> str :
266323 return f"proc:{ locator } "
324+
267325 @staticmethod
268326 def is_failed_locator_error (message : str ) -> bool :
269327 return "failed" in message .lower ()
328+
270329 class Cfg :
271330 request_limit : int = 9
272331 total_tokens_limit : int = 999
273332 use_llm_for_locator_generation : bool = use_llm
274333 locator_agent_provider : str = "prov"
275334 locator_agent_model : str = "mod"
335+
276336 return Impl (Cfg (), dom )
277337
278338
@@ -288,7 +348,9 @@ def test_init_configures_agents_by_mode(mod_and_cls: Tuple[Any, Any, Any, Any])
288348 assert inst_dom .selection_agent is not None
289349
290350
291- def test_output_validator_processes_and_filters (mod_and_cls : Tuple [Any , Any , Any , Any ]) -> None :
351+ def test_output_validator_processes_and_filters (
352+ mod_and_cls : Tuple [Any , Any , Any , Any ],
353+ ) -> None :
292354 BaseLocatorAgent , LocatorHealingResponse , PromptPayload , _ = mod_and_cls
293355 dom = _DomStub (
294356 valid = True ,
@@ -302,14 +364,23 @@ def test_output_validator_processes_and_filters(mod_and_cls: Tuple[Any, Any, Any
302364 assert isinstance (res , LocatorHealingResponse )
303365 assert res .suggestions == ["proc:a" ]
304366 with pytest .raises (_FakeModelRetry ):
305- _run (val (_ctx (_payload (PromptPayload , keyword = "Click" )), LocatorHealingResponse ([])))
367+ _run (
368+ val (
369+ _ctx (_payload (PromptPayload , keyword = "Click" )),
370+ LocatorHealingResponse ([]),
371+ )
372+ )
306373
307374
308- def test_heal_with_llm_success_and_type_check (mod_and_cls : Tuple [Any , Any , Any , Any ]) -> None :
375+ def test_heal_with_llm_success_and_type_check (
376+ mod_and_cls : Tuple [Any , Any , Any , Any ],
377+ ) -> None :
309378 BaseLocatorAgent , LocatorHealingResponse , PromptPayload , _ = mod_and_cls
310379 dom = _DomStub (valid = True )
311380 inst = _ConcreteAgentFactory .make (BaseLocatorAgent , dom , use_llm = True )
312- inst .generation_agent .run_result = _FakeAgentRunResult (LocatorHealingResponse (["x" ]))
381+ inst .generation_agent .run_result = _FakeAgentRunResult (
382+ LocatorHealingResponse (["x" ])
383+ )
313384 out = _run (inst ._heal_with_llm (_ctx (_payload (PromptPayload ))))
314385 assert isinstance (out , LocatorHealingResponse )
315386 assert out .suggestions == ["x" ]
@@ -318,7 +389,9 @@ def test_heal_with_llm_success_and_type_check(mod_and_cls: Tuple[Any, Any, Any,
318389 _run (inst ._heal_with_llm (_ctx (_payload (PromptPayload ))))
319390
320391
321- def test_heal_with_dom_utils_json_and_raw_selection (mod_and_cls : Tuple [Any , Any , Any , Any ]) -> None :
392+ def test_heal_with_dom_utils_json_and_raw_selection (
393+ mod_and_cls : Tuple [Any , Any , Any , Any ],
394+ ) -> None :
322395 BaseLocatorAgent , LocatorHealingResponse , PromptPayload , _ = mod_and_cls
323396 dom = _DomStub (
324397 proposals = ["l2" , "l1" ],
@@ -328,25 +401,35 @@ def test_heal_with_dom_utils_json_and_raw_selection(mod_and_cls: Tuple[Any, Any,
328401 )
329402 inst = _ConcreteAgentFactory .make (BaseLocatorAgent , dom , use_llm = False )
330403 inst .selection_agent .run_result = _FakeAgentRunResult ('{"suggestions":"#best"}' )
331- out = _run (inst ._heal_with_dom_utils (_ctx (_payload (PromptPayload , keyword = "Click" ))))
404+ out = _run (
405+ inst ._heal_with_dom_utils (_ctx (_payload (PromptPayload , keyword = "Click" )))
406+ )
332407 assert isinstance (out , LocatorHealingResponse )
333408 assert out .suggestions == ["#best" ]
334409 inst .selection_agent .run_result = _FakeAgentRunResult ("#alt" )
335- out2 = _run (inst ._heal_with_dom_utils (_ctx (_payload (PromptPayload , keyword = "Click" ))))
410+ out2 = _run (
411+ inst ._heal_with_dom_utils (_ctx (_payload (PromptPayload , keyword = "Click" )))
412+ )
336413 assert out2 .suggestions == ["#alt" ]
337414
338415
339- def test_heal_with_dom_utils_empty_proposals_raises (mod_and_cls : Tuple [Any , Any , Any , Any ]) -> None :
416+ def test_heal_with_dom_utils_empty_proposals_raises (
417+ mod_and_cls : Tuple [Any , Any , Any , Any ],
418+ ) -> None :
340419 BaseLocatorAgent , _ , PromptPayload , _ = mod_and_cls
341420 dom = _DomStub (proposals = [], valid = True )
342421 inst = _ConcreteAgentFactory .make (BaseLocatorAgent , dom , use_llm = False )
343422 with pytest .raises (_FakeModelRetry ):
344423 _run (inst ._heal_with_dom_utils (_ctx (_payload (PromptPayload ))))
345424
346425
347- def test_is_locator_valid_unique_clickable_with_exceptions (mod_and_cls : Tuple [Any , Any , Any , Any ]) -> None :
426+ def test_is_locator_valid_unique_clickable_with_exceptions (
427+ mod_and_cls : Tuple [Any , Any , Any , Any ],
428+ ) -> None :
348429 BaseLocatorAgent , _ , __ , ___ = mod_and_cls
349- dom = _DomStub (valid = True , unique_map = {"proc:x" : True }, clickable_map = {"proc:x" : True })
430+ dom = _DomStub (
431+ valid = True , unique_map = {"proc:x" : True }, clickable_map = {"proc:x" : True }
432+ )
350433 inst = _ConcreteAgentFactory .make (BaseLocatorAgent , dom , use_llm = False )
351434 assert inst ._is_locator_valid ("proc:x" ) is True
352435 assert inst ._is_locator_unique ("proc:x" ) is True
0 commit comments