hud-python 0.4.22__py3-none-any.whl → 0.4.24__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 (53) hide show
  1. hud/agents/base.py +85 -59
  2. hud/agents/claude.py +5 -1
  3. hud/agents/grounded_openai.py +3 -1
  4. hud/agents/misc/response_agent.py +3 -2
  5. hud/agents/openai.py +2 -2
  6. hud/agents/openai_chat_generic.py +3 -1
  7. hud/cli/__init__.py +34 -24
  8. hud/cli/analyze.py +27 -26
  9. hud/cli/build.py +50 -46
  10. hud/cli/debug.py +7 -7
  11. hud/cli/dev.py +107 -99
  12. hud/cli/eval.py +31 -29
  13. hud/cli/hf.py +53 -53
  14. hud/cli/init.py +28 -28
  15. hud/cli/list_func.py +22 -22
  16. hud/cli/pull.py +36 -36
  17. hud/cli/push.py +76 -74
  18. hud/cli/remove.py +42 -40
  19. hud/cli/rl/__init__.py +2 -2
  20. hud/cli/rl/init.py +41 -41
  21. hud/cli/rl/pod.py +97 -91
  22. hud/cli/rl/ssh.py +42 -40
  23. hud/cli/rl/train.py +75 -73
  24. hud/cli/rl/utils.py +10 -10
  25. hud/cli/tests/test_analyze.py +1 -1
  26. hud/cli/tests/test_analyze_metadata.py +2 -2
  27. hud/cli/tests/test_pull.py +45 -45
  28. hud/cli/tests/test_push.py +31 -29
  29. hud/cli/tests/test_registry.py +15 -15
  30. hud/cli/utils/environment.py +11 -11
  31. hud/cli/utils/interactive.py +17 -17
  32. hud/cli/utils/logging.py +12 -12
  33. hud/cli/utils/metadata.py +12 -12
  34. hud/cli/utils/registry.py +5 -5
  35. hud/cli/utils/runner.py +23 -23
  36. hud/cli/utils/server.py +16 -16
  37. hud/clients/mcp_use.py +19 -5
  38. hud/clients/utils/__init__.py +25 -0
  39. hud/clients/utils/retry.py +186 -0
  40. hud/datasets/execution/parallel.py +71 -46
  41. hud/shared/hints.py +7 -7
  42. hud/tools/grounding/grounder.py +2 -1
  43. hud/types.py +4 -4
  44. hud/utils/__init__.py +3 -3
  45. hud/utils/{design.py → hud_console.py} +39 -33
  46. hud/utils/pretty_errors.py +6 -6
  47. hud/utils/tests/test_version.py +1 -1
  48. hud/version.py +1 -1
  49. {hud_python-0.4.22.dist-info → hud_python-0.4.24.dist-info}/METADATA +3 -1
  50. {hud_python-0.4.22.dist-info → hud_python-0.4.24.dist-info}/RECORD +53 -52
  51. {hud_python-0.4.22.dist-info → hud_python-0.4.24.dist-info}/WHEEL +0 -0
  52. {hud_python-0.4.22.dist-info → hud_python-0.4.24.dist-info}/entry_points.txt +0 -0
  53. {hud_python-0.4.22.dist-info → hud_python-0.4.24.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.design import HUDDesign
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
- design = HUDDesign()
21
- design.header("HUD Environment Removal")
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
- design.error(f"Error loading lock file: {e}")
48
+ hud_console.error(f"Error loading lock file: {e}")
49
49
  continue
50
50
 
51
51
  if not found_entry:
52
- design.error(f"Environment not found: {target}")
53
- design.info("Use 'hud list' to see available environments")
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
- design.section_title("Environment Details")
67
- design.status_item("Image", image)
68
- design.status_item("Digest", found_digest)
69
- design.status_item("Description", description)
70
- design.status_item("Location", str(found_entry.parent))
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
- design.warning(f"Could not read environment details: {e}")
73
+ hud_console.warning(f"Could not read environment details: {e}")
74
74
 
75
75
  # Confirm deletion
76
76
  if not yes:
77
- design.info("")
77
+ hud_console.info("")
78
78
  if not typer.confirm(f"Remove environment {found_digest}?"):
79
- design.info("Aborted")
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
- design.success(f"Removed environment: {found_digest}")
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
- design.info("")
93
- design.info("Note: The Docker image may still exist locally.")
94
- design.info(f"To remove it, run: [cyan]docker rmi {image.split('@')[0]}[/cyan]")
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
- design.error(f"Failed to remove environment: {e}")
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
- design = HUDDesign()
106
- design.header("Remove All HUD Environments")
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
- design.info("No environments found in local registry.")
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
- design.info("No environments found in local registry.")
118
+ hud_console.info("No environments found in local registry.")
117
119
  return
118
120
 
119
- design.warning(f"This will remove {len(entries)} environment(s) from the local registry!")
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
- design.section_title("Environments to Remove")
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
- design.info(f" • {digest[:12]} - {image}")
130
+ hud_console.info(f" • {digest[:12]} - {image}")
129
131
  except Exception:
130
- design.info(f" • {digest[:12]}")
132
+ hud_console.info(f" • {digest[:12]}")
131
133
 
132
134
  # Confirm deletion
133
135
  if not yes:
134
- design.info("")
136
+ hud_console.info("")
135
137
  if not typer.confirm("Remove ALL environments?", default=False):
136
- design.info("Aborted")
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
- design.success(f"Removed: {digest}")
151
+ hud_console.success(f"Removed: {digest}")
150
152
  except Exception as e:
151
153
  failed += 1
152
154
  if verbose:
153
- design.error(f"Failed to remove {digest}: {e}")
155
+ hud_console.error(f"Failed to remove {digest}: {e}")
154
156
 
155
- design.info("")
157
+ hud_console.info("")
156
158
  if failed == 0:
157
- design.success(f"Successfully removed {removed} environment(s)")
159
+ hud_console.success(f"Successfully removed {removed} environment(s)")
158
160
  else:
159
- design.warning(f"Removed {removed} environment(s), failed to remove {failed}")
161
+ hud_console.warning(f"Removed {removed} environment(s), failed to remove {failed}")
160
162
 
161
- design.info("")
162
- design.info("Note: Docker images may still exist locally.")
163
- design.info("To remove them, use: [cyan]docker image prune[/cyan]")
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
- design = HUDDesign()
187
- design.error("Please specify an environment to remove or 'all'")
188
- design.info("Use 'hud list' to see available environments")
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.design import HUDDesign
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
- design = HUDDesign()
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.design import HUDDesign
13
+ from hud.utils.hud_console import HUDConsole
14
14
 
15
- design = HUDDesign()
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
- design.header("RL Config Generator", icon="🔧")
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
- design.info("Building environment...")
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
- design.warning(f"No hud.lock.yaml found in {directory}")
45
+ hud_console.warning(f"No hud.lock.yaml found in {directory}")
46
46
  # Need to handle interactive prompt here, before async
47
- action = design.select(
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
- design.info("Building environment...")
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
- design.info(f"Found lock file: {lock_path}")
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
- design.error("Failed to read lock file")
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
- design.error("No image found in lock file")
94
- design.hint("Run 'hud build' to create a proper lock file")
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
- design.error("No tools found in lock file")
99
- design.hint("Lock file may be outdated. Run 'hud build' to regenerate")
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
- design.info(f"Using cached image: {image}")
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
- design.error("No valid image or lock file found")
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
- design.error(f"Failed to read lock file: {e}")
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
- design.error(f"Config file already exists: {output}")
152
- design.info("Use --force to overwrite")
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
- design.success(f"Generated config: {output}")
181
+ hud_console.success(f"Generated config: {output}")
182
182
 
183
183
  # Show summary
184
- design.section_title("📋 Generated Configuration")
185
- design.info("Source: hud.lock.yaml")
186
- design.info(f"Image: {image}")
187
- design.info(f"System prompt: {len(config['system_prompt'])} characters")
188
- design.info(f"Action mappings: {len(config['action_mappings'])} tools")
189
- design.info("")
190
- design.info("Next steps:")
191
- design.command_example("hud hf tasks.json --name my-tasks", "Create dataset")
192
- design.command_example(f"hud rl --config {output}", "Start training")
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
- design.error(f"Config file already exists: {output}")
206
- design.info("Use --force to overwrite")
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
- design.info(f"Analyzing environment: {image}")
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
- design.success(f"Generated config: {output}")
234
+ hud_console.success(f"Generated config: {output}")
235
235
 
236
236
  # Show summary
237
- design.section_title("📋 Generated Configuration")
238
- design.info(f"System prompt: {len(config['system_prompt'])} characters")
239
- design.info(f"Action mappings: {len(config['action_mappings'])} tools")
240
- design.info("")
241
- design.info("Next steps:")
242
- design.command_example("hud hf tasks.json --name my-tasks", "Create dataset")
243
- design.command_example(f"hud rl --config {output}", "Start training")
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
- design.error(f"Failed to analyze environment: {e}")
250
- design.hint("Make sure the Docker image exists and contains a valid MCP server")
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