hud-python 0.3.5__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 -420
  87. hud/tools/computer/hud.py +376 -334
  88. hud/tools/computer/openai.py +295 -292
  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.5.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.5.dist-info/METADATA +0 -284
  190. hud_python-0.3.5.dist-info/RECORD +0 -120
  191. /hud/{adapters/common → shared}/tests/__init__.py +0 -0
  192. {hud_python-0.3.5.dist-info → hud_python-0.4.0.dist-info}/WHEEL +0 -0
hud/mcp/client.py DELETED
@@ -1,312 +0,0 @@
1
- """MCP Client wrapper with automatic initialization and debugging capabilities."""
2
-
3
- from __future__ import annotations
4
-
5
- import json
6
- import logging
7
- from typing import TYPE_CHECKING, Any
8
-
9
- from mcp_use.client import MCPClient as MCPUseClient
10
- from pydantic import AnyUrl
11
-
12
- if TYPE_CHECKING:
13
- from typing import Self
14
-
15
- from mcp import types
16
- from mcp_use.session import MCPSession as MCPUseSession
17
-
18
- logger = logging.getLogger(__name__)
19
-
20
-
21
- class MCPClient:
22
- """
23
- High-level MCP client wrapper that handles initialization, tool discovery,
24
- and provides debugging capabilities.
25
- """
26
-
27
- def __init__(
28
- self,
29
- mcp_config: dict[str, dict[str, Any]],
30
- verbose: bool = False,
31
- ) -> None:
32
- """
33
- Initialize the MCP client.
34
-
35
- Args:
36
- mcp_config: MCP server configuration dict (required)
37
- verbose: Enable verbose logging of server communications
38
- auto_initialize: Whether to automatically initialize on construction
39
- """
40
- self.verbose = verbose
41
-
42
- # Initialize mcp_use client with proper config
43
- # Use from_dict to properly initialize with config
44
- config = {"mcpServers": mcp_config}
45
- self._mcp_client = MCPUseClient.from_dict(config)
46
-
47
- self._sessions: dict[str, MCPUseSession] = {}
48
- self._available_tools: list[types.Tool] = []
49
- self._tool_map: dict[str, tuple[str, types.Tool]] = {}
50
- self._telemetry_data: dict[str, Any] = {}
51
-
52
- # Set up verbose logging if requested
53
- if self.verbose:
54
- self._setup_verbose_logging()
55
-
56
- def _setup_verbose_logging(self) -> None:
57
- """Configure verbose logging for debugging."""
58
- # Set MCP-related loggers to DEBUG
59
- logging.getLogger("mcp").setLevel(logging.DEBUG)
60
- logging.getLogger("mcp_use").setLevel(logging.DEBUG)
61
- logging.getLogger("mcp.client.stdio").setLevel(logging.DEBUG)
62
-
63
- # Add handler for server communications
64
- if not any(isinstance(h, logging.StreamHandler) for h in logger.handlers):
65
- handler = logging.StreamHandler()
66
- handler.setFormatter(
67
- logging.Formatter("[%(levelname)s] %(asctime)s - %(name)s - %(message)s")
68
- )
69
- logger.addHandler(handler)
70
- logger.setLevel(logging.DEBUG)
71
-
72
- async def initialize(self) -> None:
73
- """Perform async initialization tasks."""
74
- await self.create_sessions()
75
- await self.discover_tools()
76
- await self.fetch_telemetry()
77
-
78
- async def create_sessions(self) -> dict[str, MCPUseSession]:
79
- # Create all sessions at once
80
- try:
81
- self._sessions = await self._mcp_client.create_all_sessions()
82
- except Exception as e:
83
- # If session creation fails, try to get Docker logs
84
- logger.error("Failed to create sessions: %s", e)
85
- if self.verbose:
86
- logger.info("Attempting to check Docker container status...")
87
- # await self._check_docker_containers()
88
- raise
89
-
90
- # Log session details in verbose mode
91
- if self.verbose and self._sessions:
92
- for name, session in self._sessions.items():
93
- logger.debug(" - %s: %s", name, type(session).__name__)
94
-
95
- return self._sessions
96
-
97
- async def discover_tools(self) -> list[types.Tool]:
98
- """Discover all available tools from connected servers."""
99
- logger.info("Discovering available tools...")
100
-
101
- self._available_tools = []
102
- self._tool_map = {}
103
-
104
- for server_name, session in self._sessions.items():
105
- try:
106
- # Ensure session is initialized
107
- if not hasattr(session, "connector") or not hasattr(
108
- session.connector, "client_session"
109
- ):
110
- await session.initialize()
111
-
112
- if session.connector.client_session is None:
113
- logger.warning("Client session not initialized for %s", server_name)
114
- continue
115
-
116
- # List tools
117
- tools_result = await session.connector.client_session.list_tools()
118
-
119
- logger.info(
120
- "Discovered %d tools from '%s': %s",
121
- len(tools_result.tools),
122
- server_name,
123
- [tool.name for tool in tools_result.tools],
124
- )
125
-
126
- # Add to collections
127
- for tool in tools_result.tools:
128
- self._available_tools.append(tool)
129
- self._tool_map[tool.name] = (server_name, tool)
130
-
131
- # Log detailed tool info in verbose mode
132
- if self.verbose:
133
- for tool in tools_result.tools:
134
- description = tool.description or ""
135
- logger.debug(
136
- " Tool '%s': %s",
137
- tool.name,
138
- description[:100] + "..." if len(description) > 100 else description,
139
- )
140
-
141
- except Exception as e:
142
- logger.error("Error discovering tools from '%s': %s", server_name, e)
143
- if self.verbose:
144
- logger.exception("Full error details:")
145
-
146
- logger.info("Total tools discovered: %d", len(self._available_tools))
147
- return self._available_tools
148
-
149
- async def fetch_telemetry(self) -> dict[str, Any]:
150
- """Fetch telemetry resource from all servers that provide it."""
151
- logger.info("Fetching telemetry resources...")
152
-
153
- for server_name, session in self._sessions.items():
154
- try:
155
- if not hasattr(session, "connector") or not hasattr(
156
- session.connector, "client_session"
157
- ):
158
- continue
159
-
160
- if session.connector.client_session is None:
161
- continue
162
-
163
- # Try to read telemetry resource
164
- try:
165
- result = await session.connector.client_session.read_resource(
166
- AnyUrl("telemetry://live")
167
- )
168
- if result and result.contents and len(result.contents) > 0:
169
- telemetry_data = json.loads(result.contents[0].text) # type: ignore
170
- self._telemetry_data[server_name] = telemetry_data
171
-
172
- logger.info("📡 Telemetry data from server '%s':", server_name)
173
- if "live_url" in telemetry_data:
174
- logger.info(" 🖥️ Live URL: %s", telemetry_data["live_url"])
175
- if "status" in telemetry_data:
176
- logger.info(" 📊 Status: %s", telemetry_data["status"])
177
- if "services" in telemetry_data:
178
- logger.info(" 📋 Services:")
179
- for service, status in telemetry_data["services"].items():
180
- status_icon = "✅" if status == "running" else "❌"
181
- logger.info(" %s %s: %s", status_icon, service, status)
182
-
183
- if self.verbose:
184
- logger.debug(
185
- "Full telemetry data:\n%s", json.dumps(telemetry_data, indent=2)
186
- )
187
-
188
- except Exception as e:
189
- # Resource might not exist, which is fine
190
- if self.verbose:
191
- logger.debug("No telemetry resource from '%s': %s", server_name, e)
192
-
193
- except Exception as e:
194
- logger.error("Error fetching telemetry from '%s': %s", server_name, e)
195
-
196
- return self._telemetry_data
197
-
198
- async def call_tool(
199
- self, tool_name: str, arguments: dict[str, Any] | None = None
200
- ) -> types.CallToolResult:
201
- """
202
- Call a tool by name with the given arguments.
203
-
204
- Args:
205
- tool_name: Name of the tool to call
206
- arguments: Tool arguments
207
-
208
- Returns:
209
- Tool execution result
210
-
211
- Raises:
212
- ValueError: If tool not found
213
- """
214
- if tool_name not in self._tool_map:
215
- raise ValueError(f"Tool '{tool_name}' not found")
216
-
217
- server_name, tool = self._tool_map[tool_name]
218
- session = self._sessions[server_name]
219
-
220
- if self.verbose:
221
- logger.debug(
222
- "Calling tool '%s' on server '%s' with arguments: %s",
223
- tool_name,
224
- server_name,
225
- json.dumps(arguments, indent=2) if arguments else "None",
226
- )
227
-
228
- if session.connector.client_session is None:
229
- raise ValueError(f"Client session not initialized for {server_name}")
230
-
231
- result = await session.connector.client_session.call_tool(
232
- name=tool_name, arguments=arguments or {}
233
- )
234
-
235
- if self.verbose:
236
- logger.debug("Tool '%s' result: %s", tool_name, result)
237
-
238
- return result
239
-
240
- async def read_resource(self, uri: AnyUrl) -> types.ReadResourceResult | None:
241
- """
242
- Read a resource by URI from any server that provides it.
243
-
244
- Args:
245
- uri: Resource URI (e.g., "telemetry://live")
246
-
247
- Returns:
248
- Resource contents or None if not found
249
- """
250
- for server_name, session in self._sessions.items():
251
- try:
252
- if not hasattr(session, "connector") or not hasattr(
253
- session.connector, "client_session"
254
- ):
255
- continue
256
-
257
- if session.connector.client_session is None:
258
- continue
259
-
260
- result = await session.connector.client_session.read_resource(uri)
261
-
262
- if self.verbose:
263
- logger.debug(
264
- "Successfully read resource '%s' from server '%s'", uri, server_name
265
- )
266
-
267
- return result
268
-
269
- except Exception as e:
270
- if self.verbose:
271
- logger.debug(
272
- "Could not read resource '%s' from server '%s': %s", uri, server_name, e
273
- )
274
- continue
275
-
276
- return None
277
-
278
- def get_available_tools(self) -> list[types.Tool]:
279
- """Get list of all available tools."""
280
- return self._available_tools
281
-
282
- def get_tool_map(self) -> dict[str, tuple[str, types.Tool]]:
283
- """Get mapping of tool names to (server_name, tool) tuples."""
284
- return self._tool_map
285
-
286
- def get_sessions(self) -> dict[str, MCPUseSession]:
287
- """Get active MCP sessions."""
288
- return self._sessions
289
-
290
- def get_telemetry_data(self) -> dict[str, Any]:
291
- """Get collected telemetry data from all servers."""
292
- return self._telemetry_data
293
-
294
- def get_all_active_sessions(self) -> dict[str, MCPUseSession]:
295
- """Get all active sessions (compatibility method)."""
296
- return self._sessions
297
-
298
- async def close(self) -> None:
299
- """Close all active sessions."""
300
- await self._mcp_client.close_all_sessions()
301
-
302
- self._sessions = {}
303
- self._available_tools = []
304
- self._tool_map = {}
305
-
306
- async def __aenter__(self) -> Self:
307
- """Async context manager entry."""
308
- return self
309
-
310
- async def __aexit__(self, exc_type: object, exc_val: object, exc_tb: object) -> None:
311
- """Async context manager exit."""
312
- await self.close()