hud-python 0.3.5__py3-none-any.whl → 0.4.0__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.

Files changed (192) hide show
  1. hud/__init__.py +22 -89
  2. hud/agents/__init__.py +17 -0
  3. hud/agents/art.py +101 -0
  4. hud/agents/base.py +599 -0
  5. hud/{mcp → agents}/claude.py +373 -321
  6. hud/{mcp → agents}/langchain.py +250 -250
  7. hud/agents/misc/__init__.py +7 -0
  8. hud/{agent → agents}/misc/response_agent.py +80 -80
  9. hud/{mcp → agents}/openai.py +352 -334
  10. hud/agents/openai_chat_generic.py +154 -0
  11. hud/{mcp → agents}/tests/__init__.py +1 -1
  12. hud/agents/tests/test_base.py +742 -0
  13. hud/agents/tests/test_claude.py +324 -0
  14. hud/{mcp → agents}/tests/test_client.py +363 -324
  15. hud/{mcp → agents}/tests/test_openai.py +237 -238
  16. hud/cli/__init__.py +617 -0
  17. hud/cli/__main__.py +8 -0
  18. hud/cli/analyze.py +371 -0
  19. hud/cli/analyze_metadata.py +230 -0
  20. hud/cli/build.py +427 -0
  21. hud/cli/clone.py +185 -0
  22. hud/cli/cursor.py +92 -0
  23. hud/cli/debug.py +392 -0
  24. hud/cli/docker_utils.py +83 -0
  25. hud/cli/init.py +281 -0
  26. hud/cli/interactive.py +353 -0
  27. hud/cli/mcp_server.py +756 -0
  28. hud/cli/pull.py +336 -0
  29. hud/cli/push.py +379 -0
  30. hud/cli/remote_runner.py +311 -0
  31. hud/cli/runner.py +160 -0
  32. hud/cli/tests/__init__.py +3 -0
  33. hud/cli/tests/test_analyze.py +284 -0
  34. hud/cli/tests/test_cli_init.py +265 -0
  35. hud/cli/tests/test_cli_main.py +27 -0
  36. hud/cli/tests/test_clone.py +142 -0
  37. hud/cli/tests/test_cursor.py +253 -0
  38. hud/cli/tests/test_debug.py +453 -0
  39. hud/cli/tests/test_mcp_server.py +139 -0
  40. hud/cli/tests/test_utils.py +388 -0
  41. hud/cli/utils.py +263 -0
  42. hud/clients/README.md +143 -0
  43. hud/clients/__init__.py +16 -0
  44. hud/clients/base.py +354 -0
  45. hud/clients/fastmcp.py +202 -0
  46. hud/clients/mcp_use.py +278 -0
  47. hud/clients/tests/__init__.py +1 -0
  48. hud/clients/tests/test_client_integration.py +111 -0
  49. hud/clients/tests/test_fastmcp.py +342 -0
  50. hud/clients/tests/test_protocol.py +188 -0
  51. hud/clients/utils/__init__.py +1 -0
  52. hud/clients/utils/retry_transport.py +160 -0
  53. hud/datasets.py +322 -192
  54. hud/misc/__init__.py +1 -0
  55. hud/{agent → misc}/claude_plays_pokemon.py +292 -283
  56. hud/otel/__init__.py +35 -0
  57. hud/otel/collector.py +142 -0
  58. hud/otel/config.py +164 -0
  59. hud/otel/context.py +536 -0
  60. hud/otel/exporters.py +366 -0
  61. hud/otel/instrumentation.py +97 -0
  62. hud/otel/processors.py +118 -0
  63. hud/otel/tests/__init__.py +1 -0
  64. hud/otel/tests/test_processors.py +197 -0
  65. hud/server/__init__.py +5 -5
  66. hud/server/context.py +114 -0
  67. hud/server/helper/__init__.py +5 -0
  68. hud/server/low_level.py +132 -0
  69. hud/server/server.py +166 -0
  70. hud/server/tests/__init__.py +3 -0
  71. hud/settings.py +73 -79
  72. hud/shared/__init__.py +5 -0
  73. hud/{exceptions.py → shared/exceptions.py} +180 -180
  74. hud/{server → shared}/requests.py +264 -264
  75. hud/shared/tests/test_exceptions.py +157 -0
  76. hud/{server → shared}/tests/test_requests.py +275 -275
  77. hud/telemetry/__init__.py +25 -30
  78. hud/telemetry/instrument.py +379 -0
  79. hud/telemetry/job.py +309 -141
  80. hud/telemetry/replay.py +74 -0
  81. hud/telemetry/trace.py +83 -0
  82. hud/tools/__init__.py +33 -34
  83. hud/tools/base.py +365 -65
  84. hud/tools/bash.py +161 -137
  85. hud/tools/computer/__init__.py +15 -13
  86. hud/tools/computer/anthropic.py +437 -420
  87. hud/tools/computer/hud.py +376 -334
  88. hud/tools/computer/openai.py +295 -292
  89. hud/tools/computer/settings.py +82 -0
  90. hud/tools/edit.py +314 -290
  91. hud/tools/executors/__init__.py +30 -30
  92. hud/tools/executors/base.py +539 -532
  93. hud/tools/executors/pyautogui.py +621 -619
  94. hud/tools/executors/tests/__init__.py +1 -1
  95. hud/tools/executors/tests/test_base_executor.py +338 -338
  96. hud/tools/executors/tests/test_pyautogui_executor.py +165 -165
  97. hud/tools/executors/xdo.py +511 -503
  98. hud/tools/{playwright_tool.py → playwright.py} +412 -379
  99. hud/tools/tests/__init__.py +3 -3
  100. hud/tools/tests/test_base.py +282 -0
  101. hud/tools/tests/test_bash.py +158 -152
  102. hud/tools/tests/test_bash_extended.py +197 -0
  103. hud/tools/tests/test_computer.py +425 -52
  104. hud/tools/tests/test_computer_actions.py +34 -34
  105. hud/tools/tests/test_edit.py +259 -240
  106. hud/tools/tests/test_init.py +27 -27
  107. hud/tools/tests/test_playwright_tool.py +183 -183
  108. hud/tools/tests/test_tools.py +145 -157
  109. hud/tools/tests/test_utils.py +156 -156
  110. hud/tools/types.py +72 -0
  111. hud/tools/utils.py +50 -50
  112. hud/types.py +136 -89
  113. hud/utils/__init__.py +10 -16
  114. hud/utils/async_utils.py +65 -0
  115. hud/utils/design.py +168 -0
  116. hud/utils/mcp.py +55 -0
  117. hud/utils/progress.py +149 -149
  118. hud/utils/telemetry.py +66 -66
  119. hud/utils/tests/test_async_utils.py +173 -0
  120. hud/utils/tests/test_init.py +17 -21
  121. hud/utils/tests/test_progress.py +261 -225
  122. hud/utils/tests/test_telemetry.py +82 -37
  123. hud/utils/tests/test_version.py +8 -8
  124. hud/version.py +7 -7
  125. hud_python-0.4.0.dist-info/METADATA +474 -0
  126. hud_python-0.4.0.dist-info/RECORD +132 -0
  127. hud_python-0.4.0.dist-info/entry_points.txt +3 -0
  128. {hud_python-0.3.5.dist-info → hud_python-0.4.0.dist-info}/licenses/LICENSE +21 -21
  129. hud/adapters/__init__.py +0 -8
  130. hud/adapters/claude/__init__.py +0 -5
  131. hud/adapters/claude/adapter.py +0 -180
  132. hud/adapters/claude/tests/__init__.py +0 -1
  133. hud/adapters/claude/tests/test_adapter.py +0 -519
  134. hud/adapters/common/__init__.py +0 -6
  135. hud/adapters/common/adapter.py +0 -178
  136. hud/adapters/common/tests/test_adapter.py +0 -289
  137. hud/adapters/common/types.py +0 -446
  138. hud/adapters/operator/__init__.py +0 -5
  139. hud/adapters/operator/adapter.py +0 -108
  140. hud/adapters/operator/tests/__init__.py +0 -1
  141. hud/adapters/operator/tests/test_adapter.py +0 -370
  142. hud/agent/__init__.py +0 -19
  143. hud/agent/base.py +0 -126
  144. hud/agent/claude.py +0 -271
  145. hud/agent/langchain.py +0 -215
  146. hud/agent/misc/__init__.py +0 -3
  147. hud/agent/operator.py +0 -268
  148. hud/agent/tests/__init__.py +0 -1
  149. hud/agent/tests/test_base.py +0 -202
  150. hud/env/__init__.py +0 -11
  151. hud/env/client.py +0 -35
  152. hud/env/docker_client.py +0 -349
  153. hud/env/environment.py +0 -446
  154. hud/env/local_docker_client.py +0 -358
  155. hud/env/remote_client.py +0 -212
  156. hud/env/remote_docker_client.py +0 -292
  157. hud/gym.py +0 -130
  158. hud/job.py +0 -773
  159. hud/mcp/__init__.py +0 -17
  160. hud/mcp/base.py +0 -631
  161. hud/mcp/client.py +0 -312
  162. hud/mcp/tests/test_base.py +0 -512
  163. hud/mcp/tests/test_claude.py +0 -294
  164. hud/task.py +0 -149
  165. hud/taskset.py +0 -237
  166. hud/telemetry/_trace.py +0 -347
  167. hud/telemetry/context.py +0 -230
  168. hud/telemetry/exporter.py +0 -575
  169. hud/telemetry/instrumentation/__init__.py +0 -3
  170. hud/telemetry/instrumentation/mcp.py +0 -259
  171. hud/telemetry/instrumentation/registry.py +0 -59
  172. hud/telemetry/mcp_models.py +0 -270
  173. hud/telemetry/tests/__init__.py +0 -1
  174. hud/telemetry/tests/test_context.py +0 -210
  175. hud/telemetry/tests/test_trace.py +0 -312
  176. hud/tools/helper/README.md +0 -56
  177. hud/tools/helper/__init__.py +0 -9
  178. hud/tools/helper/mcp_server.py +0 -78
  179. hud/tools/helper/server_initialization.py +0 -115
  180. hud/tools/helper/utils.py +0 -58
  181. hud/trajectory.py +0 -94
  182. hud/utils/agent.py +0 -37
  183. hud/utils/common.py +0 -256
  184. hud/utils/config.py +0 -120
  185. hud/utils/deprecation.py +0 -115
  186. hud/utils/misc.py +0 -53
  187. hud/utils/tests/test_common.py +0 -277
  188. hud/utils/tests/test_config.py +0 -129
  189. hud_python-0.3.5.dist-info/METADATA +0 -284
  190. hud_python-0.3.5.dist-info/RECORD +0 -120
  191. /hud/{adapters/common → shared}/tests/__init__.py +0 -0
  192. {hud_python-0.3.5.dist-info → hud_python-0.4.0.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
@@ -1,6 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from .adapter import Adapter
4
- from .types import CLA
5
-
6
- __all__ = ["CLA", "Adapter"]