hud-python 0.4.45__py3-none-any.whl → 0.5.1__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.
- hud/__init__.py +27 -7
- hud/agents/__init__.py +11 -5
- hud/agents/base.py +220 -500
- hud/agents/claude.py +200 -240
- hud/agents/gemini.py +275 -0
- hud/agents/gemini_cua.py +335 -0
- hud/agents/grounded_openai.py +98 -100
- hud/agents/misc/integration_test_agent.py +51 -20
- hud/agents/misc/response_agent.py +41 -36
- hud/agents/openai.py +291 -292
- hud/agents/{openai_chat_generic.py → openai_chat.py} +80 -34
- hud/agents/operator.py +211 -0
- hud/agents/tests/conftest.py +133 -0
- hud/agents/tests/test_base.py +300 -622
- hud/agents/tests/test_base_runtime.py +233 -0
- hud/agents/tests/test_claude.py +379 -210
- hud/agents/tests/test_client.py +9 -10
- hud/agents/tests/test_gemini.py +369 -0
- hud/agents/tests/test_grounded_openai_agent.py +65 -50
- hud/agents/tests/test_openai.py +376 -140
- hud/agents/tests/test_operator.py +362 -0
- hud/agents/tests/test_run_eval.py +179 -0
- hud/cli/__init__.py +461 -545
- hud/cli/analyze.py +43 -5
- hud/cli/build.py +664 -110
- hud/cli/debug.py +8 -5
- hud/cli/dev.py +882 -734
- hud/cli/eval.py +782 -668
- hud/cli/flows/dev.py +167 -0
- hud/cli/flows/init.py +191 -0
- hud/cli/flows/tasks.py +153 -56
- hud/cli/flows/templates.py +151 -0
- hud/cli/flows/tests/__init__.py +1 -0
- hud/cli/flows/tests/test_dev.py +126 -0
- hud/cli/init.py +60 -58
- hud/cli/push.py +29 -11
- hud/cli/rft.py +311 -0
- hud/cli/rft_status.py +145 -0
- hud/cli/tests/test_analyze.py +5 -5
- hud/cli/tests/test_analyze_metadata.py +3 -2
- hud/cli/tests/test_analyze_module.py +120 -0
- hud/cli/tests/test_build.py +108 -6
- hud/cli/tests/test_build_failure.py +41 -0
- hud/cli/tests/test_build_module.py +50 -0
- hud/cli/tests/test_cli_init.py +6 -1
- hud/cli/tests/test_cli_more_wrappers.py +30 -0
- hud/cli/tests/test_cli_root.py +140 -0
- hud/cli/tests/test_convert.py +361 -0
- hud/cli/tests/test_debug.py +12 -10
- hud/cli/tests/test_dev.py +197 -0
- hud/cli/tests/test_eval.py +251 -0
- hud/cli/tests/test_eval_bedrock.py +51 -0
- hud/cli/tests/test_init.py +124 -0
- hud/cli/tests/test_main_module.py +11 -5
- hud/cli/tests/test_mcp_server.py +12 -100
- hud/cli/tests/test_push_happy.py +74 -0
- hud/cli/tests/test_push_wrapper.py +23 -0
- hud/cli/tests/test_registry.py +1 -1
- hud/cli/tests/test_utils.py +1 -1
- hud/cli/{rl → utils}/celebrate.py +14 -12
- hud/cli/utils/config.py +18 -1
- hud/cli/utils/docker.py +130 -4
- hud/cli/utils/env_check.py +9 -9
- hud/cli/utils/git.py +136 -0
- hud/cli/utils/interactive.py +39 -5
- hud/cli/utils/metadata.py +69 -0
- hud/cli/utils/runner.py +1 -1
- hud/cli/utils/server.py +2 -2
- hud/cli/utils/source_hash.py +3 -3
- hud/cli/utils/tasks.py +4 -1
- hud/cli/utils/tests/__init__.py +0 -0
- hud/cli/utils/tests/test_config.py +58 -0
- hud/cli/utils/tests/test_docker.py +93 -0
- hud/cli/utils/tests/test_docker_hints.py +71 -0
- hud/cli/utils/tests/test_env_check.py +74 -0
- hud/cli/utils/tests/test_environment.py +42 -0
- hud/cli/utils/tests/test_git.py +142 -0
- hud/cli/utils/tests/test_interactive_module.py +60 -0
- hud/cli/utils/tests/test_local_runner.py +50 -0
- hud/cli/utils/tests/test_logging_utils.py +23 -0
- hud/cli/utils/tests/test_metadata.py +49 -0
- hud/cli/utils/tests/test_package_runner.py +35 -0
- hud/cli/utils/tests/test_registry_utils.py +49 -0
- hud/cli/utils/tests/test_remote_runner.py +25 -0
- hud/cli/utils/tests/test_runner_modules.py +52 -0
- hud/cli/utils/tests/test_source_hash.py +36 -0
- hud/cli/utils/tests/test_tasks.py +80 -0
- hud/cli/utils/version_check.py +258 -0
- hud/cli/{rl → utils}/viewer.py +2 -2
- hud/clients/README.md +12 -11
- hud/clients/__init__.py +4 -3
- hud/clients/base.py +166 -26
- hud/clients/environment.py +51 -0
- hud/clients/fastmcp.py +13 -6
- hud/clients/mcp_use.py +40 -15
- hud/clients/tests/test_analyze_scenarios.py +206 -0
- hud/clients/tests/test_protocol.py +9 -3
- hud/datasets/__init__.py +23 -20
- hud/datasets/loader.py +327 -0
- hud/datasets/runner.py +192 -105
- hud/datasets/tests/__init__.py +0 -0
- hud/datasets/tests/test_loader.py +221 -0
- hud/datasets/tests/test_utils.py +315 -0
- hud/datasets/utils.py +270 -90
- hud/environment/__init__.py +50 -0
- hud/environment/connection.py +206 -0
- hud/environment/connectors/__init__.py +33 -0
- hud/environment/connectors/base.py +68 -0
- hud/environment/connectors/local.py +177 -0
- hud/environment/connectors/mcp_config.py +109 -0
- hud/environment/connectors/openai.py +101 -0
- hud/environment/connectors/remote.py +172 -0
- hud/environment/environment.py +694 -0
- hud/environment/integrations/__init__.py +45 -0
- hud/environment/integrations/adk.py +67 -0
- hud/environment/integrations/anthropic.py +196 -0
- hud/environment/integrations/gemini.py +92 -0
- hud/environment/integrations/langchain.py +82 -0
- hud/environment/integrations/llamaindex.py +68 -0
- hud/environment/integrations/openai.py +238 -0
- hud/environment/mock.py +306 -0
- hud/environment/router.py +112 -0
- hud/environment/scenarios.py +493 -0
- hud/environment/tests/__init__.py +1 -0
- hud/environment/tests/test_connection.py +317 -0
- hud/environment/tests/test_connectors.py +218 -0
- hud/environment/tests/test_environment.py +161 -0
- hud/environment/tests/test_integrations.py +257 -0
- hud/environment/tests/test_local_connectors.py +201 -0
- hud/environment/tests/test_scenarios.py +280 -0
- hud/environment/tests/test_tools.py +208 -0
- hud/environment/types.py +23 -0
- hud/environment/utils/__init__.py +35 -0
- hud/environment/utils/formats.py +215 -0
- hud/environment/utils/schema.py +171 -0
- hud/environment/utils/tool_wrappers.py +113 -0
- hud/eval/__init__.py +67 -0
- hud/eval/context.py +674 -0
- hud/eval/display.py +299 -0
- hud/eval/instrument.py +185 -0
- hud/eval/manager.py +466 -0
- hud/eval/parallel.py +268 -0
- hud/eval/task.py +340 -0
- hud/eval/tests/__init__.py +1 -0
- hud/eval/tests/test_context.py +178 -0
- hud/eval/tests/test_eval.py +210 -0
- hud/eval/tests/test_manager.py +152 -0
- hud/eval/tests/test_parallel.py +168 -0
- hud/eval/tests/test_task.py +145 -0
- hud/eval/types.py +63 -0
- hud/eval/utils.py +183 -0
- hud/patches/__init__.py +19 -0
- hud/patches/mcp_patches.py +151 -0
- hud/patches/warnings.py +54 -0
- hud/samples/browser.py +4 -4
- hud/server/__init__.py +2 -1
- hud/server/low_level.py +2 -1
- hud/server/router.py +164 -0
- hud/server/server.py +567 -80
- hud/server/tests/test_mcp_server_integration.py +11 -11
- hud/server/tests/test_mcp_server_more.py +1 -1
- hud/server/tests/test_server_extra.py +2 -0
- hud/settings.py +45 -3
- hud/shared/exceptions.py +36 -10
- hud/shared/hints.py +26 -1
- hud/shared/requests.py +15 -3
- hud/shared/tests/test_exceptions.py +40 -31
- hud/shared/tests/test_hints.py +167 -0
- hud/telemetry/__init__.py +20 -19
- hud/telemetry/exporter.py +201 -0
- hud/telemetry/instrument.py +158 -253
- hud/telemetry/tests/test_eval_telemetry.py +356 -0
- hud/telemetry/tests/test_exporter.py +258 -0
- hud/telemetry/tests/test_instrument.py +401 -0
- hud/tools/__init__.py +16 -2
- hud/tools/apply_patch.py +639 -0
- hud/tools/base.py +54 -4
- hud/tools/bash.py +2 -2
- hud/tools/computer/__init__.py +4 -0
- hud/tools/computer/anthropic.py +2 -2
- hud/tools/computer/gemini.py +385 -0
- hud/tools/computer/hud.py +23 -6
- hud/tools/computer/openai.py +20 -21
- hud/tools/computer/qwen.py +434 -0
- hud/tools/computer/settings.py +37 -0
- hud/tools/edit.py +3 -7
- hud/tools/executors/base.py +4 -2
- hud/tools/executors/pyautogui.py +1 -1
- hud/tools/grounding/grounded_tool.py +13 -18
- hud/tools/grounding/grounder.py +10 -31
- hud/tools/grounding/tests/test_grounded_tool.py +26 -44
- hud/tools/jupyter.py +330 -0
- hud/tools/playwright.py +18 -3
- hud/tools/shell.py +308 -0
- hud/tools/tests/test_apply_patch.py +718 -0
- hud/tools/tests/test_computer.py +4 -9
- hud/tools/tests/test_computer_actions.py +24 -2
- hud/tools/tests/test_jupyter_tool.py +181 -0
- hud/tools/tests/test_shell.py +596 -0
- hud/tools/tests/test_submit.py +85 -0
- hud/tools/tests/test_types.py +193 -0
- hud/tools/types.py +21 -1
- hud/types.py +167 -57
- hud/utils/__init__.py +2 -0
- hud/utils/env.py +67 -0
- hud/utils/hud_console.py +61 -3
- hud/utils/mcp.py +15 -58
- hud/utils/strict_schema.py +162 -0
- hud/utils/tests/test_init.py +1 -2
- hud/utils/tests/test_mcp.py +1 -28
- hud/utils/tests/test_pretty_errors.py +186 -0
- hud/utils/tests/test_tool_shorthand.py +154 -0
- hud/utils/tests/test_version.py +1 -1
- hud/utils/types.py +20 -0
- hud/version.py +1 -1
- hud_python-0.5.1.dist-info/METADATA +264 -0
- hud_python-0.5.1.dist-info/RECORD +299 -0
- {hud_python-0.4.45.dist-info → hud_python-0.5.1.dist-info}/WHEEL +1 -1
- hud/agents/langchain.py +0 -261
- hud/agents/lite_llm.py +0 -72
- hud/cli/rl/__init__.py +0 -180
- hud/cli/rl/config.py +0 -101
- hud/cli/rl/display.py +0 -133
- hud/cli/rl/gpu.py +0 -63
- hud/cli/rl/gpu_utils.py +0 -321
- hud/cli/rl/local_runner.py +0 -595
- hud/cli/rl/presets.py +0 -96
- hud/cli/rl/remote_runner.py +0 -463
- hud/cli/rl/rl_api.py +0 -150
- hud/cli/rl/vllm.py +0 -177
- hud/cli/rl/wait_utils.py +0 -89
- hud/datasets/parallel.py +0 -687
- hud/misc/__init__.py +0 -1
- hud/misc/claude_plays_pokemon.py +0 -292
- hud/otel/__init__.py +0 -35
- hud/otel/collector.py +0 -142
- hud/otel/config.py +0 -181
- hud/otel/context.py +0 -570
- hud/otel/exporters.py +0 -369
- hud/otel/instrumentation.py +0 -135
- hud/otel/processors.py +0 -121
- hud/otel/tests/__init__.py +0 -1
- hud/otel/tests/test_processors.py +0 -197
- hud/rl/README.md +0 -30
- hud/rl/__init__.py +0 -1
- hud/rl/actor.py +0 -176
- hud/rl/buffer.py +0 -405
- hud/rl/chat_template.jinja +0 -101
- hud/rl/config.py +0 -192
- hud/rl/distributed.py +0 -132
- hud/rl/learner.py +0 -637
- hud/rl/tests/__init__.py +0 -1
- hud/rl/tests/test_learner.py +0 -186
- hud/rl/train.py +0 -382
- hud/rl/types.py +0 -101
- hud/rl/utils/start_vllm_server.sh +0 -30
- hud/rl/utils.py +0 -524
- hud/rl/vllm_adapter.py +0 -143
- hud/telemetry/job.py +0 -352
- hud/telemetry/replay.py +0 -74
- hud/telemetry/tests/test_replay.py +0 -40
- hud/telemetry/tests/test_trace.py +0 -63
- hud/telemetry/trace.py +0 -158
- hud/utils/agent_factories.py +0 -86
- hud/utils/async_utils.py +0 -65
- hud/utils/group_eval.py +0 -223
- hud/utils/progress.py +0 -149
- hud/utils/tasks.py +0 -127
- hud/utils/tests/test_async_utils.py +0 -173
- hud/utils/tests/test_progress.py +0 -261
- hud_python-0.4.45.dist-info/METADATA +0 -552
- hud_python-0.4.45.dist-info/RECORD +0 -228
- {hud_python-0.4.45.dist-info → hud_python-0.5.1.dist-info}/entry_points.txt +0 -0
- {hud_python-0.4.45.dist-info → hud_python-0.5.1.dist-info}/licenses/LICENSE +0 -0
hud/cli/flows/dev.py
ADDED
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import base64
|
|
4
|
+
import contextlib
|
|
5
|
+
import json
|
|
6
|
+
import logging
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
from hud.settings import settings
|
|
10
|
+
from hud.shared.requests import make_request
|
|
11
|
+
from hud.utils.hud_console import hud_console
|
|
12
|
+
|
|
13
|
+
logger = logging.getLogger(__name__)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
async def create_dynamic_trace(
|
|
17
|
+
*,
|
|
18
|
+
mcp_config: dict[str, dict[str, Any]],
|
|
19
|
+
build_status: bool,
|
|
20
|
+
environment_name: str,
|
|
21
|
+
) -> tuple[str | None, str | None]:
|
|
22
|
+
"""
|
|
23
|
+
Create a dynamic trace for HUD dev sessions when running in HTTP mode.
|
|
24
|
+
|
|
25
|
+
Sends a POST to the HUD API with:
|
|
26
|
+
- mcp_config: points to the local MCP config (same as Cursor)
|
|
27
|
+
- build_status: True if Docker mode (built image), False if basic Python mode
|
|
28
|
+
- environment_name: Name of the environment/server/image
|
|
29
|
+
- git_info: Repository information (if available)
|
|
30
|
+
|
|
31
|
+
Returns the full URL to the live trace when successful, otherwise None.
|
|
32
|
+
"""
|
|
33
|
+
api_base = settings.hud_api_url.rstrip("/")
|
|
34
|
+
# Endpoint TBD; use a sensible default path that the backend can wire up
|
|
35
|
+
url = f"{api_base}/dev/dynamic-traces"
|
|
36
|
+
|
|
37
|
+
# Get git repository information
|
|
38
|
+
from hud.cli.utils.git import get_git_info
|
|
39
|
+
|
|
40
|
+
git_info = get_git_info()
|
|
41
|
+
|
|
42
|
+
payload = {
|
|
43
|
+
"mcp_config": mcp_config,
|
|
44
|
+
"build_status": bool(build_status),
|
|
45
|
+
"environment_name": environment_name,
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
# Add git info if available
|
|
49
|
+
if git_info and git_info.get("remote_url"):
|
|
50
|
+
payload["git_info"] = git_info
|
|
51
|
+
logger.info("Detected git repository: %s", git_info.get("remote_url"))
|
|
52
|
+
else:
|
|
53
|
+
logger.info("No git repository detected")
|
|
54
|
+
|
|
55
|
+
# Require API key for dev mode
|
|
56
|
+
api_key = settings.api_key
|
|
57
|
+
if not api_key:
|
|
58
|
+
hud_console.error("HUD_API_KEY is required for hud dev command")
|
|
59
|
+
hud_console.info("")
|
|
60
|
+
hud_console.info("Please set your API key using one of these methods:")
|
|
61
|
+
hud_console.info(" 1. Set environment variable: export HUD_API_KEY=your_key")
|
|
62
|
+
hud_console.info(" 2. Use hud set command: hud set api_key your_key")
|
|
63
|
+
hud_console.info("")
|
|
64
|
+
hud_console.info("Get your API key at: https://hud.ai/settings")
|
|
65
|
+
import sys
|
|
66
|
+
|
|
67
|
+
sys.exit(1)
|
|
68
|
+
|
|
69
|
+
try:
|
|
70
|
+
resp = await make_request("POST", url=url, json=payload, api_key=api_key)
|
|
71
|
+
# New API returns an id; construct the URL as https://hud.ai/trace/{id}
|
|
72
|
+
trace_id = resp.get("id")
|
|
73
|
+
|
|
74
|
+
if isinstance(trace_id, str) and trace_id:
|
|
75
|
+
return trace_id, f"https://hud.ai/trace/{trace_id}"
|
|
76
|
+
return None, None
|
|
77
|
+
except Exception as e:
|
|
78
|
+
# Do not interrupt dev flow
|
|
79
|
+
try:
|
|
80
|
+
preview = json.dumps(payload)[:500]
|
|
81
|
+
logger.warning("Failed to create dynamic dev trace: %s | payload=%s", e, preview)
|
|
82
|
+
except Exception:
|
|
83
|
+
logger.warning("Failed to create dynamic dev trace: %s", e)
|
|
84
|
+
return None, None
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def show_dev_ui(
|
|
88
|
+
*,
|
|
89
|
+
live_trace_url: str,
|
|
90
|
+
server_name: str,
|
|
91
|
+
port: int,
|
|
92
|
+
cursor_deeplink: str,
|
|
93
|
+
is_docker: bool = False,
|
|
94
|
+
) -> None:
|
|
95
|
+
"""
|
|
96
|
+
Show the minimal dev UI with live trace link.
|
|
97
|
+
|
|
98
|
+
This is called only when we have a successful trace URL.
|
|
99
|
+
For full UI mode, the caller should use show_dev_server_info() directly.
|
|
100
|
+
|
|
101
|
+
Args:
|
|
102
|
+
live_trace_url: URL to the live trace
|
|
103
|
+
server_name: Name of the server/image
|
|
104
|
+
port: Port the server is running on
|
|
105
|
+
cursor_deeplink: Pre-generated Cursor deeplink URL
|
|
106
|
+
is_docker: Whether this is Docker mode (affects hot-reload message)
|
|
107
|
+
"""
|
|
108
|
+
import webbrowser
|
|
109
|
+
|
|
110
|
+
from rich.panel import Panel
|
|
111
|
+
|
|
112
|
+
# Show header first
|
|
113
|
+
hud_console.header("HUD Development Server", icon="🚀")
|
|
114
|
+
|
|
115
|
+
# Try to open the live trace in the default browser
|
|
116
|
+
with contextlib.suppress(Exception):
|
|
117
|
+
# new=2 -> open in a new tab, if possible
|
|
118
|
+
webbrowser.open(live_trace_url, new=2)
|
|
119
|
+
|
|
120
|
+
# Show panel with just the link
|
|
121
|
+
# Center the link and style it: blue, bold, underlined
|
|
122
|
+
link_markup = f"[bold underline rgb(108,113,196)][link={live_trace_url}]{live_trace_url}[/link][/bold underline rgb(108,113,196)]" # noqa: E501
|
|
123
|
+
# Use center alignment by surrounding with spaces via justify
|
|
124
|
+
from rich.align import Align
|
|
125
|
+
|
|
126
|
+
panel = Panel(
|
|
127
|
+
Align.center(link_markup),
|
|
128
|
+
title="🔗 Live Dev Trace",
|
|
129
|
+
border_style="rgb(192,150,12)", # HUD gold
|
|
130
|
+
padding=(1, 2),
|
|
131
|
+
)
|
|
132
|
+
hud_console.console.print(panel)
|
|
133
|
+
|
|
134
|
+
# Show other info below
|
|
135
|
+
label = "Base image" if is_docker else "Server"
|
|
136
|
+
hud_console.info("")
|
|
137
|
+
hud_console.info(f"{hud_console.sym.ITEM} {label}: {server_name}")
|
|
138
|
+
hud_console.info(f"{hud_console.sym.ITEM} Cursor:")
|
|
139
|
+
# Display the Cursor link on its own line to prevent wrapping
|
|
140
|
+
hud_console.link(cursor_deeplink)
|
|
141
|
+
hud_console.info("")
|
|
142
|
+
hud_console.info(f"{hud_console.sym.SUCCESS} Hot-reload enabled")
|
|
143
|
+
if is_docker:
|
|
144
|
+
hud_console.dim_info(
|
|
145
|
+
"",
|
|
146
|
+
"Container restarts on file changes in watched folders (-w), "
|
|
147
|
+
"rebuild with 'hud dev' if changing other files",
|
|
148
|
+
)
|
|
149
|
+
hud_console.info("")
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def generate_cursor_deeplink(server_name: str, port: int) -> str:
|
|
153
|
+
"""Generate a Cursor deeplink for the MCP server.
|
|
154
|
+
|
|
155
|
+
Args:
|
|
156
|
+
server_name: Name of the server
|
|
157
|
+
port: Port the server is running on
|
|
158
|
+
|
|
159
|
+
Returns:
|
|
160
|
+
Cursor deeplink URL
|
|
161
|
+
"""
|
|
162
|
+
server_config = {"url": f"http://localhost:{port}/mcp"}
|
|
163
|
+
config_json = json.dumps(server_config, indent=2)
|
|
164
|
+
config_base64 = base64.b64encode(config_json.encode()).decode()
|
|
165
|
+
return (
|
|
166
|
+
f"cursor://anysphere.cursor-deeplink/mcp/install?name={server_name}&config={config_base64}"
|
|
167
|
+
)
|
hud/cli/flows/init.py
ADDED
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
"""Smart HUD environment initialization."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import subprocess
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
from hud.utils.hud_console import HUDConsole
|
|
9
|
+
|
|
10
|
+
from .templates import DOCKERFILE_HUD, ENV_PY, PYPROJECT_TOML
|
|
11
|
+
|
|
12
|
+
# Files that indicate this might be an existing project
|
|
13
|
+
PROJECT_INDICATORS = {
|
|
14
|
+
"pyproject.toml",
|
|
15
|
+
"package.json",
|
|
16
|
+
"requirements.txt",
|
|
17
|
+
"setup.py",
|
|
18
|
+
"Cargo.toml",
|
|
19
|
+
"go.mod",
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _normalize_name(name: str) -> str:
|
|
24
|
+
"""Normalize name for Python identifiers."""
|
|
25
|
+
name = name.replace("-", "_").replace(" ", "_")
|
|
26
|
+
return "".join(c if c.isalnum() or c == "_" else "_" for c in name)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _has_hud_dependency(directory: Path) -> bool:
|
|
30
|
+
"""Check if hud-python is already in pyproject.toml."""
|
|
31
|
+
pyproject = directory / "pyproject.toml"
|
|
32
|
+
if not pyproject.exists():
|
|
33
|
+
return False
|
|
34
|
+
content = pyproject.read_text()
|
|
35
|
+
return "hud-python" in content or "hud_python" in content
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _add_hud_dependency(directory: Path) -> str:
|
|
39
|
+
"""Add hud-python using uv if available.
|
|
40
|
+
|
|
41
|
+
Returns:
|
|
42
|
+
"exists" if already present, "added" if added, "failed" if failed
|
|
43
|
+
"""
|
|
44
|
+
if _has_hud_dependency(directory):
|
|
45
|
+
return "exists"
|
|
46
|
+
|
|
47
|
+
try:
|
|
48
|
+
result = subprocess.run(
|
|
49
|
+
["uv", "add", "hud-python", "openai"], # noqa: S607
|
|
50
|
+
capture_output=True,
|
|
51
|
+
text=True,
|
|
52
|
+
cwd=directory,
|
|
53
|
+
check=False,
|
|
54
|
+
)
|
|
55
|
+
if result.returncode == 0 or "already" in result.stderr.lower():
|
|
56
|
+
return "added"
|
|
57
|
+
return "failed"
|
|
58
|
+
except FileNotFoundError:
|
|
59
|
+
return "failed"
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def _is_empty_or_trivial(directory: Path) -> bool:
|
|
63
|
+
"""Check if directory is empty or only has trivial files."""
|
|
64
|
+
if not directory.exists():
|
|
65
|
+
return True
|
|
66
|
+
files = list(directory.iterdir())
|
|
67
|
+
# Empty
|
|
68
|
+
if not files:
|
|
69
|
+
return True
|
|
70
|
+
# Only has hidden files or common trivial files
|
|
71
|
+
trivial = {".git", ".gitignore", ".DS_Store", "README.md", "LICENSE"}
|
|
72
|
+
return all(f.name in trivial or f.name.startswith(".") for f in files)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def _has_project_files(directory: Path) -> bool:
|
|
76
|
+
"""Check if directory has files indicating an existing project."""
|
|
77
|
+
if not directory.exists():
|
|
78
|
+
return False
|
|
79
|
+
return any(f.name in PROJECT_INDICATORS for f in directory.iterdir())
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def smart_init(
|
|
83
|
+
name: str | None = None,
|
|
84
|
+
directory: str = ".",
|
|
85
|
+
force: bool = False,
|
|
86
|
+
) -> None:
|
|
87
|
+
"""Initialize HUD environment files in a directory.
|
|
88
|
+
|
|
89
|
+
- If directory is empty: delegate to preset selection
|
|
90
|
+
- If directory has project files: add HUD files to existing project
|
|
91
|
+
- Otherwise: create new HUD environment
|
|
92
|
+
"""
|
|
93
|
+
from hud.settings import settings
|
|
94
|
+
|
|
95
|
+
hud_console = HUDConsole()
|
|
96
|
+
|
|
97
|
+
# Check for API key first
|
|
98
|
+
if not settings.api_key:
|
|
99
|
+
hud_console.error("HUD_API_KEY not found")
|
|
100
|
+
hud_console.info("")
|
|
101
|
+
hud_console.info("Set your API key:")
|
|
102
|
+
hud_console.info(" hud set HUD_API_KEY=your-key-here")
|
|
103
|
+
hud_console.info(" Or: export HUD_API_KEY=your-key")
|
|
104
|
+
hud_console.info("")
|
|
105
|
+
hud_console.info("Get your key at: https://hud.ai/settings/api-keys")
|
|
106
|
+
return
|
|
107
|
+
|
|
108
|
+
target = Path(directory).resolve()
|
|
109
|
+
|
|
110
|
+
# If directory is empty, use preset selection
|
|
111
|
+
if _is_empty_or_trivial(target):
|
|
112
|
+
from hud.cli.init import create_environment
|
|
113
|
+
|
|
114
|
+
hud_console.info("Empty directory - showing preset selection")
|
|
115
|
+
create_environment(name, directory, force, preset=None)
|
|
116
|
+
return
|
|
117
|
+
|
|
118
|
+
# Directory has files - use smart init
|
|
119
|
+
target.mkdir(parents=True, exist_ok=True)
|
|
120
|
+
env_name = _normalize_name(name or target.name)
|
|
121
|
+
has_pyproject = (target / "pyproject.toml").exists()
|
|
122
|
+
|
|
123
|
+
hud_console.header(f"HUD Init: {env_name}")
|
|
124
|
+
|
|
125
|
+
if has_pyproject:
|
|
126
|
+
hud_console.info("Found pyproject.toml - adding HUD files")
|
|
127
|
+
else:
|
|
128
|
+
hud_console.info("Creating HUD environment in existing directory")
|
|
129
|
+
|
|
130
|
+
created = []
|
|
131
|
+
|
|
132
|
+
# Create pyproject.toml if needed
|
|
133
|
+
if not has_pyproject:
|
|
134
|
+
pyproject = target / "pyproject.toml"
|
|
135
|
+
pyproject.write_text(PYPROJECT_TOML.format(name=env_name.replace("_", "-")))
|
|
136
|
+
created.append("pyproject.toml")
|
|
137
|
+
|
|
138
|
+
# Create Dockerfile.hud
|
|
139
|
+
dockerfile = target / "Dockerfile.hud"
|
|
140
|
+
if not dockerfile.exists() or force:
|
|
141
|
+
dockerfile.write_text(DOCKERFILE_HUD)
|
|
142
|
+
created.append("Dockerfile.hud")
|
|
143
|
+
else:
|
|
144
|
+
hud_console.warning("Dockerfile.hud exists, skipping (use --force)")
|
|
145
|
+
|
|
146
|
+
# Create env.py
|
|
147
|
+
env_py = target / "env.py"
|
|
148
|
+
if not env_py.exists() or force:
|
|
149
|
+
env_py.write_text(ENV_PY.format(env_name=env_name))
|
|
150
|
+
created.append("env.py")
|
|
151
|
+
else:
|
|
152
|
+
hud_console.warning("env.py exists, skipping (use --force)")
|
|
153
|
+
|
|
154
|
+
# Add dependency
|
|
155
|
+
dep_result = _add_hud_dependency(target)
|
|
156
|
+
if dep_result == "added":
|
|
157
|
+
hud_console.success("Added hud-python dependency")
|
|
158
|
+
elif dep_result == "exists":
|
|
159
|
+
hud_console.info("hud-python already in dependencies")
|
|
160
|
+
else:
|
|
161
|
+
hud_console.info("Run manually: uv add hud-python openai")
|
|
162
|
+
|
|
163
|
+
# Summary
|
|
164
|
+
if created:
|
|
165
|
+
hud_console.section_title("Created")
|
|
166
|
+
for f in created:
|
|
167
|
+
hud_console.status_item(f, "✓")
|
|
168
|
+
|
|
169
|
+
hud_console.section_title("Next Steps")
|
|
170
|
+
hud_console.info("")
|
|
171
|
+
hud_console.info("1. Define your tools in env.py")
|
|
172
|
+
hud_console.info(" Tools are functions the agent can call. Wrap existing code")
|
|
173
|
+
hud_console.info(" with @env.tool() or connect FastAPI/OpenAPI servers.")
|
|
174
|
+
hud_console.info("")
|
|
175
|
+
hud_console.info("2. Write scripts that test agent behavior")
|
|
176
|
+
hud_console.info(" Scripts define prompts and scoring. The agent runs between")
|
|
177
|
+
hud_console.info(" two yields: first sends the task, second scores the result.")
|
|
178
|
+
hud_console.info("")
|
|
179
|
+
hud_console.info("3. Run locally to iterate")
|
|
180
|
+
hud_console.command_example("python env.py", "Run the test script")
|
|
181
|
+
hud_console.info("")
|
|
182
|
+
hud_console.info("4. Deploy for scale")
|
|
183
|
+
hud_console.info(" Push to GitHub, connect on hud.ai. Then run hundreds of")
|
|
184
|
+
hud_console.info(" evals in parallel and collect training data.")
|
|
185
|
+
hud_console.info("")
|
|
186
|
+
hud_console.section_title("Files")
|
|
187
|
+
hud_console.info("• env.py Your tools, scripts, and test code")
|
|
188
|
+
hud_console.info("• Dockerfile.hud Container config for remote deployment")
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
__all__ = ["smart_init"]
|