hud-python 0.4.51__py3-none-any.whl → 0.4.53__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 +13 -1
- hud/agents/base.py +14 -3
- hud/agents/lite_llm.py +1 -1
- hud/agents/openai_chat_generic.py +15 -3
- hud/agents/tests/test_base.py +9 -2
- hud/agents/tests/test_base_runtime.py +164 -0
- hud/cli/__init__.py +18 -25
- hud/cli/build.py +35 -27
- hud/cli/dev.py +11 -29
- hud/cli/eval.py +114 -145
- hud/cli/tests/test_analyze_module.py +120 -0
- hud/cli/tests/test_build.py +26 -3
- hud/cli/tests/test_build_failure.py +41 -0
- hud/cli/tests/test_build_module.py +50 -0
- hud/cli/tests/test_cli_more_wrappers.py +30 -0
- hud/cli/tests/test_cli_root.py +134 -0
- hud/cli/tests/test_eval.py +4 -0
- hud/cli/tests/test_mcp_server.py +8 -7
- hud/cli/tests/test_push_happy.py +74 -0
- hud/cli/tests/test_push_wrapper.py +23 -0
- hud/cli/utils/docker.py +120 -1
- hud/cli/utils/runner.py +1 -1
- hud/cli/utils/tasks.py +4 -1
- hud/cli/utils/tests/__init__.py +0 -0
- hud/cli/utils/tests/test_config.py +58 -0
- hud/cli/utils/tests/test_docker.py +93 -0
- hud/cli/utils/tests/test_docker_hints.py +71 -0
- hud/cli/utils/tests/test_env_check.py +74 -0
- hud/cli/utils/tests/test_environment.py +42 -0
- hud/cli/utils/tests/test_interactive_module.py +60 -0
- hud/cli/utils/tests/test_local_runner.py +50 -0
- hud/cli/utils/tests/test_logging_utils.py +23 -0
- hud/cli/utils/tests/test_metadata.py +49 -0
- hud/cli/utils/tests/test_package_runner.py +35 -0
- hud/cli/utils/tests/test_registry_utils.py +49 -0
- hud/cli/utils/tests/test_remote_runner.py +25 -0
- hud/cli/utils/tests/test_runner_modules.py +52 -0
- hud/cli/utils/tests/test_source_hash.py +36 -0
- hud/cli/utils/tests/test_tasks.py +80 -0
- hud/cli/utils/version_check.py +257 -0
- hud/clients/base.py +1 -1
- hud/clients/mcp_use.py +3 -1
- hud/datasets/parallel.py +2 -2
- hud/datasets/runner.py +85 -24
- hud/datasets/tests/__init__.py +0 -0
- hud/datasets/tests/test_runner.py +106 -0
- hud/datasets/tests/test_utils.py +228 -0
- hud/otel/config.py +8 -6
- hud/otel/context.py +4 -4
- hud/otel/exporters.py +231 -57
- hud/otel/tests/__init__.py +0 -1
- hud/otel/tests/test_instrumentation.py +207 -0
- hud/rl/learner.py +1 -1
- hud/server/tests/test_server_extra.py +2 -0
- hud/shared/exceptions.py +35 -9
- hud/shared/hints.py +25 -0
- hud/shared/requests.py +15 -3
- hud/shared/tests/test_exceptions.py +39 -30
- hud/shared/tests/test_hints.py +167 -0
- hud/telemetry/__init__.py +30 -6
- hud/telemetry/async_context.py +331 -0
- hud/telemetry/job.py +51 -12
- hud/telemetry/tests/test_async_context.py +242 -0
- hud/telemetry/tests/test_instrument.py +414 -0
- hud/telemetry/tests/test_job.py +609 -0
- hud/telemetry/tests/test_trace.py +184 -6
- hud/telemetry/trace.py +16 -17
- hud/tools/computer/qwen.py +4 -1
- hud/tools/computer/settings.py +2 -2
- hud/tools/executors/base.py +4 -2
- hud/tools/tests/test_submit.py +85 -0
- hud/tools/tests/test_types.py +193 -0
- hud/types.py +7 -1
- hud/utils/agent_factories.py +1 -3
- hud/utils/mcp.py +1 -1
- hud/utils/task_tracking.py +223 -0
- hud/utils/tests/test_agent_factories.py +60 -0
- hud/utils/tests/test_mcp.py +4 -6
- hud/utils/tests/test_pretty_errors.py +186 -0
- hud/utils/tests/test_tasks.py +187 -0
- hud/utils/tests/test_tool_shorthand.py +154 -0
- hud/utils/tests/test_version.py +1 -1
- hud/version.py +1 -1
- {hud_python-0.4.51.dist-info → hud_python-0.4.53.dist-info}/METADATA +48 -48
- {hud_python-0.4.51.dist-info → hud_python-0.4.53.dist-info}/RECORD +88 -47
- {hud_python-0.4.51.dist-info → hud_python-0.4.53.dist-info}/WHEEL +0 -0
- {hud_python-0.4.51.dist-info → hud_python-0.4.53.dist-info}/entry_points.txt +0 -0
- {hud_python-0.4.51.dist-info → hud_python-0.4.53.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,609 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
from unittest.mock import AsyncMock, patch
|
|
5
|
+
|
|
6
|
+
import pytest
|
|
7
|
+
|
|
8
|
+
from hud.telemetry.job import (
|
|
9
|
+
Job,
|
|
10
|
+
_print_job_complete_url,
|
|
11
|
+
_print_job_url,
|
|
12
|
+
create_job,
|
|
13
|
+
get_current_job,
|
|
14
|
+
job,
|
|
15
|
+
job_decorator,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def test_job_initialization():
|
|
20
|
+
"""Test Job initialization with all parameters."""
|
|
21
|
+
job_obj = Job(
|
|
22
|
+
job_id="test-id",
|
|
23
|
+
name="Test Job",
|
|
24
|
+
metadata={"key": "value"},
|
|
25
|
+
dataset_link="test/dataset",
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
assert job_obj.id == "test-id"
|
|
29
|
+
assert job_obj.name == "Test Job"
|
|
30
|
+
assert job_obj.metadata == {"key": "value"}
|
|
31
|
+
assert job_obj.dataset_link == "test/dataset"
|
|
32
|
+
assert job_obj.status == "created"
|
|
33
|
+
assert isinstance(job_obj.created_at, datetime)
|
|
34
|
+
assert job_obj.tasks == []
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def test_job_initialization_minimal():
|
|
38
|
+
"""Test Job initialization with minimal parameters."""
|
|
39
|
+
job_obj = Job(job_id="test-id", name="Test")
|
|
40
|
+
|
|
41
|
+
assert job_obj.metadata == {}
|
|
42
|
+
assert job_obj.dataset_link is None
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def test_job_add_task():
|
|
46
|
+
"""Test adding tasks to a job."""
|
|
47
|
+
job_obj = Job(job_id="test-id", name="Test")
|
|
48
|
+
|
|
49
|
+
job_obj.add_task("task1")
|
|
50
|
+
job_obj.add_task("task2")
|
|
51
|
+
|
|
52
|
+
assert job_obj.tasks == ["task1", "task2"]
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
@pytest.mark.asyncio
|
|
56
|
+
async def test_job_update_status_async():
|
|
57
|
+
"""Test async status update."""
|
|
58
|
+
job_obj = Job(job_id="test-id", name="Test")
|
|
59
|
+
|
|
60
|
+
with (
|
|
61
|
+
patch("hud.telemetry.job.settings") as mock_settings,
|
|
62
|
+
patch("hud.telemetry.job.make_request", new_callable=AsyncMock) as mock_request,
|
|
63
|
+
):
|
|
64
|
+
mock_settings.telemetry_enabled = True
|
|
65
|
+
mock_settings.api_key = "test_key"
|
|
66
|
+
mock_settings.hud_telemetry_url = "https://test.com"
|
|
67
|
+
|
|
68
|
+
await job_obj.update_status("running")
|
|
69
|
+
|
|
70
|
+
assert job_obj.status == "running"
|
|
71
|
+
mock_request.assert_called_once()
|
|
72
|
+
call_kwargs = mock_request.call_args[1]
|
|
73
|
+
assert call_kwargs["method"] == "POST"
|
|
74
|
+
assert "test-id" in call_kwargs["url"]
|
|
75
|
+
assert call_kwargs["json"]["status"] == "running"
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
@pytest.mark.asyncio
|
|
79
|
+
async def test_job_update_status_async_with_dataset():
|
|
80
|
+
"""Test async status update includes dataset link."""
|
|
81
|
+
job_obj = Job(job_id="test-id", name="Test", dataset_link="test/dataset")
|
|
82
|
+
|
|
83
|
+
with (
|
|
84
|
+
patch("hud.telemetry.job.settings") as mock_settings,
|
|
85
|
+
patch("hud.telemetry.job.make_request", new_callable=AsyncMock) as mock_request,
|
|
86
|
+
):
|
|
87
|
+
mock_settings.telemetry_enabled = True
|
|
88
|
+
mock_settings.api_key = "test_key"
|
|
89
|
+
mock_settings.hud_telemetry_url = "https://test.com"
|
|
90
|
+
|
|
91
|
+
await job_obj.update_status("running")
|
|
92
|
+
|
|
93
|
+
call_kwargs = mock_request.call_args[1]
|
|
94
|
+
assert call_kwargs["json"]["dataset_link"] == "test/dataset"
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
@pytest.mark.asyncio
|
|
98
|
+
async def test_job_update_status_async_telemetry_disabled():
|
|
99
|
+
"""Test async status update when telemetry is disabled."""
|
|
100
|
+
job_obj = Job(job_id="test-id", name="Test")
|
|
101
|
+
|
|
102
|
+
with (
|
|
103
|
+
patch("hud.telemetry.job.settings") as mock_settings,
|
|
104
|
+
patch("hud.telemetry.job.make_request", new_callable=AsyncMock) as mock_request,
|
|
105
|
+
):
|
|
106
|
+
mock_settings.telemetry_enabled = False
|
|
107
|
+
|
|
108
|
+
await job_obj.update_status("running")
|
|
109
|
+
|
|
110
|
+
assert job_obj.status == "running"
|
|
111
|
+
mock_request.assert_not_called()
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
@pytest.mark.asyncio
|
|
115
|
+
async def test_job_update_status_async_error():
|
|
116
|
+
"""Test async status update handles errors gracefully."""
|
|
117
|
+
job_obj = Job(job_id="test-id", name="Test")
|
|
118
|
+
|
|
119
|
+
with (
|
|
120
|
+
patch("hud.telemetry.job.settings") as mock_settings,
|
|
121
|
+
patch("hud.telemetry.job.make_request", new_callable=AsyncMock) as mock_request,
|
|
122
|
+
):
|
|
123
|
+
mock_settings.telemetry_enabled = True
|
|
124
|
+
mock_settings.api_key = "test_key"
|
|
125
|
+
mock_settings.hud_telemetry_url = "https://test.com"
|
|
126
|
+
mock_request.side_effect = Exception("Network error")
|
|
127
|
+
|
|
128
|
+
# Should not raise
|
|
129
|
+
await job_obj.update_status("running")
|
|
130
|
+
assert job_obj.status == "running"
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def test_job_update_status_sync():
|
|
134
|
+
"""Test sync status update."""
|
|
135
|
+
job_obj = Job(job_id="test-id", name="Test")
|
|
136
|
+
|
|
137
|
+
with (
|
|
138
|
+
patch("hud.telemetry.job.settings") as mock_settings,
|
|
139
|
+
patch("hud.telemetry.job.make_request_sync") as mock_request,
|
|
140
|
+
):
|
|
141
|
+
mock_settings.telemetry_enabled = True
|
|
142
|
+
mock_settings.api_key = "test_key"
|
|
143
|
+
mock_settings.hud_telemetry_url = "https://test.com"
|
|
144
|
+
|
|
145
|
+
job_obj.update_status_sync("completed")
|
|
146
|
+
|
|
147
|
+
assert job_obj.status == "completed"
|
|
148
|
+
mock_request.assert_called_once()
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def test_job_update_status_sync_with_dataset():
|
|
152
|
+
"""Test sync status update includes dataset link."""
|
|
153
|
+
job_obj = Job(job_id="test-id", name="Test", dataset_link="test/dataset")
|
|
154
|
+
|
|
155
|
+
with (
|
|
156
|
+
patch("hud.telemetry.job.settings") as mock_settings,
|
|
157
|
+
patch("hud.telemetry.job.make_request_sync") as mock_request,
|
|
158
|
+
):
|
|
159
|
+
mock_settings.telemetry_enabled = True
|
|
160
|
+
mock_settings.api_key = "test_key"
|
|
161
|
+
mock_settings.hud_telemetry_url = "https://test.com"
|
|
162
|
+
|
|
163
|
+
job_obj.update_status_sync("completed")
|
|
164
|
+
|
|
165
|
+
call_kwargs = mock_request.call_args[1]
|
|
166
|
+
assert call_kwargs["json"]["dataset_link"] == "test/dataset"
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def test_job_update_status_sync_telemetry_disabled():
|
|
170
|
+
"""Test sync status update when telemetry is disabled."""
|
|
171
|
+
job_obj = Job(job_id="test-id", name="Test")
|
|
172
|
+
|
|
173
|
+
with (
|
|
174
|
+
patch("hud.telemetry.job.settings") as mock_settings,
|
|
175
|
+
patch("hud.telemetry.job.make_request_sync") as mock_request,
|
|
176
|
+
):
|
|
177
|
+
mock_settings.telemetry_enabled = False
|
|
178
|
+
|
|
179
|
+
job_obj.update_status_sync("completed")
|
|
180
|
+
|
|
181
|
+
mock_request.assert_not_called()
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def test_job_update_status_sync_error():
|
|
185
|
+
"""Test sync status update handles errors gracefully."""
|
|
186
|
+
job_obj = Job(job_id="test-id", name="Test")
|
|
187
|
+
|
|
188
|
+
with (
|
|
189
|
+
patch("hud.telemetry.job.settings") as mock_settings,
|
|
190
|
+
patch("hud.telemetry.job.make_request_sync") as mock_request,
|
|
191
|
+
):
|
|
192
|
+
mock_settings.telemetry_enabled = True
|
|
193
|
+
mock_settings.api_key = "test_key"
|
|
194
|
+
mock_settings.hud_telemetry_url = "https://test.com"
|
|
195
|
+
mock_request.side_effect = Exception("Network error")
|
|
196
|
+
|
|
197
|
+
# Should not raise
|
|
198
|
+
job_obj.update_status_sync("completed")
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
def test_job_update_status_fire_and_forget():
|
|
202
|
+
"""Test fire-and-forget status update."""
|
|
203
|
+
job_obj = Job(job_id="test-id", name="Test")
|
|
204
|
+
|
|
205
|
+
with (
|
|
206
|
+
patch("hud.telemetry.job.settings") as mock_settings,
|
|
207
|
+
patch("hud.utils.async_utils.fire_and_forget") as mock_fire,
|
|
208
|
+
):
|
|
209
|
+
mock_settings.telemetry_enabled = True
|
|
210
|
+
|
|
211
|
+
job_obj.update_status_fire_and_forget("running")
|
|
212
|
+
|
|
213
|
+
assert job_obj.status == "running"
|
|
214
|
+
mock_fire.assert_called_once()
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
def test_job_update_status_fire_and_forget_with_dataset():
|
|
218
|
+
"""Test fire-and-forget update includes dataset link."""
|
|
219
|
+
job_obj = Job(job_id="test-id", name="Test", dataset_link="test/dataset")
|
|
220
|
+
|
|
221
|
+
with (
|
|
222
|
+
patch("hud.telemetry.job.settings") as mock_settings,
|
|
223
|
+
patch("hud.utils.async_utils.fire_and_forget"),
|
|
224
|
+
):
|
|
225
|
+
mock_settings.telemetry_enabled = True
|
|
226
|
+
|
|
227
|
+
job_obj.update_status_fire_and_forget("running")
|
|
228
|
+
|
|
229
|
+
assert job_obj.status == "running"
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
def test_job_update_status_fire_and_forget_telemetry_disabled():
|
|
233
|
+
"""Test fire-and-forget when telemetry is disabled."""
|
|
234
|
+
job_obj = Job(job_id="test-id", name="Test")
|
|
235
|
+
|
|
236
|
+
with (
|
|
237
|
+
patch("hud.telemetry.job.settings") as mock_settings,
|
|
238
|
+
patch("hud.utils.async_utils.fire_and_forget") as mock_fire,
|
|
239
|
+
):
|
|
240
|
+
mock_settings.telemetry_enabled = False
|
|
241
|
+
|
|
242
|
+
job_obj.update_status_fire_and_forget("running")
|
|
243
|
+
|
|
244
|
+
mock_fire.assert_not_called()
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
@pytest.mark.asyncio
|
|
248
|
+
async def test_job_log():
|
|
249
|
+
"""Test async log method."""
|
|
250
|
+
job_obj = Job(job_id="test-id", name="Test")
|
|
251
|
+
|
|
252
|
+
with (
|
|
253
|
+
patch("hud.telemetry.job.settings") as mock_settings,
|
|
254
|
+
patch("hud.telemetry.job.make_request", new_callable=AsyncMock) as mock_request,
|
|
255
|
+
):
|
|
256
|
+
mock_settings.telemetry_enabled = True
|
|
257
|
+
mock_settings.api_key = "test_key"
|
|
258
|
+
mock_settings.hud_telemetry_url = "https://test.com"
|
|
259
|
+
|
|
260
|
+
await job_obj.log({"loss": 0.5, "accuracy": 0.95})
|
|
261
|
+
|
|
262
|
+
mock_request.assert_called_once()
|
|
263
|
+
call_kwargs = mock_request.call_args[1]
|
|
264
|
+
assert call_kwargs["json"]["metrics"] == {"loss": 0.5, "accuracy": 0.95}
|
|
265
|
+
assert "timestamp" in call_kwargs["json"]
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
@pytest.mark.asyncio
|
|
269
|
+
async def test_job_log_telemetry_disabled():
|
|
270
|
+
"""Test async log when telemetry is disabled."""
|
|
271
|
+
job_obj = Job(job_id="test-id", name="Test")
|
|
272
|
+
|
|
273
|
+
with (
|
|
274
|
+
patch("hud.telemetry.job.settings") as mock_settings,
|
|
275
|
+
patch("hud.telemetry.job.make_request", new_callable=AsyncMock) as mock_request,
|
|
276
|
+
):
|
|
277
|
+
mock_settings.telemetry_enabled = False
|
|
278
|
+
|
|
279
|
+
await job_obj.log({"loss": 0.5})
|
|
280
|
+
|
|
281
|
+
mock_request.assert_not_called()
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
@pytest.mark.asyncio
|
|
285
|
+
async def test_job_log_error():
|
|
286
|
+
"""Test async log handles errors gracefully."""
|
|
287
|
+
job_obj = Job(job_id="test-id", name="Test")
|
|
288
|
+
|
|
289
|
+
with (
|
|
290
|
+
patch("hud.telemetry.job.settings") as mock_settings,
|
|
291
|
+
patch("hud.telemetry.job.make_request", new_callable=AsyncMock) as mock_request,
|
|
292
|
+
):
|
|
293
|
+
mock_settings.telemetry_enabled = True
|
|
294
|
+
mock_settings.api_key = "test_key"
|
|
295
|
+
mock_settings.hud_telemetry_url = "https://test.com"
|
|
296
|
+
mock_request.side_effect = Exception("Network error")
|
|
297
|
+
|
|
298
|
+
# Should not raise
|
|
299
|
+
await job_obj.log({"loss": 0.5})
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
def test_job_log_sync():
|
|
303
|
+
"""Test sync log method."""
|
|
304
|
+
job_obj = Job(job_id="test-id", name="Test")
|
|
305
|
+
|
|
306
|
+
with (
|
|
307
|
+
patch("hud.telemetry.job.settings") as mock_settings,
|
|
308
|
+
patch("hud.telemetry.job.make_request_sync") as mock_request,
|
|
309
|
+
):
|
|
310
|
+
mock_settings.telemetry_enabled = True
|
|
311
|
+
mock_settings.api_key = "test_key"
|
|
312
|
+
mock_settings.hud_telemetry_url = "https://test.com"
|
|
313
|
+
|
|
314
|
+
job_obj.log_sync({"loss": 0.5, "accuracy": 0.95})
|
|
315
|
+
|
|
316
|
+
mock_request.assert_called_once()
|
|
317
|
+
call_kwargs = mock_request.call_args[1]
|
|
318
|
+
assert call_kwargs["json"]["metrics"] == {"loss": 0.5, "accuracy": 0.95}
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
def test_job_log_sync_telemetry_disabled():
|
|
322
|
+
"""Test sync log when telemetry is disabled."""
|
|
323
|
+
job_obj = Job(job_id="test-id", name="Test")
|
|
324
|
+
|
|
325
|
+
with (
|
|
326
|
+
patch("hud.telemetry.job.settings") as mock_settings,
|
|
327
|
+
patch("hud.telemetry.job.make_request_sync") as mock_request,
|
|
328
|
+
):
|
|
329
|
+
mock_settings.telemetry_enabled = False
|
|
330
|
+
|
|
331
|
+
job_obj.log_sync({"loss": 0.5})
|
|
332
|
+
|
|
333
|
+
mock_request.assert_not_called()
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
def test_job_log_sync_error():
|
|
337
|
+
"""Test sync log handles errors gracefully."""
|
|
338
|
+
job_obj = Job(job_id="test-id", name="Test")
|
|
339
|
+
|
|
340
|
+
with (
|
|
341
|
+
patch("hud.telemetry.job.settings") as mock_settings,
|
|
342
|
+
patch("hud.telemetry.job.make_request_sync") as mock_request,
|
|
343
|
+
):
|
|
344
|
+
mock_settings.telemetry_enabled = True
|
|
345
|
+
mock_settings.api_key = "test_key"
|
|
346
|
+
mock_settings.hud_telemetry_url = "https://test.com"
|
|
347
|
+
mock_request.side_effect = Exception("Network error")
|
|
348
|
+
|
|
349
|
+
# Should not raise
|
|
350
|
+
job_obj.log_sync({"loss": 0.5})
|
|
351
|
+
|
|
352
|
+
|
|
353
|
+
def test_job_repr():
|
|
354
|
+
"""Test Job __repr__."""
|
|
355
|
+
job_obj = Job(job_id="test-id", name="Test Job")
|
|
356
|
+
job_obj.status = "running"
|
|
357
|
+
|
|
358
|
+
repr_str = repr(job_obj)
|
|
359
|
+
assert "test-id" in repr_str
|
|
360
|
+
assert "Test Job" in repr_str
|
|
361
|
+
assert "running" in repr_str
|
|
362
|
+
|
|
363
|
+
|
|
364
|
+
def test_print_job_url_enabled():
|
|
365
|
+
"""Test _print_job_url when telemetry is enabled."""
|
|
366
|
+
with (
|
|
367
|
+
patch("hud.telemetry.job.settings") as mock_settings,
|
|
368
|
+
patch("builtins.print") as mock_print,
|
|
369
|
+
):
|
|
370
|
+
mock_settings.telemetry_enabled = True
|
|
371
|
+
mock_settings.api_key = "test_key"
|
|
372
|
+
|
|
373
|
+
_print_job_url("job-123", "My Job")
|
|
374
|
+
|
|
375
|
+
# Should print multiple lines (box)
|
|
376
|
+
assert mock_print.call_count > 0
|
|
377
|
+
|
|
378
|
+
|
|
379
|
+
def test_print_job_url_disabled():
|
|
380
|
+
"""Test _print_job_url when telemetry is disabled."""
|
|
381
|
+
with (
|
|
382
|
+
patch("hud.telemetry.job.settings") as mock_settings,
|
|
383
|
+
patch("builtins.print") as mock_print,
|
|
384
|
+
):
|
|
385
|
+
mock_settings.telemetry_enabled = False
|
|
386
|
+
|
|
387
|
+
_print_job_url("job-123", "My Job")
|
|
388
|
+
|
|
389
|
+
mock_print.assert_not_called()
|
|
390
|
+
|
|
391
|
+
|
|
392
|
+
def test_print_job_url_no_api_key():
|
|
393
|
+
"""Test _print_job_url when no API key is set."""
|
|
394
|
+
with (
|
|
395
|
+
patch("hud.telemetry.job.settings") as mock_settings,
|
|
396
|
+
patch("builtins.print") as mock_print,
|
|
397
|
+
):
|
|
398
|
+
mock_settings.telemetry_enabled = True
|
|
399
|
+
mock_settings.api_key = None
|
|
400
|
+
|
|
401
|
+
_print_job_url("job-123", "My Job")
|
|
402
|
+
|
|
403
|
+
mock_print.assert_not_called()
|
|
404
|
+
|
|
405
|
+
|
|
406
|
+
def test_print_job_complete_url_success():
|
|
407
|
+
"""Test _print_job_complete_url for successful completion."""
|
|
408
|
+
with (
|
|
409
|
+
patch("hud.telemetry.job.settings") as mock_settings,
|
|
410
|
+
patch("builtins.print") as mock_print,
|
|
411
|
+
):
|
|
412
|
+
mock_settings.telemetry_enabled = True
|
|
413
|
+
mock_settings.api_key = "test_key"
|
|
414
|
+
|
|
415
|
+
_print_job_complete_url("job-123", "My Job", error_occurred=False)
|
|
416
|
+
|
|
417
|
+
mock_print.assert_called_once()
|
|
418
|
+
call_str = str(mock_print.call_args)
|
|
419
|
+
assert "complete" in call_str.lower() or "✓" in call_str
|
|
420
|
+
|
|
421
|
+
|
|
422
|
+
def test_print_job_complete_url_failure():
|
|
423
|
+
"""Test _print_job_complete_url for failed completion."""
|
|
424
|
+
with (
|
|
425
|
+
patch("hud.telemetry.job.settings") as mock_settings,
|
|
426
|
+
patch("builtins.print") as mock_print,
|
|
427
|
+
):
|
|
428
|
+
mock_settings.telemetry_enabled = True
|
|
429
|
+
mock_settings.api_key = "test_key"
|
|
430
|
+
|
|
431
|
+
_print_job_complete_url("job-123", "My Job", error_occurred=True)
|
|
432
|
+
|
|
433
|
+
mock_print.assert_called_once()
|
|
434
|
+
call_str = str(mock_print.call_args)
|
|
435
|
+
assert "fail" in call_str.lower() or "✗" in call_str
|
|
436
|
+
|
|
437
|
+
|
|
438
|
+
def test_print_job_complete_url_disabled():
|
|
439
|
+
"""Test _print_job_complete_url when telemetry is disabled."""
|
|
440
|
+
with (
|
|
441
|
+
patch("hud.telemetry.job.settings") as mock_settings,
|
|
442
|
+
patch("builtins.print") as mock_print,
|
|
443
|
+
):
|
|
444
|
+
mock_settings.telemetry_enabled = False
|
|
445
|
+
|
|
446
|
+
_print_job_complete_url("job-123", "My Job")
|
|
447
|
+
|
|
448
|
+
mock_print.assert_not_called()
|
|
449
|
+
|
|
450
|
+
|
|
451
|
+
def test_get_current_job_none():
|
|
452
|
+
"""Test get_current_job when no job is active."""
|
|
453
|
+
result = get_current_job()
|
|
454
|
+
assert result is None
|
|
455
|
+
|
|
456
|
+
|
|
457
|
+
def test_job_context_manager():
|
|
458
|
+
"""Test job context manager."""
|
|
459
|
+
with (
|
|
460
|
+
patch("hud.telemetry.job.settings") as mock_settings,
|
|
461
|
+
patch("hud.utils.async_utils.fire_and_forget"),
|
|
462
|
+
patch("builtins.print"),
|
|
463
|
+
):
|
|
464
|
+
mock_settings.telemetry_enabled = True
|
|
465
|
+
mock_settings.api_key = "test_key"
|
|
466
|
+
|
|
467
|
+
with job("Test Job", {"key": "value"}) as job_obj:
|
|
468
|
+
assert job_obj.name == "Test Job"
|
|
469
|
+
assert job_obj.metadata == {"key": "value"}
|
|
470
|
+
assert get_current_job() == job_obj
|
|
471
|
+
|
|
472
|
+
# After context, job should be cleared
|
|
473
|
+
assert get_current_job() is None
|
|
474
|
+
|
|
475
|
+
|
|
476
|
+
def test_job_context_manager_with_job_id():
|
|
477
|
+
"""Test job context manager with explicit job_id."""
|
|
478
|
+
with (
|
|
479
|
+
patch("hud.telemetry.job.settings") as mock_settings,
|
|
480
|
+
patch("hud.utils.async_utils.fire_and_forget"),
|
|
481
|
+
patch("builtins.print"),
|
|
482
|
+
):
|
|
483
|
+
mock_settings.telemetry_enabled = True
|
|
484
|
+
mock_settings.api_key = "test_key"
|
|
485
|
+
|
|
486
|
+
with job("Test", job_id="my-custom-id") as job_obj:
|
|
487
|
+
assert job_obj.id == "my-custom-id"
|
|
488
|
+
|
|
489
|
+
|
|
490
|
+
def test_job_context_manager_with_dataset_link():
|
|
491
|
+
"""Test job context manager with dataset link."""
|
|
492
|
+
with (
|
|
493
|
+
patch("hud.telemetry.job.settings") as mock_settings,
|
|
494
|
+
patch("hud.utils.async_utils.fire_and_forget"),
|
|
495
|
+
patch("builtins.print"),
|
|
496
|
+
):
|
|
497
|
+
mock_settings.telemetry_enabled = True
|
|
498
|
+
mock_settings.api_key = "test_key"
|
|
499
|
+
|
|
500
|
+
with job("Test", dataset_link="test/dataset") as job_obj:
|
|
501
|
+
assert job_obj.dataset_link == "test/dataset"
|
|
502
|
+
|
|
503
|
+
|
|
504
|
+
def test_job_context_manager_exception():
|
|
505
|
+
"""Test job context manager handles exceptions."""
|
|
506
|
+
with (
|
|
507
|
+
patch("hud.telemetry.job.settings") as mock_settings,
|
|
508
|
+
patch("hud.utils.async_utils.fire_and_forget"),
|
|
509
|
+
patch("builtins.print"),
|
|
510
|
+
):
|
|
511
|
+
mock_settings.telemetry_enabled = True
|
|
512
|
+
mock_settings.api_key = "test_key"
|
|
513
|
+
|
|
514
|
+
with pytest.raises(ValueError), job("Test"):
|
|
515
|
+
raise ValueError("Test error")
|
|
516
|
+
|
|
517
|
+
# Job should be cleared even after exception
|
|
518
|
+
assert get_current_job() is None
|
|
519
|
+
|
|
520
|
+
|
|
521
|
+
def test_create_job():
|
|
522
|
+
"""Test create_job function."""
|
|
523
|
+
job_obj = create_job("Test Job", {"key": "value"}, dataset_link="test/dataset")
|
|
524
|
+
|
|
525
|
+
assert job_obj.name == "Test Job"
|
|
526
|
+
assert job_obj.metadata == {"key": "value"}
|
|
527
|
+
assert job_obj.dataset_link == "test/dataset"
|
|
528
|
+
assert job_obj.id # Should have an auto-generated ID
|
|
529
|
+
|
|
530
|
+
|
|
531
|
+
def test_create_job_with_job_id():
|
|
532
|
+
"""Test create_job with explicit job_id."""
|
|
533
|
+
job_obj = create_job("Test", job_id="custom-id")
|
|
534
|
+
|
|
535
|
+
assert job_obj.id == "custom-id"
|
|
536
|
+
|
|
537
|
+
|
|
538
|
+
@pytest.mark.asyncio
|
|
539
|
+
async def test_job_decorator_async():
|
|
540
|
+
"""Test job_decorator on async function."""
|
|
541
|
+
with (
|
|
542
|
+
patch("hud.telemetry.job.settings") as mock_settings,
|
|
543
|
+
patch("hud.utils.async_utils.fire_and_forget"),
|
|
544
|
+
patch("builtins.print"),
|
|
545
|
+
):
|
|
546
|
+
mock_settings.telemetry_enabled = True
|
|
547
|
+
mock_settings.api_key = "test_key"
|
|
548
|
+
|
|
549
|
+
@job_decorator("test_job", model="gpt-4")
|
|
550
|
+
async def test_func(x: int) -> int:
|
|
551
|
+
return x * 2
|
|
552
|
+
|
|
553
|
+
result = await test_func(5)
|
|
554
|
+
assert result == 10
|
|
555
|
+
|
|
556
|
+
|
|
557
|
+
def test_job_decorator_sync():
|
|
558
|
+
"""Test job_decorator on sync function."""
|
|
559
|
+
with (
|
|
560
|
+
patch("hud.telemetry.job.settings") as mock_settings,
|
|
561
|
+
patch("hud.utils.async_utils.fire_and_forget"),
|
|
562
|
+
patch("builtins.print"),
|
|
563
|
+
):
|
|
564
|
+
mock_settings.telemetry_enabled = True
|
|
565
|
+
mock_settings.api_key = "test_key"
|
|
566
|
+
|
|
567
|
+
@job_decorator("test_job", model="gpt-4")
|
|
568
|
+
def test_func(x: int) -> int:
|
|
569
|
+
return x * 2
|
|
570
|
+
|
|
571
|
+
result = test_func(5)
|
|
572
|
+
assert result == 10
|
|
573
|
+
|
|
574
|
+
|
|
575
|
+
@pytest.mark.asyncio
|
|
576
|
+
async def test_job_decorator_async_default_name():
|
|
577
|
+
"""Test job_decorator uses function name as default."""
|
|
578
|
+
with (
|
|
579
|
+
patch("hud.telemetry.job.settings") as mock_settings,
|
|
580
|
+
patch("hud.utils.async_utils.fire_and_forget"),
|
|
581
|
+
patch("builtins.print"),
|
|
582
|
+
):
|
|
583
|
+
mock_settings.telemetry_enabled = True
|
|
584
|
+
mock_settings.api_key = "test_key"
|
|
585
|
+
|
|
586
|
+
@job_decorator()
|
|
587
|
+
async def my_function():
|
|
588
|
+
return "success"
|
|
589
|
+
|
|
590
|
+
result = await my_function()
|
|
591
|
+
assert result == "success"
|
|
592
|
+
|
|
593
|
+
|
|
594
|
+
def test_job_decorator_sync_default_name():
|
|
595
|
+
"""Test job_decorator sync uses function name as default."""
|
|
596
|
+
with (
|
|
597
|
+
patch("hud.telemetry.job.settings") as mock_settings,
|
|
598
|
+
patch("hud.utils.async_utils.fire_and_forget"),
|
|
599
|
+
patch("builtins.print"),
|
|
600
|
+
):
|
|
601
|
+
mock_settings.telemetry_enabled = True
|
|
602
|
+
mock_settings.api_key = "test_key"
|
|
603
|
+
|
|
604
|
+
@job_decorator()
|
|
605
|
+
def my_function():
|
|
606
|
+
return "success"
|
|
607
|
+
|
|
608
|
+
result = my_function()
|
|
609
|
+
assert result == "success"
|