refactor(core): remove dead parallel agent architecture
This commit is contained in:
1 parent
cbe8f4f0c6
commit
f68d4288e0
29 files changed
+8
-2736
No files matched your search
@@ -15,7 +15,6 @@
|
||||
"""Global pytest fixtures for ARTEMIS test suite."""
|
||||
|
||||
import pytest
|
||||
from artemis.core.context import ExecutionContext
|
||||
from artemis.drivers.mock.mock_driver import MockDeviceDriver
|
||||
|
||||
|
||||
@@ -23,9 +22,3 @@ from artemis.drivers.mock.mock_driver import MockDeviceDriver
|
||||
def mock_driver():
|
||||
"""Provides an isolated MockDeviceDriver instance."""
|
||||
return MockDeviceDriver(device_id="fixture-mock-device", width=1080, height=2400)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def test_context():
|
||||
"""Provides a fresh ExecutionContext."""
|
||||
return ExecutionContext(task_goal="Test sample task objective", device_id="fixture-mock-device")
|
||||
@@ -1,45 +0,0 @@
|
||||
# Copyright 2026 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""Integration tests for ReactiveRunner and Pipeline."""
|
||||
|
||||
import pytest
|
||||
from artemis.core.context import ExecutionContext
|
||||
from artemis.core.state import ExecutionStatus
|
||||
from artemis.drivers.mock.mock_driver import MockDeviceDriver
|
||||
from artemis.engine.pipeline import Pipeline
|
||||
from artemis.engine.reactive_runner import ReactiveRunner
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_reactive_runner_execution():
|
||||
"""Verify ReactiveRunner executes workflow cycles on mock driver."""
|
||||
ctx = ExecutionContext(task_goal="Turn on Bluetooth in Settings", device_id="test-emulator")
|
||||
driver = MockDeviceDriver(device_id="test-emulator")
|
||||
|
||||
runner = ReactiveRunner(ctx=ctx, driver=driver)
|
||||
state = await runner.run(max_turns=5)
|
||||
|
||||
assert state.status == ExecutionStatus.SUCCESS
|
||||
assert state.current_turn >= 1
|
||||
assert len(state.steps) >= 1
|
||||
assert state.steps[0].action_name == "wait_for_delay"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_pipeline_dispatch():
|
||||
"""Verify Pipeline executes with default mock driver."""
|
||||
state = await Pipeline.execute(goal="Open YouTube and play video", profile="flash")
|
||||
assert state.status == ExecutionStatus.SUCCESS
|
||||
assert state.task_goal == "Open YouTube and play video"
|
||||
@@ -1,146 +0,0 @@
|
||||
# Copyright 2026 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""Unit tests for Base Agent Protocol & Lifecycle Registry."""
|
||||
|
||||
import pytest
|
||||
from typing import Any
|
||||
from artemis.agents.base import AgentConfig, AgentRegistry, BaseAgent
|
||||
from artemis.agents.validator.failure_analyzer_agent import FailureAnalyzerAgent
|
||||
from artemis.agents.validator.recovery_strategies import (
|
||||
RecoveryStrategyRegistry,
|
||||
)
|
||||
from artemis.drivers.mock.mock_driver import MockDeviceDriver
|
||||
|
||||
|
||||
class DummyTestAgent(BaseAgent):
|
||||
"""Simple test agent implementing BaseAgent protocol."""
|
||||
|
||||
def __init__(self, config: AgentConfig | None = None, ctx: Any | None = None, **kwargs: Any):
|
||||
super().__init__(
|
||||
name="dummy_test_agent",
|
||||
role="Test Worker",
|
||||
description="Agent used for testing lifecycle transitions.",
|
||||
config=config,
|
||||
ctx=ctx,
|
||||
)
|
||||
|
||||
async def process(self, state: Any) -> dict[str, Any]:
|
||||
return {"status": "success", "processed_state": True}
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_base_agent_lifecycle():
|
||||
"""Verify BaseAgent initialization, execution, and cleanup lifecycle."""
|
||||
agent = DummyTestAgent()
|
||||
assert agent.name == "dummy_test_agent"
|
||||
assert not agent._is_initialized
|
||||
|
||||
# 1. Initialize
|
||||
mock_ctx = object()
|
||||
await agent.initialize(mock_ctx)
|
||||
assert agent._is_initialized
|
||||
assert agent.ctx is mock_ctx
|
||||
|
||||
# 2. Process
|
||||
result = await agent.process({})
|
||||
assert result.get("status") == "success"
|
||||
assert result.get("processed_state") is True
|
||||
|
||||
# 3. Cleanup
|
||||
await agent.cleanup()
|
||||
assert not agent._is_initialized
|
||||
|
||||
|
||||
def test_agent_registry():
|
||||
"""Verify AgentRegistry registers and instantiates agents correctly."""
|
||||
registered = AgentRegistry.list_agents()
|
||||
assert "planner" in registered
|
||||
assert "summarizer" in registered
|
||||
assert "operator" in registered
|
||||
assert "failure_analyzer" in registered
|
||||
|
||||
# Instantiate PlannerAgent via registry
|
||||
planner = AgentRegistry.create_agent("planner", ctx=None)
|
||||
assert planner.name == "planner"
|
||||
assert planner.role == "Strategic Task Planner"
|
||||
|
||||
# Instantiate SummarizerAgent via registry
|
||||
summarizer = AgentRegistry.create_agent("summarizer", ctx=None)
|
||||
assert summarizer.name == "summarizer"
|
||||
assert summarizer.role == "Execution History Summarizer"
|
||||
|
||||
# Instantiate OperatorAgent via registry
|
||||
operator = AgentRegistry.create_agent("operator", ctx=None)
|
||||
assert operator.name == "operator"
|
||||
assert operator.role == "Mobile UI Interaction Operator"
|
||||
|
||||
# Instantiate FailureAnalyzerAgent via registry
|
||||
fa = AgentRegistry.create_agent("failure_analyzer", ctx=None)
|
||||
assert fa.name == "failure_analyzer"
|
||||
assert fa.role == "Autonomous Self-Healing Failure Analyzer"
|
||||
|
||||
|
||||
def test_agent_config():
|
||||
"""Verify AgentConfig parameters can be customized."""
|
||||
config = AgentConfig(
|
||||
model_name="gemini-2.5-pro",
|
||||
temperature=0.7,
|
||||
max_tokens=2048,
|
||||
enabled_tools=["click", "swipe"],
|
||||
)
|
||||
agent = DummyTestAgent(config=config)
|
||||
assert agent.config.model_name == "gemini-2.5-pro"
|
||||
assert agent.config.temperature == 0.7
|
||||
assert agent.config.enabled_tools == ["click", "swipe"]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_recovery_strategies_and_failure_analyzer():
|
||||
"""Verify self-healing recovery strategies and FailureAnalyzerAgent execution."""
|
||||
driver = MockDeviceDriver(device_id="mock-rec-1")
|
||||
|
||||
# 1. Test dialog dismissal strategy
|
||||
dialog_ctx = {"category": "ui_obstruction", "error_message": "Unexpected permission popup"}
|
||||
dialog_res = await RecoveryStrategyRegistry.attempt_recovery(driver, dialog_ctx)
|
||||
assert dialog_res is not None
|
||||
assert dialog_res.success
|
||||
assert dialog_res.action_type.value == "dismiss_dialog"
|
||||
assert driver.action_history[-1].get("key") == "back"
|
||||
|
||||
# 2. Test navigate back strategy
|
||||
nav_ctx = {"category": "wrong_screen"}
|
||||
nav_res = await RecoveryStrategyRegistry.attempt_recovery(driver, nav_ctx)
|
||||
assert nav_res is not None
|
||||
assert nav_res.success
|
||||
assert nav_res.action_type.value == "navigate_back"
|
||||
|
||||
# 3. Test scroll and search strategy
|
||||
scroll_ctx = {"category": "element_not_found", "scroll_direction": "down"}
|
||||
scroll_res = await RecoveryStrategyRegistry.attempt_recovery(driver, scroll_ctx)
|
||||
assert scroll_res is not None
|
||||
assert scroll_res.success
|
||||
assert scroll_res.action_type.value == "scroll_and_retry"
|
||||
|
||||
# 4. Test FailureAnalyzerAgent process
|
||||
class MockContext:
|
||||
pass
|
||||
|
||||
ctx = MockContext()
|
||||
setattr(ctx, "_active_driver", driver)
|
||||
|
||||
fa_agent = FailureAnalyzerAgent(ctx=ctx)
|
||||
fa_result = await fa_agent.process({"failure_context": {"category": "wrong_screen"}})
|
||||
assert fa_result["status"] == "recovered"
|
||||
assert fa_result["recovery_action"] == "navigate_back"
|
||||
@@ -12,9 +12,8 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""Unit tests for OperatorPromptBuilder and LoopDetector."""
|
||||
"""Unit tests for OperatorPromptBuilder."""
|
||||
|
||||
from artemis.agents.operator.loop_detector import LoopDetector
|
||||
from artemis.agents.operator.prompt_builder import OperatorPromptBuilder
|
||||
|
||||
|
||||
@@ -33,24 +32,3 @@ def test_operator_prompt_builder():
|
||||
assert "Open Settings" in human_msg
|
||||
assert "Tap Network & internet" in human_msg
|
||||
assert "Current Turn: 2" in human_msg
|
||||
|
||||
|
||||
def test_loop_detector():
|
||||
"""Verify loop detector flags repeated action sequences."""
|
||||
detector = LoopDetector(repetition_threshold=3)
|
||||
assert not detector.is_loop_detected()
|
||||
|
||||
# Record distinct actions
|
||||
detector.record_action("click", {"target": [100, 200]})
|
||||
detector.record_action("swipe", {"action": "up"})
|
||||
assert not detector.is_loop_detected()
|
||||
|
||||
# Record 3 identical actions
|
||||
detector.record_action("click", {"target": [500, 500]})
|
||||
detector.record_action("click", {"target": [500, 500]})
|
||||
detector.record_action("click", {"target": [500, 500]})
|
||||
assert detector.is_loop_detected()
|
||||
|
||||
# Reset
|
||||
detector.reset()
|
||||
assert not detector.is_loop_detected()
|
||||
Reference in new issue
Block a user