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/env/remote_docker_client.py
DELETED
|
@@ -1,292 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
import logging
|
|
4
|
-
from base64 import b64decode, b64encode
|
|
5
|
-
from typing import TYPE_CHECKING, Any
|
|
6
|
-
|
|
7
|
-
import httpx
|
|
8
|
-
|
|
9
|
-
from hud.env.docker_client import DockerClient
|
|
10
|
-
from hud.exceptions import HudResponseError
|
|
11
|
-
from hud.server import make_request
|
|
12
|
-
from hud.settings import settings
|
|
13
|
-
from hud.types import EnvironmentStatus
|
|
14
|
-
from hud.utils import ExecuteResult
|
|
15
|
-
from hud.utils.common import directory_to_zip_bytes, get_gym_id
|
|
16
|
-
|
|
17
|
-
if TYPE_CHECKING:
|
|
18
|
-
from pathlib import Path
|
|
19
|
-
|
|
20
|
-
logger = logging.getLogger("hud.env.remote_env_client")
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
async def upload_bytes_to_presigned_url(
|
|
24
|
-
presigned_url: str,
|
|
25
|
-
data_bytes: bytes,
|
|
26
|
-
timeout: float = 600, # noqa: ASYNC109
|
|
27
|
-
) -> None:
|
|
28
|
-
try:
|
|
29
|
-
async with httpx.AsyncClient() as client:
|
|
30
|
-
response = await client.put(presigned_url, content=data_bytes, timeout=timeout)
|
|
31
|
-
response.raise_for_status()
|
|
32
|
-
except httpx.HTTPStatusError as e:
|
|
33
|
-
logger.exception("Failed to upload to presigned URL")
|
|
34
|
-
raise HudResponseError(message=f"Failed to upload to presigned URL: {e}") from e
|
|
35
|
-
except httpx.RequestError as e:
|
|
36
|
-
logger.exception("Network error uploading to presigned URL")
|
|
37
|
-
raise HudResponseError(message=f"Network error uploading to presigned URL: {e}") from e
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
class RemoteDockerClient(DockerClient):
|
|
41
|
-
"""
|
|
42
|
-
Remote environment client implementation.
|
|
43
|
-
|
|
44
|
-
Uses the HUD API to manage a remote environment.
|
|
45
|
-
"""
|
|
46
|
-
|
|
47
|
-
@classmethod
|
|
48
|
-
async def build_image(cls, build_context: Path) -> tuple[str, dict[str, Any]]:
|
|
49
|
-
"""
|
|
50
|
-
Build an image from a build context.
|
|
51
|
-
"""
|
|
52
|
-
# create the presigned url by making a POST request to /v2/builds
|
|
53
|
-
logger.info("Creating build")
|
|
54
|
-
response = await make_request(
|
|
55
|
-
method="POST",
|
|
56
|
-
url=f"{settings.base_url}/v2/builds",
|
|
57
|
-
api_key=settings.api_key,
|
|
58
|
-
)
|
|
59
|
-
logger.info("Build created")
|
|
60
|
-
presigned_url = response["presigned_url"]
|
|
61
|
-
|
|
62
|
-
# List files in the build context
|
|
63
|
-
files = list(build_context.glob("**/*"))
|
|
64
|
-
logger.info("Found %d files in build context %s", len(files), build_context)
|
|
65
|
-
|
|
66
|
-
if len(files) == 0:
|
|
67
|
-
raise HudResponseError(message="Build context is empty")
|
|
68
|
-
|
|
69
|
-
# zip the build context
|
|
70
|
-
logger.info("Zipping build context")
|
|
71
|
-
zip_bytes = directory_to_zip_bytes(build_context)
|
|
72
|
-
logger.info("Created zip archive of size %d kb", len(zip_bytes) // 1024)
|
|
73
|
-
# upload the zip bytes to the presigned url
|
|
74
|
-
logger.info("Uploading build context")
|
|
75
|
-
await upload_bytes_to_presigned_url(presigned_url, zip_bytes)
|
|
76
|
-
logger.info("Build context uploaded")
|
|
77
|
-
|
|
78
|
-
# start the build and return uri and logs
|
|
79
|
-
logger.info("Starting build")
|
|
80
|
-
response = await make_request(
|
|
81
|
-
method="POST",
|
|
82
|
-
url=f"{settings.base_url}/v2/builds/{response['id']}/start",
|
|
83
|
-
api_key=settings.api_key,
|
|
84
|
-
)
|
|
85
|
-
logger.info("Build completed")
|
|
86
|
-
|
|
87
|
-
return response["uri"], {"logs": response["logs"]}
|
|
88
|
-
|
|
89
|
-
@classmethod
|
|
90
|
-
async def create(
|
|
91
|
-
cls,
|
|
92
|
-
image_uri: str,
|
|
93
|
-
*,
|
|
94
|
-
job_id: str | None = None,
|
|
95
|
-
task_id: str | None = None,
|
|
96
|
-
metadata: dict[str, Any] | None = None,
|
|
97
|
-
) -> RemoteDockerClient:
|
|
98
|
-
"""
|
|
99
|
-
Creates a remote environment client from an image.
|
|
100
|
-
|
|
101
|
-
Args:
|
|
102
|
-
image_uri: The image uri to create the environment from
|
|
103
|
-
job_id: The job_id of the environment to create
|
|
104
|
-
task_id: The task_id of the environment to create
|
|
105
|
-
metadata: Metadata to associate with the environment
|
|
106
|
-
|
|
107
|
-
Returns:
|
|
108
|
-
A tuple containing the remote environment client and the build metadata
|
|
109
|
-
|
|
110
|
-
Raises:
|
|
111
|
-
HudResponseError: If the environment creation fails.
|
|
112
|
-
"""
|
|
113
|
-
|
|
114
|
-
# Validate arguments
|
|
115
|
-
if metadata is None:
|
|
116
|
-
metadata = {}
|
|
117
|
-
|
|
118
|
-
logger.info("Creating remote environment")
|
|
119
|
-
|
|
120
|
-
# true_gym_id = await get_gym_id("local-docker")
|
|
121
|
-
true_gym_id = await get_gym_id("docker")
|
|
122
|
-
|
|
123
|
-
# augment metadata with dockerfile
|
|
124
|
-
if "environment_config" not in metadata:
|
|
125
|
-
metadata["environment_config"] = {}
|
|
126
|
-
|
|
127
|
-
metadata["environment_config"]["image_uri"] = image_uri
|
|
128
|
-
|
|
129
|
-
# Create a new environment via the HUD API
|
|
130
|
-
response = await make_request(
|
|
131
|
-
method="POST",
|
|
132
|
-
url=f"{settings.base_url}/v2/create_environment",
|
|
133
|
-
json={
|
|
134
|
-
# still named run_id for backwards compatibility
|
|
135
|
-
"run_id": job_id,
|
|
136
|
-
"metadata": metadata,
|
|
137
|
-
"gym_id": true_gym_id,
|
|
138
|
-
"task_id": task_id,
|
|
139
|
-
},
|
|
140
|
-
api_key=settings.api_key,
|
|
141
|
-
)
|
|
142
|
-
|
|
143
|
-
# Get the environment ID from the response
|
|
144
|
-
env_id = response.get("id")
|
|
145
|
-
if not env_id:
|
|
146
|
-
raise HudResponseError(
|
|
147
|
-
message=(
|
|
148
|
-
"Failed to create remote environment: No ID returned in API response. "
|
|
149
|
-
"Please contact support if this issue persists."
|
|
150
|
-
),
|
|
151
|
-
response_json=response,
|
|
152
|
-
)
|
|
153
|
-
|
|
154
|
-
return cls(env_id)
|
|
155
|
-
|
|
156
|
-
def __init__(self, env_id: str) -> None:
|
|
157
|
-
"""
|
|
158
|
-
Initialize the RemoteClient.
|
|
159
|
-
|
|
160
|
-
Args:
|
|
161
|
-
env_id: ID of the remote environment to control
|
|
162
|
-
"""
|
|
163
|
-
super().__init__()
|
|
164
|
-
self._env_id = env_id
|
|
165
|
-
|
|
166
|
-
@property
|
|
167
|
-
def env_id(self) -> str:
|
|
168
|
-
"""The ID of the remote environment."""
|
|
169
|
-
return self._env_id
|
|
170
|
-
|
|
171
|
-
async def get_status(self) -> EnvironmentStatus:
|
|
172
|
-
"""
|
|
173
|
-
Get the current status of the remote environment.
|
|
174
|
-
|
|
175
|
-
Returns:
|
|
176
|
-
EnvironmentStatus: The current status of the environment
|
|
177
|
-
"""
|
|
178
|
-
try:
|
|
179
|
-
response = await make_request(
|
|
180
|
-
method="GET",
|
|
181
|
-
url=f"{settings.base_url}/v2/environments/{self.env_id}/state",
|
|
182
|
-
api_key=settings.api_key,
|
|
183
|
-
)
|
|
184
|
-
logger.debug("Environment status response: %s", response)
|
|
185
|
-
|
|
186
|
-
status = response.get("state", "").lower()
|
|
187
|
-
|
|
188
|
-
if status == "running":
|
|
189
|
-
return EnvironmentStatus.RUNNING
|
|
190
|
-
elif status == "initializing" or status == "pending":
|
|
191
|
-
return EnvironmentStatus.INITIALIZING
|
|
192
|
-
elif status == "completed" or status == "terminated":
|
|
193
|
-
return EnvironmentStatus.COMPLETED
|
|
194
|
-
else:
|
|
195
|
-
# Any other status is considered an error
|
|
196
|
-
logger.warning("Abnormal environment status response: %s", response)
|
|
197
|
-
return EnvironmentStatus.ERROR
|
|
198
|
-
|
|
199
|
-
except Exception:
|
|
200
|
-
# If we can't connect to the API or there's any other error
|
|
201
|
-
logger.info("(potentially transient) Error getting environment status")
|
|
202
|
-
return EnvironmentStatus.ERROR
|
|
203
|
-
|
|
204
|
-
async def execute(
|
|
205
|
-
self,
|
|
206
|
-
command: list[str],
|
|
207
|
-
*,
|
|
208
|
-
workdir: str | None = None,
|
|
209
|
-
timeout: float | None = None, # noqa: ASYNC109
|
|
210
|
-
) -> ExecuteResult:
|
|
211
|
-
"""
|
|
212
|
-
Execute a command in the environment.
|
|
213
|
-
No-op in some environments (like browser use).
|
|
214
|
-
|
|
215
|
-
Args:
|
|
216
|
-
command: Command to execute
|
|
217
|
-
workdir: Working directory for the command (ignored for remote environments)
|
|
218
|
-
|
|
219
|
-
Returns:
|
|
220
|
-
ExecuteResult: Result of the command execution
|
|
221
|
-
"""
|
|
222
|
-
data = await make_request(
|
|
223
|
-
method="POST",
|
|
224
|
-
url=f"{settings.base_url}/v2/environments/{self.env_id}/execute",
|
|
225
|
-
json={
|
|
226
|
-
"command": command,
|
|
227
|
-
"workdir": workdir,
|
|
228
|
-
"timeout": timeout,
|
|
229
|
-
},
|
|
230
|
-
api_key=settings.api_key,
|
|
231
|
-
)
|
|
232
|
-
|
|
233
|
-
return ExecuteResult(
|
|
234
|
-
stdout=b64decode(data["stdout"]),
|
|
235
|
-
stderr=b64decode(data["stderr"]),
|
|
236
|
-
exit_code=data["exit_code"],
|
|
237
|
-
)
|
|
238
|
-
|
|
239
|
-
async def get_archive(self, path: str) -> bytes:
|
|
240
|
-
"""
|
|
241
|
-
Get an archive of a path from the environment.
|
|
242
|
-
May not be supported for all environments.
|
|
243
|
-
|
|
244
|
-
Args:
|
|
245
|
-
path: Path in the environment to archive
|
|
246
|
-
|
|
247
|
-
Returns:
|
|
248
|
-
bytes: Content of the file or archive
|
|
249
|
-
"""
|
|
250
|
-
data = await make_request(
|
|
251
|
-
method="POST",
|
|
252
|
-
url=f"{settings.base_url}/v2/environments/{self.env_id}/get_archive",
|
|
253
|
-
json={"path": path},
|
|
254
|
-
api_key=settings.api_key,
|
|
255
|
-
)
|
|
256
|
-
|
|
257
|
-
# Return the content decoded from base64
|
|
258
|
-
return b64decode(data["content"])
|
|
259
|
-
|
|
260
|
-
async def put_archive(self, path: str, data: bytes) -> bool:
|
|
261
|
-
"""
|
|
262
|
-
Put an archive of data at a path in the environment.
|
|
263
|
-
May not be supported for all environments.
|
|
264
|
-
|
|
265
|
-
Args:
|
|
266
|
-
path: Path in the environment to extract the archive to
|
|
267
|
-
data: Bytes of the data to send
|
|
268
|
-
|
|
269
|
-
Returns:
|
|
270
|
-
bool: True if successful
|
|
271
|
-
"""
|
|
272
|
-
await make_request(
|
|
273
|
-
method="POST",
|
|
274
|
-
url=f"{settings.base_url}/v2/environments/{self.env_id}/put_archive",
|
|
275
|
-
json={
|
|
276
|
-
"path": path,
|
|
277
|
-
"content": b64encode(data).decode("utf-8"),
|
|
278
|
-
},
|
|
279
|
-
api_key=settings.api_key,
|
|
280
|
-
)
|
|
281
|
-
|
|
282
|
-
return True
|
|
283
|
-
|
|
284
|
-
async def close(self) -> None:
|
|
285
|
-
"""
|
|
286
|
-
Close the remote environment by making a request to the server.
|
|
287
|
-
"""
|
|
288
|
-
await make_request(
|
|
289
|
-
method="POST",
|
|
290
|
-
url=f"{settings.base_url}/v2/environments/{self.env_id}/close",
|
|
291
|
-
api_key=settings.api_key,
|
|
292
|
-
)
|
hud/gym.py
DELETED
|
@@ -1,130 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
import logging
|
|
4
|
-
from pathlib import Path
|
|
5
|
-
from typing import TYPE_CHECKING, Any
|
|
6
|
-
|
|
7
|
-
from hud.env.environment import Environment
|
|
8
|
-
from hud.env.local_docker_client import LocalDockerClient
|
|
9
|
-
from hud.env.remote_client import RemoteClient
|
|
10
|
-
from hud.env.remote_docker_client import RemoteDockerClient
|
|
11
|
-
from hud.exceptions import GymMakeException
|
|
12
|
-
from hud.task import Task
|
|
13
|
-
from hud.telemetry.context import get_current_task_run_id
|
|
14
|
-
from hud.types import CustomGym, Gym
|
|
15
|
-
from hud.utils.common import get_gym_id
|
|
16
|
-
|
|
17
|
-
if TYPE_CHECKING:
|
|
18
|
-
from hud.job import Job
|
|
19
|
-
|
|
20
|
-
logger = logging.getLogger("hud.gym")
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
async def make(
|
|
24
|
-
env_src: Gym | Task,
|
|
25
|
-
*,
|
|
26
|
-
job: Job | None = None,
|
|
27
|
-
job_id: str | None = None,
|
|
28
|
-
metadata: dict[str, Any] | None = None,
|
|
29
|
-
) -> Environment:
|
|
30
|
-
"""
|
|
31
|
-
Create an environment from an environment ID or a Task object.
|
|
32
|
-
|
|
33
|
-
Args:
|
|
34
|
-
env_src: Environment ID or Task object
|
|
35
|
-
job: Job object to associate with this environment
|
|
36
|
-
job_id: ID of job to associate with this environment (deprecated, use job instead)
|
|
37
|
-
metadata: Additional metadata for the environment
|
|
38
|
-
"""
|
|
39
|
-
task = None
|
|
40
|
-
if isinstance(env_src, str | CustomGym):
|
|
41
|
-
gym = env_src
|
|
42
|
-
elif isinstance(env_src, Task):
|
|
43
|
-
gym = env_src.gym
|
|
44
|
-
task = env_src
|
|
45
|
-
else:
|
|
46
|
-
raise GymMakeException(f"Invalid gym source: {env_src}", {})
|
|
47
|
-
|
|
48
|
-
effective_job_id = None
|
|
49
|
-
if job is not None:
|
|
50
|
-
effective_job_id = job.id
|
|
51
|
-
elif job_id is not None:
|
|
52
|
-
effective_job_id = job_id
|
|
53
|
-
|
|
54
|
-
build_data = {}
|
|
55
|
-
try:
|
|
56
|
-
metadata_copy = {} if metadata is None else metadata.copy()
|
|
57
|
-
|
|
58
|
-
current_task_run_id = get_current_task_run_id()
|
|
59
|
-
if current_task_run_id:
|
|
60
|
-
metadata_copy["task_run_id"] = current_task_run_id
|
|
61
|
-
logger.debug(
|
|
62
|
-
"Passing task_run_id %s from hud.telemetry context to environment metadata.",
|
|
63
|
-
current_task_run_id,
|
|
64
|
-
)
|
|
65
|
-
|
|
66
|
-
if isinstance(gym, CustomGym):
|
|
67
|
-
if isinstance(gym.image_or_build_context, str):
|
|
68
|
-
uri = gym.image_or_build_context
|
|
69
|
-
elif isinstance(gym.image_or_build_context, Path):
|
|
70
|
-
if gym.location == "local":
|
|
71
|
-
uri, build_data = await LocalDockerClient.build_image(
|
|
72
|
-
gym.image_or_build_context
|
|
73
|
-
)
|
|
74
|
-
elif gym.location == "remote":
|
|
75
|
-
uri, build_data = await RemoteDockerClient.build_image(
|
|
76
|
-
gym.image_or_build_context
|
|
77
|
-
)
|
|
78
|
-
else:
|
|
79
|
-
raise ValueError(f"Invalid environment location: {gym.location}")
|
|
80
|
-
else:
|
|
81
|
-
raise ValueError(f"Invalid image or build context: {gym.image_or_build_context}")
|
|
82
|
-
|
|
83
|
-
if gym.location == "local":
|
|
84
|
-
logger.info("Creating local environment")
|
|
85
|
-
if gym.host_config:
|
|
86
|
-
logger.info("Using host config: %s", gym.host_config)
|
|
87
|
-
client = await LocalDockerClient.create(uri, gym.host_config)
|
|
88
|
-
else:
|
|
89
|
-
client = await LocalDockerClient.create(uri)
|
|
90
|
-
|
|
91
|
-
elif gym.location == "remote":
|
|
92
|
-
logger.info("Creating remote environment")
|
|
93
|
-
|
|
94
|
-
if gym.host_config:
|
|
95
|
-
raise ValueError("host_config is not supported for remote environments")
|
|
96
|
-
|
|
97
|
-
client = await RemoteDockerClient.create(
|
|
98
|
-
image_uri=uri,
|
|
99
|
-
job_id=effective_job_id,
|
|
100
|
-
task_id=task.id if task else None,
|
|
101
|
-
metadata=metadata_copy,
|
|
102
|
-
)
|
|
103
|
-
else:
|
|
104
|
-
raise ValueError(f"Invalid environment location: {gym.location}")
|
|
105
|
-
|
|
106
|
-
if isinstance(gym.image_or_build_context, Path):
|
|
107
|
-
logger.info("Setting source path %s", gym.image_or_build_context)
|
|
108
|
-
client.set_source_path(gym.image_or_build_context)
|
|
109
|
-
elif isinstance(gym, str):
|
|
110
|
-
logger.debug("Creating private environment")
|
|
111
|
-
true_gym_id = await get_gym_id(gym)
|
|
112
|
-
client, build_data = await RemoteClient.create(
|
|
113
|
-
gym_id=true_gym_id,
|
|
114
|
-
job_id=effective_job_id,
|
|
115
|
-
task_id=task.id if task else None,
|
|
116
|
-
metadata=metadata_copy,
|
|
117
|
-
)
|
|
118
|
-
else:
|
|
119
|
-
raise ValueError(f"Invalid gym source: {gym}")
|
|
120
|
-
|
|
121
|
-
environment = Environment(
|
|
122
|
-
client=client, metadata=metadata_copy, task=task, build_data=build_data
|
|
123
|
-
)
|
|
124
|
-
|
|
125
|
-
if task:
|
|
126
|
-
await environment._setup()
|
|
127
|
-
return environment
|
|
128
|
-
except Exception as e:
|
|
129
|
-
build_data["exception"] = str(e)
|
|
130
|
-
raise GymMakeException("Failed to create environment", build_data) from e
|