hud-python 0.3.5__py3-none-any.whl → 0.4.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of hud-python might be problematic. Click here for more details.
- hud/__init__.py +22 -89
- hud/agents/__init__.py +15 -0
- hud/agents/art.py +101 -0
- hud/agents/base.py +599 -0
- hud/{mcp → agents}/claude.py +373 -321
- hud/{mcp → agents}/langchain.py +250 -250
- hud/agents/misc/__init__.py +7 -0
- hud/{agent → agents}/misc/response_agent.py +80 -80
- hud/{mcp → agents}/openai.py +352 -334
- hud/agents/openai_chat_generic.py +154 -0
- hud/{mcp → agents}/tests/__init__.py +1 -1
- hud/agents/tests/test_base.py +742 -0
- hud/agents/tests/test_claude.py +324 -0
- hud/{mcp → agents}/tests/test_client.py +363 -324
- hud/{mcp → agents}/tests/test_openai.py +237 -238
- hud/cli/__init__.py +617 -0
- hud/cli/__main__.py +8 -0
- hud/cli/analyze.py +371 -0
- hud/cli/analyze_metadata.py +230 -0
- hud/cli/build.py +427 -0
- hud/cli/clone.py +185 -0
- hud/cli/cursor.py +92 -0
- hud/cli/debug.py +392 -0
- hud/cli/docker_utils.py +83 -0
- hud/cli/init.py +281 -0
- hud/cli/interactive.py +353 -0
- hud/cli/mcp_server.py +756 -0
- hud/cli/pull.py +336 -0
- hud/cli/push.py +370 -0
- hud/cli/remote_runner.py +311 -0
- hud/cli/runner.py +160 -0
- hud/cli/tests/__init__.py +3 -0
- hud/cli/tests/test_analyze.py +284 -0
- hud/cli/tests/test_cli_init.py +265 -0
- hud/cli/tests/test_cli_main.py +27 -0
- hud/cli/tests/test_clone.py +142 -0
- hud/cli/tests/test_cursor.py +253 -0
- hud/cli/tests/test_debug.py +453 -0
- hud/cli/tests/test_mcp_server.py +139 -0
- hud/cli/tests/test_utils.py +388 -0
- hud/cli/utils.py +263 -0
- hud/clients/README.md +143 -0
- hud/clients/__init__.py +16 -0
- hud/clients/base.py +379 -0
- hud/clients/fastmcp.py +222 -0
- hud/clients/mcp_use.py +278 -0
- hud/clients/tests/__init__.py +1 -0
- hud/clients/tests/test_client_integration.py +111 -0
- hud/clients/tests/test_fastmcp.py +342 -0
- hud/clients/tests/test_protocol.py +188 -0
- hud/clients/utils/__init__.py +1 -0
- hud/clients/utils/retry_transport.py +160 -0
- hud/datasets.py +322 -192
- hud/misc/__init__.py +1 -0
- hud/{agent → misc}/claude_plays_pokemon.py +292 -283
- hud/otel/__init__.py +35 -0
- hud/otel/collector.py +142 -0
- hud/otel/config.py +164 -0
- hud/otel/context.py +536 -0
- hud/otel/exporters.py +366 -0
- hud/otel/instrumentation.py +97 -0
- hud/otel/processors.py +118 -0
- hud/otel/tests/__init__.py +1 -0
- hud/otel/tests/test_processors.py +197 -0
- hud/server/__init__.py +5 -5
- hud/server/context.py +114 -0
- hud/server/helper/__init__.py +5 -0
- hud/server/low_level.py +132 -0
- hud/server/server.py +166 -0
- hud/server/tests/__init__.py +3 -0
- hud/settings.py +73 -79
- hud/shared/__init__.py +5 -0
- hud/{exceptions.py → shared/exceptions.py} +180 -180
- hud/{server → shared}/requests.py +264 -264
- hud/shared/tests/test_exceptions.py +157 -0
- hud/{server → shared}/tests/test_requests.py +275 -275
- hud/telemetry/__init__.py +25 -30
- hud/telemetry/instrument.py +379 -0
- hud/telemetry/job.py +309 -141
- hud/telemetry/replay.py +74 -0
- hud/telemetry/trace.py +83 -0
- hud/tools/__init__.py +33 -34
- hud/tools/base.py +365 -65
- hud/tools/bash.py +161 -137
- hud/tools/computer/__init__.py +15 -13
- hud/tools/computer/anthropic.py +437 -420
- hud/tools/computer/hud.py +376 -334
- hud/tools/computer/openai.py +295 -292
- hud/tools/computer/settings.py +82 -0
- hud/tools/edit.py +314 -290
- hud/tools/executors/__init__.py +30 -30
- hud/tools/executors/base.py +539 -532
- hud/tools/executors/pyautogui.py +621 -619
- hud/tools/executors/tests/__init__.py +1 -1
- hud/tools/executors/tests/test_base_executor.py +338 -338
- hud/tools/executors/tests/test_pyautogui_executor.py +165 -165
- hud/tools/executors/xdo.py +511 -503
- hud/tools/{playwright_tool.py → playwright.py} +412 -379
- hud/tools/tests/__init__.py +3 -3
- hud/tools/tests/test_base.py +282 -0
- hud/tools/tests/test_bash.py +158 -152
- hud/tools/tests/test_bash_extended.py +197 -0
- hud/tools/tests/test_computer.py +425 -52
- hud/tools/tests/test_computer_actions.py +34 -34
- hud/tools/tests/test_edit.py +259 -240
- hud/tools/tests/test_init.py +27 -27
- hud/tools/tests/test_playwright_tool.py +183 -183
- hud/tools/tests/test_tools.py +145 -157
- hud/tools/tests/test_utils.py +156 -156
- hud/tools/types.py +72 -0
- hud/tools/utils.py +50 -50
- hud/types.py +136 -89
- hud/utils/__init__.py +10 -16
- hud/utils/async_utils.py +65 -0
- hud/utils/design.py +168 -0
- hud/utils/mcp.py +55 -0
- hud/utils/progress.py +149 -149
- hud/utils/telemetry.py +66 -66
- hud/utils/tests/test_async_utils.py +173 -0
- hud/utils/tests/test_init.py +17 -21
- hud/utils/tests/test_progress.py +261 -225
- hud/utils/tests/test_telemetry.py +82 -37
- hud/utils/tests/test_version.py +8 -8
- hud/version.py +7 -7
- hud_python-0.4.1.dist-info/METADATA +476 -0
- hud_python-0.4.1.dist-info/RECORD +132 -0
- hud_python-0.4.1.dist-info/entry_points.txt +3 -0
- {hud_python-0.3.5.dist-info → hud_python-0.4.1.dist-info}/licenses/LICENSE +21 -21
- hud/adapters/__init__.py +0 -8
- hud/adapters/claude/__init__.py +0 -5
- hud/adapters/claude/adapter.py +0 -180
- hud/adapters/claude/tests/__init__.py +0 -1
- hud/adapters/claude/tests/test_adapter.py +0 -519
- hud/adapters/common/__init__.py +0 -6
- hud/adapters/common/adapter.py +0 -178
- hud/adapters/common/tests/test_adapter.py +0 -289
- hud/adapters/common/types.py +0 -446
- hud/adapters/operator/__init__.py +0 -5
- hud/adapters/operator/adapter.py +0 -108
- hud/adapters/operator/tests/__init__.py +0 -1
- hud/adapters/operator/tests/test_adapter.py +0 -370
- hud/agent/__init__.py +0 -19
- hud/agent/base.py +0 -126
- hud/agent/claude.py +0 -271
- hud/agent/langchain.py +0 -215
- hud/agent/misc/__init__.py +0 -3
- hud/agent/operator.py +0 -268
- hud/agent/tests/__init__.py +0 -1
- hud/agent/tests/test_base.py +0 -202
- hud/env/__init__.py +0 -11
- hud/env/client.py +0 -35
- hud/env/docker_client.py +0 -349
- hud/env/environment.py +0 -446
- hud/env/local_docker_client.py +0 -358
- hud/env/remote_client.py +0 -212
- hud/env/remote_docker_client.py +0 -292
- hud/gym.py +0 -130
- hud/job.py +0 -773
- hud/mcp/__init__.py +0 -17
- hud/mcp/base.py +0 -631
- hud/mcp/client.py +0 -312
- hud/mcp/tests/test_base.py +0 -512
- hud/mcp/tests/test_claude.py +0 -294
- hud/task.py +0 -149
- hud/taskset.py +0 -237
- hud/telemetry/_trace.py +0 -347
- hud/telemetry/context.py +0 -230
- hud/telemetry/exporter.py +0 -575
- hud/telemetry/instrumentation/__init__.py +0 -3
- hud/telemetry/instrumentation/mcp.py +0 -259
- hud/telemetry/instrumentation/registry.py +0 -59
- hud/telemetry/mcp_models.py +0 -270
- hud/telemetry/tests/__init__.py +0 -1
- hud/telemetry/tests/test_context.py +0 -210
- hud/telemetry/tests/test_trace.py +0 -312
- hud/tools/helper/README.md +0 -56
- hud/tools/helper/__init__.py +0 -9
- hud/tools/helper/mcp_server.py +0 -78
- hud/tools/helper/server_initialization.py +0 -115
- hud/tools/helper/utils.py +0 -58
- hud/trajectory.py +0 -94
- hud/utils/agent.py +0 -37
- hud/utils/common.py +0 -256
- hud/utils/config.py +0 -120
- hud/utils/deprecation.py +0 -115
- hud/utils/misc.py +0 -53
- hud/utils/tests/test_common.py +0 -277
- hud/utils/tests/test_config.py +0 -129
- hud_python-0.3.5.dist-info/METADATA +0 -284
- hud_python-0.3.5.dist-info/RECORD +0 -120
- /hud/{adapters/common → shared}/tests/__init__.py +0 -0
- {hud_python-0.3.5.dist-info → hud_python-0.4.1.dist-info}/WHEEL +0 -0
|
@@ -1,519 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
import pytest
|
|
4
|
-
|
|
5
|
-
from hud.adapters.claude import ClaudeAdapter
|
|
6
|
-
from hud.adapters.common.types import (
|
|
7
|
-
ClickAction,
|
|
8
|
-
DragAction,
|
|
9
|
-
MoveAction,
|
|
10
|
-
PositionFetch,
|
|
11
|
-
PressAction,
|
|
12
|
-
ResponseAction,
|
|
13
|
-
ScreenshotFetch,
|
|
14
|
-
ScrollAction,
|
|
15
|
-
TypeAction,
|
|
16
|
-
WaitAction,
|
|
17
|
-
)
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
class TestClaudeAdapter:
|
|
21
|
-
"""Test the ClaudeAdapter class."""
|
|
22
|
-
|
|
23
|
-
@pytest.fixture
|
|
24
|
-
def adapter(self):
|
|
25
|
-
"""Fixture providing a clean adapter instance."""
|
|
26
|
-
return ClaudeAdapter()
|
|
27
|
-
|
|
28
|
-
def test_init(self, adapter):
|
|
29
|
-
"""Test adapter initialization."""
|
|
30
|
-
assert adapter.agent_width == 1024
|
|
31
|
-
assert adapter.agent_height == 768
|
|
32
|
-
assert adapter.env_width == 1920 # Inherited from parent
|
|
33
|
-
assert adapter.env_height == 1080 # Inherited from parent
|
|
34
|
-
|
|
35
|
-
def test_key_map_constants(self, adapter):
|
|
36
|
-
"""Test KEY_MAP constants."""
|
|
37
|
-
assert adapter.KEY_MAP["return"] == "enter"
|
|
38
|
-
assert adapter.KEY_MAP["super"] == "win"
|
|
39
|
-
assert adapter.KEY_MAP["super_l"] == "win"
|
|
40
|
-
assert adapter.KEY_MAP["super_r"] == "win"
|
|
41
|
-
assert adapter.KEY_MAP["right shift"] == "shift"
|
|
42
|
-
assert adapter.KEY_MAP["left shift"] == "shift"
|
|
43
|
-
|
|
44
|
-
def test_map_key_mapped(self, adapter):
|
|
45
|
-
"""Test _map_key with mapped keys."""
|
|
46
|
-
assert adapter._map_key("return") == "enter"
|
|
47
|
-
assert adapter._map_key("RETURN") == "enter" # Test case insensitive
|
|
48
|
-
assert adapter._map_key("super") == "win"
|
|
49
|
-
assert adapter._map_key("Super_L") == "win"
|
|
50
|
-
|
|
51
|
-
def test_map_key_unmapped(self, adapter):
|
|
52
|
-
"""Test _map_key with unmapped keys."""
|
|
53
|
-
assert adapter._map_key("space") == "space"
|
|
54
|
-
assert adapter._map_key("CTRL") == "ctrl"
|
|
55
|
-
assert adapter._map_key("Unknown") == "unknown"
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
class TestClaudeAdapterConvert:
|
|
59
|
-
"""Test the convert method of ClaudeAdapter."""
|
|
60
|
-
|
|
61
|
-
@pytest.fixture
|
|
62
|
-
def adapter(self):
|
|
63
|
-
"""Fixture providing a clean adapter instance."""
|
|
64
|
-
return ClaudeAdapter()
|
|
65
|
-
|
|
66
|
-
def test_convert_key_single(self, adapter):
|
|
67
|
-
"""Test converting single key action."""
|
|
68
|
-
data = {"action": "key", "text": "space"}
|
|
69
|
-
result = adapter.convert(data)
|
|
70
|
-
|
|
71
|
-
assert isinstance(result, PressAction)
|
|
72
|
-
assert result.keys == ["space"]
|
|
73
|
-
|
|
74
|
-
def test_convert_key_mapped(self, adapter):
|
|
75
|
-
"""Test converting mapped key action."""
|
|
76
|
-
data = {"action": "key", "text": "return"}
|
|
77
|
-
result = adapter.convert(data)
|
|
78
|
-
|
|
79
|
-
assert isinstance(result, PressAction)
|
|
80
|
-
assert result.keys == ["enter"]
|
|
81
|
-
|
|
82
|
-
def test_convert_key_combination(self, adapter):
|
|
83
|
-
"""Test converting key combination action."""
|
|
84
|
-
data = {"action": "key", "text": "ctrl+c"}
|
|
85
|
-
result = adapter.convert(data)
|
|
86
|
-
|
|
87
|
-
assert isinstance(result, PressAction)
|
|
88
|
-
assert result.keys == ["ctrl", "c"]
|
|
89
|
-
|
|
90
|
-
def test_convert_key_combination_mapped(self, adapter):
|
|
91
|
-
"""Test converting key combination with mapped keys."""
|
|
92
|
-
data = {"action": "key", "text": "super+return"}
|
|
93
|
-
result = adapter.convert(data)
|
|
94
|
-
|
|
95
|
-
assert isinstance(result, PressAction)
|
|
96
|
-
assert result.keys == ["win", "enter"]
|
|
97
|
-
|
|
98
|
-
def test_convert_key_missing_text(self, adapter):
|
|
99
|
-
"""Test converting key action with missing text."""
|
|
100
|
-
data = {"action": "key"}
|
|
101
|
-
|
|
102
|
-
with pytest.raises(ValueError) as exc_info:
|
|
103
|
-
adapter.convert(data)
|
|
104
|
-
|
|
105
|
-
assert "Invalid action" in str(exc_info.value)
|
|
106
|
-
|
|
107
|
-
def test_convert_type_action(self, adapter):
|
|
108
|
-
"""Test converting type action."""
|
|
109
|
-
data = {"action": "type", "text": "Hello, World!"}
|
|
110
|
-
result = adapter.convert(data)
|
|
111
|
-
|
|
112
|
-
assert isinstance(result, TypeAction)
|
|
113
|
-
assert result.text == "Hello, World!"
|
|
114
|
-
assert result.enter_after is False
|
|
115
|
-
|
|
116
|
-
def test_convert_type_missing_text(self, adapter):
|
|
117
|
-
"""Test converting type action with missing text."""
|
|
118
|
-
data = {"action": "type"}
|
|
119
|
-
|
|
120
|
-
with pytest.raises(ValueError) as exc_info:
|
|
121
|
-
adapter.convert(data)
|
|
122
|
-
|
|
123
|
-
assert "Invalid action" in str(exc_info.value)
|
|
124
|
-
|
|
125
|
-
def test_convert_mouse_move(self, adapter):
|
|
126
|
-
"""Test converting mouse move action."""
|
|
127
|
-
data = {"action": "mouse_move", "coordinate": [100, 200]}
|
|
128
|
-
result = adapter.convert(data)
|
|
129
|
-
|
|
130
|
-
assert isinstance(result, MoveAction)
|
|
131
|
-
assert result.point is not None
|
|
132
|
-
assert result.point.x == 100
|
|
133
|
-
assert result.point.y == 200
|
|
134
|
-
|
|
135
|
-
def test_convert_mouse_move_invalid_coordinate(self, adapter):
|
|
136
|
-
"""Test converting mouse move with invalid coordinate."""
|
|
137
|
-
# Wrong number of coordinates
|
|
138
|
-
data = {"action": "mouse_move", "coordinate": [100]}
|
|
139
|
-
|
|
140
|
-
with pytest.raises(ValueError) as exc_info:
|
|
141
|
-
adapter.convert(data)
|
|
142
|
-
|
|
143
|
-
assert "Invalid action" in str(exc_info.value)
|
|
144
|
-
|
|
145
|
-
def test_convert_mouse_move_missing_coordinate(self, adapter):
|
|
146
|
-
"""Test converting mouse move with missing coordinate."""
|
|
147
|
-
data = {"action": "mouse_move"}
|
|
148
|
-
|
|
149
|
-
with pytest.raises(ValueError) as exc_info:
|
|
150
|
-
adapter.convert(data)
|
|
151
|
-
|
|
152
|
-
assert "Invalid action" in str(exc_info.value)
|
|
153
|
-
|
|
154
|
-
def test_convert_left_click(self, adapter):
|
|
155
|
-
"""Test converting left click action."""
|
|
156
|
-
data = {"action": "left_click", "coordinate": [150, 250]}
|
|
157
|
-
result = adapter.convert(data)
|
|
158
|
-
|
|
159
|
-
assert isinstance(result, ClickAction)
|
|
160
|
-
assert result.point is not None
|
|
161
|
-
assert result.point.x == 150
|
|
162
|
-
assert result.point.y == 250
|
|
163
|
-
assert result.button == "left"
|
|
164
|
-
|
|
165
|
-
def test_convert_right_click(self, adapter):
|
|
166
|
-
"""Test converting right click action."""
|
|
167
|
-
data = {"action": "right_click", "coordinate": [300, 400]}
|
|
168
|
-
result = adapter.convert(data)
|
|
169
|
-
|
|
170
|
-
assert isinstance(result, ClickAction)
|
|
171
|
-
assert result.point is not None
|
|
172
|
-
assert result.point.x == 300
|
|
173
|
-
assert result.point.y == 400
|
|
174
|
-
assert result.button == "right"
|
|
175
|
-
|
|
176
|
-
def test_convert_middle_click(self, adapter):
|
|
177
|
-
"""Test converting middle click action."""
|
|
178
|
-
data = {"action": "middle_click", "coordinate": [350, 450]}
|
|
179
|
-
result = adapter.convert(data)
|
|
180
|
-
|
|
181
|
-
assert isinstance(result, ClickAction)
|
|
182
|
-
assert result.point is not None
|
|
183
|
-
assert result.point.x == 350
|
|
184
|
-
assert result.point.y == 450
|
|
185
|
-
assert result.button == "middle"
|
|
186
|
-
|
|
187
|
-
def test_convert_double_click(self, adapter):
|
|
188
|
-
"""Test converting double click action."""
|
|
189
|
-
data = {"action": "double_click", "coordinate": [200, 300]}
|
|
190
|
-
result = adapter.convert(data)
|
|
191
|
-
|
|
192
|
-
assert isinstance(result, ClickAction)
|
|
193
|
-
assert result.point is not None
|
|
194
|
-
assert result.point.x == 200
|
|
195
|
-
assert result.point.y == 300
|
|
196
|
-
assert result.button == "left"
|
|
197
|
-
assert result.pattern == [100]
|
|
198
|
-
|
|
199
|
-
def test_convert_triple_click(self, adapter):
|
|
200
|
-
"""Test converting triple click action."""
|
|
201
|
-
data = {"action": "triple_click", "coordinate": [250, 350]}
|
|
202
|
-
result = adapter.convert(data)
|
|
203
|
-
|
|
204
|
-
assert isinstance(result, ClickAction)
|
|
205
|
-
assert result.point is not None
|
|
206
|
-
assert result.point.x == 250
|
|
207
|
-
assert result.point.y == 350
|
|
208
|
-
assert result.button == "left"
|
|
209
|
-
assert result.pattern == [100, 100]
|
|
210
|
-
|
|
211
|
-
def test_convert_left_click_drag_with_move_history(self, adapter):
|
|
212
|
-
"""Test converting left click drag with move action in history."""
|
|
213
|
-
# First add a move action to memory
|
|
214
|
-
move_data = {"action": "mouse_move", "coordinate": [100, 200]}
|
|
215
|
-
adapter.adapt(move_data)
|
|
216
|
-
|
|
217
|
-
# Now test drag
|
|
218
|
-
drag_data = {"action": "left_click_drag", "coordinate": [300, 400]}
|
|
219
|
-
result = adapter.convert(drag_data)
|
|
220
|
-
|
|
221
|
-
assert isinstance(result, DragAction)
|
|
222
|
-
assert len(result.path) == 2
|
|
223
|
-
assert result.path[0].x == 100
|
|
224
|
-
assert result.path[0].y == 200
|
|
225
|
-
assert result.path[1].x == 300
|
|
226
|
-
assert result.path[1].y == 400
|
|
227
|
-
|
|
228
|
-
def test_convert_left_click_drag_with_click_history(self, adapter):
|
|
229
|
-
"""Test converting left click drag with click action in history."""
|
|
230
|
-
# First add a click action to memory
|
|
231
|
-
click_data = {"action": "left_click", "coordinate": [150, 250]}
|
|
232
|
-
adapter.adapt(click_data)
|
|
233
|
-
|
|
234
|
-
# Now test drag
|
|
235
|
-
drag_data = {"action": "left_click_drag", "coordinate": [350, 450]}
|
|
236
|
-
result = adapter.convert(drag_data)
|
|
237
|
-
|
|
238
|
-
assert isinstance(result, DragAction)
|
|
239
|
-
assert len(result.path) == 2
|
|
240
|
-
assert result.path[0].x == 150
|
|
241
|
-
assert result.path[0].y == 250
|
|
242
|
-
assert result.path[1].x == 350
|
|
243
|
-
assert result.path[1].y == 450
|
|
244
|
-
|
|
245
|
-
def test_convert_left_click_drag_without_history(self, adapter):
|
|
246
|
-
"""Test converting left click drag without proper history."""
|
|
247
|
-
data = {"action": "left_click_drag", "coordinate": [300, 400]}
|
|
248
|
-
|
|
249
|
-
with pytest.raises(ValueError) as exc_info:
|
|
250
|
-
adapter.convert(data)
|
|
251
|
-
|
|
252
|
-
assert "Left click drag must be preceded by a move or click action" in str(exc_info.value)
|
|
253
|
-
|
|
254
|
-
def test_convert_left_click_drag_with_invalid_history(self, adapter):
|
|
255
|
-
"""Test converting left click drag with invalid history."""
|
|
256
|
-
# Add a type action (not move or click) to memory
|
|
257
|
-
type_data = {"action": "type", "text": "hello"}
|
|
258
|
-
adapter.adapt(type_data)
|
|
259
|
-
|
|
260
|
-
# Now test drag should fail
|
|
261
|
-
drag_data = {"action": "left_click_drag", "coordinate": [300, 400]}
|
|
262
|
-
|
|
263
|
-
with pytest.raises(ValueError) as exc_info:
|
|
264
|
-
adapter.convert(drag_data)
|
|
265
|
-
|
|
266
|
-
assert "Left click drag must be preceded by a move or click action" in str(exc_info.value)
|
|
267
|
-
|
|
268
|
-
def test_convert_scroll_up(self, adapter):
|
|
269
|
-
"""Test converting scroll up action."""
|
|
270
|
-
data = {
|
|
271
|
-
"action": "scroll",
|
|
272
|
-
"coordinate": [500, 600],
|
|
273
|
-
"scroll_direction": "up",
|
|
274
|
-
"scroll_amount": 3,
|
|
275
|
-
}
|
|
276
|
-
result = adapter.convert(data)
|
|
277
|
-
|
|
278
|
-
assert isinstance(result, ScrollAction)
|
|
279
|
-
assert result.point is not None
|
|
280
|
-
assert result.scroll is not None
|
|
281
|
-
assert result.point.x == 500
|
|
282
|
-
assert result.point.y == 600
|
|
283
|
-
assert result.scroll.x == 0
|
|
284
|
-
assert result.scroll.y == -3
|
|
285
|
-
|
|
286
|
-
def test_convert_scroll_down(self, adapter):
|
|
287
|
-
"""Test converting scroll down action."""
|
|
288
|
-
data = {
|
|
289
|
-
"action": "scroll",
|
|
290
|
-
"coordinate": [500, 600],
|
|
291
|
-
"scroll_direction": "down",
|
|
292
|
-
"scroll_amount": 5,
|
|
293
|
-
}
|
|
294
|
-
result = adapter.convert(data)
|
|
295
|
-
|
|
296
|
-
assert isinstance(result, ScrollAction)
|
|
297
|
-
assert result.point is not None
|
|
298
|
-
assert result.scroll is not None
|
|
299
|
-
assert result.point.x == 500
|
|
300
|
-
assert result.point.y == 600
|
|
301
|
-
assert result.scroll.x == 0
|
|
302
|
-
assert result.scroll.y == 5
|
|
303
|
-
|
|
304
|
-
def test_convert_scroll_left(self, adapter):
|
|
305
|
-
"""Test converting scroll left action."""
|
|
306
|
-
data = {
|
|
307
|
-
"action": "scroll",
|
|
308
|
-
"coordinate": [500, 600],
|
|
309
|
-
"scroll_direction": "left",
|
|
310
|
-
"scroll_amount": 2,
|
|
311
|
-
}
|
|
312
|
-
result = adapter.convert(data)
|
|
313
|
-
|
|
314
|
-
assert isinstance(result, ScrollAction)
|
|
315
|
-
assert result.point is not None
|
|
316
|
-
assert result.scroll is not None
|
|
317
|
-
assert result.point.x == 500
|
|
318
|
-
assert result.point.y == 600
|
|
319
|
-
assert result.scroll.x == -2
|
|
320
|
-
assert result.scroll.y == 0
|
|
321
|
-
|
|
322
|
-
def test_convert_scroll_right(self, adapter):
|
|
323
|
-
"""Test converting scroll right action."""
|
|
324
|
-
data = {
|
|
325
|
-
"action": "scroll",
|
|
326
|
-
"coordinate": [500, 600],
|
|
327
|
-
"scroll_direction": "right",
|
|
328
|
-
"scroll_amount": 4,
|
|
329
|
-
}
|
|
330
|
-
result = adapter.convert(data)
|
|
331
|
-
|
|
332
|
-
assert isinstance(result, ScrollAction)
|
|
333
|
-
assert result.point is not None
|
|
334
|
-
assert result.scroll is not None
|
|
335
|
-
assert result.point.x == 500
|
|
336
|
-
assert result.point.y == 600
|
|
337
|
-
assert result.scroll.x == 4
|
|
338
|
-
assert result.scroll.y == 0
|
|
339
|
-
|
|
340
|
-
def test_convert_scroll_invalid_direction(self, adapter):
|
|
341
|
-
"""Test converting scroll with invalid direction."""
|
|
342
|
-
data = {
|
|
343
|
-
"action": "scroll",
|
|
344
|
-
"coordinate": [500, 600],
|
|
345
|
-
"scroll_direction": "diagonal",
|
|
346
|
-
"scroll_amount": 3,
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
with pytest.raises(ValueError) as exc_info:
|
|
350
|
-
adapter.convert(data)
|
|
351
|
-
|
|
352
|
-
assert "Unsupported scroll direction: diagonal" in str(exc_info.value)
|
|
353
|
-
|
|
354
|
-
def test_convert_scroll_missing_direction(self, adapter):
|
|
355
|
-
"""Test converting scroll with missing direction."""
|
|
356
|
-
data = {"action": "scroll", "coordinate": [500, 600], "scroll_amount": 3}
|
|
357
|
-
|
|
358
|
-
with pytest.raises(ValueError) as exc_info:
|
|
359
|
-
adapter.convert(data)
|
|
360
|
-
|
|
361
|
-
assert "Invalid action" in str(exc_info.value)
|
|
362
|
-
|
|
363
|
-
def test_convert_screenshot(self, adapter):
|
|
364
|
-
"""Test converting screenshot action."""
|
|
365
|
-
data = {"action": "screenshot"}
|
|
366
|
-
result = adapter.convert(data)
|
|
367
|
-
|
|
368
|
-
assert isinstance(result, ScreenshotFetch)
|
|
369
|
-
|
|
370
|
-
def test_convert_cursor_position(self, adapter):
|
|
371
|
-
"""Test converting cursor position action."""
|
|
372
|
-
data = {"action": "cursor_position"}
|
|
373
|
-
result = adapter.convert(data)
|
|
374
|
-
|
|
375
|
-
assert isinstance(result, PositionFetch)
|
|
376
|
-
|
|
377
|
-
def test_convert_wait(self, adapter):
|
|
378
|
-
"""Test converting wait action."""
|
|
379
|
-
data = {"action": "wait", "duration": 2500}
|
|
380
|
-
result = adapter.convert(data)
|
|
381
|
-
|
|
382
|
-
assert isinstance(result, WaitAction)
|
|
383
|
-
assert result.time == 2500
|
|
384
|
-
|
|
385
|
-
def test_convert_wait_missing_duration(self, adapter):
|
|
386
|
-
"""Test converting wait action with missing duration."""
|
|
387
|
-
data = {"action": "wait"}
|
|
388
|
-
|
|
389
|
-
with pytest.raises(ValueError) as exc_info:
|
|
390
|
-
adapter.convert(data)
|
|
391
|
-
|
|
392
|
-
assert "Invalid action" in str(exc_info.value)
|
|
393
|
-
|
|
394
|
-
def test_convert_response(self, adapter):
|
|
395
|
-
"""Test converting response action."""
|
|
396
|
-
data = {"action": "response", "text": "Task completed successfully"}
|
|
397
|
-
result = adapter.convert(data)
|
|
398
|
-
|
|
399
|
-
assert isinstance(result, ResponseAction)
|
|
400
|
-
assert result.text == "Task completed successfully"
|
|
401
|
-
|
|
402
|
-
def test_convert_response_default_text(self, adapter):
|
|
403
|
-
"""Test converting response action with default text."""
|
|
404
|
-
data = {"action": "response"}
|
|
405
|
-
result = adapter.convert(data)
|
|
406
|
-
|
|
407
|
-
assert isinstance(result, ResponseAction)
|
|
408
|
-
assert result.text == ""
|
|
409
|
-
|
|
410
|
-
def test_convert_unsupported_action(self, adapter):
|
|
411
|
-
"""Test converting unsupported action type."""
|
|
412
|
-
data = {"action": "unsupported_action"}
|
|
413
|
-
|
|
414
|
-
with pytest.raises(ValueError) as exc_info:
|
|
415
|
-
adapter.convert(data)
|
|
416
|
-
|
|
417
|
-
assert "Unsupported action type: unsupported_action" in str(exc_info.value)
|
|
418
|
-
|
|
419
|
-
def test_convert_missing_action_field(self, adapter):
|
|
420
|
-
"""Test converting data without action field."""
|
|
421
|
-
data = {"text": "hello"} # Missing action
|
|
422
|
-
|
|
423
|
-
with pytest.raises(ValueError) as exc_info:
|
|
424
|
-
adapter.convert(data)
|
|
425
|
-
|
|
426
|
-
assert "Unsupported action type: None" in str(exc_info.value)
|
|
427
|
-
|
|
428
|
-
def test_convert_invalid_data_structure(self, adapter):
|
|
429
|
-
"""Test converting invalid data structure."""
|
|
430
|
-
with pytest.raises(ValueError) as exc_info:
|
|
431
|
-
adapter.convert("invalid_data")
|
|
432
|
-
|
|
433
|
-
assert "Invalid action" in str(exc_info.value)
|
|
434
|
-
|
|
435
|
-
def test_convert_none_data(self, adapter):
|
|
436
|
-
"""Test converting None data."""
|
|
437
|
-
with pytest.raises(ValueError) as exc_info:
|
|
438
|
-
adapter.convert(None)
|
|
439
|
-
|
|
440
|
-
assert "Invalid action" in str(exc_info.value)
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
class TestClaudeAdapterIntegration:
|
|
444
|
-
"""Integration tests for ClaudeAdapter."""
|
|
445
|
-
|
|
446
|
-
@pytest.fixture
|
|
447
|
-
def adapter(self):
|
|
448
|
-
"""Fixture providing a clean adapter instance."""
|
|
449
|
-
return ClaudeAdapter()
|
|
450
|
-
|
|
451
|
-
def test_full_click_pipeline(self, adapter):
|
|
452
|
-
"""Test full click action processing pipeline."""
|
|
453
|
-
# Set adapter dimensions to avoid scaling
|
|
454
|
-
adapter.agent_width = 1920
|
|
455
|
-
adapter.agent_height = 1080
|
|
456
|
-
adapter.env_width = 1920
|
|
457
|
-
adapter.env_height = 1080
|
|
458
|
-
|
|
459
|
-
raw_action = {"action": "left_click", "coordinate": [100, 200]}
|
|
460
|
-
|
|
461
|
-
result = adapter.adapt(raw_action)
|
|
462
|
-
|
|
463
|
-
assert isinstance(result, ClickAction)
|
|
464
|
-
assert result.point is not None
|
|
465
|
-
assert result.point.x == 100
|
|
466
|
-
assert result.point.y == 200
|
|
467
|
-
assert result.button == "left"
|
|
468
|
-
|
|
469
|
-
# Check that it was added to memory
|
|
470
|
-
assert len(adapter.memory) == 1
|
|
471
|
-
assert adapter.memory[0] == result
|
|
472
|
-
|
|
473
|
-
def test_drag_sequence(self, adapter):
|
|
474
|
-
"""Test complete drag sequence."""
|
|
475
|
-
# Set adapter dimensions to avoid scaling
|
|
476
|
-
adapter.agent_width = 1920
|
|
477
|
-
adapter.agent_height = 1080
|
|
478
|
-
adapter.env_width = 1920
|
|
479
|
-
adapter.env_height = 1080
|
|
480
|
-
|
|
481
|
-
# First move to start position
|
|
482
|
-
move_action = {"action": "mouse_move", "coordinate": [100, 200]}
|
|
483
|
-
move_result = adapter.adapt(move_action)
|
|
484
|
-
|
|
485
|
-
# Then drag to end position
|
|
486
|
-
drag_action = {"action": "left_click_drag", "coordinate": [300, 400]}
|
|
487
|
-
drag_result = adapter.adapt(drag_action)
|
|
488
|
-
|
|
489
|
-
assert isinstance(move_result, MoveAction)
|
|
490
|
-
assert isinstance(drag_result, DragAction)
|
|
491
|
-
assert len(drag_result.path) == 2
|
|
492
|
-
assert drag_result.path[0] == move_result.point
|
|
493
|
-
|
|
494
|
-
# Check memory contains both actions
|
|
495
|
-
assert len(adapter.memory) == 2
|
|
496
|
-
|
|
497
|
-
def test_complex_action_sequence(self, adapter):
|
|
498
|
-
"""Test complex sequence of different actions."""
|
|
499
|
-
actions = [
|
|
500
|
-
{"action": "mouse_move", "coordinate": [100, 200]},
|
|
501
|
-
{"action": "left_click", "coordinate": [150, 250]},
|
|
502
|
-
{"action": "type", "text": "Hello"},
|
|
503
|
-
{"action": "key", "text": "ctrl+a"},
|
|
504
|
-
{"action": "wait", "duration": 1000},
|
|
505
|
-
{"action": "screenshot"},
|
|
506
|
-
]
|
|
507
|
-
|
|
508
|
-
results = adapter.adapt_list(actions)
|
|
509
|
-
|
|
510
|
-
assert len(results) == 6
|
|
511
|
-
assert isinstance(results[0], MoveAction)
|
|
512
|
-
assert isinstance(results[1], ClickAction)
|
|
513
|
-
assert isinstance(results[2], TypeAction)
|
|
514
|
-
assert isinstance(results[3], PressAction)
|
|
515
|
-
assert isinstance(results[4], WaitAction)
|
|
516
|
-
assert isinstance(results[5], ScreenshotFetch)
|
|
517
|
-
|
|
518
|
-
# Check memory
|
|
519
|
-
assert len(adapter.memory) == 6
|