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,188 +1,188 @@
1
- """Tests for the MCP client protocol and implementations."""
2
-
3
- from __future__ import annotations
4
-
5
- from typing import Any
6
-
7
- import pytest
8
- from mcp import types
9
-
10
- from hud.clients.base import AgentMCPClient, BaseHUDClient
11
- from hud.clients.fastmcp import FastMCPHUDClient
12
- from hud.clients.mcp_use import MCPUseHUDClient
13
- from hud.types import MCPToolCall, MCPToolResult
14
-
15
-
16
- class MockClient(BaseHUDClient):
17
- """Mock client for testing the base class."""
18
-
19
- def __init__(self, **kwargs):
20
- super().__init__(mcp_config={"test": {"url": "mock://test"}}, **kwargs)
21
- self._connected = False
22
- self._mock_tools = [
23
- types.Tool(
24
- name="test_tool",
25
- description="A test tool",
26
- inputSchema={"type": "object", "properties": {}},
27
- )
28
- ]
29
-
30
- async def _connect(self, mcp_config: dict[str, dict[str, Any]]) -> None:
31
- self._connected = True
32
-
33
- async def list_tools(self) -> list[types.Tool]:
34
- if not self._connected:
35
- raise RuntimeError("Not connected")
36
- return self._mock_tools
37
-
38
- async def list_resources(self) -> list[types.Resource]:
39
- """Minimal list_resources for protocol satisfaction in tests."""
40
- return []
41
-
42
- async def _call_tool(self, tool_call: MCPToolCall) -> MCPToolResult:
43
- if tool_call.name == "test_tool":
44
- return MCPToolResult(
45
- content=[types.TextContent(type="text", text="Success")], isError=False
46
- )
47
- raise ValueError(f"Tool {tool_call.name} not found")
48
-
49
- async def read_resource(self, uri: str) -> types.ReadResourceResult | None:
50
- if uri == "telemetry://live":
51
- from pydantic import AnyUrl
52
-
53
- return types.ReadResourceResult(
54
- contents=[
55
- types.TextResourceContents(
56
- uri=AnyUrl(uri),
57
- mimeType="application/json",
58
- text='{"status": "healthy", "services": {"api": "running"}}',
59
- )
60
- ]
61
- )
62
- return None
63
-
64
- async def _disconnect(self) -> None:
65
- """Disconnect from the MCP server."""
66
- self._connected = False
67
-
68
-
69
- class TestProtocol:
70
- """Test that all clients implement the protocol correctly."""
71
-
72
- def test_mock_client_implements_protocol(self):
73
- """Test that our mock client implements the protocol."""
74
- client = MockClient()
75
- assert isinstance(client, AgentMCPClient)
76
-
77
- def test_fastmcp_client_implements_protocol(self):
78
- """Test that FastMCPHUDClient implements the protocol."""
79
- client = FastMCPHUDClient({"test": {"url": "http://localhost"}})
80
- assert isinstance(client, AgentMCPClient)
81
-
82
- def test_mcp_use_client_implements_protocol(self):
83
- """Test that MCPUseHUDClient implements the protocol."""
84
- client = MCPUseHUDClient({"test": {"url": "http://localhost"}})
85
- assert isinstance(client, AgentMCPClient)
86
-
87
- @pytest.mark.asyncio
88
- async def test_base_client_initialization(self):
89
- """Test that base client initialization works correctly."""
90
- client = MockClient()
91
-
92
- # Not initialized yet
93
- assert not client._initialized
94
- # Can't call list_tools before initialization, it would raise an error
95
-
96
- # Initialize
97
- await client.initialize()
98
-
99
- # Should be initialized with tools discovered
100
- assert client._initialized
101
- tools = await client.list_tools()
102
- assert len(tools) == 1
103
- assert tools[0].name == "test_tool"
104
-
105
- @pytest.mark.asyncio
106
- async def test_telemetry_fetching(self):
107
- """Test that telemetry is fetched during initialization."""
108
- client = MockClient()
109
-
110
- # No telemetry before initialization
111
- assert not hasattr(client, "_telemetry_data") or client._telemetry_data == {}
112
-
113
- # Initialize
114
- await client.initialize()
115
-
116
- # Should have telemetry
117
- assert hasattr(client, "_telemetry_data")
118
- assert client._telemetry_data["status"] == "healthy"
119
- assert client._telemetry_data["services"]["api"] == "running"
120
-
121
- @pytest.mark.asyncio
122
- async def test_context_manager(self):
123
- """Test that clients work as context managers."""
124
- client = MockClient()
125
-
126
- async with client:
127
- assert client._initialized
128
- tools = await client.list_tools()
129
- assert len(tools) == 1
130
-
131
- # Should be closed after exiting context
132
- assert not client._initialized
133
-
134
- @pytest.mark.asyncio
135
- async def test_tool_execution(self):
136
- """Test tool execution through the protocol."""
137
- client = MockClient()
138
-
139
- await client.initialize()
140
-
141
- # Execute a tool - test both call signatures
142
- # Test with MCPToolCall
143
- tool_call = MCPToolCall(name="test_tool", arguments={"arg": "value"})
144
- result = await client.call_tool(tool_call)
145
-
146
- assert isinstance(result, MCPToolResult)
147
- assert not result.isError
148
- from mcp.types import TextContent
149
-
150
- assert isinstance(result.content[0], TextContent) and result.content[0].text == "Success"
151
-
152
- # Test with name/arguments
153
- result2 = await client.call_tool(name="test_tool", arguments={"arg": "value"})
154
- assert isinstance(result2, MCPToolResult)
155
- assert not result2.isError
156
- assert isinstance(result2.content[0], TextContent) and result2.content[0].text == "Success"
157
-
158
- @pytest.mark.asyncio
159
- async def test_tool_not_found(self):
160
- """Test error handling for missing tools."""
161
- client = MockClient()
162
-
163
- await client.initialize()
164
-
165
- # Try to execute non-existent tool
166
- with pytest.raises(ValueError, match="Tool unknown_tool not found"):
167
- await client.call_tool(name="unknown_tool", arguments={})
168
-
169
-
170
- class TestClientCompatibility:
171
- """Test that clients are compatible with agents."""
172
-
173
- def test_protocol_satisfied(self):
174
- """Test that all clients satisfy the protocol."""
175
- # Test mock client
176
- mock_client = MockClient()
177
- assert isinstance(mock_client, AgentMCPClient)
178
- assert hasattr(mock_client, "initialize")
179
- assert hasattr(mock_client, "list_tools")
180
- assert hasattr(mock_client, "call_tool")
181
-
182
- # Test FastMCP client
183
- fastmcp_client = FastMCPHUDClient({"test": {"url": "http://localhost"}})
184
- assert isinstance(fastmcp_client, AgentMCPClient)
185
-
186
- # Test MCP-use client
187
- mcp_use_client = MCPUseHUDClient({"test": {"url": "http://localhost"}})
188
- assert isinstance(mcp_use_client, AgentMCPClient)
1
+ """Tests for the MCP client protocol and implementations."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any
6
+
7
+ import pytest
8
+ from mcp import types
9
+
10
+ from hud.clients.base import AgentMCPClient, BaseHUDClient
11
+ from hud.clients.fastmcp import FastMCPHUDClient
12
+ from hud.clients.mcp_use import MCPUseHUDClient
13
+ from hud.types import MCPToolCall, MCPToolResult
14
+
15
+
16
+ class MockClient(BaseHUDClient):
17
+ """Mock client for testing the base class."""
18
+
19
+ def __init__(self, **kwargs):
20
+ super().__init__(mcp_config={"test": {"url": "mock://test"}}, **kwargs)
21
+ self._connected = False
22
+ self._mock_tools = [
23
+ types.Tool(
24
+ name="test_tool",
25
+ description="A test tool",
26
+ inputSchema={"type": "object", "properties": {}},
27
+ )
28
+ ]
29
+
30
+ async def _connect(self, mcp_config: dict[str, dict[str, Any]]) -> None:
31
+ self._connected = True
32
+
33
+ async def list_tools(self) -> list[types.Tool]:
34
+ if not self._connected:
35
+ raise RuntimeError("Not connected")
36
+ return self._mock_tools
37
+
38
+ async def list_resources(self) -> list[types.Resource]:
39
+ """Minimal list_resources for protocol satisfaction in tests."""
40
+ return []
41
+
42
+ async def _call_tool(self, tool_call: MCPToolCall) -> MCPToolResult:
43
+ if tool_call.name == "test_tool":
44
+ return MCPToolResult(
45
+ content=[types.TextContent(type="text", text="Success")], isError=False
46
+ )
47
+ raise ValueError(f"Tool {tool_call.name} not found")
48
+
49
+ async def read_resource(self, uri: str) -> types.ReadResourceResult | None:
50
+ if uri == "telemetry://live":
51
+ from pydantic import AnyUrl
52
+
53
+ return types.ReadResourceResult(
54
+ contents=[
55
+ types.TextResourceContents(
56
+ uri=AnyUrl(uri),
57
+ mimeType="application/json",
58
+ text='{"status": "healthy", "services": {"api": "running"}}',
59
+ )
60
+ ]
61
+ )
62
+ return None
63
+
64
+ async def _disconnect(self) -> None:
65
+ """Disconnect from the MCP server."""
66
+ self._connected = False
67
+
68
+
69
+ class TestProtocol:
70
+ """Test that all clients implement the protocol correctly."""
71
+
72
+ def test_mock_client_implements_protocol(self):
73
+ """Test that our mock client implements the protocol."""
74
+ client = MockClient()
75
+ assert isinstance(client, AgentMCPClient)
76
+
77
+ def test_fastmcp_client_implements_protocol(self):
78
+ """Test that FastMCPHUDClient implements the protocol."""
79
+ client = FastMCPHUDClient({"test": {"url": "http://localhost"}})
80
+ assert isinstance(client, AgentMCPClient)
81
+
82
+ def test_mcp_use_client_implements_protocol(self):
83
+ """Test that MCPUseHUDClient implements the protocol."""
84
+ client = MCPUseHUDClient({"test": {"url": "http://localhost"}})
85
+ assert isinstance(client, AgentMCPClient)
86
+
87
+ @pytest.mark.asyncio
88
+ async def test_base_client_initialization(self):
89
+ """Test that base client initialization works correctly."""
90
+ client = MockClient()
91
+
92
+ # Not initialized yet
93
+ assert not client._initialized
94
+ # Can't call list_tools before initialization, it would raise an error
95
+
96
+ # Initialize
97
+ await client.initialize()
98
+
99
+ # Should be initialized with tools discovered
100
+ assert client._initialized
101
+ tools = await client.list_tools()
102
+ assert len(tools) == 1
103
+ assert tools[0].name == "test_tool"
104
+
105
+ @pytest.mark.asyncio
106
+ async def test_telemetry_fetching(self):
107
+ """Test that telemetry is fetched during initialization."""
108
+ client = MockClient()
109
+
110
+ # No telemetry before initialization
111
+ assert not hasattr(client, "_telemetry_data") or client._telemetry_data == {}
112
+
113
+ # Initialize
114
+ await client.initialize()
115
+
116
+ # Should have telemetry
117
+ assert hasattr(client, "_telemetry_data")
118
+ assert client._telemetry_data["status"] == "healthy"
119
+ assert client._telemetry_data["services"]["api"] == "running"
120
+
121
+ @pytest.mark.asyncio
122
+ async def test_context_manager(self):
123
+ """Test that clients work as context managers."""
124
+ client = MockClient()
125
+
126
+ async with client:
127
+ assert client._initialized
128
+ tools = await client.list_tools()
129
+ assert len(tools) == 1
130
+
131
+ # Should be closed after exiting context
132
+ assert not client._initialized
133
+
134
+ @pytest.mark.asyncio
135
+ async def test_tool_execution(self):
136
+ """Test tool execution through the protocol."""
137
+ client = MockClient()
138
+
139
+ await client.initialize()
140
+
141
+ # Execute a tool - test both call signatures
142
+ # Test with MCPToolCall
143
+ tool_call = MCPToolCall(name="test_tool", arguments={"arg": "value"})
144
+ result = await client.call_tool(tool_call)
145
+
146
+ assert isinstance(result, MCPToolResult)
147
+ assert not result.isError
148
+ from mcp.types import TextContent
149
+
150
+ assert isinstance(result.content[0], TextContent) and result.content[0].text == "Success"
151
+
152
+ # Test with name/arguments
153
+ result2 = await client.call_tool(name="test_tool", arguments={"arg": "value"})
154
+ assert isinstance(result2, MCPToolResult)
155
+ assert not result2.isError
156
+ assert isinstance(result2.content[0], TextContent) and result2.content[0].text == "Success"
157
+
158
+ @pytest.mark.asyncio
159
+ async def test_tool_not_found(self):
160
+ """Test error handling for missing tools."""
161
+ client = MockClient()
162
+
163
+ await client.initialize()
164
+
165
+ # Try to execute non-existent tool
166
+ with pytest.raises(ValueError, match="Tool unknown_tool not found"):
167
+ await client.call_tool(name="unknown_tool", arguments={})
168
+
169
+
170
+ class TestClientCompatibility:
171
+ """Test that clients are compatible with agents."""
172
+
173
+ def test_protocol_satisfied(self):
174
+ """Test that all clients satisfy the protocol."""
175
+ # Test mock client
176
+ mock_client = MockClient()
177
+ assert isinstance(mock_client, AgentMCPClient)
178
+ assert hasattr(mock_client, "initialize")
179
+ assert hasattr(mock_client, "list_tools")
180
+ assert hasattr(mock_client, "call_tool")
181
+
182
+ # Test FastMCP client
183
+ fastmcp_client = FastMCPHUDClient({"test": {"url": "http://localhost"}})
184
+ assert isinstance(fastmcp_client, AgentMCPClient)
185
+
186
+ # Test MCP-use client
187
+ mcp_use_client = MCPUseHUDClient({"test": {"url": "http://localhost"}})
188
+ assert isinstance(mcp_use_client, AgentMCPClient)
@@ -1 +1 @@
1
- """HUD MCP client utilities."""
1
+ """HUD MCP client utilities."""