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
hud/utils/tests/test_init.py
CHANGED
|
@@ -1,21 +1,17 @@
|
|
|
1
|
-
"""Test utils package imports."""
|
|
2
|
-
|
|
3
|
-
from __future__ import annotations
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
def test_utils_imports():
|
|
7
|
-
"""Test that utils package can be imported."""
|
|
8
|
-
import hud.utils
|
|
9
|
-
|
|
10
|
-
# Check that the module exists
|
|
11
|
-
assert hud.utils is not None
|
|
12
|
-
|
|
13
|
-
# Try importing submodules
|
|
14
|
-
from hud.utils import
|
|
15
|
-
|
|
16
|
-
assert
|
|
17
|
-
assert
|
|
18
|
-
assert config is not None
|
|
19
|
-
assert misc is not None
|
|
20
|
-
assert progress is not None
|
|
21
|
-
assert telemetry is not None
|
|
1
|
+
"""Test utils package imports."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def test_utils_imports():
|
|
7
|
+
"""Test that utils package can be imported."""
|
|
8
|
+
import hud.utils
|
|
9
|
+
|
|
10
|
+
# Check that the module exists
|
|
11
|
+
assert hud.utils is not None
|
|
12
|
+
|
|
13
|
+
# Try importing submodules
|
|
14
|
+
from hud.utils import progress, telemetry
|
|
15
|
+
|
|
16
|
+
assert progress is not None
|
|
17
|
+
assert telemetry is not None
|
hud/utils/tests/test_progress.py
CHANGED
|
@@ -1,225 +1,261 @@
|
|
|
1
|
-
"""Tests for the progress tracking utilities."""
|
|
2
|
-
|
|
3
|
-
from __future__ import annotations
|
|
4
|
-
|
|
5
|
-
import pytest
|
|
6
|
-
|
|
7
|
-
from hud.utils.progress import StepProgressTracker
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
@pytest.fixture
|
|
11
|
-
def tracker():
|
|
12
|
-
return StepProgressTracker(total_tasks=2, max_steps_per_task=10)
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
def test_invalid_inputs_init():
|
|
16
|
-
with pytest.raises(ValueError, match="total_tasks must be positive"):
|
|
17
|
-
StepProgressTracker(total_tasks=0, max_steps_per_task=10)
|
|
18
|
-
|
|
19
|
-
with pytest.raises(ValueError, match="max_steps_per_task must be positive"):
|
|
20
|
-
StepProgressTracker(total_tasks=5, max_steps_per_task=0)
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
def test_start_task(tracker):
|
|
24
|
-
assert tracker.start_time is None
|
|
25
|
-
assert tracker._tasks_started == 0
|
|
26
|
-
|
|
27
|
-
tracker.start_task("task1")
|
|
28
|
-
|
|
29
|
-
assert tracker.start_time is not None
|
|
30
|
-
assert tracker._tasks_started == 1
|
|
31
|
-
assert tracker._task_steps["task1"] == 0
|
|
32
|
-
assert not tracker._finished_tasks["task1"]
|
|
33
|
-
|
|
34
|
-
tracker.start_task("task2")
|
|
35
|
-
assert tracker._tasks_started == 2
|
|
36
|
-
assert tracker._task_steps["task2"] == 0
|
|
37
|
-
assert not tracker._finished_tasks["task2"]
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
def test_increment_step(tracker):
|
|
41
|
-
tracker.start_task("task1")
|
|
42
|
-
assert tracker.current_total_steps == 0
|
|
43
|
-
|
|
44
|
-
tracker.increment_step("task1")
|
|
45
|
-
assert tracker._task_steps["task1"] == 1
|
|
46
|
-
assert tracker.current_total_steps == 1
|
|
47
|
-
|
|
48
|
-
tracker.increment_step("task1")
|
|
49
|
-
tracker.increment_step("task1")
|
|
50
|
-
assert tracker._task_steps["task1"] == 3
|
|
51
|
-
assert tracker.current_total_steps == 3
|
|
52
|
-
|
|
53
|
-
tracker.start_task("task2")
|
|
54
|
-
tracker.increment_step("task2")
|
|
55
|
-
assert tracker._task_steps["task2"] == 1
|
|
56
|
-
assert tracker.current_total_steps == 4
|
|
57
|
-
|
|
58
|
-
tracker.finish_task("task1")
|
|
59
|
-
initial_steps = tracker.current_total_steps
|
|
60
|
-
tracker.increment_step("task1")
|
|
61
|
-
assert tracker.current_total_steps == initial_steps
|
|
62
|
-
|
|
63
|
-
for _ in range(15):
|
|
64
|
-
tracker.increment_step("task2")
|
|
65
|
-
assert tracker._task_steps["task2"] <= tracker.max_steps_per_task
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
def test_finish_task(tracker):
|
|
69
|
-
tracker.start_task("task1")
|
|
70
|
-
tracker.start_task("task2")
|
|
71
|
-
|
|
72
|
-
tracker.increment_step("task1")
|
|
73
|
-
tracker.increment_step("task1")
|
|
74
|
-
initial_steps = tracker._task_steps["task1"]
|
|
75
|
-
|
|
76
|
-
tracker.finish_task("task1")
|
|
77
|
-
|
|
78
|
-
assert tracker._finished_tasks["task1"]
|
|
79
|
-
assert tracker._tasks_finished == 1
|
|
80
|
-
assert tracker._task_steps["task1"] == tracker.max_steps_per_task
|
|
81
|
-
assert tracker.current_total_steps > initial_steps
|
|
82
|
-
|
|
83
|
-
current_steps = tracker.current_total_steps
|
|
84
|
-
tracker.finish_task("task1")
|
|
85
|
-
assert tracker._tasks_finished == 1
|
|
86
|
-
assert tracker.current_total_steps == current_steps
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
def test_get_progress(tracker):
|
|
90
|
-
steps, total, percentage = tracker.get_progress()
|
|
91
|
-
assert steps == 0
|
|
92
|
-
assert total == tracker.total_potential_steps
|
|
93
|
-
assert percentage == 0.0
|
|
94
|
-
|
|
95
|
-
tracker.start_task("task1")
|
|
96
|
-
tracker.increment_step("task1")
|
|
97
|
-
steps, total, percentage = tracker.get_progress()
|
|
98
|
-
assert steps == 1
|
|
99
|
-
assert total == tracker.total_potential_steps
|
|
100
|
-
assert percentage == (1 / tracker.total_potential_steps) * 100
|
|
101
|
-
|
|
102
|
-
tracker.finish_task("task1")
|
|
103
|
-
steps, total, percentage = tracker.get_progress()
|
|
104
|
-
assert steps == tracker.max_steps_per_task
|
|
105
|
-
assert total == tracker.total_potential_steps
|
|
106
|
-
assert percentage == (tracker.max_steps_per_task / tracker.total_potential_steps) * 100
|
|
107
|
-
|
|
108
|
-
tracker.start_task("task2")
|
|
109
|
-
tracker.finish_task("task2")
|
|
110
|
-
steps, total, percentage = tracker.get_progress()
|
|
111
|
-
assert steps == tracker.total_potential_steps
|
|
112
|
-
assert percentage == 100.0
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
def test_get_stats_no_progress(tracker
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
tracker.
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
tracker.
|
|
161
|
-
tracker.
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
for _ in range(
|
|
207
|
-
tracker.increment_step("
|
|
208
|
-
|
|
209
|
-
tracker.finish_task("
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
assert
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
tracker.
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
1
|
+
"""Tests for the progress tracking utilities."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import pytest
|
|
6
|
+
|
|
7
|
+
from hud.utils.progress import StepProgressTracker
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@pytest.fixture
|
|
11
|
+
def tracker():
|
|
12
|
+
return StepProgressTracker(total_tasks=2, max_steps_per_task=10)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def test_invalid_inputs_init():
|
|
16
|
+
with pytest.raises(ValueError, match="total_tasks must be positive"):
|
|
17
|
+
StepProgressTracker(total_tasks=0, max_steps_per_task=10)
|
|
18
|
+
|
|
19
|
+
with pytest.raises(ValueError, match="max_steps_per_task must be positive"):
|
|
20
|
+
StepProgressTracker(total_tasks=5, max_steps_per_task=0)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def test_start_task(tracker):
|
|
24
|
+
assert tracker.start_time is None
|
|
25
|
+
assert tracker._tasks_started == 0
|
|
26
|
+
|
|
27
|
+
tracker.start_task("task1")
|
|
28
|
+
|
|
29
|
+
assert tracker.start_time is not None
|
|
30
|
+
assert tracker._tasks_started == 1
|
|
31
|
+
assert tracker._task_steps["task1"] == 0
|
|
32
|
+
assert not tracker._finished_tasks["task1"]
|
|
33
|
+
|
|
34
|
+
tracker.start_task("task2")
|
|
35
|
+
assert tracker._tasks_started == 2
|
|
36
|
+
assert tracker._task_steps["task2"] == 0
|
|
37
|
+
assert not tracker._finished_tasks["task2"]
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def test_increment_step(tracker):
|
|
41
|
+
tracker.start_task("task1")
|
|
42
|
+
assert tracker.current_total_steps == 0
|
|
43
|
+
|
|
44
|
+
tracker.increment_step("task1")
|
|
45
|
+
assert tracker._task_steps["task1"] == 1
|
|
46
|
+
assert tracker.current_total_steps == 1
|
|
47
|
+
|
|
48
|
+
tracker.increment_step("task1")
|
|
49
|
+
tracker.increment_step("task1")
|
|
50
|
+
assert tracker._task_steps["task1"] == 3
|
|
51
|
+
assert tracker.current_total_steps == 3
|
|
52
|
+
|
|
53
|
+
tracker.start_task("task2")
|
|
54
|
+
tracker.increment_step("task2")
|
|
55
|
+
assert tracker._task_steps["task2"] == 1
|
|
56
|
+
assert tracker.current_total_steps == 4
|
|
57
|
+
|
|
58
|
+
tracker.finish_task("task1")
|
|
59
|
+
initial_steps = tracker.current_total_steps
|
|
60
|
+
tracker.increment_step("task1")
|
|
61
|
+
assert tracker.current_total_steps == initial_steps
|
|
62
|
+
|
|
63
|
+
for _ in range(15):
|
|
64
|
+
tracker.increment_step("task2")
|
|
65
|
+
assert tracker._task_steps["task2"] <= tracker.max_steps_per_task
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def test_finish_task(tracker):
|
|
69
|
+
tracker.start_task("task1")
|
|
70
|
+
tracker.start_task("task2")
|
|
71
|
+
|
|
72
|
+
tracker.increment_step("task1")
|
|
73
|
+
tracker.increment_step("task1")
|
|
74
|
+
initial_steps = tracker._task_steps["task1"]
|
|
75
|
+
|
|
76
|
+
tracker.finish_task("task1")
|
|
77
|
+
|
|
78
|
+
assert tracker._finished_tasks["task1"]
|
|
79
|
+
assert tracker._tasks_finished == 1
|
|
80
|
+
assert tracker._task_steps["task1"] == tracker.max_steps_per_task
|
|
81
|
+
assert tracker.current_total_steps > initial_steps
|
|
82
|
+
|
|
83
|
+
current_steps = tracker.current_total_steps
|
|
84
|
+
tracker.finish_task("task1")
|
|
85
|
+
assert tracker._tasks_finished == 1
|
|
86
|
+
assert tracker.current_total_steps == current_steps
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def test_get_progress(tracker):
|
|
90
|
+
steps, total, percentage = tracker.get_progress()
|
|
91
|
+
assert steps == 0
|
|
92
|
+
assert total == tracker.total_potential_steps
|
|
93
|
+
assert percentage == 0.0
|
|
94
|
+
|
|
95
|
+
tracker.start_task("task1")
|
|
96
|
+
tracker.increment_step("task1")
|
|
97
|
+
steps, total, percentage = tracker.get_progress()
|
|
98
|
+
assert steps == 1
|
|
99
|
+
assert total == tracker.total_potential_steps
|
|
100
|
+
assert percentage == (1 / tracker.total_potential_steps) * 100
|
|
101
|
+
|
|
102
|
+
tracker.finish_task("task1")
|
|
103
|
+
steps, total, percentage = tracker.get_progress()
|
|
104
|
+
assert steps == tracker.max_steps_per_task
|
|
105
|
+
assert total == tracker.total_potential_steps
|
|
106
|
+
assert percentage == (tracker.max_steps_per_task / tracker.total_potential_steps) * 100
|
|
107
|
+
|
|
108
|
+
tracker.start_task("task2")
|
|
109
|
+
tracker.finish_task("task2")
|
|
110
|
+
steps, total, percentage = tracker.get_progress()
|
|
111
|
+
assert steps == tracker.total_potential_steps
|
|
112
|
+
assert percentage == 100.0
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def test_get_stats_no_progress(tracker):
|
|
116
|
+
from unittest.mock import patch
|
|
117
|
+
|
|
118
|
+
rate, eta = tracker.get_stats()
|
|
119
|
+
assert rate == 0.0
|
|
120
|
+
assert eta is None
|
|
121
|
+
|
|
122
|
+
with patch("time.monotonic", return_value=100.0):
|
|
123
|
+
tracker.start_task("task1")
|
|
124
|
+
|
|
125
|
+
rate, eta = tracker.get_stats()
|
|
126
|
+
assert rate == 0.0
|
|
127
|
+
assert eta is None
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def test_get_stats_with_progress():
|
|
131
|
+
from unittest.mock import patch
|
|
132
|
+
|
|
133
|
+
with patch("time.monotonic") as mock_time:
|
|
134
|
+
mock_time.return_value = 100.0
|
|
135
|
+
|
|
136
|
+
tracker = StepProgressTracker(total_tasks=1, max_steps_per_task=10)
|
|
137
|
+
tracker.start_task("task1")
|
|
138
|
+
|
|
139
|
+
mock_time.return_value = 160.0
|
|
140
|
+
for _ in range(5):
|
|
141
|
+
tracker.increment_step("task1")
|
|
142
|
+
|
|
143
|
+
rate, eta = tracker.get_stats()
|
|
144
|
+
|
|
145
|
+
assert rate == pytest.approx(5.0)
|
|
146
|
+
assert eta == pytest.approx(60.0)
|
|
147
|
+
|
|
148
|
+
for _ in range(5):
|
|
149
|
+
tracker.increment_step("task1")
|
|
150
|
+
|
|
151
|
+
rate, eta = tracker.get_stats()
|
|
152
|
+
assert rate == pytest.approx(10.0)
|
|
153
|
+
assert eta == pytest.approx(0.0)
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def test_is_finished(tracker):
|
|
157
|
+
assert not tracker.is_finished()
|
|
158
|
+
|
|
159
|
+
tracker.start_task("task1")
|
|
160
|
+
tracker.finish_task("task1")
|
|
161
|
+
assert not tracker.is_finished()
|
|
162
|
+
|
|
163
|
+
tracker.start_task("task2")
|
|
164
|
+
tracker.finish_task("task2")
|
|
165
|
+
assert tracker.is_finished()
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def test_display(tracker):
|
|
169
|
+
from unittest.mock import patch
|
|
170
|
+
|
|
171
|
+
with patch("time.monotonic") as mock_time:
|
|
172
|
+
mock_time.return_value = 100.0
|
|
173
|
+
tracker.start_task("task1")
|
|
174
|
+
|
|
175
|
+
mock_time.return_value = 130.0
|
|
176
|
+
tracker.increment_step("task1")
|
|
177
|
+
tracker.increment_step("task1")
|
|
178
|
+
|
|
179
|
+
display_str = tracker.display()
|
|
180
|
+
|
|
181
|
+
assert "%" in display_str
|
|
182
|
+
assert "2/20" in display_str
|
|
183
|
+
assert "0:30" in display_str
|
|
184
|
+
assert "steps/min" in display_str
|
|
185
|
+
|
|
186
|
+
tracker.finish_task("task1")
|
|
187
|
+
display_str = tracker.display()
|
|
188
|
+
assert "10/20" in display_str
|
|
189
|
+
|
|
190
|
+
tracker.start_task("task2")
|
|
191
|
+
tracker.finish_task("task2")
|
|
192
|
+
display_str = tracker.display()
|
|
193
|
+
assert "100%" in display_str
|
|
194
|
+
assert "20/20" in display_str
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def test_complex_workflow():
|
|
198
|
+
tracker = StepProgressTracker(total_tasks=5, max_steps_per_task=20)
|
|
199
|
+
|
|
200
|
+
for i in range(5):
|
|
201
|
+
tracker.start_task(f"task{i}")
|
|
202
|
+
|
|
203
|
+
for _ in range(10):
|
|
204
|
+
tracker.increment_step("task0")
|
|
205
|
+
|
|
206
|
+
for _ in range(5):
|
|
207
|
+
tracker.increment_step("task1")
|
|
208
|
+
|
|
209
|
+
tracker.finish_task("task2")
|
|
210
|
+
|
|
211
|
+
for _ in range(15):
|
|
212
|
+
tracker.increment_step("task3")
|
|
213
|
+
|
|
214
|
+
tracker.finish_task("task3")
|
|
215
|
+
|
|
216
|
+
steps, total, percentage = tracker.get_progress()
|
|
217
|
+
expected_steps = 10 + 5 + 20 + 20 + 0
|
|
218
|
+
assert steps == expected_steps
|
|
219
|
+
assert total == 5 * 20
|
|
220
|
+
assert percentage == (expected_steps / total) * 100
|
|
221
|
+
|
|
222
|
+
assert tracker._tasks_finished == 2
|
|
223
|
+
assert not tracker.is_finished()
|
|
224
|
+
|
|
225
|
+
tracker.finish_task("task0")
|
|
226
|
+
tracker.finish_task("task1")
|
|
227
|
+
tracker.finish_task("task4")
|
|
228
|
+
|
|
229
|
+
assert tracker.is_finished()
|
|
230
|
+
assert tracker.get_progress()[2] == 100.0
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
def test_display_eta_when_finished(tracker):
|
|
234
|
+
from unittest.mock import patch
|
|
235
|
+
|
|
236
|
+
"""Test that ETA shows 0:00 when progress is finished."""
|
|
237
|
+
|
|
238
|
+
with patch("time.monotonic") as mock_time:
|
|
239
|
+
mock_time.return_value = 100.0
|
|
240
|
+
|
|
241
|
+
# Start and complete all tasks
|
|
242
|
+
tracker.start_task("task1")
|
|
243
|
+
for _ in range(10):
|
|
244
|
+
tracker.increment_step("task1")
|
|
245
|
+
tracker.finish_task("task1")
|
|
246
|
+
|
|
247
|
+
tracker.start_task("task2")
|
|
248
|
+
for _ in range(10):
|
|
249
|
+
tracker.increment_step("task2")
|
|
250
|
+
tracker.finish_task("task2")
|
|
251
|
+
|
|
252
|
+
# Some time has passed
|
|
253
|
+
mock_time.return_value = 120.0
|
|
254
|
+
|
|
255
|
+
display = tracker.display()
|
|
256
|
+
|
|
257
|
+
# When finished, ETA should be 0:00 (not ??:??)
|
|
258
|
+
assert tracker.is_finished()
|
|
259
|
+
assert "0:00" in display
|
|
260
|
+
assert "100%" in display
|
|
261
|
+
assert "20/20" in display
|
|
@@ -1,37 +1,82 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
from
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
assert
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
"
|
|
19
|
-
"
|
|
20
|
-
)
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from unittest.mock import patch
|
|
4
|
+
|
|
5
|
+
from hud.utils.telemetry import stream
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def test_stream():
|
|
9
|
+
html_content = stream("https://example.com")
|
|
10
|
+
assert html_content is not None
|
|
11
|
+
assert "<div style=" in html_content
|
|
12
|
+
assert 'src="https://example.com"' in html_content
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def test_stream_with_display_exception():
|
|
16
|
+
"""Test stream when IPython display raises an exception."""
|
|
17
|
+
with (
|
|
18
|
+
patch("IPython.display.display", side_effect=Exception("Display error")),
|
|
19
|
+
patch("hud.utils.telemetry.logger") as mock_logger,
|
|
20
|
+
):
|
|
21
|
+
html_content = stream("https://example.com")
|
|
22
|
+
|
|
23
|
+
# Should still return the HTML content
|
|
24
|
+
assert html_content is not None
|
|
25
|
+
assert 'src="https://example.com"' in html_content
|
|
26
|
+
|
|
27
|
+
# Should log the warning
|
|
28
|
+
mock_logger.warning.assert_called_once()
|
|
29
|
+
args = mock_logger.warning.call_args[0]
|
|
30
|
+
assert "Display error" in str(args[0])
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def test_display_screenshot():
|
|
34
|
+
from hud.utils.telemetry import display_screenshot
|
|
35
|
+
|
|
36
|
+
# This is a simple 1x1 transparent PNG image in base64 format
|
|
37
|
+
base64_image = (
|
|
38
|
+
"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQ"
|
|
39
|
+
"AAABJRU5ErkJggg=="
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
html_content = display_screenshot(base64_image)
|
|
43
|
+
assert html_content is not None
|
|
44
|
+
assert "<div style=" in html_content
|
|
45
|
+
assert "width: 960px" in html_content
|
|
46
|
+
assert "height: 540px" in html_content
|
|
47
|
+
assert f"data:image/png;base64,{base64_image}" in html_content
|
|
48
|
+
|
|
49
|
+
# Test with custom dimensions
|
|
50
|
+
custom_html = display_screenshot(base64_image, width=800, height=600)
|
|
51
|
+
assert "width: 800px" in custom_html
|
|
52
|
+
assert "height: 600px" in custom_html
|
|
53
|
+
|
|
54
|
+
# Test with data URI already included
|
|
55
|
+
data_uri = f"data:image/png;base64,{base64_image}"
|
|
56
|
+
uri_html = display_screenshot(data_uri)
|
|
57
|
+
assert data_uri in uri_html
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def test_display_screenshot_with_exception():
|
|
61
|
+
"""Test display_screenshot when IPython display raises an exception."""
|
|
62
|
+
from hud.utils.telemetry import display_screenshot
|
|
63
|
+
|
|
64
|
+
base64_image = (
|
|
65
|
+
"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQ"
|
|
66
|
+
"AAABJRU5ErkJggg=="
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
with (
|
|
70
|
+
patch("IPython.display.display", side_effect=Exception("Display error")),
|
|
71
|
+
patch("hud.utils.telemetry.logger") as mock_logger,
|
|
72
|
+
):
|
|
73
|
+
html_content = display_screenshot(base64_image)
|
|
74
|
+
|
|
75
|
+
# Should still return the HTML content
|
|
76
|
+
assert html_content is not None
|
|
77
|
+
assert f"data:image/png;base64,{base64_image}" in html_content
|
|
78
|
+
|
|
79
|
+
# Should log the warning
|
|
80
|
+
mock_logger.warning.assert_called_once()
|
|
81
|
+
args = mock_logger.warning.call_args[0]
|
|
82
|
+
assert "Display error" in str(args[0])
|
hud/utils/tests/test_version.py
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
def test_import():
|
|
5
|
-
"""Test that the package can be imported."""
|
|
6
|
-
import hud
|
|
7
|
-
|
|
8
|
-
assert hud.__version__ == "0.
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def test_import():
|
|
5
|
+
"""Test that the package can be imported."""
|
|
6
|
+
import hud
|
|
7
|
+
|
|
8
|
+
assert hud.__version__ == "0.4.1"
|