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.
- hud/__init__.py +22 -22
- hud/agents/__init__.py +13 -15
- hud/agents/base.py +599 -599
- hud/agents/claude.py +373 -373
- hud/agents/langchain.py +261 -250
- hud/agents/misc/__init__.py +7 -7
- hud/agents/misc/response_agent.py +82 -80
- hud/agents/openai.py +352 -352
- hud/agents/openai_chat_generic.py +154 -154
- hud/agents/tests/__init__.py +1 -1
- hud/agents/tests/test_base.py +742 -742
- hud/agents/tests/test_claude.py +324 -324
- hud/agents/tests/test_client.py +363 -363
- hud/agents/tests/test_openai.py +237 -237
- hud/cli/__init__.py +617 -617
- hud/cli/__main__.py +8 -8
- hud/cli/analyze.py +371 -371
- hud/cli/analyze_metadata.py +230 -230
- hud/cli/build.py +498 -427
- hud/cli/clone.py +185 -185
- hud/cli/cursor.py +92 -92
- hud/cli/debug.py +392 -392
- hud/cli/docker_utils.py +83 -83
- hud/cli/init.py +280 -281
- hud/cli/interactive.py +353 -353
- hud/cli/mcp_server.py +764 -756
- hud/cli/pull.py +330 -336
- hud/cli/push.py +404 -370
- hud/cli/remote_runner.py +311 -311
- hud/cli/runner.py +160 -160
- hud/cli/tests/__init__.py +3 -3
- hud/cli/tests/test_analyze.py +284 -284
- hud/cli/tests/test_cli_init.py +265 -265
- hud/cli/tests/test_cli_main.py +27 -27
- hud/cli/tests/test_clone.py +142 -142
- hud/cli/tests/test_cursor.py +253 -253
- hud/cli/tests/test_debug.py +453 -453
- hud/cli/tests/test_mcp_server.py +139 -139
- hud/cli/tests/test_utils.py +388 -388
- hud/cli/utils.py +263 -263
- hud/clients/README.md +143 -143
- hud/clients/__init__.py +16 -16
- hud/clients/base.py +378 -379
- hud/clients/fastmcp.py +222 -222
- hud/clients/mcp_use.py +298 -278
- hud/clients/tests/__init__.py +1 -1
- hud/clients/tests/test_client_integration.py +111 -111
- hud/clients/tests/test_fastmcp.py +342 -342
- hud/clients/tests/test_protocol.py +188 -188
- hud/clients/utils/__init__.py +1 -1
- hud/clients/utils/retry_transport.py +160 -160
- hud/datasets.py +327 -322
- hud/misc/__init__.py +1 -1
- hud/misc/claude_plays_pokemon.py +292 -292
- hud/otel/__init__.py +35 -35
- hud/otel/collector.py +142 -142
- hud/otel/config.py +164 -164
- hud/otel/context.py +536 -536
- hud/otel/exporters.py +366 -366
- hud/otel/instrumentation.py +97 -97
- hud/otel/processors.py +118 -118
- hud/otel/tests/__init__.py +1 -1
- hud/otel/tests/test_processors.py +197 -197
- hud/server/__init__.py +5 -5
- hud/server/context.py +114 -114
- hud/server/helper/__init__.py +5 -5
- hud/server/low_level.py +132 -132
- hud/server/server.py +170 -166
- hud/server/tests/__init__.py +3 -3
- hud/settings.py +73 -73
- hud/shared/__init__.py +5 -5
- hud/shared/exceptions.py +180 -180
- hud/shared/requests.py +264 -264
- hud/shared/tests/test_exceptions.py +157 -157
- hud/shared/tests/test_requests.py +275 -275
- hud/telemetry/__init__.py +25 -25
- hud/telemetry/instrument.py +379 -379
- hud/telemetry/job.py +309 -309
- hud/telemetry/replay.py +74 -74
- hud/telemetry/trace.py +83 -83
- hud/tools/__init__.py +33 -33
- hud/tools/base.py +365 -365
- hud/tools/bash.py +161 -161
- hud/tools/computer/__init__.py +15 -15
- hud/tools/computer/anthropic.py +437 -437
- hud/tools/computer/hud.py +376 -376
- hud/tools/computer/openai.py +295 -295
- hud/tools/computer/settings.py +82 -82
- hud/tools/edit.py +314 -314
- hud/tools/executors/__init__.py +30 -30
- hud/tools/executors/base.py +539 -539
- hud/tools/executors/pyautogui.py +621 -621
- 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 -511
- hud/tools/playwright.py +412 -412
- hud/tools/tests/__init__.py +3 -3
- hud/tools/tests/test_base.py +282 -282
- hud/tools/tests/test_bash.py +158 -158
- hud/tools/tests/test_bash_extended.py +197 -197
- hud/tools/tests/test_computer.py +425 -425
- hud/tools/tests/test_computer_actions.py +34 -34
- hud/tools/tests/test_edit.py +259 -259
- hud/tools/tests/test_init.py +27 -27
- hud/tools/tests/test_playwright_tool.py +183 -183
- hud/tools/tests/test_tools.py +145 -145
- hud/tools/tests/test_utils.py +156 -156
- hud/tools/types.py +72 -72
- hud/tools/utils.py +50 -50
- hud/types.py +136 -136
- hud/utils/__init__.py +10 -10
- hud/utils/async_utils.py +65 -65
- hud/utils/design.py +236 -168
- hud/utils/mcp.py +55 -55
- hud/utils/progress.py +149 -149
- hud/utils/telemetry.py +66 -66
- hud/utils/tests/test_async_utils.py +173 -173
- hud/utils/tests/test_init.py +17 -17
- hud/utils/tests/test_progress.py +261 -261
- hud/utils/tests/test_telemetry.py +82 -82
- hud/utils/tests/test_version.py +8 -8
- hud/version.py +7 -7
- {hud_python-0.4.1.dist-info → hud_python-0.4.3.dist-info}/METADATA +10 -8
- hud_python-0.4.3.dist-info/RECORD +131 -0
- {hud_python-0.4.1.dist-info → hud_python-0.4.3.dist-info}/licenses/LICENSE +21 -21
- hud/agents/art.py +0 -101
- hud_python-0.4.1.dist-info/RECORD +0 -132
- {hud_python-0.4.1.dist-info → hud_python-0.4.3.dist-info}/WHEEL +0 -0
- {hud_python-0.4.1.dist-info → hud_python-0.4.3.dist-info}/entry_points.txt +0 -0
hud/tools/tests/test_bash.py
CHANGED
|
@@ -1,158 +1,158 @@
|
|
|
1
|
-
"""Tests for bash tool."""
|
|
2
|
-
|
|
3
|
-
from __future__ import annotations
|
|
4
|
-
|
|
5
|
-
from unittest.mock import AsyncMock, MagicMock, patch
|
|
6
|
-
|
|
7
|
-
import pytest
|
|
8
|
-
|
|
9
|
-
from hud.tools.bash import BashTool, ContentResult, ToolError, _BashSession
|
|
10
|
-
from hud.tools.types import TextContent
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
class TestBashSession:
|
|
14
|
-
"""Tests for _BashSession."""
|
|
15
|
-
|
|
16
|
-
@pytest.mark.asyncio
|
|
17
|
-
async def test_session_start(self):
|
|
18
|
-
"""Test starting a bash session."""
|
|
19
|
-
session = _BashSession()
|
|
20
|
-
assert session._started is False
|
|
21
|
-
|
|
22
|
-
with patch("asyncio.create_subprocess_shell") as mock_create:
|
|
23
|
-
mock_process = MagicMock()
|
|
24
|
-
mock_create.return_value = mock_process
|
|
25
|
-
|
|
26
|
-
await session.start()
|
|
27
|
-
|
|
28
|
-
assert session._started is True
|
|
29
|
-
assert session._process == mock_process
|
|
30
|
-
mock_create.assert_called_once()
|
|
31
|
-
|
|
32
|
-
def test_session_stop_not_started(self):
|
|
33
|
-
"""Test stopping a session that hasn't started."""
|
|
34
|
-
session = _BashSession()
|
|
35
|
-
|
|
36
|
-
with pytest.raises(ToolError) as exc_info:
|
|
37
|
-
session.stop()
|
|
38
|
-
|
|
39
|
-
assert "Session has not started" in str(exc_info.value)
|
|
40
|
-
|
|
41
|
-
@pytest.mark.asyncio
|
|
42
|
-
async def test_session_run_not_started(self):
|
|
43
|
-
"""Test running command on a session that hasn't started."""
|
|
44
|
-
session = _BashSession()
|
|
45
|
-
|
|
46
|
-
with pytest.raises(ToolError) as exc_info:
|
|
47
|
-
await session.run("echo test")
|
|
48
|
-
|
|
49
|
-
assert "Session has not started" in str(exc_info.value)
|
|
50
|
-
|
|
51
|
-
@pytest.mark.asyncio
|
|
52
|
-
async def test_session_run_success(self):
|
|
53
|
-
"""Test successful command execution."""
|
|
54
|
-
session = _BashSession()
|
|
55
|
-
session._started = True
|
|
56
|
-
|
|
57
|
-
# Mock process
|
|
58
|
-
mock_process = MagicMock()
|
|
59
|
-
mock_process.returncode = None
|
|
60
|
-
mock_process.stdin = MagicMock()
|
|
61
|
-
mock_process.stdin.write = MagicMock()
|
|
62
|
-
mock_process.stdin.drain = AsyncMock()
|
|
63
|
-
mock_process.stdout = MagicMock()
|
|
64
|
-
mock_process.stdout.readuntil = AsyncMock(return_value=b"Hello World\n<<exit>>\n")
|
|
65
|
-
mock_process.stderr = MagicMock()
|
|
66
|
-
mock_process.stderr.read = AsyncMock(return_value=b"")
|
|
67
|
-
|
|
68
|
-
session._process = mock_process
|
|
69
|
-
|
|
70
|
-
result = await session.run("echo Hello World")
|
|
71
|
-
|
|
72
|
-
assert result.output == "Hello World\n"
|
|
73
|
-
assert result.error == ""
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
class TestBashTool:
|
|
77
|
-
"""Tests for BashTool."""
|
|
78
|
-
|
|
79
|
-
def test_bash_tool_init(self):
|
|
80
|
-
"""Test BashTool initialization."""
|
|
81
|
-
tool = BashTool()
|
|
82
|
-
assert tool.session is None
|
|
83
|
-
|
|
84
|
-
@pytest.mark.asyncio
|
|
85
|
-
async def test_call_with_command(self):
|
|
86
|
-
"""Test calling tool with a command."""
|
|
87
|
-
tool = BashTool()
|
|
88
|
-
|
|
89
|
-
# Mock session
|
|
90
|
-
mock_session = MagicMock()
|
|
91
|
-
mock_session.run = AsyncMock(return_value=ContentResult(output="test output"))
|
|
92
|
-
|
|
93
|
-
# Mock _BashSession creation
|
|
94
|
-
with patch("hud.tools.bash._BashSession") as mock_session_class:
|
|
95
|
-
mock_session_class.return_value = mock_session
|
|
96
|
-
mock_session.start = AsyncMock()
|
|
97
|
-
|
|
98
|
-
result = await tool(command="echo test")
|
|
99
|
-
|
|
100
|
-
assert isinstance(result, list)
|
|
101
|
-
assert len(result) == 1
|
|
102
|
-
assert isinstance(result[0], TextContent)
|
|
103
|
-
assert result[0].text == "test output"
|
|
104
|
-
mock_session.start.assert_called_once()
|
|
105
|
-
mock_session.run.assert_called_once_with("echo test")
|
|
106
|
-
|
|
107
|
-
@pytest.mark.asyncio
|
|
108
|
-
async def test_call_restart(self):
|
|
109
|
-
"""Test restarting the tool."""
|
|
110
|
-
tool = BashTool()
|
|
111
|
-
|
|
112
|
-
# Set up existing session
|
|
113
|
-
old_session = MagicMock()
|
|
114
|
-
old_session.stop = MagicMock()
|
|
115
|
-
tool.session = old_session
|
|
116
|
-
|
|
117
|
-
# Mock new session
|
|
118
|
-
new_session = MagicMock()
|
|
119
|
-
new_session.start = AsyncMock()
|
|
120
|
-
|
|
121
|
-
with patch("hud.tools.bash._BashSession", return_value=new_session):
|
|
122
|
-
result = await tool(restart=True)
|
|
123
|
-
|
|
124
|
-
assert isinstance(result, list)
|
|
125
|
-
assert len(result) == 1
|
|
126
|
-
assert isinstance(result[0], TextContent)
|
|
127
|
-
assert result[0].text == "Bash session restarted."
|
|
128
|
-
old_session.stop.assert_called_once()
|
|
129
|
-
new_session.start.assert_called_once()
|
|
130
|
-
assert tool.session == new_session
|
|
131
|
-
|
|
132
|
-
@pytest.mark.asyncio
|
|
133
|
-
async def test_call_no_command_error(self):
|
|
134
|
-
"""Test calling without command raises error."""
|
|
135
|
-
tool = BashTool()
|
|
136
|
-
|
|
137
|
-
with pytest.raises(ToolError) as exc_info:
|
|
138
|
-
await tool()
|
|
139
|
-
|
|
140
|
-
assert str(exc_info.value) == "No command provided."
|
|
141
|
-
|
|
142
|
-
@pytest.mark.asyncio
|
|
143
|
-
async def test_call_with_existing_session(self):
|
|
144
|
-
"""Test calling with an existing session."""
|
|
145
|
-
tool = BashTool()
|
|
146
|
-
|
|
147
|
-
# Set up existing session
|
|
148
|
-
existing_session = MagicMock()
|
|
149
|
-
existing_session.run = AsyncMock(return_value=ContentResult(output="result"))
|
|
150
|
-
tool.session = existing_session
|
|
151
|
-
|
|
152
|
-
result = await tool(command="ls")
|
|
153
|
-
|
|
154
|
-
assert isinstance(result, list)
|
|
155
|
-
assert len(result) == 1
|
|
156
|
-
assert isinstance(result[0], TextContent)
|
|
157
|
-
assert result[0].text == "result"
|
|
158
|
-
existing_session.run.assert_called_once_with("ls")
|
|
1
|
+
"""Tests for bash tool."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from unittest.mock import AsyncMock, MagicMock, patch
|
|
6
|
+
|
|
7
|
+
import pytest
|
|
8
|
+
|
|
9
|
+
from hud.tools.bash import BashTool, ContentResult, ToolError, _BashSession
|
|
10
|
+
from hud.tools.types import TextContent
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class TestBashSession:
|
|
14
|
+
"""Tests for _BashSession."""
|
|
15
|
+
|
|
16
|
+
@pytest.mark.asyncio
|
|
17
|
+
async def test_session_start(self):
|
|
18
|
+
"""Test starting a bash session."""
|
|
19
|
+
session = _BashSession()
|
|
20
|
+
assert session._started is False
|
|
21
|
+
|
|
22
|
+
with patch("asyncio.create_subprocess_shell") as mock_create:
|
|
23
|
+
mock_process = MagicMock()
|
|
24
|
+
mock_create.return_value = mock_process
|
|
25
|
+
|
|
26
|
+
await session.start()
|
|
27
|
+
|
|
28
|
+
assert session._started is True
|
|
29
|
+
assert session._process == mock_process
|
|
30
|
+
mock_create.assert_called_once()
|
|
31
|
+
|
|
32
|
+
def test_session_stop_not_started(self):
|
|
33
|
+
"""Test stopping a session that hasn't started."""
|
|
34
|
+
session = _BashSession()
|
|
35
|
+
|
|
36
|
+
with pytest.raises(ToolError) as exc_info:
|
|
37
|
+
session.stop()
|
|
38
|
+
|
|
39
|
+
assert "Session has not started" in str(exc_info.value)
|
|
40
|
+
|
|
41
|
+
@pytest.mark.asyncio
|
|
42
|
+
async def test_session_run_not_started(self):
|
|
43
|
+
"""Test running command on a session that hasn't started."""
|
|
44
|
+
session = _BashSession()
|
|
45
|
+
|
|
46
|
+
with pytest.raises(ToolError) as exc_info:
|
|
47
|
+
await session.run("echo test")
|
|
48
|
+
|
|
49
|
+
assert "Session has not started" in str(exc_info.value)
|
|
50
|
+
|
|
51
|
+
@pytest.mark.asyncio
|
|
52
|
+
async def test_session_run_success(self):
|
|
53
|
+
"""Test successful command execution."""
|
|
54
|
+
session = _BashSession()
|
|
55
|
+
session._started = True
|
|
56
|
+
|
|
57
|
+
# Mock process
|
|
58
|
+
mock_process = MagicMock()
|
|
59
|
+
mock_process.returncode = None
|
|
60
|
+
mock_process.stdin = MagicMock()
|
|
61
|
+
mock_process.stdin.write = MagicMock()
|
|
62
|
+
mock_process.stdin.drain = AsyncMock()
|
|
63
|
+
mock_process.stdout = MagicMock()
|
|
64
|
+
mock_process.stdout.readuntil = AsyncMock(return_value=b"Hello World\n<<exit>>\n")
|
|
65
|
+
mock_process.stderr = MagicMock()
|
|
66
|
+
mock_process.stderr.read = AsyncMock(return_value=b"")
|
|
67
|
+
|
|
68
|
+
session._process = mock_process
|
|
69
|
+
|
|
70
|
+
result = await session.run("echo Hello World")
|
|
71
|
+
|
|
72
|
+
assert result.output == "Hello World\n"
|
|
73
|
+
assert result.error == ""
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
class TestBashTool:
|
|
77
|
+
"""Tests for BashTool."""
|
|
78
|
+
|
|
79
|
+
def test_bash_tool_init(self):
|
|
80
|
+
"""Test BashTool initialization."""
|
|
81
|
+
tool = BashTool()
|
|
82
|
+
assert tool.session is None
|
|
83
|
+
|
|
84
|
+
@pytest.mark.asyncio
|
|
85
|
+
async def test_call_with_command(self):
|
|
86
|
+
"""Test calling tool with a command."""
|
|
87
|
+
tool = BashTool()
|
|
88
|
+
|
|
89
|
+
# Mock session
|
|
90
|
+
mock_session = MagicMock()
|
|
91
|
+
mock_session.run = AsyncMock(return_value=ContentResult(output="test output"))
|
|
92
|
+
|
|
93
|
+
# Mock _BashSession creation
|
|
94
|
+
with patch("hud.tools.bash._BashSession") as mock_session_class:
|
|
95
|
+
mock_session_class.return_value = mock_session
|
|
96
|
+
mock_session.start = AsyncMock()
|
|
97
|
+
|
|
98
|
+
result = await tool(command="echo test")
|
|
99
|
+
|
|
100
|
+
assert isinstance(result, list)
|
|
101
|
+
assert len(result) == 1
|
|
102
|
+
assert isinstance(result[0], TextContent)
|
|
103
|
+
assert result[0].text == "test output"
|
|
104
|
+
mock_session.start.assert_called_once()
|
|
105
|
+
mock_session.run.assert_called_once_with("echo test")
|
|
106
|
+
|
|
107
|
+
@pytest.mark.asyncio
|
|
108
|
+
async def test_call_restart(self):
|
|
109
|
+
"""Test restarting the tool."""
|
|
110
|
+
tool = BashTool()
|
|
111
|
+
|
|
112
|
+
# Set up existing session
|
|
113
|
+
old_session = MagicMock()
|
|
114
|
+
old_session.stop = MagicMock()
|
|
115
|
+
tool.session = old_session
|
|
116
|
+
|
|
117
|
+
# Mock new session
|
|
118
|
+
new_session = MagicMock()
|
|
119
|
+
new_session.start = AsyncMock()
|
|
120
|
+
|
|
121
|
+
with patch("hud.tools.bash._BashSession", return_value=new_session):
|
|
122
|
+
result = await tool(restart=True)
|
|
123
|
+
|
|
124
|
+
assert isinstance(result, list)
|
|
125
|
+
assert len(result) == 1
|
|
126
|
+
assert isinstance(result[0], TextContent)
|
|
127
|
+
assert result[0].text == "Bash session restarted."
|
|
128
|
+
old_session.stop.assert_called_once()
|
|
129
|
+
new_session.start.assert_called_once()
|
|
130
|
+
assert tool.session == new_session
|
|
131
|
+
|
|
132
|
+
@pytest.mark.asyncio
|
|
133
|
+
async def test_call_no_command_error(self):
|
|
134
|
+
"""Test calling without command raises error."""
|
|
135
|
+
tool = BashTool()
|
|
136
|
+
|
|
137
|
+
with pytest.raises(ToolError) as exc_info:
|
|
138
|
+
await tool()
|
|
139
|
+
|
|
140
|
+
assert str(exc_info.value) == "No command provided."
|
|
141
|
+
|
|
142
|
+
@pytest.mark.asyncio
|
|
143
|
+
async def test_call_with_existing_session(self):
|
|
144
|
+
"""Test calling with an existing session."""
|
|
145
|
+
tool = BashTool()
|
|
146
|
+
|
|
147
|
+
# Set up existing session
|
|
148
|
+
existing_session = MagicMock()
|
|
149
|
+
existing_session.run = AsyncMock(return_value=ContentResult(output="result"))
|
|
150
|
+
tool.session = existing_session
|
|
151
|
+
|
|
152
|
+
result = await tool(command="ls")
|
|
153
|
+
|
|
154
|
+
assert isinstance(result, list)
|
|
155
|
+
assert len(result) == 1
|
|
156
|
+
assert isinstance(result[0], TextContent)
|
|
157
|
+
assert result[0].text == "result"
|
|
158
|
+
existing_session.run.assert_called_once_with("ls")
|