Spaces:
Sleeping
Sleeping
charSLee013
feat: complete Hugging Face Spaces deployment with production-ready CognitiveKernel-Launchpad
1ea26af
| import sys | |
| import os | |
| import types | |
| import threading | |
| # Ensure repo root on path | |
| sys.path.insert(0, os.path.abspath('.')) | |
| # Stub playwright modules to avoid dependency during import | |
| sync_api = types.ModuleType('playwright.sync_api') | |
| async_api = types.ModuleType('playwright.async_api') | |
| # Minimal symbols referenced by imports | |
| def _dummy(): | |
| raise RuntimeError('should not be called in unit test') | |
| sync_api.sync_playwright = lambda: types.SimpleNamespace(start=_dummy) | |
| class _Dummy: ... | |
| sync_api.Browser = _Dummy | |
| sync_api.BrowserContext = _Dummy | |
| sync_api.Page = _Dummy | |
| async_api.async_playwright = _dummy | |
| async_api.Browser = _Dummy | |
| async_api.BrowserContext = _Dummy | |
| async_api.Page = _Dummy | |
| sys.modules['playwright.sync_api'] = sync_api | |
| sys.modules['playwright.async_api'] = async_api | |
| # Stub LLM to avoid heavy deps | |
| stub_model_mod = types.ModuleType('ck_pro.agents.model') | |
| class _StubLLM: | |
| def __init__(self, *_args, **_kwargs): | |
| pass | |
| def __call__(self, messages): | |
| return "ok" | |
| stub_model_mod.LLM = _StubLLM | |
| sys.modules['ck_pro.agents.model'] = stub_model_mod | |
| # Import module under test after stubbing | |
| import importlib | |
| # Ensure previous test's stub of ck_pro.ck_web.agent is cleared | |
| sys.modules.pop('ck_pro.ck_web.agent', None) | |
| # Stub tools to avoid heavy deps | |
| stub_tools_mod = types.ModuleType('ck_pro.agents.tool') | |
| class _StubTool: | |
| name = 'tool' | |
| class _StubSimpleSearchTool(_StubTool): | |
| name = 'simple_web_search' | |
| def __init__(self, *args, **kwargs): | |
| pass | |
| def set_llm(self, *args, **kwargs): | |
| pass | |
| def __call__(self, *args, **kwargs): | |
| return 'search:stub' | |
| stub_tools_mod.SimpleSearchTool = _StubSimpleSearchTool | |
| sys.modules['ck_pro.agents.tool'] = stub_tools_mod | |
| plutils = importlib.import_module('ck_pro.ck_web.playwright_utils') | |
| # Stub PlaywrightWebEnv to capture thread affinity and lifecycle | |
| class _StubEnv: | |
| instances = [] | |
| def __init__(self, **kwargs): | |
| self.created_thread = threading.current_thread().name | |
| self.calls = [] | |
| self.stopped = False | |
| class _Pool: | |
| def __init__(self, outer): | |
| self.outer = outer | |
| self.stopped = False | |
| def stop(self): | |
| self.stopped = True | |
| self.browser_pool = _Pool(self) | |
| _StubEnv.instances.append(self) | |
| def get_state(self, export_to_dict=True, return_copy=True): | |
| self.calls.append(('get_state', threading.current_thread().name)) | |
| return { | |
| 'current_accessibility_tree': 'ok', | |
| 'downloaded_file_path': [], | |
| 'error_message': '', | |
| 'current_has_cookie_popup': False, | |
| 'html_md': '' | |
| } | |
| def step_state(self, action_string: str) -> str: | |
| self.calls.append(('step_state', threading.current_thread().name, action_string)) | |
| return 'ok' | |
| def sync_files(self): | |
| self.calls.append(('sync_files', threading.current_thread().name)) | |
| return True | |
| def stop(self): | |
| self.calls.append(('stop', threading.current_thread().name)) | |
| self.stopped = True | |
| plutils.PlaywrightWebEnv = _StubEnv | |
| from ck_pro.ck_web.agent import WebAgent | |
| def test_threaded_webenv_runs_all_calls_on_same_dedicated_thread_and_cleans_up(): | |
| agent = WebAgent() | |
| # Force builtin path by making web_ip check fail (default will fail) | |
| session = type('S', (), {'id': 'sess1', 'info': {}})() | |
| agent.init_run(session) | |
| env = agent.web_envs[session.id] | |
| # Calls should execute on the dedicated thread, not MainThread | |
| state = env.get_state() | |
| assert state['current_accessibility_tree'] == 'ok' | |
| step_res = env.step_state('click [1]') | |
| assert step_res == 'ok' | |
| env.sync_files() | |
| # Verify underlying stub saw consistent thread usage | |
| stub = _StubEnv.instances[-1] | |
| created = stub.created_thread | |
| call_threads = [t for (_name, t, *_) in stub.calls if _name in ('get_state', 'step_state', 'sync_files')] | |
| assert created != 'MainThread' | |
| assert all(t == created for t in call_threads) | |
| # Ensure cleanup releases resources | |
| agent.end_run(session) | |
| assert stub.stopped is True | |
| assert stub.browser_pool.stopped is True | |