hud-python 0.4.2__tar.gz → 0.4.3__tar.gz
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_python-0.4.2 → hud_python-0.4.3}/PKG-INFO +2 -2
- {hud_python-0.4.2 → hud_python-0.4.3}/README.md +1 -1
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/agents/langchain.py +12 -1
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/agents/misc/response_agent.py +3 -1
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/cli/build.py +98 -27
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/cli/init.py +14 -15
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/cli/mcp_server.py +55 -80
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/cli/pull.py +19 -25
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/cli/push.py +92 -58
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/clients/base.py +2 -3
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/clients/fastmcp.py +22 -2
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/clients/mcp_use.py +35 -15
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/datasets.py +5 -0
- hud_python-0.4.3/hud/utils/design.py +236 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/utils/mcp.py +1 -1
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/utils/tests/test_version.py +1 -1
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/version.py +1 -1
- {hud_python-0.4.2 → hud_python-0.4.3}/pyproject.toml +1 -1
- hud_python-0.4.2/hud/utils/design.py +0 -168
- {hud_python-0.4.2 → hud_python-0.4.3}/.gitignore +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/LICENSE +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/environments/README.md +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/environments/browser/README.md +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/environments/browser/apps/2048/README.md +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/environments/browser/apps/2048/backend/pyproject.toml +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/environments/browser/apps/README.md +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/environments/browser/apps/todo/README.md +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/environments/browser/apps/todo/backend/pyproject.toml +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/environments/browser/pyproject.toml +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/environments/browser/src/hud_controller/README.md +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/environments/remote_browser/README.md +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/environments/remote_browser/pyproject.toml +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/environments/remote_browser/src/hud_controller/providers/README.md +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/environments/text_2048/README.md +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/environments/text_2048/pyproject.toml +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/examples/README.md +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/__init__.py +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/agents/__init__.py +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/agents/base.py +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/agents/claude.py +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/agents/misc/__init__.py +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/agents/openai.py +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/agents/openai_chat_generic.py +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/agents/tests/__init__.py +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/agents/tests/test_base.py +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/agents/tests/test_claude.py +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/agents/tests/test_client.py +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/agents/tests/test_openai.py +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/cli/__init__.py +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/cli/__main__.py +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/cli/analyze.py +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/cli/analyze_metadata.py +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/cli/clone.py +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/cli/cursor.py +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/cli/debug.py +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/cli/docker_utils.py +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/cli/interactive.py +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/cli/remote_runner.py +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/cli/runner.py +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/cli/tests/__init__.py +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/cli/tests/test_analyze.py +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/cli/tests/test_cli_init.py +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/cli/tests/test_cli_main.py +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/cli/tests/test_clone.py +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/cli/tests/test_cursor.py +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/cli/tests/test_debug.py +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/cli/tests/test_mcp_server.py +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/cli/tests/test_utils.py +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/cli/utils.py +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/clients/README.md +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/clients/__init__.py +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/clients/tests/__init__.py +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/clients/tests/test_client_integration.py +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/clients/tests/test_fastmcp.py +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/clients/tests/test_protocol.py +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/clients/utils/__init__.py +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/clients/utils/retry_transport.py +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/misc/__init__.py +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/misc/claude_plays_pokemon.py +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/otel/__init__.py +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/otel/collector.py +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/otel/config.py +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/otel/context.py +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/otel/exporters.py +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/otel/instrumentation.py +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/otel/processors.py +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/otel/tests/__init__.py +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/otel/tests/test_processors.py +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/py.typed +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/server/__init__.py +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/server/context.py +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/server/helper/__init__.py +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/server/low_level.py +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/server/server.py +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/server/tests/__init__.py +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/settings.py +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/shared/__init__.py +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/shared/exceptions.py +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/shared/requests.py +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/shared/tests/__init__.py +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/shared/tests/test_exceptions.py +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/shared/tests/test_requests.py +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/telemetry/__init__.py +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/telemetry/instrument.py +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/telemetry/job.py +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/telemetry/replay.py +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/telemetry/trace.py +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/tools/__init__.py +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/tools/base.py +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/tools/bash.py +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/tools/computer/__init__.py +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/tools/computer/anthropic.py +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/tools/computer/hud.py +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/tools/computer/openai.py +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/tools/computer/settings.py +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/tools/edit.py +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/tools/executors/__init__.py +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/tools/executors/base.py +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/tools/executors/pyautogui.py +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/tools/executors/tests/__init__.py +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/tools/executors/tests/test_base_executor.py +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/tools/executors/tests/test_pyautogui_executor.py +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/tools/executors/xdo.py +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/tools/playwright.py +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/tools/tests/__init__.py +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/tools/tests/test_base.py +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/tools/tests/test_bash.py +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/tools/tests/test_bash_extended.py +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/tools/tests/test_computer.py +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/tools/tests/test_computer_actions.py +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/tools/tests/test_edit.py +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/tools/tests/test_init.py +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/tools/tests/test_playwright_tool.py +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/tools/tests/test_tools.py +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/tools/tests/test_utils.py +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/tools/types.py +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/tools/utils.py +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/types.py +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/utils/__init__.py +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/utils/async_utils.py +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/utils/progress.py +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/utils/telemetry.py +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/utils/tests/__init__.py +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/utils/tests/test_async_utils.py +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/utils/tests/test_init.py +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/utils/tests/test_progress.py +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/hud/utils/tests/test_telemetry.py +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/rl/README.md +0 -0
- {hud_python-0.4.2 → hud_python-0.4.3}/rl/pyproject.toml +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: hud-python
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.3
|
|
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
|
|
@@ -202,7 +202,7 @@ The above example let's the agent play 2048 ([See replay](https://app.hud.so/tra
|
|
|
202
202
|
|
|
203
203
|
## Reinforcement Learning with GRPO
|
|
204
204
|
|
|
205
|
-
This is a Qwen-2.5-3B agent training a policy on the [`text-2048`](environments/text_2048/) environment (see above) using [Verifiers](rl/
|
|
205
|
+
This is a Qwen-2.5-3B agent training a policy on the [`text-2048`](environments/text_2048/) environment (see above) using [Verifiers](rl/):
|
|
206
206
|
|
|
207
207
|

|
|
208
208
|
|
|
@@ -110,7 +110,7 @@ The above example let's the agent play 2048 ([See replay](https://app.hud.so/tra
|
|
|
110
110
|
|
|
111
111
|
## Reinforcement Learning with GRPO
|
|
112
112
|
|
|
113
|
-
This is a Qwen-2.5-3B agent training a policy on the [`text-2048`](environments/text_2048/) environment (see above) using [Verifiers](rl/
|
|
113
|
+
This is a Qwen-2.5-3B agent training a policy on the [`text-2048`](environments/text_2048/) environment (see above) using [Verifiers](rl/):
|
|
114
114
|
|
|
115
115
|

|
|
116
116
|
|
|
@@ -9,13 +9,18 @@ import mcp.types as types
|
|
|
9
9
|
from langchain.agents import AgentExecutor, create_tool_calling_agent
|
|
10
10
|
from langchain.prompts import ChatPromptTemplate, MessagesPlaceholder
|
|
11
11
|
from langchain.schema import AIMessage, BaseMessage, HumanMessage, SystemMessage
|
|
12
|
-
from mcp_use.adapters.langchain_adapter import LangChainAdapter
|
|
13
12
|
|
|
14
13
|
import hud
|
|
15
14
|
|
|
16
15
|
if TYPE_CHECKING:
|
|
17
16
|
from langchain.schema.language_model import BaseLanguageModel
|
|
18
17
|
from langchain_core.tools import BaseTool
|
|
18
|
+
from mcp_use.adapters.langchain_adapter import LangChainAdapter
|
|
19
|
+
|
|
20
|
+
try:
|
|
21
|
+
from mcp_use.adapters.langchain_adapter import LangChainAdapter
|
|
22
|
+
except ImportError:
|
|
23
|
+
LangChainAdapter = None # type: ignore[misc, assignment]
|
|
19
24
|
|
|
20
25
|
from hud.types import AgentResponse, MCPToolCall, MCPToolResult
|
|
21
26
|
|
|
@@ -51,6 +56,12 @@ class LangChainAgent(MCPAgent):
|
|
|
51
56
|
"""
|
|
52
57
|
super().__init__(**kwargs)
|
|
53
58
|
|
|
59
|
+
if LangChainAdapter is None:
|
|
60
|
+
raise ImportError(
|
|
61
|
+
"LangChainAdapter is not available. "
|
|
62
|
+
"Please install the optional agent dependencies: pip install 'hud-python[agent]'"
|
|
63
|
+
)
|
|
64
|
+
|
|
54
65
|
self.llm = llm
|
|
55
66
|
self.adapter = LangChainAdapter(disallowed_tools=self.disallowed_tools)
|
|
56
67
|
self._langchain_tools: list[BaseTool] | None = None
|
|
@@ -5,6 +5,8 @@ from typing import Literal
|
|
|
5
5
|
|
|
6
6
|
from openai import AsyncOpenAI
|
|
7
7
|
|
|
8
|
+
from hud.settings import settings
|
|
9
|
+
|
|
8
10
|
ResponseType = Literal["STOP", "CONTINUE"]
|
|
9
11
|
|
|
10
12
|
|
|
@@ -15,7 +17,7 @@ class ResponseAgent:
|
|
|
15
17
|
"""
|
|
16
18
|
|
|
17
19
|
def __init__(self, api_key: str | None = None) -> None:
|
|
18
|
-
self.api_key = api_key or os.environ.get("OPENAI_API_KEY")
|
|
20
|
+
self.api_key = api_key or settings.openai_api_key or os.environ.get("OPENAI_API_KEY")
|
|
19
21
|
if not self.api_key:
|
|
20
22
|
raise ValueError(
|
|
21
23
|
"OpenAI API key must be provided or set as OPENAI_API_KEY environment variable"
|
|
@@ -12,13 +12,54 @@ from typing import Any
|
|
|
12
12
|
|
|
13
13
|
import typer
|
|
14
14
|
import yaml
|
|
15
|
-
from rich.console import Console
|
|
16
15
|
|
|
17
16
|
from hud.clients import MCPClient
|
|
18
17
|
from hud.utils.design import HUDDesign
|
|
19
18
|
from hud.version import __version__ as hud_version
|
|
20
19
|
|
|
21
|
-
|
|
20
|
+
|
|
21
|
+
def parse_version(version_str: str) -> tuple[int, int, int]:
|
|
22
|
+
"""Parse version string like '1.0.0' or '1.0' into tuple of integers."""
|
|
23
|
+
# Remove 'v' prefix if present
|
|
24
|
+
version_str = version_str.lstrip('v')
|
|
25
|
+
|
|
26
|
+
# Split by dots and pad with zeros if needed
|
|
27
|
+
parts = version_str.split('.')
|
|
28
|
+
parts.extend(['0'] * (3 - len(parts))) # Ensure we have at least 3 parts
|
|
29
|
+
|
|
30
|
+
try:
|
|
31
|
+
return (int(parts[0]), int(parts[1]), int(parts[2]))
|
|
32
|
+
except (ValueError, IndexError):
|
|
33
|
+
# Default to 0.0.0 if parsing fails
|
|
34
|
+
return (0, 0, 0)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def increment_version(version_str: str, increment_type: str = "patch") -> str:
|
|
38
|
+
"""Increment version string. increment_type can be 'major', 'minor', or 'patch'."""
|
|
39
|
+
major, minor, patch = parse_version(version_str)
|
|
40
|
+
|
|
41
|
+
if increment_type == "major":
|
|
42
|
+
return f"{major + 1}.0.0"
|
|
43
|
+
elif increment_type == "minor":
|
|
44
|
+
return f"{major}.{minor + 1}.0"
|
|
45
|
+
else: # patch
|
|
46
|
+
return f"{major}.{minor}.{patch + 1}"
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def get_existing_version(lock_path: Path) -> str | None:
|
|
50
|
+
"""Get the internal version from existing lock file if it exists."""
|
|
51
|
+
if not lock_path.exists():
|
|
52
|
+
return None
|
|
53
|
+
|
|
54
|
+
try:
|
|
55
|
+
with open(lock_path) as f:
|
|
56
|
+
lock_data = yaml.safe_load(f)
|
|
57
|
+
|
|
58
|
+
# Look for internal version in build metadata
|
|
59
|
+
build_data = lock_data.get("build", {})
|
|
60
|
+
return build_data.get("version", None)
|
|
61
|
+
except Exception:
|
|
62
|
+
return None
|
|
22
63
|
|
|
23
64
|
|
|
24
65
|
def get_docker_image_digest(image: str) -> str | None:
|
|
@@ -39,7 +80,8 @@ def get_docker_image_digest(image: str) -> str | None:
|
|
|
39
80
|
# Return full image reference with digest
|
|
40
81
|
return digest_list[0]
|
|
41
82
|
except Exception:
|
|
42
|
-
|
|
83
|
+
# Don't print error here, let calling code handle it
|
|
84
|
+
pass
|
|
43
85
|
return None
|
|
44
86
|
|
|
45
87
|
|
|
@@ -272,17 +314,17 @@ def build_environment(
|
|
|
272
314
|
|
|
273
315
|
# Provide helpful debugging tips
|
|
274
316
|
design.section_title("Debugging Tips")
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
317
|
+
design.info("1. Test your server directly:")
|
|
318
|
+
design.command_example(f"docker run --rm -it {temp_tag}")
|
|
319
|
+
design.dim_info(" Expected output", "MCP initialization messages")
|
|
320
|
+
design.info("")
|
|
321
|
+
design.info("2. Check for common issues:")
|
|
322
|
+
design.info(" - Server crashes on startup")
|
|
323
|
+
design.info(" - Missing dependencies")
|
|
324
|
+
design.info(" - Syntax errors in server.py")
|
|
325
|
+
design.info("")
|
|
326
|
+
design.info("3. Run with verbose mode:")
|
|
327
|
+
design.command_example("hud build . --verbose")
|
|
286
328
|
|
|
287
329
|
raise typer.Exit(1)
|
|
288
330
|
|
|
@@ -292,6 +334,19 @@ def build_environment(
|
|
|
292
334
|
dockerfile_path = env_dir / "Dockerfile"
|
|
293
335
|
required_env, optional_env = extract_env_vars_from_dockerfile(dockerfile_path)
|
|
294
336
|
|
|
337
|
+
# Check for existing version and increment
|
|
338
|
+
lock_path = env_dir / "hud.lock.yaml"
|
|
339
|
+
existing_version = get_existing_version(lock_path)
|
|
340
|
+
|
|
341
|
+
if existing_version:
|
|
342
|
+
# Increment existing version
|
|
343
|
+
new_version = increment_version(existing_version)
|
|
344
|
+
design.info(f"Incrementing version: {existing_version} → {new_version}")
|
|
345
|
+
else:
|
|
346
|
+
# Start with 0.1.0 for new environments
|
|
347
|
+
new_version = "0.1.0"
|
|
348
|
+
design.info(f"Setting initial version: {new_version}")
|
|
349
|
+
|
|
295
350
|
# Create lock file content - minimal and useful
|
|
296
351
|
lock_content = {
|
|
297
352
|
"version": "1.0", # Lock file format version
|
|
@@ -300,6 +355,7 @@ def build_environment(
|
|
|
300
355
|
"generatedAt": datetime.utcnow().isoformat() + "Z",
|
|
301
356
|
"hudVersion": hud_version,
|
|
302
357
|
"directory": str(env_dir.name),
|
|
358
|
+
"version": new_version, # Internal environment version
|
|
303
359
|
},
|
|
304
360
|
"environment": {
|
|
305
361
|
"initializeMs": analysis["initializeMs"],
|
|
@@ -338,13 +394,21 @@ def build_environment(
|
|
|
338
394
|
design.progress_message("Rebuilding with lock file metadata...")
|
|
339
395
|
|
|
340
396
|
# Build final image with label (uses cache from first build)
|
|
397
|
+
# Also tag with version
|
|
398
|
+
base_name = tag.split(":")[0] if tag and ":" in tag else tag
|
|
399
|
+
version_tag = f"{base_name}:{new_version}"
|
|
400
|
+
|
|
341
401
|
label_cmd = [
|
|
342
402
|
"docker",
|
|
343
403
|
"build",
|
|
344
404
|
"--label",
|
|
345
405
|
f"org.hud.manifest.head={lock_hash}:{lock_size}",
|
|
406
|
+
"--label",
|
|
407
|
+
f"org.hud.version={new_version}",
|
|
346
408
|
"-t",
|
|
347
409
|
tag,
|
|
410
|
+
"-t",
|
|
411
|
+
version_tag,
|
|
348
412
|
str(env_dir),
|
|
349
413
|
]
|
|
350
414
|
|
|
@@ -398,21 +462,28 @@ def build_environment(
|
|
|
398
462
|
# Print summary
|
|
399
463
|
design.section_title("Build Complete")
|
|
400
464
|
|
|
401
|
-
# Show the
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
465
|
+
# Show the version tag as primary since that's what will be pushed
|
|
466
|
+
design.status_item("Built image", version_tag, primary=True)
|
|
467
|
+
if tag:
|
|
468
|
+
design.status_item("Also tagged", tag)
|
|
469
|
+
design.status_item("Version", new_version)
|
|
470
|
+
design.status_item("Lock file", "hud.lock.yaml")
|
|
471
|
+
design.status_item("Tools found", str(analysis['toolCount']))
|
|
472
|
+
|
|
473
|
+
# Show the digest info separately if we have it
|
|
474
|
+
if image_id:
|
|
475
|
+
design.dim_info("\nImage digest", image_id)
|
|
406
476
|
|
|
407
477
|
design.section_title("Next Steps")
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
478
|
+
design.info("Test locally:")
|
|
479
|
+
design.command_example("hud dev", "Hot-reload development")
|
|
480
|
+
design.command_example(f"hud run {tag}", "Run the built image")
|
|
481
|
+
design.info("")
|
|
482
|
+
design.info("Publish to registry:")
|
|
483
|
+
design.command_example("hud push", f"Push as {version_tag}")
|
|
484
|
+
design.command_example("hud push --tag latest", "Push with custom tag")
|
|
485
|
+
design.info("")
|
|
486
|
+
design.info("The lock file can be used to reproduce this exact environment.")
|
|
416
487
|
|
|
417
488
|
|
|
418
489
|
def build_command(
|
|
@@ -5,11 +5,10 @@ from __future__ import annotations
|
|
|
5
5
|
from pathlib import Path
|
|
6
6
|
|
|
7
7
|
import typer
|
|
8
|
-
from rich.console import Console
|
|
9
8
|
from rich.panel import Panel
|
|
10
9
|
from rich.syntax import Syntax
|
|
11
10
|
|
|
12
|
-
|
|
11
|
+
from hud.utils.design import HUDDesign
|
|
13
12
|
|
|
14
13
|
# Embedded templates
|
|
15
14
|
DOCKERFILE_TEMPLATE = """FROM python:3.11-slim
|
|
@@ -249,26 +248,26 @@ def create_environment(name: str | None, directory: str, force: bool) -> None:
|
|
|
249
248
|
|
|
250
249
|
design.section_title("Files created")
|
|
251
250
|
for file in files_created:
|
|
252
|
-
|
|
251
|
+
design.status_item(file, "created")
|
|
253
252
|
|
|
254
253
|
design.section_title("Next steps")
|
|
255
254
|
|
|
256
255
|
# Show commands based on where we created the environment
|
|
257
256
|
if target_dir == Path.cwd():
|
|
258
|
-
|
|
259
|
-
|
|
257
|
+
design.info("1. Start development server (with MCP inspector):")
|
|
258
|
+
design.command_example("hud dev --inspector")
|
|
260
259
|
else:
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
260
|
+
design.info("1. Enter the directory:")
|
|
261
|
+
design.command_example(f"cd {target_dir}")
|
|
262
|
+
design.info("\n2. Start development server (with MCP inspector):")
|
|
263
|
+
design.command_example("hud dev --inspector")
|
|
265
264
|
|
|
266
|
-
|
|
267
|
-
|
|
265
|
+
design.info("\n3. Connect from Cursor or test via the MCP inspector:")
|
|
266
|
+
design.info(" Follow the instructions shown by hud dev --inspector")
|
|
268
267
|
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
268
|
+
design.info("\n4. Customize your environment:")
|
|
269
|
+
design.info(" - Add tools to src/hud_controller/server.py")
|
|
270
|
+
design.info(" - Add state to src/hud_controller/context.py")
|
|
272
271
|
|
|
273
272
|
# Show a sample of the server code
|
|
274
273
|
design.section_title("Your MCP server")
|
|
@@ -278,4 +277,4 @@ async def act() -> str:
|
|
|
278
277
|
return f"Action #{ctx.act()}"'''
|
|
279
278
|
|
|
280
279
|
syntax = Syntax(sample_code, "python", theme="monokai", line_numbers=False)
|
|
281
|
-
console.print(Panel(syntax, border_style="dim"))
|
|
280
|
+
design.console.print(Panel(syntax, border_style="dim"))
|
|
@@ -12,8 +12,12 @@ import click
|
|
|
12
12
|
import toml
|
|
13
13
|
from fastmcp import FastMCP
|
|
14
14
|
|
|
15
|
+
from hud.utils.design import HUDDesign
|
|
15
16
|
from .docker_utils import get_docker_cmd, image_exists, inject_supervisor
|
|
16
17
|
|
|
18
|
+
# Global design instance
|
|
19
|
+
design = HUDDesign()
|
|
20
|
+
|
|
17
21
|
|
|
18
22
|
def get_image_name(directory: str | Path, image_override: str | None = None) -> tuple[str, str]:
|
|
19
23
|
"""
|
|
@@ -34,7 +38,7 @@ def get_image_name(directory: str | Path, image_override: str | None = None) ->
|
|
|
34
38
|
if config.get("tool", {}).get("hud", {}).get("image"):
|
|
35
39
|
return config["tool"]["hud"]["image"], "cache"
|
|
36
40
|
except Exception:
|
|
37
|
-
|
|
41
|
+
pass # Silent failure, will use auto-generated name
|
|
38
42
|
|
|
39
43
|
# Auto-generate with :dev tag
|
|
40
44
|
dir_path = Path(directory).resolve() # Get absolute path first
|
|
@@ -68,17 +72,14 @@ def update_pyproject_toml(directory: str | Path, image_name: str, silent: bool =
|
|
|
68
72
|
toml.dump(config, f)
|
|
69
73
|
|
|
70
74
|
if not silent:
|
|
71
|
-
|
|
75
|
+
design.success(f"Updated pyproject.toml with image: {image_name}")
|
|
72
76
|
except Exception as e:
|
|
73
77
|
if not silent:
|
|
74
|
-
|
|
78
|
+
design.warning(f"Could not update pyproject.toml: {e}")
|
|
75
79
|
|
|
76
80
|
|
|
77
81
|
def build_and_update(directory: str | Path, image_name: str, no_cache: bool = False) -> None:
|
|
78
82
|
"""Build Docker image and update pyproject.toml."""
|
|
79
|
-
from hud.utils.design import HUDDesign
|
|
80
|
-
|
|
81
|
-
design = HUDDesign()
|
|
82
83
|
|
|
83
84
|
build_cmd = ["docker", "build", "-t", image_name]
|
|
84
85
|
if no_cache:
|
|
@@ -115,7 +116,7 @@ def create_proxy_server(
|
|
|
115
116
|
# Get the original CMD from the image
|
|
116
117
|
original_cmd = get_docker_cmd(image_name)
|
|
117
118
|
if not original_cmd:
|
|
118
|
-
|
|
119
|
+
design.warning(f"Could not extract CMD from {image_name}, using default")
|
|
119
120
|
original_cmd = ["python", "-m", "hud_controller.server"]
|
|
120
121
|
|
|
121
122
|
# Generate container name from image
|
|
@@ -167,18 +168,15 @@ def create_proxy_server(
|
|
|
167
168
|
# Debug output - only if verbose
|
|
168
169
|
if verbose:
|
|
169
170
|
if not no_reload:
|
|
170
|
-
|
|
171
|
+
design.info("Watching: /app/src for changes")
|
|
171
172
|
else:
|
|
172
|
-
|
|
173
|
-
|
|
173
|
+
design.info("Container will run without hot-reload")
|
|
174
|
+
design.command_example(f"docker logs -f {container_name}", "View container logs")
|
|
174
175
|
|
|
175
176
|
# Create the HTTP proxy server using config
|
|
176
177
|
try:
|
|
177
178
|
proxy = FastMCP.as_proxy(config, name=f"HUD Dev Proxy - {image_name}")
|
|
178
179
|
except Exception as e:
|
|
179
|
-
from hud.utils.design import HUDDesign
|
|
180
|
-
|
|
181
|
-
design = HUDDesign(stderr=True)
|
|
182
180
|
design.error(f"Failed to create proxy server: {e}")
|
|
183
181
|
design.info("")
|
|
184
182
|
design.info("💡 Tip: Run the following command to debug the container:")
|
|
@@ -277,7 +275,7 @@ async def start_mcp_proxy(
|
|
|
277
275
|
# Now check for src directory
|
|
278
276
|
src_path = Path(directory) / "src"
|
|
279
277
|
if not src_path.exists():
|
|
280
|
-
|
|
278
|
+
design.error(f"Source directory not found: {src_path}")
|
|
281
279
|
raise click.Abort
|
|
282
280
|
|
|
283
281
|
# Extract container name from the proxy configuration
|
|
@@ -293,20 +291,20 @@ async def start_mcp_proxy(
|
|
|
293
291
|
check=False, # Don't raise error if container doesn't exist
|
|
294
292
|
)
|
|
295
293
|
except Exception:
|
|
296
|
-
|
|
294
|
+
pass # Silent failure, container might not exist
|
|
297
295
|
|
|
298
296
|
if transport == "stdio":
|
|
299
297
|
if verbose:
|
|
300
|
-
|
|
298
|
+
design.info("Starting stdio proxy (each connection gets its own container)")
|
|
301
299
|
else:
|
|
302
300
|
# Find available port for HTTP
|
|
303
301
|
actual_port = find_free_port(port)
|
|
304
302
|
if actual_port is None:
|
|
305
|
-
|
|
303
|
+
design.error(f"No available ports found starting from {port}")
|
|
306
304
|
raise click.Abort
|
|
307
305
|
|
|
308
306
|
if actual_port != port and verbose:
|
|
309
|
-
|
|
307
|
+
design.warning(f"Port {port} in use, using port {actual_port} instead")
|
|
310
308
|
|
|
311
309
|
# Launch MCP Inspector if requested
|
|
312
310
|
if inspector:
|
|
@@ -329,11 +327,8 @@ async def start_mcp_proxy(
|
|
|
329
327
|
)
|
|
330
328
|
|
|
331
329
|
# Print inspector info cleanly
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
inspector_design = HUDDesign(stderr=(transport == "stdio"))
|
|
335
|
-
inspector_design.section_title("MCP Inspector")
|
|
336
|
-
inspector_design.link(inspector_url)
|
|
330
|
+
design.section_title("MCP Inspector")
|
|
331
|
+
design.link(inspector_url)
|
|
337
332
|
|
|
338
333
|
# Set environment to disable auth (for development only)
|
|
339
334
|
env = os.environ.copy()
|
|
@@ -359,7 +354,7 @@ async def start_mcp_proxy(
|
|
|
359
354
|
|
|
360
355
|
except (FileNotFoundError, Exception):
|
|
361
356
|
# Silently fail - inspector is optional
|
|
362
|
-
|
|
357
|
+
design.error("Failed to launch inspector")
|
|
363
358
|
|
|
364
359
|
# Launch inspector asynchronously so it doesn't block
|
|
365
360
|
asyncio.create_task(launch_inspector())
|
|
@@ -369,8 +364,7 @@ async def start_mcp_proxy(
|
|
|
369
364
|
if transport != "http":
|
|
370
365
|
from hud.utils.design import HUDDesign
|
|
371
366
|
|
|
372
|
-
|
|
373
|
-
interactive_design.warning("Interactive mode only works with HTTP transport")
|
|
367
|
+
design.warning("Interactive mode only works with HTTP transport")
|
|
374
368
|
else:
|
|
375
369
|
server_url = f"http://localhost:{actual_port}/mcp"
|
|
376
370
|
|
|
@@ -383,12 +377,9 @@ async def start_mcp_proxy(
|
|
|
383
377
|
time.sleep(3)
|
|
384
378
|
|
|
385
379
|
try:
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
interactive_design.section_title("Interactive Mode")
|
|
390
|
-
interactive_design.info("Starting interactive testing mode...")
|
|
391
|
-
interactive_design.info("Press Ctrl+C in the interactive session to exit")
|
|
380
|
+
design.section_title("Interactive Mode")
|
|
381
|
+
design.info("Starting interactive testing mode...")
|
|
382
|
+
design.info("Press Ctrl+C in the interactive session to exit")
|
|
392
383
|
|
|
393
384
|
# Import and run interactive mode in a new event loop
|
|
394
385
|
from .interactive import run_interactive_mode
|
|
@@ -404,7 +395,7 @@ async def start_mcp_proxy(
|
|
|
404
395
|
except Exception as e:
|
|
405
396
|
# Log error but don't crash the server
|
|
406
397
|
if verbose:
|
|
407
|
-
|
|
398
|
+
design.error(f"Interactive mode error: {e}")
|
|
408
399
|
|
|
409
400
|
# Launch interactive mode in a separate thread
|
|
410
401
|
import threading
|
|
@@ -415,10 +406,7 @@ async def start_mcp_proxy(
|
|
|
415
406
|
# Function to stream Docker logs
|
|
416
407
|
async def stream_docker_logs() -> None:
|
|
417
408
|
"""Stream Docker container logs asynchronously."""
|
|
418
|
-
|
|
419
|
-
from hud.utils.design import HUDDesign
|
|
420
|
-
|
|
421
|
-
log_design = HUDDesign(stderr=(transport == "stdio"))
|
|
409
|
+
log_design = design
|
|
422
410
|
|
|
423
411
|
# Always show waiting message
|
|
424
412
|
log_design.info("") # Empty line for spacing
|
|
@@ -567,39 +555,33 @@ async def start_mcp_proxy(
|
|
|
567
555
|
show_banner=False,
|
|
568
556
|
)
|
|
569
557
|
except (ConnectionError, OSError) as e:
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
error_design.info(" • Container failed to start or crashed immediately")
|
|
580
|
-
error_design.info(" • Server initialization failed")
|
|
581
|
-
error_design.info(" • Port binding conflicts")
|
|
558
|
+
design.error(f"Failed to connect to Docker container: {e}")
|
|
559
|
+
design.info("")
|
|
560
|
+
design.info("💡 Tip: Run the following command to debug the container:")
|
|
561
|
+
design.info(f" hud debug {image_name}")
|
|
562
|
+
design.info("")
|
|
563
|
+
design.info("Common issues:")
|
|
564
|
+
design.info(" • Container failed to start or crashed immediately")
|
|
565
|
+
design.info(" • Server initialization failed")
|
|
566
|
+
design.info(" • Port binding conflicts")
|
|
582
567
|
raise
|
|
583
568
|
except KeyboardInterrupt:
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
shutdown_design = HUDDesign(stderr=(transport == "stdio"))
|
|
587
|
-
shutdown_design.info("\n👋 Shutting down...")
|
|
569
|
+
design.info("\n👋 Shutting down...")
|
|
588
570
|
|
|
589
571
|
# Show next steps tutorial
|
|
590
572
|
if not interactive: # Only show if not in interactive mode
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
573
|
+
design.section_title("Next Steps")
|
|
574
|
+
design.info("🏗️ Ready to test with real agents? Run:")
|
|
575
|
+
design.info(f" [cyan]hud build {directory}[/cyan]")
|
|
576
|
+
design.info("")
|
|
577
|
+
design.info("This will:")
|
|
578
|
+
design.info(" 1. Build your environment image")
|
|
579
|
+
design.info(" 2. Generate a hud.lock.yaml file")
|
|
580
|
+
design.info(" 3. Prepare it for testing with agents")
|
|
581
|
+
design.info("")
|
|
582
|
+
design.info("Then you can:")
|
|
583
|
+
design.info(" • Test locally: [cyan]hud run <image>[/cyan]")
|
|
584
|
+
design.info(
|
|
603
585
|
" • Push to registry: [cyan]hud push --image <registry/name>[/cyan]"
|
|
604
586
|
)
|
|
605
587
|
except Exception as e:
|
|
@@ -613,10 +595,7 @@ async def start_mcp_proxy(
|
|
|
613
595
|
"Application shutdown complete",
|
|
614
596
|
]
|
|
615
597
|
):
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
shutdown_design = HUDDesign(stderr=(transport == "stdio"))
|
|
619
|
-
shutdown_design.error(f"Unexpected error: {e}")
|
|
598
|
+
design.error(f"Unexpected error: {e}")
|
|
620
599
|
finally:
|
|
621
600
|
# Cancel log streaming task if it exists
|
|
622
601
|
if log_task and not log_task.done():
|
|
@@ -624,7 +603,7 @@ async def start_mcp_proxy(
|
|
|
624
603
|
try:
|
|
625
604
|
await log_task
|
|
626
605
|
except asyncio.CancelledError:
|
|
627
|
-
|
|
606
|
+
pass # Log streaming cancelled, normal shutdown
|
|
628
607
|
|
|
629
608
|
|
|
630
609
|
def run_mcp_dev_server(
|
|
@@ -654,10 +633,6 @@ def run_mcp_dev_server(
|
|
|
654
633
|
hud dev . --image custom:tag # Use specific image
|
|
655
634
|
hud dev . --no-cache # Force clean rebuild
|
|
656
635
|
"""
|
|
657
|
-
from hud.utils.design import HUDDesign
|
|
658
|
-
|
|
659
|
-
design = HUDDesign(stderr=(transport == "stdio"))
|
|
660
|
-
|
|
661
636
|
# Ensure directory exists
|
|
662
637
|
if not Path(directory).exists():
|
|
663
638
|
design.error(f"Directory not found: {directory}")
|
|
@@ -780,10 +755,10 @@ def run_mcp_dev_server(
|
|
|
780
755
|
)
|
|
781
756
|
)
|
|
782
757
|
except Exception as e:
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
758
|
+
d.error(f"Failed to start MCP server: {e}")
|
|
759
|
+
d.info("")
|
|
760
|
+
d.info("💡 Tip: Run the following command to debug the container:")
|
|
761
|
+
d.info(f" hud debug {resolved_image}")
|
|
762
|
+
d.info("")
|
|
763
|
+
d.info("This will help identify connection issues or initialization failures.")
|
|
789
764
|
raise
|