hud-python 0.4.21__py3-none-any.whl → 0.4.22__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.

@@ -0,0 +1,196 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+ from typing import Any
5
+
6
+ import mcp.types as types
7
+ import pytest
8
+
9
+ from hud.tools.grounding.grounded_tool import GroundedComputerTool
10
+ from hud.types import MCPToolCall, MCPToolResult
11
+
12
+
13
+ @dataclass
14
+ class FakeResult:
15
+ content: list[types.ContentBlock]
16
+ isError: bool = False
17
+ structuredContent: dict | None = None
18
+
19
+
20
+ class FakeMCPClient:
21
+ """Fake MCP client that implements AgentMCPClient protocol."""
22
+
23
+ _initialized: bool
24
+
25
+ def __init__(self) -> None:
26
+ self.calls: list[tuple[str, dict[str, Any]]] = []
27
+ self._initialized = False
28
+
29
+ @property
30
+ def mcp_config(self) -> dict[str, dict[str, Any]]:
31
+ return {"test": {"command": "echo", "args": ["test"]}}
32
+
33
+ @property
34
+ def is_connected(self) -> bool:
35
+ return self._initialized
36
+
37
+ async def initialize(self, mcp_config: dict[str, dict[str, Any]] | None = None) -> None:
38
+ self._initialized = True
39
+
40
+ async def list_tools(self) -> list[types.Tool]:
41
+ return [types.Tool(name="computer", description="Test tool", inputSchema={})]
42
+
43
+ async def call_tool(self, tool_call: MCPToolCall) -> MCPToolResult:
44
+ self.calls.append((tool_call.name, tool_call.arguments or {}))
45
+ return MCPToolResult(content=[types.TextContent(text="ok", type="text")], isError=False)
46
+
47
+ async def shutdown(self) -> None:
48
+ self._initialized = False
49
+
50
+
51
+ class FakeGrounder:
52
+ """Fake grounder that implements Grounder interface."""
53
+
54
+ def __init__(self, coords: tuple[int, int] | None = (10, 20)) -> None:
55
+ self.coords = coords
56
+ self.calls: list[tuple[str, str]] = []
57
+
58
+ async def predict_click(
59
+ self, *, image_b64: str, instruction: str, max_retries: int = 3
60
+ ) -> tuple[int, int] | None:
61
+ self.calls.append((image_b64[:10], instruction))
62
+ return self.coords
63
+
64
+
65
+ def _png_b64() -> str:
66
+ # 1x1 transparent PNG base64 (valid minimal image)
67
+ return (
68
+ "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR4nGMAAQAABQAB"
69
+ "J2n0mQAAAABJRU5ErkJggg=="
70
+ )
71
+
72
+
73
+ @pytest.mark.asyncio
74
+ async def test_click_action_grounds_and_calls_mcp() -> None:
75
+ client = FakeMCPClient()
76
+ grounder = FakeGrounder(coords=(123, 456))
77
+ tool = GroundedComputerTool(grounder=grounder, mcp_client=client) # type: ignore
78
+
79
+ blocks = await tool(
80
+ action="click",
81
+ element_description="red button",
82
+ screenshot_b64=_png_b64(),
83
+ button="left",
84
+ )
85
+
86
+ assert isinstance(blocks, list)
87
+ # Grounder called once
88
+ assert len(grounder.calls) == 1
89
+ # MCP called with resolved coordinates
90
+ assert client.calls == [("computer", {"action": "click", "x": 123, "y": 456, "button": "left"})]
91
+
92
+
93
+ @pytest.mark.asyncio
94
+ async def test_move_and_scroll_require_element_description_and_screenshot() -> None:
95
+ client = FakeMCPClient()
96
+ grounder = FakeGrounder(coords=(5, 6))
97
+ tool = GroundedComputerTool(grounder=grounder, mcp_client=client) # type: ignore
98
+
99
+ # Missing element_description
100
+ with pytest.raises(Exception) as ei:
101
+ await tool(action="move", screenshot_b64=_png_b64())
102
+ assert "element_description is required" in str(ei.value)
103
+
104
+ # Missing screenshot
105
+ with pytest.raises(Exception) as ei2:
106
+ await tool(action="scroll", element_description="list", scroll_y=100)
107
+ assert "No screenshot available" in str(ei2.value)
108
+
109
+
110
+ @pytest.mark.asyncio
111
+ async def test_drag_grounds_both_points_and_calls_mcp() -> None:
112
+ client = FakeMCPClient()
113
+ grounder = FakeGrounder(coords=(10, 20))
114
+ tool = GroundedComputerTool(grounder=grounder, mcp_client=client) # type: ignore
115
+
116
+ await tool(
117
+ action="drag",
118
+ start_element_description="source",
119
+ end_element_description="target",
120
+ screenshot_b64=_png_b64(),
121
+ button="left",
122
+ )
123
+
124
+ # Two grounding calls (start and end)
125
+ assert len(grounder.calls) == 2
126
+ # Drag path contains two points, same coords from fake grounder
127
+ name, args = client.calls[0]
128
+ assert name == "computer"
129
+ assert args["action"] == "drag"
130
+ assert args["button"] == "left"
131
+ assert args["path"] == [(10, 20), (10, 20)]
132
+
133
+
134
+ @pytest.mark.asyncio
135
+ async def test_drag_requires_both_descriptions_and_screenshot() -> None:
136
+ client = FakeMCPClient()
137
+ grounder = FakeGrounder()
138
+ tool = GroundedComputerTool(grounder=grounder, mcp_client=client) # type: ignore
139
+
140
+ with pytest.raises(Exception) as ei:
141
+ await tool(action="drag", start_element_description="a", screenshot_b64=_png_b64())
142
+ assert "start_element_description and end_element_description" in str(ei.value)
143
+
144
+ with pytest.raises(Exception) as ei2:
145
+ await tool(
146
+ action="drag",
147
+ start_element_description="a",
148
+ end_element_description="b",
149
+ )
150
+ assert "No screenshot available" in str(ei2.value)
151
+
152
+
153
+ @pytest.mark.asyncio
154
+ async def test_direct_actions_bypass_grounding_and_call_mcp() -> None:
155
+ client = FakeMCPClient()
156
+ grounder = FakeGrounder()
157
+ tool = GroundedComputerTool(grounder=grounder, mcp_client=client) # type: ignore
158
+
159
+ # Actions that bypass grounding
160
+ for action, extra in [
161
+ ("screenshot", {}),
162
+ ("type", {"text": "hello"}),
163
+ ("keypress", {"keys": ["ctrl", "a"]}),
164
+ ("wait", {}),
165
+ ("get_current_url", {}),
166
+ ("get_dimensions", {}),
167
+ ("get_environment", {}),
168
+ ]:
169
+ client.calls.clear()
170
+ _ = await tool(action=action, **extra)
171
+ assert client.calls and client.calls[0][0] == "computer"
172
+ assert client.calls[0][1]["action"] == action
173
+ # Grounder not invoked for these
174
+ assert grounder.calls == []
175
+
176
+
177
+ @pytest.mark.asyncio
178
+ async def test_unsupported_action_raises() -> None:
179
+ client = FakeMCPClient()
180
+ grounder = FakeGrounder()
181
+ tool = GroundedComputerTool(grounder=grounder, mcp_client=client) # type: ignore
182
+
183
+ with pytest.raises(Exception) as ei:
184
+ await tool(action="zoom")
185
+ assert "Unsupported action" in str(ei.value)
186
+
187
+
188
+ @pytest.mark.asyncio
189
+ async def test_grounding_failure_propagates_as_error() -> None:
190
+ client = FakeMCPClient()
191
+ grounder = FakeGrounder(coords=None)
192
+ tool = GroundedComputerTool(grounder=grounder, mcp_client=client) # type: ignore
193
+
194
+ with pytest.raises(Exception) as ei:
195
+ await tool(action="click", element_description="x", screenshot_b64=_png_b64())
196
+ assert "Could not locate element" in str(ei.value)
@@ -52,7 +52,7 @@ class TestPlaywrightTool:
52
52
  assert any(isinstance(b, TextContent) for b in blocks)
53
53
  # The actual call includes wait_until parameter with a Field object
54
54
  mock_page.goto.assert_called_once()
55
- args, kwargs = mock_page.goto.call_args
55
+ args, _kwargs = mock_page.goto.call_args
56
56
  assert args[0] == "https://example.com"
57
57
  mock_ensure.assert_called_once()
58
58
 
@@ -33,7 +33,7 @@ class TestToolsInit:
33
33
  """Test lazy import with invalid attribute name."""
34
34
  import hud.tools as tools_module
35
35
 
36
- with pytest.raises(AttributeError, match="module '.*' has no attribute 'InvalidTool'"):
36
+ with pytest.raises(AttributeError, match=r"module '.*' has no attribute 'InvalidTool'"):
37
37
  _ = tools_module.InvalidTool
38
38
 
39
39
  def test_direct_imports_available(self):
@@ -58,7 +58,7 @@ class TestRun:
58
58
  mock_proc.communicate = AsyncMock(return_value=(b"processed", b""))
59
59
 
60
60
  with patch("asyncio.create_subprocess_shell", return_value=mock_proc):
61
- return_code, stdout, stderr = await run("cat", input="test input")
61
+ return_code, stdout, _stderr = await run("cat", input="test input")
62
62
 
63
63
  assert return_code == 0
64
64
  assert stdout == "processed"
@@ -91,7 +91,7 @@ class TestRun:
91
91
  ):
92
92
  mock_wait_for.return_value = (b"done", b"")
93
93
 
94
- return_code, stdout, stderr = await run("sleep 1", timeout=5.0)
94
+ _return_code, _stdout, _stderr = await run("sleep 1", timeout=5.0)
95
95
 
96
96
  # Check that wait_for was called with the correct timeout
97
97
  mock_wait_for.assert_called_once()
@@ -0,0 +1,86 @@
1
+ """Factory functions for creating agents compatible with run_dataset."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any
6
+
7
+ from openai import AsyncOpenAI
8
+
9
+ from hud.agents.grounded_openai import GroundedOpenAIChatAgent
10
+ from hud.agents.openai_chat_generic import GenericOpenAIChatAgent
11
+ from hud.tools.grounding import GrounderConfig
12
+
13
+
14
+ def create_openai_agent(**kwargs: Any) -> GenericOpenAIChatAgent:
15
+ """Factory for GenericOpenAIChatAgent with run_dataset compatibility.
16
+
17
+ Args:
18
+ api_key: OpenAI API key
19
+ base_url: Optional custom API endpoint
20
+ model_name: Model to use (e.g., "gpt-4o-mini")
21
+ **kwargs: Additional arguments passed to GenericOpenAIChatAgent
22
+
23
+ Returns:
24
+ Configured GenericOpenAIChatAgent instance
25
+
26
+ Example:
27
+ >>> from hud.datasets import run_dataset
28
+ >>> from hud.utils.agent_factories import create_openai_agent
29
+ >>> results = await run_dataset(
30
+ ... "My Eval",
31
+ ... "hud-evals/SheetBench-50",
32
+ ... create_openai_agent,
33
+ ... {"api_key": "your-key", "model_name": "gpt-4o-mini"},
34
+ ... )
35
+ """
36
+ api_key = kwargs.pop("api_key", None)
37
+ base_url = kwargs.pop("base_url", None)
38
+
39
+ openai_client = AsyncOpenAI(api_key=api_key, base_url=base_url)
40
+
41
+ return GenericOpenAIChatAgent(openai_client=openai_client, **kwargs)
42
+
43
+
44
+ def create_grounded_agent(**kwargs: Any) -> GroundedOpenAIChatAgent:
45
+ """Factory for GroundedOpenAIChatAgent with run_dataset compatibility.
46
+
47
+ Args:
48
+ api_key: OpenAI API key for planning model
49
+ base_url: Optional custom API endpoint for planning model
50
+ model_name: Planning model to use (e.g., "gpt-4o-mini")
51
+ grounder_api_key: API key for grounding model
52
+ grounder_api_base: API base URL for grounding model (default: OpenRouter)
53
+ grounder_model: Grounding model to use (default: qwen/qwen-2.5-vl-7b-instruct)
54
+ **kwargs: Additional arguments passed to GroundedOpenAIChatAgent
55
+
56
+ Returns:
57
+ Configured GroundedOpenAIChatAgent instance
58
+
59
+ Example:
60
+ >>> from hud.datasets import run_dataset
61
+ >>> from hud.utils.agent_factories import create_grounded_agent
62
+ >>> results = await run_dataset(
63
+ ... "Grounded Eval",
64
+ ... dataset,
65
+ ... create_grounded_agent,
66
+ ... {
67
+ ... "api_key": "openai-key",
68
+ ... "grounder_api_key": "openrouter-key",
69
+ ... "model_name": "gpt-4o-mini",
70
+ ... },
71
+ ... )
72
+ """
73
+ api_key = kwargs.pop("api_key", None)
74
+ base_url = kwargs.pop("base_url", None)
75
+ grounder_api_key = kwargs.pop("grounder_api_key", None)
76
+ grounder_api_base = kwargs.pop("grounder_api_base", "https://openrouter.ai/api/v1")
77
+ grounder_model = kwargs.pop("grounder_model", "qwen/qwen-2.5-vl-7b-instruct")
78
+
79
+ openai_client = AsyncOpenAI(api_key=api_key, base_url=base_url)
80
+ grounder_config = GrounderConfig(
81
+ api_base=grounder_api_base, model=grounder_model, api_key=grounder_api_key
82
+ )
83
+
84
+ return GroundedOpenAIChatAgent(
85
+ openai_client=openai_client, grounder_config=grounder_config, **kwargs
86
+ )
@@ -5,4 +5,4 @@ def test_import():
5
5
  """Test that the package can be imported."""
6
6
  import hud
7
7
 
8
- assert hud.__version__ == "0.4.21"
8
+ assert hud.__version__ == "0.4.22"
hud/version.py CHANGED
@@ -4,4 +4,4 @@ Version information for the HUD SDK.
4
4
 
5
5
  from __future__ import annotations
6
6
 
7
- __version__ = "0.4.21"
7
+ __version__ = "0.4.22"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hud-python
3
- Version: 0.4.21
3
+ Version: 0.4.22
4
4
  Summary: SDK for the HUD platform.
5
5
  Project-URL: Homepage, https://github.com/hud-evals/hud-python
6
6
  Project-URL: Bug Tracker, https://github.com/hud-evals/hud-python/issues
@@ -1,11 +1,12 @@
1
1
  hud/__init__.py,sha256=KU7G-_Mj6Mjf7trXA6X0ufN6QUmqhVi19NKbnNIvD74,532
2
2
  hud/__main__.py,sha256=YR8Dq8OhINOsVfQ55PmRXXg4fEK84Rt_-rMtJ5rvhWo,145
3
- hud/settings.py,sha256=q9aZiHjvbL4oLE-N8AttTW4rmzS8zPMnsca-iMGyEGc,2362
3
+ hud/settings.py,sha256=bgq_zstNGlan7UDZOiElY8Aw5ZU4xL7Ds5HP_Xzph1A,2535
4
4
  hud/types.py,sha256=DDK7-jcoI0iZbyIOfXlGJy9MpHAGcw_4napLs-v-8vY,6077
5
- hud/version.py,sha256=888JgQ-gt1NqBeShBZC3JqfOBDr4dkkOuQ33EZpv7yA,105
5
+ hud/version.py,sha256=T_tMaYLlvl2YQTkRh6f_ODEmmcIi1QPFX6xOChwfcCA,105
6
6
  hud/agents/__init__.py,sha256=UoIkljWdbq4bM0LD-mSaw6w826EqdEjOk7r6glNYwYQ,286
7
- hud/agents/base.py,sha256=2kZ8KOuNk_RQGqE7MvdMPm_jrlAkiaNNBz1tEW0Uxpg,30937
8
- hud/agents/claude.py,sha256=upWnGGwLz88k8i6zg0A6l1rK7DSTwJEU8pRevnR3irc,15314
7
+ hud/agents/base.py,sha256=kvHimAckIMGl4mge6KajxjQKd_v1PmUYRZUNRtcggHs,30965
8
+ hud/agents/claude.py,sha256=Ixc05qg-r0H1OoC_DE5Ov6RtODWp7uJcTJJqpmoIob0,15478
9
+ hud/agents/grounded_openai.py,sha256=bUB0sOYkWPx1NfASI97gVixV6CM5GY3K61S_upCSPm8,11176
9
10
  hud/agents/langchain.py,sha256=1EgCy8jfjunsWxlPC5XfvfLS6_XZVrIF1ZjtHcrvhYw,9584
10
11
  hud/agents/openai.py,sha256=tvFYsZ5yaoLkfjMnHe-COxRttMsLRXBLPdSqgeipQRk,14257
11
12
  hud/agents/openai_chat_generic.py,sha256=PQAD4GGE6sHs8R95qpgDBHEbSOJ7WXCYGYFmd3Nic1g,10628
@@ -14,7 +15,8 @@ hud/agents/misc/response_agent.py,sha256=pnaomb4H-QJm1YKU3tC1YnZXxOlDbTHIXaIH-6N
14
15
  hud/agents/tests/__init__.py,sha256=W-O-_4i34d9TTyEHV-O_q1Ai1gLhzwDaaPo02_TWQIY,34
15
16
  hud/agents/tests/test_base.py,sha256=F39ajSqASGUbPyPoWSY9KARFav62qNTK74W11Tr1Tg4,28970
16
17
  hud/agents/tests/test_claude.py,sha256=wqEKlzEvx8obz1sSm4NY0j-Zyt1qWNfDOmRqYIuAEd0,13069
17
- hud/agents/tests/test_client.py,sha256=fZoT22awa-Q84kAZ0CGiFGLNDEjBQ75rf6lImmVDsoQ,13120
18
+ hud/agents/tests/test_client.py,sha256=uikgh6yhjPPX2RBU4XJQMz1mNox9uXjuwsP8t93id18,13337
19
+ hud/agents/tests/test_grounded_openai_agent.py,sha256=VK8lUvHIjWicMX00VKPE-FZyjiJqTEhb80MuRRa9fVc,5437
18
20
  hud/agents/tests/test_openai.py,sha256=lhHowQyLp09oDVwBUjUECoPeH01F16i4O9YIHeugK6E,7028
19
21
  hud/cli/__init__.py,sha256=W4McbKAvs8cMIYlruzjV8Z_nxkmOK9ktYWP-WBnD6xo,35804
20
22
  hud/cli/__main__.py,sha256=fDH7XITyuDITwSDIVwRso06aouADO0CzTHKqp5TOwJE,143
@@ -23,7 +25,7 @@ hud/cli/build.py,sha256=_3rNFhNDNAChE95Ou5AqblVfD6QlUwKtcpgrPFChuq8,17742
23
25
  hud/cli/clone.py,sha256=AwVDIuhr8mHb1oT2Af2HrD25SiTdwATpE6zd93vzLgA,6099
24
26
  hud/cli/debug.py,sha256=FNzg9-_ZzUJA1nJfubmop7_2OT5mqnWsdpZyi4AVSXA,14163
25
27
  hud/cli/dev.py,sha256=R7YJWIZCUURXzs8ovOCF9V1FwOhsAJT5D3mZQkFmTI4,31134
26
- hud/cli/eval.py,sha256=v_uvzkS-J_dsxzVqszPkotOfUsNoq501-Ygdqi9BhKA,15770
28
+ hud/cli/eval.py,sha256=3ASGiQ4IZPB5EdMoNtwsPmmhO7v3lRmq8uoQCIsf5dM,15770
27
29
  hud/cli/hf.py,sha256=EYWL-fEN9SS2QJYU7iIBXs0qa9JIvmMWOMh9a8Ewt9s,15179
28
30
  hud/cli/init.py,sha256=7Yqp3gUVmK-vVE_6A9RKhZlSTlwxxb2ylJn1H22TYSY,19573
29
31
  hud/cli/list_func.py,sha256=ENxLL4X5uuqAASWZdQuI0k-tEzmlhUn5LATgz3QPQqQ,7065
@@ -57,7 +59,7 @@ hud/cli/utils/__init__.py,sha256=L6s0oNzY2LugGp9faodCPnjzM-ZUorUH05-HmYOq5hY,35
57
59
  hud/cli/utils/cursor.py,sha256=fy850p0rVp5k_1wwOCI7rK1SggbselJrywFInSQ2gio,3009
58
60
  hud/cli/utils/docker.py,sha256=VTUcoPqxh3uXOgvL6NSqYiSDhyCPRp3jTfFbnIDwumg,3774
59
61
  hud/cli/utils/environment.py,sha256=-Z8UkXlRpdGsBkjw-x1EJ2IwcSCrlWaaf25ZoPDMrtw,4225
60
- hud/cli/utils/interactive.py,sha256=QiKL14rRdzuU_wBgCYEoA3JVMO6eJUezAZxX0GjZKRU,16526
62
+ hud/cli/utils/interactive.py,sha256=qD25ZhDHI037EAhZUBFUXUpuERM979PnsxFTfpvNxhk,16523
61
63
  hud/cli/utils/logging.py,sha256=ZgjjKVPAa7dcfJ6SMBrdfZ63d1UnnhYC-zeh7gFBXsI,8841
62
64
  hud/cli/utils/metadata.py,sha256=VBvNNqHS5-sH97H3PhVmdOcyzZ2NGH47fVUUtHBoGLQ,7808
63
65
  hud/cli/utils/registry.py,sha256=N49cDHOWDp85EawIhQFQItt-yvFZpfLm74m8l4-LQy4,4349
@@ -135,10 +137,16 @@ hud/tools/computer/settings.py,sha256=b1XJsEQjB9qhN1xHfVENATkzinEebe0ZPyLzMgCGkK
135
137
  hud/tools/executors/__init__.py,sha256=jHxfus9SLhkL6YGtebR5RyKYyVAix3yu5EkUp2Q27Kg,732
136
138
  hud/tools/executors/base.py,sha256=VP2SiIEBSXvklnkasGxVuy-OmDMd9rjuxZh_YuUQH7A,14481
137
139
  hud/tools/executors/pyautogui.py,sha256=11eUQJAgFmHxwd9INAb2L9tgBmEv2Vgn0cwhwvGKlC8,22361
138
- hud/tools/executors/xdo.py,sha256=vc3ciSojmJ58HoL1VC2MC-F_KFrk4_YQHmoJp4rgv1k,18136
140
+ hud/tools/executors/xdo.py,sha256=UF53DbMX-bRGiHd-O7cCJmCrVaYuP83xiJggER7HcDk,18137
139
141
  hud/tools/executors/tests/__init__.py,sha256=opFpGSH6cEqIZgt9izXd3Yt85pC7xkxiYmOZQTHf4AY,32
140
- hud/tools/executors/tests/test_base_executor.py,sha256=crTXzxsMF7cnUihCpTzpqOYSjXpZWMj-nofcqC4Nu8U,13548
142
+ hud/tools/executors/tests/test_base_executor.py,sha256=ovh99to5jbQfrCKfCUnDbY-q3oDk_cMmHOVSv7Sn02E,13549
141
143
  hud/tools/executors/tests/test_pyautogui_executor.py,sha256=Shv6pnWtlsMXBMlN5DjlttCu6rZ1H447d1QZumduOnU,6640
144
+ hud/tools/grounding/__init__.py,sha256=oazR_qTJqkeGtjy_0w1QW58PQ872PkVwtYI-v2KIX3k,311
145
+ hud/tools/grounding/config.py,sha256=Vsd5ASDZFL7kW7toKkgrYN5D-ZV6ovKZyX4nxRrHvRs,1869
146
+ hud/tools/grounding/grounded_tool.py,sha256=L3O8FzpDoC8ZrnT0xkjlUwAkg7mAbnhK3ju0nE2zCiQ,12679
147
+ hud/tools/grounding/grounder.py,sha256=DwRp7I30ldgdDjxtUJuBh3GWESNUyHbRhCXT28m3xLk,11060
148
+ hud/tools/grounding/tests/__init__.py,sha256=jLw4nmvvvZu2ln2_yEUUKg72IewQ4HaXCUWJuX3ECZY,33
149
+ hud/tools/grounding/tests/test_grounded_tool.py,sha256=kr-wOOJZcfdYfhRSNFnhv8EuIsjbwgHDQbUSH9WnxMw,6446
142
150
  hud/tools/tests/__init__.py,sha256=eEYYkxX5Hz9woXVOBJ2H2_CQoEih0vH6nRt3sH2Z8v8,49
143
151
  hud/tools/tests/test_base.py,sha256=m6EelJ47F_hMqvRjrr6vEdiW1AtLgz3ZH1V1IUzTxzI,8983
144
152
  hud/tools/tests/test_bash.py,sha256=-g9a6sYgKKXRXmqYGYQBgpKEF_OlKE_uDDQXYMx6rT0,5113
@@ -147,12 +155,13 @@ hud/tools/tests/test_computer.py,sha256=BmrX2PV3wzHC4-xPJnWvenDV_kWt8LF8ia0kELBg
147
155
  hud/tools/tests/test_computer_actions.py,sha256=YtUNFL7anhpXrcvg8EoUY1CqIV-TAAyaNFLZO9CiJ40,1194
148
156
  hud/tools/tests/test_edit.py,sha256=pHw1MSs-P8mDKrwKUDW77vQfoMNwmbrEBt_MkKr2rE0,9734
149
157
  hud/tools/tests/test_init.py,sha256=fl4Tf4IUUFOKhdSRHu9GE4mkaTDiXw-2auxj4s84HuE,698
150
- hud/tools/tests/test_playwright_tool.py,sha256=0va9IuZ7P5vXZDzan_sn-89ahu3piQMQDaiREzEvXNc,6736
158
+ hud/tools/tests/test_playwright_tool.py,sha256=TG0uieerc5wXq_JX66BLfXxphbSYGlDPhSbuoeizMu4,6737
151
159
  hud/tools/tests/test_response.py,sha256=pEv3p0k1reSKtjbA8xneu--OuCHydbHHl6YWorV4zOg,2212
152
160
  hud/tools/tests/test_tools.py,sha256=paz28V98Am-oR7MBJPDgY-BRV14HQo_0F6X5JIC8aic,4563
153
- hud/tools/tests/test_tools_init.py,sha256=qbUCNWbswlkbsSJR-CBnRk_h_l1CLasRffIhp8tMG84,1726
154
- hud/tools/tests/test_utils.py,sha256=oYxEnLpSA5sEeYFGUTj74QRNv0AHP3AjmYYHXgIW0BY,5496
161
+ hud/tools/tests/test_tools_init.py,sha256=aOX9IKji-4ThHEiRYULa7YlIajP0lj3eFPjgzlEg9TI,1727
162
+ hud/tools/tests/test_utils.py,sha256=qaujM1uyTMaKqWIeEgxty5GOFyfSUtrYCEHhmIazoy4,5500
155
163
  hud/utils/__init__.py,sha256=ckuIzwqgTxkzASa04XTPsOu_TqsRYUKBWUYfcSi3Xnc,164
164
+ hud/utils/agent_factories.py,sha256=cvfXByqG6gOYHtm1VGeJjCpxoLxM4aJez8rH-AerP_A,3186
156
165
  hud/utils/async_utils.py,sha256=5cKrJcnaHV2eJNxeyx0r7fPcdPTDBK7kM9-nLaF51X4,2409
157
166
  hud/utils/design.py,sha256=Ymz29qdgqmnXCupDmASCqgkoW8fS6_cg5COXiFUUl50,17621
158
167
  hud/utils/mcp.py,sha256=jvCWb5MXlMMObhrbYoiTlI-L9HNkEjLVx8GJ-HbdQ7U,2626
@@ -165,10 +174,10 @@ hud/utils/tests/test_init.py,sha256=2QLQSGgyP9wJhOvPCusm_zjJad0qApOZi1BXpxcdHXQ,
165
174
  hud/utils/tests/test_mcp.py,sha256=0pUa16mL-bqbZDXp5NHBnt1gO5o10BOg7zTMHZ1DNPM,4023
166
175
  hud/utils/tests/test_progress.py,sha256=QSF7Kpi03Ff_l3mAeqW9qs1nhK50j9vBiSobZq7T4f4,7394
167
176
  hud/utils/tests/test_telemetry.py,sha256=5jl7bEx8C8b-FfFUko5pf4UY-mPOR-9HaeL98dGtVHM,2781
168
- hud/utils/tests/test_version.py,sha256=5ReSod83u8tP-ZJBhZwzeY-rFMhIy8Mpt-zP-v7YeEA,160
177
+ hud/utils/tests/test_version.py,sha256=rjNpZ8hUWFWMassBYVxeyCM-03Rdz8W0C7HPDQuBJfo,160
169
178
  hud/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
170
- hud_python-0.4.21.dist-info/METADATA,sha256=Np2Ririw3UDdAAiaOIa0QSyDdYdjjAypm25ZoSK8KPg,20142
171
- hud_python-0.4.21.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
172
- hud_python-0.4.21.dist-info/entry_points.txt,sha256=jJbodNFg1m0-CDofe5AHvB4zKBq7sSdP97-ohaQ3ae4,63
173
- hud_python-0.4.21.dist-info/licenses/LICENSE,sha256=yIzBheVUf86FC1bztAcr7RYWWNxyd3B-UJQ3uddg1HA,1078
174
- hud_python-0.4.21.dist-info/RECORD,,
179
+ hud_python-0.4.22.dist-info/METADATA,sha256=bX0ATTSO7oM9laQrrha-tEityugKU2JbFcAm7m5AGA0,20142
180
+ hud_python-0.4.22.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
181
+ hud_python-0.4.22.dist-info/entry_points.txt,sha256=jJbodNFg1m0-CDofe5AHvB4zKBq7sSdP97-ohaQ3ae4,63
182
+ hud_python-0.4.22.dist-info/licenses/LICENSE,sha256=yIzBheVUf86FC1bztAcr7RYWWNxyd3B-UJQ3uddg1HA,1078
183
+ hud_python-0.4.22.dist-info/RECORD,,