hud-python 0.3.4__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 -414
  87. hud/tools/computer/hud.py +376 -328
  88. hud/tools/computer/openai.py +295 -286
  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.4.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.4.dist-info/METADATA +0 -284
  190. hud_python-0.3.4.dist-info/RECORD +0 -120
  191. /hud/{adapters/common → shared}/tests/__init__.py +0 -0
  192. {hud_python-0.3.4.dist-info → hud_python-0.4.0.dist-info}/WHEEL +0 -0
hud/tools/bash.py CHANGED
@@ -1,137 +1,161 @@
1
- from __future__ import annotations
2
-
3
- import asyncio
4
- import os
5
- import sys
6
- from typing import Any
7
-
8
- from .base import CLIResult, ToolError, ToolResult
9
-
10
-
11
- class _BashSession:
12
- """A session of a bash shell."""
13
-
14
- _started: bool
15
- _process: asyncio.subprocess.Process
16
-
17
- command: str = "/bin/bash"
18
- _output_delay: float = 0.2 # seconds
19
- _timeout: float = 120.0 # seconds
20
- _sentinel: str = "<<exit>>"
21
-
22
- def __init__(self) -> None:
23
- self._started = False
24
- self._timed_out = False
25
-
26
- async def start(self) -> None:
27
- if self._started:
28
- await asyncio.sleep(0)
29
- return
30
-
31
- # Platform-specific subprocess creation
32
- kwargs = {
33
- "shell": True,
34
- "bufsize": 0,
35
- "stdin": asyncio.subprocess.PIPE,
36
- "stdout": asyncio.subprocess.PIPE,
37
- "stderr": asyncio.subprocess.PIPE,
38
- }
39
-
40
- # Only use setsid on Unix-like systems
41
- if sys.platform != "win32":
42
- kwargs["preexec_fn"] = os.setsid
43
-
44
- self._process = await asyncio.create_subprocess_shell(self.command, **kwargs)
45
-
46
- self._started = True
47
-
48
- def stop(self) -> None:
49
- """Terminate the bash shell."""
50
- if not self._started:
51
- raise ToolError("Session has not started.")
52
- if self._process.returncode is not None:
53
- return
54
- self._process.terminate()
55
-
56
- async def run(self, command: str) -> CLIResult:
57
- """Execute a command in the bash shell."""
58
- if not self._started:
59
- raise ToolError("Session has not started.")
60
- if self._process.returncode is not None:
61
- await asyncio.sleep(0)
62
- return ToolResult(
63
- system="tool must be restarted",
64
- error=f"bash has exited with returncode {self._process.returncode}",
65
- )
66
- if self._timed_out:
67
- raise ToolError(
68
- f"timed out: bash did not return in {self._timeout} seconds and must be restarted",
69
- ) from None
70
-
71
- if self._process.stdin is None:
72
- raise ToolError("stdin is None")
73
- if self._process.stdout is None:
74
- raise ToolError("stdout is None")
75
- if self._process.stderr is None:
76
- raise ToolError("stderr is None")
77
-
78
- # Send command to the process
79
- self._process.stdin.write(command.encode() + f"; echo '{self._sentinel}'\n".encode())
80
- await self._process.stdin.drain()
81
-
82
- # Read output from the process, until the sentinel is found
83
- sentinel_line = f"{self._sentinel}\n"
84
- sentinel_bytes = sentinel_line.encode()
85
-
86
- try:
87
- raw_out: bytes = await asyncio.wait_for(
88
- self._process.stdout.readuntil(sentinel_bytes),
89
- timeout=self._timeout,
90
- )
91
- output = raw_out.decode()[: -len(sentinel_line)]
92
- except (TimeoutError, asyncio.LimitOverrunError):
93
- self._timed_out = True
94
- raise ToolError(
95
- f"timed out: bash did not return in {self._timeout} seconds and must be restarted",
96
- ) from None
97
-
98
- # Attempt non-blocking stderr fetch (may return empty)
99
- try:
100
- error_bytes = await asyncio.wait_for(self._process.stderr.read(), timeout=0.01)
101
- error = error_bytes.decode().rstrip("\n")
102
- except TimeoutError:
103
- error = ""
104
-
105
- return CLIResult(output=output, error=error)
106
-
107
-
108
- class BashTool:
109
- """
110
- A tool that allows the agent to run bash commands.
111
- The tool parameters are defined by Anthropic and are not editable.
112
- """
113
-
114
- _session: _BashSession | None
115
-
116
- def __init__(self) -> None:
117
- self._session = None
118
-
119
- async def __call__(
120
- self, command: str | None = None, restart: bool = False, **kwargs: Any
121
- ) -> ToolResult:
122
- if restart:
123
- if self._session:
124
- self._session.stop()
125
- self._session = _BashSession()
126
- await self._session.start()
127
-
128
- return ToolResult(system="tool has been restarted.")
129
-
130
- if self._session is None:
131
- self._session = _BashSession()
132
- await self._session.start()
133
-
134
- if command is not None:
135
- return await self._session.run(command)
136
-
137
- raise ToolError("no command provided.")
1
+ from __future__ import annotations
2
+
3
+ import asyncio
4
+ import os
5
+ import sys
6
+ from typing import TYPE_CHECKING, Any
7
+
8
+ from .base import BaseTool
9
+ from .types import ContentResult, ToolError
10
+
11
+ if TYPE_CHECKING:
12
+ from mcp.types import ContentBlock
13
+
14
+
15
+ class _BashSession:
16
+ """A session of a bash shell."""
17
+
18
+ _started: bool
19
+ _process: asyncio.subprocess.Process
20
+
21
+ command: str = "/bin/bash"
22
+ _output_delay: float = 0.2 # seconds
23
+ _timeout: float = 120.0 # seconds
24
+ _sentinel: str = "<<exit>>"
25
+
26
+ def __init__(self) -> None:
27
+ self._started = False
28
+ self._timed_out = False
29
+
30
+ async def start(self) -> None:
31
+ if self._started:
32
+ await asyncio.sleep(0)
33
+ return
34
+
35
+ # Platform-specific subprocess creation
36
+ kwargs = {
37
+ "shell": True,
38
+ "bufsize": 0,
39
+ "stdin": asyncio.subprocess.PIPE,
40
+ "stdout": asyncio.subprocess.PIPE,
41
+ "stderr": asyncio.subprocess.PIPE,
42
+ }
43
+
44
+ # Only use setsid on Unix-like systems
45
+ if sys.platform != "win32":
46
+ kwargs["preexec_fn"] = os.setsid
47
+
48
+ self._process = await asyncio.create_subprocess_shell(self.command, **kwargs)
49
+
50
+ self._started = True
51
+
52
+ def stop(self) -> None:
53
+ """Terminate the bash shell."""
54
+ if not self._started:
55
+ raise ToolError("Session has not started.")
56
+ if self._process.returncode is not None:
57
+ return
58
+ self._process.terminate()
59
+
60
+ async def run(self, command: str) -> ContentResult:
61
+ """Execute a command in the bash shell."""
62
+ if not self._started:
63
+ raise ToolError("Session has not started.")
64
+ if self._process.returncode is not None:
65
+ await asyncio.sleep(0)
66
+ return ContentResult(
67
+ system="tool must be restarted",
68
+ error=f"bash has exited with returncode {self._process.returncode}",
69
+ )
70
+ if self._timed_out:
71
+ raise ToolError(
72
+ f"timed out: bash did not return in {self._timeout} seconds and must be restarted",
73
+ ) from None
74
+
75
+ if self._process.stdin is None:
76
+ raise ToolError("stdin is None")
77
+ if self._process.stdout is None:
78
+ raise ToolError("stdout is None")
79
+ if self._process.stderr is None:
80
+ raise ToolError("stderr is None")
81
+
82
+ # Send command to the process
83
+ self._process.stdin.write(command.encode() + f"; echo '{self._sentinel}'\n".encode())
84
+ await self._process.stdin.drain()
85
+
86
+ # Read output from the process, until the sentinel is found
87
+ sentinel_line = f"{self._sentinel}\n"
88
+ sentinel_bytes = sentinel_line.encode()
89
+
90
+ try:
91
+ raw_out: bytes = await asyncio.wait_for(
92
+ self._process.stdout.readuntil(sentinel_bytes),
93
+ timeout=self._timeout,
94
+ )
95
+ output = raw_out.decode()[: -len(sentinel_line)]
96
+ except (TimeoutError, asyncio.LimitOverrunError):
97
+ self._timed_out = True
98
+ raise ToolError(
99
+ f"timed out: bash did not return in {self._timeout} seconds and must be restarted",
100
+ ) from None
101
+
102
+ # Attempt non-blocking stderr fetch (may return empty)
103
+ try:
104
+ error_bytes = await asyncio.wait_for(self._process.stderr.read(), timeout=0.01)
105
+ error = error_bytes.decode().rstrip("\n")
106
+ except TimeoutError:
107
+ error = ""
108
+
109
+ return ContentResult(output=output, error=error)
110
+
111
+
112
+ class BashTool(BaseTool):
113
+ """
114
+ A tool that allows the agent to run bash commands.
115
+ The tool maintains a persistent bash session that can be restarted.
116
+ """
117
+
118
+ def __init__(self, session: _BashSession | None = None) -> None:
119
+ """Initialize BashTool with an optional session.
120
+
121
+ Args:
122
+ session: Optional pre-configured bash session. If not provided,
123
+ a new session will be created on first use.
124
+ """
125
+ super().__init__(
126
+ env=session,
127
+ name="bash",
128
+ title="Bash Shell",
129
+ description="Execute bash commands in a persistent shell session",
130
+ )
131
+
132
+ @property
133
+ def session(self) -> _BashSession | None:
134
+ """Get the current bash session (alias for context)."""
135
+ return self.env
136
+
137
+ @session.setter
138
+ def session(self, value: _BashSession | None) -> None:
139
+ """Set the bash session (alias for context)."""
140
+ self.env = value
141
+
142
+ async def __call__(
143
+ self, command: str | None = None, restart: bool = False, **kwargs: Any
144
+ ) -> list[ContentBlock]:
145
+ if restart:
146
+ if self.session:
147
+ self.session.stop()
148
+ self.session = _BashSession()
149
+ await self.session.start()
150
+
151
+ return ContentResult(output="Bash session restarted.").to_content_blocks()
152
+
153
+ if self.session is None:
154
+ self.session = _BashSession()
155
+ await self.session.start()
156
+
157
+ if command is not None:
158
+ result = await self.session.run(command)
159
+ return result.to_content_blocks()
160
+
161
+ raise ToolError("No command provided.")
@@ -1,13 +1,15 @@
1
- """Computer control tools for different agent APIs."""
2
-
3
- from __future__ import annotations
4
-
5
- from .anthropic import AnthropicComputerTool
6
- from .hud import HudComputerTool
7
- from .openai import OpenAIComputerTool
8
-
9
- __all__ = [
10
- "AnthropicComputerTool",
11
- "HudComputerTool",
12
- "OpenAIComputerTool",
13
- ]
1
+ """Computer control tools for different agent APIs."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from .anthropic import AnthropicComputerTool
6
+ from .hud import HudComputerTool
7
+ from .openai import OpenAIComputerTool
8
+ from .settings import computer_settings
9
+
10
+ __all__ = [
11
+ "AnthropicComputerTool",
12
+ "HudComputerTool",
13
+ "OpenAIComputerTool",
14
+ "computer_settings",
15
+ ]