hud-python 0.4.1__py3-none-any.whl → 0.4.3__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 (130) hide show
  1. hud/__init__.py +22 -22
  2. hud/agents/__init__.py +13 -15
  3. hud/agents/base.py +599 -599
  4. hud/agents/claude.py +373 -373
  5. hud/agents/langchain.py +261 -250
  6. hud/agents/misc/__init__.py +7 -7
  7. hud/agents/misc/response_agent.py +82 -80
  8. hud/agents/openai.py +352 -352
  9. hud/agents/openai_chat_generic.py +154 -154
  10. hud/agents/tests/__init__.py +1 -1
  11. hud/agents/tests/test_base.py +742 -742
  12. hud/agents/tests/test_claude.py +324 -324
  13. hud/agents/tests/test_client.py +363 -363
  14. hud/agents/tests/test_openai.py +237 -237
  15. hud/cli/__init__.py +617 -617
  16. hud/cli/__main__.py +8 -8
  17. hud/cli/analyze.py +371 -371
  18. hud/cli/analyze_metadata.py +230 -230
  19. hud/cli/build.py +498 -427
  20. hud/cli/clone.py +185 -185
  21. hud/cli/cursor.py +92 -92
  22. hud/cli/debug.py +392 -392
  23. hud/cli/docker_utils.py +83 -83
  24. hud/cli/init.py +280 -281
  25. hud/cli/interactive.py +353 -353
  26. hud/cli/mcp_server.py +764 -756
  27. hud/cli/pull.py +330 -336
  28. hud/cli/push.py +404 -370
  29. hud/cli/remote_runner.py +311 -311
  30. hud/cli/runner.py +160 -160
  31. hud/cli/tests/__init__.py +3 -3
  32. hud/cli/tests/test_analyze.py +284 -284
  33. hud/cli/tests/test_cli_init.py +265 -265
  34. hud/cli/tests/test_cli_main.py +27 -27
  35. hud/cli/tests/test_clone.py +142 -142
  36. hud/cli/tests/test_cursor.py +253 -253
  37. hud/cli/tests/test_debug.py +453 -453
  38. hud/cli/tests/test_mcp_server.py +139 -139
  39. hud/cli/tests/test_utils.py +388 -388
  40. hud/cli/utils.py +263 -263
  41. hud/clients/README.md +143 -143
  42. hud/clients/__init__.py +16 -16
  43. hud/clients/base.py +378 -379
  44. hud/clients/fastmcp.py +222 -222
  45. hud/clients/mcp_use.py +298 -278
  46. hud/clients/tests/__init__.py +1 -1
  47. hud/clients/tests/test_client_integration.py +111 -111
  48. hud/clients/tests/test_fastmcp.py +342 -342
  49. hud/clients/tests/test_protocol.py +188 -188
  50. hud/clients/utils/__init__.py +1 -1
  51. hud/clients/utils/retry_transport.py +160 -160
  52. hud/datasets.py +327 -322
  53. hud/misc/__init__.py +1 -1
  54. hud/misc/claude_plays_pokemon.py +292 -292
  55. hud/otel/__init__.py +35 -35
  56. hud/otel/collector.py +142 -142
  57. hud/otel/config.py +164 -164
  58. hud/otel/context.py +536 -536
  59. hud/otel/exporters.py +366 -366
  60. hud/otel/instrumentation.py +97 -97
  61. hud/otel/processors.py +118 -118
  62. hud/otel/tests/__init__.py +1 -1
  63. hud/otel/tests/test_processors.py +197 -197
  64. hud/server/__init__.py +5 -5
  65. hud/server/context.py +114 -114
  66. hud/server/helper/__init__.py +5 -5
  67. hud/server/low_level.py +132 -132
  68. hud/server/server.py +170 -166
  69. hud/server/tests/__init__.py +3 -3
  70. hud/settings.py +73 -73
  71. hud/shared/__init__.py +5 -5
  72. hud/shared/exceptions.py +180 -180
  73. hud/shared/requests.py +264 -264
  74. hud/shared/tests/test_exceptions.py +157 -157
  75. hud/shared/tests/test_requests.py +275 -275
  76. hud/telemetry/__init__.py +25 -25
  77. hud/telemetry/instrument.py +379 -379
  78. hud/telemetry/job.py +309 -309
  79. hud/telemetry/replay.py +74 -74
  80. hud/telemetry/trace.py +83 -83
  81. hud/tools/__init__.py +33 -33
  82. hud/tools/base.py +365 -365
  83. hud/tools/bash.py +161 -161
  84. hud/tools/computer/__init__.py +15 -15
  85. hud/tools/computer/anthropic.py +437 -437
  86. hud/tools/computer/hud.py +376 -376
  87. hud/tools/computer/openai.py +295 -295
  88. hud/tools/computer/settings.py +82 -82
  89. hud/tools/edit.py +314 -314
  90. hud/tools/executors/__init__.py +30 -30
  91. hud/tools/executors/base.py +539 -539
  92. hud/tools/executors/pyautogui.py +621 -621
  93. hud/tools/executors/tests/__init__.py +1 -1
  94. hud/tools/executors/tests/test_base_executor.py +338 -338
  95. hud/tools/executors/tests/test_pyautogui_executor.py +165 -165
  96. hud/tools/executors/xdo.py +511 -511
  97. hud/tools/playwright.py +412 -412
  98. hud/tools/tests/__init__.py +3 -3
  99. hud/tools/tests/test_base.py +282 -282
  100. hud/tools/tests/test_bash.py +158 -158
  101. hud/tools/tests/test_bash_extended.py +197 -197
  102. hud/tools/tests/test_computer.py +425 -425
  103. hud/tools/tests/test_computer_actions.py +34 -34
  104. hud/tools/tests/test_edit.py +259 -259
  105. hud/tools/tests/test_init.py +27 -27
  106. hud/tools/tests/test_playwright_tool.py +183 -183
  107. hud/tools/tests/test_tools.py +145 -145
  108. hud/tools/tests/test_utils.py +156 -156
  109. hud/tools/types.py +72 -72
  110. hud/tools/utils.py +50 -50
  111. hud/types.py +136 -136
  112. hud/utils/__init__.py +10 -10
  113. hud/utils/async_utils.py +65 -65
  114. hud/utils/design.py +236 -168
  115. hud/utils/mcp.py +55 -55
  116. hud/utils/progress.py +149 -149
  117. hud/utils/telemetry.py +66 -66
  118. hud/utils/tests/test_async_utils.py +173 -173
  119. hud/utils/tests/test_init.py +17 -17
  120. hud/utils/tests/test_progress.py +261 -261
  121. hud/utils/tests/test_telemetry.py +82 -82
  122. hud/utils/tests/test_version.py +8 -8
  123. hud/version.py +7 -7
  124. {hud_python-0.4.1.dist-info → hud_python-0.4.3.dist-info}/METADATA +10 -8
  125. hud_python-0.4.3.dist-info/RECORD +131 -0
  126. {hud_python-0.4.1.dist-info → hud_python-0.4.3.dist-info}/licenses/LICENSE +21 -21
  127. hud/agents/art.py +0 -101
  128. hud_python-0.4.1.dist-info/RECORD +0 -132
  129. {hud_python-0.4.1.dist-info → hud_python-0.4.3.dist-info}/WHEEL +0 -0
  130. {hud_python-0.4.1.dist-info → hud_python-0.4.3.dist-info}/entry_points.txt +0 -0
@@ -1,197 +1,197 @@
1
- """Extended tests for bash tool to improve coverage."""
2
-
3
- from __future__ import annotations
4
-
5
- import sys
6
- from unittest.mock import AsyncMock, MagicMock, patch
7
-
8
- import pytest
9
-
10
- from hud.tools.bash import ToolError, _BashSession
11
-
12
-
13
- class TestBashSessionExtended:
14
- """Extended tests for _BashSession to improve coverage."""
15
-
16
- @pytest.mark.asyncio
17
- async def test_session_start_already_started(self):
18
- """Test starting a session that's already started."""
19
- session = _BashSession()
20
- session._started = True
21
-
22
- with patch("asyncio.sleep") as mock_sleep:
23
- mock_sleep.return_value = None
24
- await session.start()
25
-
26
- # Should call sleep and return early
27
- mock_sleep.assert_called_once_with(0)
28
-
29
- @pytest.mark.asyncio
30
- @pytest.mark.skipif(sys.platform == "win32", reason="Unix-specific test")
31
- async def test_session_start_unix_preexec(self):
32
- """Test session start on Unix systems uses preexec_fn."""
33
- session = _BashSession()
34
-
35
- with patch("asyncio.create_subprocess_shell") as mock_create:
36
- mock_process = MagicMock()
37
- mock_create.return_value = mock_process
38
-
39
- await session.start()
40
-
41
- # Check that preexec_fn was passed
42
- call_kwargs = mock_create.call_args[1]
43
- assert "preexec_fn" in call_kwargs
44
- assert call_kwargs["preexec_fn"] is not None
45
-
46
- def test_session_stop_with_terminated_process(self):
47
- """Test stopping a session with already terminated process."""
48
- session = _BashSession()
49
- session._started = True
50
-
51
- # Mock process that's already terminated
52
- mock_process = MagicMock()
53
- mock_process.returncode = 0 # Process already exited
54
- session._process = mock_process
55
-
56
- # Should not raise error and not call terminate
57
- session.stop()
58
- mock_process.terminate.assert_not_called()
59
-
60
- def test_session_stop_with_running_process(self):
61
- """Test stopping a session with running process."""
62
- session = _BashSession()
63
- session._started = True
64
-
65
- # Mock process that's still running
66
- mock_process = MagicMock()
67
- mock_process.returncode = None
68
- session._process = mock_process
69
-
70
- session.stop()
71
- mock_process.terminate.assert_called_once()
72
-
73
- @pytest.mark.asyncio
74
- async def test_session_run_with_exited_process(self):
75
- """Test running command when process has already exited."""
76
- session = _BashSession()
77
- session._started = True
78
-
79
- # Mock process that has exited
80
- mock_process = MagicMock()
81
- mock_process.returncode = 1
82
- session._process = mock_process
83
-
84
- with patch("asyncio.sleep") as mock_sleep:
85
- mock_sleep.return_value = None
86
- result = await session.run("echo test")
87
-
88
- assert result.system == "tool must be restarted"
89
- assert result.error == "bash has exited with returncode 1"
90
- mock_sleep.assert_called_once_with(0)
91
-
92
- @pytest.mark.asyncio
93
- async def test_session_run_with_stderr_output(self):
94
- """Test command execution with stderr output."""
95
- session = _BashSession()
96
- session._started = True
97
-
98
- # Mock process
99
- mock_process = MagicMock()
100
- mock_process.returncode = None
101
- mock_process.stdin = MagicMock()
102
- mock_process.stdin.write = MagicMock()
103
- mock_process.stdin.drain = AsyncMock()
104
- mock_process.stdout = MagicMock()
105
- mock_process.stdout.readuntil = AsyncMock(return_value=b"stdout output\n<<exit>>\n")
106
- mock_process.stderr = MagicMock()
107
- mock_process.stderr.read = AsyncMock(return_value=b"stderr output\n")
108
-
109
- session._process = mock_process
110
-
111
- result = await session.run("command")
112
-
113
- assert result.output == "stdout output\n"
114
- assert result.error == "stderr output" # .strip() is called on stderr
115
-
116
- @pytest.mark.asyncio
117
- async def test_session_run_with_asyncio_timeout(self):
118
- """Test command execution timing out."""
119
- session = _BashSession()
120
- session._started = True
121
-
122
- # Mock process
123
- mock_process = MagicMock()
124
- mock_process.returncode = None
125
- mock_process.stdin = MagicMock()
126
- mock_process.stdin.write = MagicMock()
127
- mock_process.stdin.drain = AsyncMock()
128
- mock_process.stdout = MagicMock()
129
- # Simulate timeout
130
- mock_process.stdout.readuntil = AsyncMock(side_effect=TimeoutError())
131
-
132
- session._process = mock_process
133
-
134
- # Should raise ToolError on timeout
135
- with pytest.raises(ToolError) as exc_info:
136
- await session.run("slow command")
137
-
138
- assert "timed out" in str(exc_info.value)
139
- assert "120.0 seconds" in str(exc_info.value)
140
-
141
- @pytest.mark.asyncio
142
- async def test_session_run_with_stdout_exception(self):
143
- """Test command execution with exception reading stdout."""
144
- session = _BashSession()
145
- session._started = True
146
-
147
- # Mock process
148
- mock_process = MagicMock()
149
- mock_process.returncode = None
150
- mock_process.stdin = MagicMock()
151
- mock_process.stdin.write = MagicMock()
152
- mock_process.stdin.drain = AsyncMock()
153
- mock_process.stdout = MagicMock()
154
- # Simulate other exception
155
- mock_process.stdout.readuntil = AsyncMock(side_effect=Exception("Read error"))
156
-
157
- session._process = mock_process
158
-
159
- # The exception should bubble up
160
- with pytest.raises(Exception) as exc_info:
161
- await session.run("bad command")
162
-
163
- assert "Read error" in str(exc_info.value)
164
-
165
- @pytest.mark.asyncio
166
- async def test_session_run_with_stderr_exception(self):
167
- """Test command execution with exception reading stderr."""
168
- session = _BashSession()
169
- session._started = True
170
-
171
- # Mock process
172
- mock_process = MagicMock()
173
- mock_process.returncode = None
174
- mock_process.stdin = MagicMock()
175
- mock_process.stdin.write = MagicMock()
176
- mock_process.stdin.drain = AsyncMock()
177
- mock_process.stdout = MagicMock()
178
- mock_process.stdout.readuntil = AsyncMock(return_value=b"output\n<<exit>>\n")
179
- mock_process.stderr = MagicMock()
180
- # Simulate stderr read error
181
- mock_process.stderr.read = AsyncMock(side_effect=Exception("Stderr read error"))
182
-
183
- session._process = mock_process
184
-
185
- # stderr exceptions should also bubble up
186
- with pytest.raises(Exception) as exc_info:
187
- await session.run("command")
188
-
189
- assert "Stderr read error" in str(exc_info.value)
190
-
191
- def test_bash_session_different_shells(self):
192
- """Test that different shells are used on different platforms."""
193
- session = _BashSession()
194
-
195
- # Currently, _BashSession always uses /bin/bash regardless of platform
196
- # This test should verify the actual implementation
197
- assert session.command == "/bin/bash"
1
+ """Extended tests for bash tool to improve coverage."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import sys
6
+ from unittest.mock import AsyncMock, MagicMock, patch
7
+
8
+ import pytest
9
+
10
+ from hud.tools.bash import ToolError, _BashSession
11
+
12
+
13
+ class TestBashSessionExtended:
14
+ """Extended tests for _BashSession to improve coverage."""
15
+
16
+ @pytest.mark.asyncio
17
+ async def test_session_start_already_started(self):
18
+ """Test starting a session that's already started."""
19
+ session = _BashSession()
20
+ session._started = True
21
+
22
+ with patch("asyncio.sleep") as mock_sleep:
23
+ mock_sleep.return_value = None
24
+ await session.start()
25
+
26
+ # Should call sleep and return early
27
+ mock_sleep.assert_called_once_with(0)
28
+
29
+ @pytest.mark.asyncio
30
+ @pytest.mark.skipif(sys.platform == "win32", reason="Unix-specific test")
31
+ async def test_session_start_unix_preexec(self):
32
+ """Test session start on Unix systems uses preexec_fn."""
33
+ session = _BashSession()
34
+
35
+ with patch("asyncio.create_subprocess_shell") as mock_create:
36
+ mock_process = MagicMock()
37
+ mock_create.return_value = mock_process
38
+
39
+ await session.start()
40
+
41
+ # Check that preexec_fn was passed
42
+ call_kwargs = mock_create.call_args[1]
43
+ assert "preexec_fn" in call_kwargs
44
+ assert call_kwargs["preexec_fn"] is not None
45
+
46
+ def test_session_stop_with_terminated_process(self):
47
+ """Test stopping a session with already terminated process."""
48
+ session = _BashSession()
49
+ session._started = True
50
+
51
+ # Mock process that's already terminated
52
+ mock_process = MagicMock()
53
+ mock_process.returncode = 0 # Process already exited
54
+ session._process = mock_process
55
+
56
+ # Should not raise error and not call terminate
57
+ session.stop()
58
+ mock_process.terminate.assert_not_called()
59
+
60
+ def test_session_stop_with_running_process(self):
61
+ """Test stopping a session with running process."""
62
+ session = _BashSession()
63
+ session._started = True
64
+
65
+ # Mock process that's still running
66
+ mock_process = MagicMock()
67
+ mock_process.returncode = None
68
+ session._process = mock_process
69
+
70
+ session.stop()
71
+ mock_process.terminate.assert_called_once()
72
+
73
+ @pytest.mark.asyncio
74
+ async def test_session_run_with_exited_process(self):
75
+ """Test running command when process has already exited."""
76
+ session = _BashSession()
77
+ session._started = True
78
+
79
+ # Mock process that has exited
80
+ mock_process = MagicMock()
81
+ mock_process.returncode = 1
82
+ session._process = mock_process
83
+
84
+ with patch("asyncio.sleep") as mock_sleep:
85
+ mock_sleep.return_value = None
86
+ result = await session.run("echo test")
87
+
88
+ assert result.system == "tool must be restarted"
89
+ assert result.error == "bash has exited with returncode 1"
90
+ mock_sleep.assert_called_once_with(0)
91
+
92
+ @pytest.mark.asyncio
93
+ async def test_session_run_with_stderr_output(self):
94
+ """Test command execution with stderr output."""
95
+ session = _BashSession()
96
+ session._started = True
97
+
98
+ # Mock process
99
+ mock_process = MagicMock()
100
+ mock_process.returncode = None
101
+ mock_process.stdin = MagicMock()
102
+ mock_process.stdin.write = MagicMock()
103
+ mock_process.stdin.drain = AsyncMock()
104
+ mock_process.stdout = MagicMock()
105
+ mock_process.stdout.readuntil = AsyncMock(return_value=b"stdout output\n<<exit>>\n")
106
+ mock_process.stderr = MagicMock()
107
+ mock_process.stderr.read = AsyncMock(return_value=b"stderr output\n")
108
+
109
+ session._process = mock_process
110
+
111
+ result = await session.run("command")
112
+
113
+ assert result.output == "stdout output\n"
114
+ assert result.error == "stderr output" # .strip() is called on stderr
115
+
116
+ @pytest.mark.asyncio
117
+ async def test_session_run_with_asyncio_timeout(self):
118
+ """Test command execution timing out."""
119
+ session = _BashSession()
120
+ session._started = True
121
+
122
+ # Mock process
123
+ mock_process = MagicMock()
124
+ mock_process.returncode = None
125
+ mock_process.stdin = MagicMock()
126
+ mock_process.stdin.write = MagicMock()
127
+ mock_process.stdin.drain = AsyncMock()
128
+ mock_process.stdout = MagicMock()
129
+ # Simulate timeout
130
+ mock_process.stdout.readuntil = AsyncMock(side_effect=TimeoutError())
131
+
132
+ session._process = mock_process
133
+
134
+ # Should raise ToolError on timeout
135
+ with pytest.raises(ToolError) as exc_info:
136
+ await session.run("slow command")
137
+
138
+ assert "timed out" in str(exc_info.value)
139
+ assert "120.0 seconds" in str(exc_info.value)
140
+
141
+ @pytest.mark.asyncio
142
+ async def test_session_run_with_stdout_exception(self):
143
+ """Test command execution with exception reading stdout."""
144
+ session = _BashSession()
145
+ session._started = True
146
+
147
+ # Mock process
148
+ mock_process = MagicMock()
149
+ mock_process.returncode = None
150
+ mock_process.stdin = MagicMock()
151
+ mock_process.stdin.write = MagicMock()
152
+ mock_process.stdin.drain = AsyncMock()
153
+ mock_process.stdout = MagicMock()
154
+ # Simulate other exception
155
+ mock_process.stdout.readuntil = AsyncMock(side_effect=Exception("Read error"))
156
+
157
+ session._process = mock_process
158
+
159
+ # The exception should bubble up
160
+ with pytest.raises(Exception) as exc_info:
161
+ await session.run("bad command")
162
+
163
+ assert "Read error" in str(exc_info.value)
164
+
165
+ @pytest.mark.asyncio
166
+ async def test_session_run_with_stderr_exception(self):
167
+ """Test command execution with exception reading stderr."""
168
+ session = _BashSession()
169
+ session._started = True
170
+
171
+ # Mock process
172
+ mock_process = MagicMock()
173
+ mock_process.returncode = None
174
+ mock_process.stdin = MagicMock()
175
+ mock_process.stdin.write = MagicMock()
176
+ mock_process.stdin.drain = AsyncMock()
177
+ mock_process.stdout = MagicMock()
178
+ mock_process.stdout.readuntil = AsyncMock(return_value=b"output\n<<exit>>\n")
179
+ mock_process.stderr = MagicMock()
180
+ # Simulate stderr read error
181
+ mock_process.stderr.read = AsyncMock(side_effect=Exception("Stderr read error"))
182
+
183
+ session._process = mock_process
184
+
185
+ # stderr exceptions should also bubble up
186
+ with pytest.raises(Exception) as exc_info:
187
+ await session.run("command")
188
+
189
+ assert "Stderr read error" in str(exc_info.value)
190
+
191
+ def test_bash_session_different_shells(self):
192
+ """Test that different shells are used on different platforms."""
193
+ session = _BashSession()
194
+
195
+ # Currently, _BashSession always uses /bin/bash regardless of platform
196
+ # This test should verify the actual implementation
197
+ assert session.command == "/bin/bash"