hud-python 0.4.21__py3-none-any.whl → 0.4.23__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/agents/base.py +37 -37
- hud/agents/claude.py +11 -6
- hud/agents/grounded_openai.py +282 -0
- hud/agents/misc/response_agent.py +3 -2
- hud/agents/openai.py +2 -2
- hud/agents/openai_chat_generic.py +3 -1
- hud/agents/tests/test_client.py +6 -1
- hud/agents/tests/test_grounded_openai_agent.py +155 -0
- hud/cli/__init__.py +34 -24
- hud/cli/analyze.py +27 -26
- hud/cli/build.py +50 -46
- hud/cli/debug.py +7 -7
- hud/cli/dev.py +107 -99
- hud/cli/eval.py +33 -31
- hud/cli/hf.py +53 -53
- hud/cli/init.py +28 -28
- hud/cli/list_func.py +22 -22
- hud/cli/pull.py +36 -36
- hud/cli/push.py +76 -74
- hud/cli/remove.py +42 -40
- hud/cli/rl/__init__.py +2 -2
- hud/cli/rl/init.py +41 -41
- hud/cli/rl/pod.py +97 -91
- hud/cli/rl/ssh.py +42 -40
- hud/cli/rl/train.py +75 -73
- hud/cli/rl/utils.py +10 -10
- hud/cli/tests/test_analyze.py +1 -1
- hud/cli/tests/test_analyze_metadata.py +2 -2
- hud/cli/tests/test_pull.py +45 -45
- hud/cli/tests/test_push.py +31 -29
- hud/cli/tests/test_registry.py +15 -15
- hud/cli/utils/environment.py +11 -11
- hud/cli/utils/interactive.py +18 -18
- hud/cli/utils/logging.py +12 -12
- hud/cli/utils/metadata.py +12 -12
- hud/cli/utils/registry.py +5 -5
- hud/cli/utils/runner.py +23 -23
- hud/cli/utils/server.py +16 -16
- hud/settings.py +6 -0
- hud/shared/hints.py +7 -7
- hud/tools/executors/tests/test_base_executor.py +1 -1
- hud/tools/executors/xdo.py +1 -1
- hud/tools/grounding/__init__.py +13 -0
- hud/tools/grounding/config.py +54 -0
- hud/tools/grounding/grounded_tool.py +314 -0
- hud/tools/grounding/grounder.py +302 -0
- hud/tools/grounding/tests/__init__.py +1 -0
- hud/tools/grounding/tests/test_grounded_tool.py +196 -0
- hud/tools/tests/test_playwright_tool.py +1 -1
- hud/tools/tests/test_tools_init.py +1 -1
- hud/tools/tests/test_utils.py +2 -2
- hud/types.py +4 -4
- hud/utils/__init__.py +3 -3
- hud/utils/agent_factories.py +86 -0
- hud/utils/{design.py → hud_console.py} +39 -33
- hud/utils/pretty_errors.py +6 -6
- hud/utils/tests/test_version.py +1 -1
- hud/version.py +1 -1
- {hud_python-0.4.21.dist-info → hud_python-0.4.23.dist-info}/METADATA +3 -1
- {hud_python-0.4.21.dist-info → hud_python-0.4.23.dist-info}/RECORD +63 -54
- {hud_python-0.4.21.dist-info → hud_python-0.4.23.dist-info}/WHEEL +0 -0
- {hud_python-0.4.21.dist-info → hud_python-0.4.23.dist-info}/entry_points.txt +0 -0
- {hud_python-0.4.21.dist-info → hud_python-0.4.23.dist-info}/licenses/LICENSE +0 -0
hud/cli/remove.py
CHANGED
|
@@ -6,7 +6,7 @@ import shutil
|
|
|
6
6
|
|
|
7
7
|
import typer
|
|
8
8
|
|
|
9
|
-
from hud.utils.
|
|
9
|
+
from hud.utils.hud_console import HUDConsole
|
|
10
10
|
|
|
11
11
|
from .utils.registry import get_registry_dir, list_registry_entries, load_from_registry
|
|
12
12
|
|
|
@@ -17,8 +17,8 @@ def remove_environment(
|
|
|
17
17
|
verbose: bool = False,
|
|
18
18
|
) -> None:
|
|
19
19
|
"""Remove an environment from the local registry."""
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
hud_console = HUDConsole()
|
|
21
|
+
hud_console.header("HUD Environment Removal")
|
|
22
22
|
|
|
23
23
|
# Find the environment to remove
|
|
24
24
|
found_entry = None
|
|
@@ -45,12 +45,12 @@ def remove_environment(
|
|
|
45
45
|
found_digest = digest
|
|
46
46
|
break
|
|
47
47
|
except Exception as e:
|
|
48
|
-
|
|
48
|
+
hud_console.error(f"Error loading lock file: {e}")
|
|
49
49
|
continue
|
|
50
50
|
|
|
51
51
|
if not found_entry:
|
|
52
|
-
|
|
53
|
-
|
|
52
|
+
hud_console.error(f"Environment not found: {target}")
|
|
53
|
+
hud_console.info("Use 'hud list' to see available environments")
|
|
54
54
|
raise typer.Exit(1)
|
|
55
55
|
|
|
56
56
|
# Load and display environment info
|
|
@@ -63,37 +63,39 @@ def remove_environment(
|
|
|
63
63
|
metadata = lock_data.get("metadata", {})
|
|
64
64
|
description = metadata.get("description", "No description")
|
|
65
65
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
66
|
+
hud_console.section_title("Environment Details")
|
|
67
|
+
hud_console.status_item("Image", image)
|
|
68
|
+
hud_console.status_item("Digest", found_digest)
|
|
69
|
+
hud_console.status_item("Description", description)
|
|
70
|
+
hud_console.status_item("Location", str(found_entry.parent))
|
|
71
71
|
except Exception as e:
|
|
72
72
|
if verbose:
|
|
73
|
-
|
|
73
|
+
hud_console.warning(f"Could not read environment details: {e}")
|
|
74
74
|
|
|
75
75
|
# Confirm deletion
|
|
76
76
|
if not yes:
|
|
77
|
-
|
|
77
|
+
hud_console.info("")
|
|
78
78
|
if not typer.confirm(f"Remove environment {found_digest}?"):
|
|
79
|
-
|
|
79
|
+
hud_console.info("Aborted")
|
|
80
80
|
raise typer.Exit(0)
|
|
81
81
|
|
|
82
82
|
# Remove the environment directory
|
|
83
83
|
try:
|
|
84
84
|
env_dir = found_entry.parent
|
|
85
85
|
shutil.rmtree(env_dir)
|
|
86
|
-
|
|
86
|
+
hud_console.success(f"Removed environment: {found_digest}")
|
|
87
87
|
|
|
88
88
|
# Check if the image is still available locally
|
|
89
89
|
if lock_data:
|
|
90
90
|
image = lock_data.get("image", "")
|
|
91
91
|
if image:
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
92
|
+
hud_console.info("")
|
|
93
|
+
hud_console.info("Note: The Docker image may still exist locally.")
|
|
94
|
+
hud_console.info(
|
|
95
|
+
f"To remove it, run: [cyan]docker rmi {image.split('@')[0]}[/cyan]"
|
|
96
|
+
)
|
|
95
97
|
except Exception as e:
|
|
96
|
-
|
|
98
|
+
hud_console.error(f"Failed to remove environment: {e}")
|
|
97
99
|
raise typer.Exit(1) from e
|
|
98
100
|
|
|
99
101
|
|
|
@@ -102,38 +104,38 @@ def remove_all_environments(
|
|
|
102
104
|
verbose: bool = False,
|
|
103
105
|
) -> None:
|
|
104
106
|
"""Remove all environments from the local registry."""
|
|
105
|
-
|
|
106
|
-
|
|
107
|
+
hud_console = HUDConsole()
|
|
108
|
+
hud_console.header("Remove All HUD Environments")
|
|
107
109
|
|
|
108
110
|
registry_dir = get_registry_dir()
|
|
109
111
|
if not registry_dir.exists():
|
|
110
|
-
|
|
112
|
+
hud_console.info("No environments found in local registry.")
|
|
111
113
|
return
|
|
112
114
|
|
|
113
115
|
# Count environments
|
|
114
116
|
entries = list(list_registry_entries())
|
|
115
117
|
if not entries:
|
|
116
|
-
|
|
118
|
+
hud_console.info("No environments found in local registry.")
|
|
117
119
|
return
|
|
118
120
|
|
|
119
|
-
|
|
121
|
+
hud_console.warning(f"This will remove {len(entries)} environment(s) from the local registry!")
|
|
120
122
|
|
|
121
123
|
# List environments that will be removed
|
|
122
|
-
|
|
124
|
+
hud_console.section_title("Environments to Remove")
|
|
123
125
|
for digest, _ in entries:
|
|
124
126
|
try:
|
|
125
127
|
lock_data = load_from_registry(digest)
|
|
126
128
|
if lock_data:
|
|
127
129
|
image = lock_data.get("image", "unknown")
|
|
128
|
-
|
|
130
|
+
hud_console.info(f" • {digest[:12]} - {image}")
|
|
129
131
|
except Exception:
|
|
130
|
-
|
|
132
|
+
hud_console.info(f" • {digest[:12]}")
|
|
131
133
|
|
|
132
134
|
# Confirm deletion
|
|
133
135
|
if not yes:
|
|
134
|
-
|
|
136
|
+
hud_console.info("")
|
|
135
137
|
if not typer.confirm("Remove ALL environments?", default=False):
|
|
136
|
-
|
|
138
|
+
hud_console.info("Aborted")
|
|
137
139
|
raise typer.Exit(0)
|
|
138
140
|
|
|
139
141
|
# Remove all environments
|
|
@@ -146,21 +148,21 @@ def remove_all_environments(
|
|
|
146
148
|
shutil.rmtree(env_dir)
|
|
147
149
|
removed += 1
|
|
148
150
|
if verbose:
|
|
149
|
-
|
|
151
|
+
hud_console.success(f"Removed: {digest}")
|
|
150
152
|
except Exception as e:
|
|
151
153
|
failed += 1
|
|
152
154
|
if verbose:
|
|
153
|
-
|
|
155
|
+
hud_console.error(f"Failed to remove {digest}: {e}")
|
|
154
156
|
|
|
155
|
-
|
|
157
|
+
hud_console.info("")
|
|
156
158
|
if failed == 0:
|
|
157
|
-
|
|
159
|
+
hud_console.success(f"Successfully removed {removed} environment(s)")
|
|
158
160
|
else:
|
|
159
|
-
|
|
161
|
+
hud_console.warning(f"Removed {removed} environment(s), failed to remove {failed}")
|
|
160
162
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
163
|
+
hud_console.info("")
|
|
164
|
+
hud_console.info("Note: Docker images may still exist locally.")
|
|
165
|
+
hud_console.info("To remove them, use: [cyan]docker image prune[/cyan]")
|
|
164
166
|
|
|
165
167
|
|
|
166
168
|
def remove_command(
|
|
@@ -183,9 +185,9 @@ def remove_command(
|
|
|
183
185
|
hud remove all --yes # Remove all without confirmation
|
|
184
186
|
"""
|
|
185
187
|
if not target:
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
188
|
+
hud_console = HUDConsole()
|
|
189
|
+
hud_console.error("Please specify an environment to remove or 'all'")
|
|
190
|
+
hud_console.info("Use 'hud list' to see available environments")
|
|
189
191
|
raise typer.Exit(1)
|
|
190
192
|
|
|
191
193
|
if target.lower() == "all":
|
hud/cli/rl/__init__.py
CHANGED
|
@@ -6,7 +6,7 @@ from pathlib import Path # noqa: TC003
|
|
|
6
6
|
|
|
7
7
|
import typer
|
|
8
8
|
|
|
9
|
-
from hud.utils.
|
|
9
|
+
from hud.utils.hud_console import HUDConsole
|
|
10
10
|
|
|
11
11
|
# Create the RL subcommand app
|
|
12
12
|
rl_app = typer.Typer(
|
|
@@ -15,7 +15,7 @@ rl_app = typer.Typer(
|
|
|
15
15
|
rich_markup_mode="rich",
|
|
16
16
|
)
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
hud_console = HUDConsole()
|
|
19
19
|
|
|
20
20
|
|
|
21
21
|
@rl_app.callback(invoke_without_command=True)
|
hud/cli/rl/init.py
CHANGED
|
@@ -10,14 +10,14 @@ import typer
|
|
|
10
10
|
import yaml
|
|
11
11
|
|
|
12
12
|
from hud.clients import MCPClient
|
|
13
|
-
from hud.utils.
|
|
13
|
+
from hud.utils.hud_console import HUDConsole
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
hud_console = HUDConsole()
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
def init_command_wrapper(directory: str, output: Path | None, force: bool, build: bool) -> None:
|
|
19
19
|
"""Wrapper to handle interactive prompts before entering async context."""
|
|
20
|
-
|
|
20
|
+
hud_console.header("RL Config Generator", icon="🔧")
|
|
21
21
|
|
|
22
22
|
# Determine if this is a directory or Docker image
|
|
23
23
|
path = Path(directory)
|
|
@@ -30,7 +30,7 @@ def init_command_wrapper(directory: str, output: Path | None, force: bool, build
|
|
|
30
30
|
if not lock_path.exists():
|
|
31
31
|
if build:
|
|
32
32
|
# Auto-build was requested
|
|
33
|
-
|
|
33
|
+
hud_console.info("Building environment...")
|
|
34
34
|
from hud.cli.build import build_command
|
|
35
35
|
|
|
36
36
|
build_command(str(directory), None, False, False, {})
|
|
@@ -42,15 +42,15 @@ def init_command_wrapper(directory: str, output: Path | None, force: bool, build
|
|
|
42
42
|
image, source = get_image_name(directory)
|
|
43
43
|
|
|
44
44
|
if not (source == "cache" and image_exists(image)):
|
|
45
|
-
|
|
45
|
+
hud_console.warning(f"No hud.lock.yaml found in {directory}")
|
|
46
46
|
# Need to handle interactive prompt here, before async
|
|
47
|
-
action =
|
|
47
|
+
action = hud_console.select(
|
|
48
48
|
"No lock file found. Would you like to:",
|
|
49
49
|
["Build the environment", "Use Docker image directly", "Cancel"],
|
|
50
50
|
)
|
|
51
51
|
|
|
52
52
|
if action == "Build the environment":
|
|
53
|
-
|
|
53
|
+
hud_console.info("Building environment...")
|
|
54
54
|
from hud.cli.build import build_command
|
|
55
55
|
|
|
56
56
|
build_command(str(directory), None, False, False, {})
|
|
@@ -78,11 +78,11 @@ async def init_command(directory: str, output: Path | None, force: bool, build:
|
|
|
78
78
|
lock_path = path / "hud.lock.yaml"
|
|
79
79
|
|
|
80
80
|
if lock_path.exists():
|
|
81
|
-
|
|
81
|
+
hud_console.info(f"Found lock file: {lock_path}")
|
|
82
82
|
lock_data = read_lock_file_path(lock_path)
|
|
83
83
|
|
|
84
84
|
if not lock_data:
|
|
85
|
-
|
|
85
|
+
hud_console.error("Failed to read lock file")
|
|
86
86
|
raise typer.Exit(1)
|
|
87
87
|
|
|
88
88
|
# Get image and tools from lock file
|
|
@@ -90,13 +90,13 @@ async def init_command(directory: str, output: Path | None, force: bool, build:
|
|
|
90
90
|
tools = lock_data.get("tools", [])
|
|
91
91
|
|
|
92
92
|
if not image:
|
|
93
|
-
|
|
94
|
-
|
|
93
|
+
hud_console.error("No image found in lock file")
|
|
94
|
+
hud_console.hint("Run 'hud build' to create a proper lock file")
|
|
95
95
|
raise typer.Exit(1)
|
|
96
96
|
|
|
97
97
|
if not tools:
|
|
98
|
-
|
|
99
|
-
|
|
98
|
+
hud_console.error("No tools found in lock file")
|
|
99
|
+
hud_console.hint("Lock file may be outdated. Run 'hud build' to regenerate")
|
|
100
100
|
raise typer.Exit(1)
|
|
101
101
|
|
|
102
102
|
# Use lock file data to generate config
|
|
@@ -111,11 +111,11 @@ async def init_command(directory: str, output: Path | None, force: bool, build:
|
|
|
111
111
|
|
|
112
112
|
if source == "cache" and image_exists(image):
|
|
113
113
|
# Found cached image in pyproject.toml
|
|
114
|
-
|
|
114
|
+
hud_console.info(f"Using cached image: {image}")
|
|
115
115
|
await analyze_and_generate(image, output, force)
|
|
116
116
|
else:
|
|
117
117
|
# This should have been handled in the wrapper
|
|
118
|
-
|
|
118
|
+
hud_console.error("No valid image or lock file found")
|
|
119
119
|
raise typer.Exit(1)
|
|
120
120
|
|
|
121
121
|
else:
|
|
@@ -130,7 +130,7 @@ def read_lock_file_path(lock_path: Path) -> dict[str, Any]:
|
|
|
130
130
|
with open(lock_path) as f:
|
|
131
131
|
return yaml.safe_load(f) or {}
|
|
132
132
|
except Exception as e:
|
|
133
|
-
|
|
133
|
+
hud_console.error(f"Failed to read lock file: {e}")
|
|
134
134
|
return {}
|
|
135
135
|
|
|
136
136
|
|
|
@@ -148,8 +148,8 @@ async def generate_from_lock(
|
|
|
148
148
|
|
|
149
149
|
# Check if file exists
|
|
150
150
|
if output.exists() and not force:
|
|
151
|
-
|
|
152
|
-
|
|
151
|
+
hud_console.error(f"Config file already exists: {output}")
|
|
152
|
+
hud_console.info("Use --force to overwrite")
|
|
153
153
|
raise typer.Exit(1)
|
|
154
154
|
|
|
155
155
|
# Create output directory if needed
|
|
@@ -178,18 +178,18 @@ async def generate_from_lock(
|
|
|
178
178
|
with open(output, "w") as f: # noqa: ASYNC230
|
|
179
179
|
yaml.dump(config, f, default_flow_style=False, sort_keys=False)
|
|
180
180
|
|
|
181
|
-
|
|
181
|
+
hud_console.success(f"Generated config: {output}")
|
|
182
182
|
|
|
183
183
|
# Show summary
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
184
|
+
hud_console.section_title("📋 Generated Configuration")
|
|
185
|
+
hud_console.info("Source: hud.lock.yaml")
|
|
186
|
+
hud_console.info(f"Image: {image}")
|
|
187
|
+
hud_console.info(f"System prompt: {len(config['system_prompt'])} characters")
|
|
188
|
+
hud_console.info(f"Action mappings: {len(config['action_mappings'])} tools")
|
|
189
|
+
hud_console.info("")
|
|
190
|
+
hud_console.info("Next steps:")
|
|
191
|
+
hud_console.command_example("hud hf tasks.json --name my-tasks", "Create dataset")
|
|
192
|
+
hud_console.command_example(f"hud rl --config {output}", "Start training")
|
|
193
193
|
|
|
194
194
|
|
|
195
195
|
async def analyze_and_generate(image: str, output: Path | None, force: bool) -> None:
|
|
@@ -202,14 +202,14 @@ async def analyze_and_generate(image: str, output: Path | None, force: bool) ->
|
|
|
202
202
|
|
|
203
203
|
# Check if file exists
|
|
204
204
|
if output.exists() and not force:
|
|
205
|
-
|
|
206
|
-
|
|
205
|
+
hud_console.error(f"Config file already exists: {output}")
|
|
206
|
+
hud_console.info("Use --force to overwrite")
|
|
207
207
|
raise typer.Exit(1)
|
|
208
208
|
|
|
209
209
|
# Create output directory if needed
|
|
210
210
|
output.parent.mkdir(parents=True, exist_ok=True)
|
|
211
211
|
|
|
212
|
-
|
|
212
|
+
hud_console.info(f"Analyzing environment: {image}")
|
|
213
213
|
|
|
214
214
|
# Analyze the environment
|
|
215
215
|
try:
|
|
@@ -231,23 +231,23 @@ async def analyze_and_generate(image: str, output: Path | None, force: bool) ->
|
|
|
231
231
|
with open(output, "w") as f: # noqa: ASYNC230
|
|
232
232
|
yaml.dump(config, f, default_flow_style=False, sort_keys=False)
|
|
233
233
|
|
|
234
|
-
|
|
234
|
+
hud_console.success(f"Generated config: {output}")
|
|
235
235
|
|
|
236
236
|
# Show summary
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
237
|
+
hud_console.section_title("📋 Generated Configuration")
|
|
238
|
+
hud_console.info(f"System prompt: {len(config['system_prompt'])} characters")
|
|
239
|
+
hud_console.info(f"Action mappings: {len(config['action_mappings'])} tools")
|
|
240
|
+
hud_console.info("")
|
|
241
|
+
hud_console.info("Next steps:")
|
|
242
|
+
hud_console.command_example("hud hf tasks.json --name my-tasks", "Create dataset")
|
|
243
|
+
hud_console.command_example(f"hud rl --config {output}", "Start training")
|
|
244
244
|
|
|
245
245
|
finally:
|
|
246
246
|
await client.shutdown()
|
|
247
247
|
|
|
248
248
|
except Exception as e:
|
|
249
|
-
|
|
250
|
-
|
|
249
|
+
hud_console.error(f"Failed to analyze environment: {e}")
|
|
250
|
+
hud_console.hint("Make sure the Docker image exists and contains a valid MCP server")
|
|
251
251
|
raise typer.Exit(1) from e
|
|
252
252
|
|
|
253
253
|
|