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
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"""Provider integrations - format conversion and framework tools."""
|
|
2
|
+
|
|
3
|
+
from hud.environment.integrations.adk import ADKMixin
|
|
4
|
+
from hud.environment.integrations.anthropic import AnthropicMixin
|
|
5
|
+
from hud.environment.integrations.gemini import GeminiMixin
|
|
6
|
+
from hud.environment.integrations.langchain import LangChainMixin
|
|
7
|
+
from hud.environment.integrations.llamaindex import LlamaIndexMixin
|
|
8
|
+
from hud.environment.integrations.openai import OpenAIMixin
|
|
9
|
+
|
|
10
|
+
__all__ = ["IntegrationsMixin"]
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class IntegrationsMixin(
|
|
14
|
+
OpenAIMixin,
|
|
15
|
+
AnthropicMixin,
|
|
16
|
+
GeminiMixin,
|
|
17
|
+
LangChainMixin,
|
|
18
|
+
LlamaIndexMixin,
|
|
19
|
+
ADKMixin,
|
|
20
|
+
):
|
|
21
|
+
"""Combined integration mixin for all providers.
|
|
22
|
+
|
|
23
|
+
OpenAI:
|
|
24
|
+
as_openai_chat_tools() - Chat Completions format
|
|
25
|
+
as_openai_responses_tools() - Responses API format
|
|
26
|
+
as_openai_agent_tools() - Agents SDK (requires openai-agents)
|
|
27
|
+
|
|
28
|
+
Anthropic/Claude:
|
|
29
|
+
as_claude_tools() - Claude API format
|
|
30
|
+
as_claude_programmatic_tools() - Programmatic tool use
|
|
31
|
+
as_anthropic_runner() - Tool runner (requires anthropic)
|
|
32
|
+
|
|
33
|
+
Google/Gemini:
|
|
34
|
+
as_gemini_tools() - Gemini format
|
|
35
|
+
as_gemini_tool_config() - Tool config
|
|
36
|
+
|
|
37
|
+
Google ADK:
|
|
38
|
+
as_adk_tools() - ADK FunctionTool objects (requires google-adk)
|
|
39
|
+
|
|
40
|
+
LangChain:
|
|
41
|
+
as_langchain_tools() - StructuredTools (requires langchain-core)
|
|
42
|
+
|
|
43
|
+
LlamaIndex:
|
|
44
|
+
as_llamaindex_tools() - FunctionTools (requires llama-index-core)
|
|
45
|
+
"""
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"""Google ADK integration."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import TYPE_CHECKING, Any
|
|
6
|
+
|
|
7
|
+
from hud.environment.utils.tool_wrappers import create_async_tool_fn
|
|
8
|
+
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
import mcp.types as mcp_types
|
|
11
|
+
|
|
12
|
+
__all__ = ["ADKMixin"]
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class ADKMixin:
|
|
16
|
+
"""Mixin providing Google ADK (Agent Development Kit) integration.
|
|
17
|
+
|
|
18
|
+
Integration methods (requires google-adk):
|
|
19
|
+
as_adk_tools() - ADK FunctionTool objects
|
|
20
|
+
|
|
21
|
+
Requires: as_tools() -> list[mcp_types.Tool], call_tool(name, args)
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
def as_tools(self) -> list[mcp_types.Tool]:
|
|
25
|
+
raise NotImplementedError
|
|
26
|
+
|
|
27
|
+
async def call_tool(self, name: str, arguments: dict[str, Any]) -> Any:
|
|
28
|
+
raise NotImplementedError
|
|
29
|
+
|
|
30
|
+
def as_adk_tools(self) -> list[Any]:
|
|
31
|
+
"""Convert to Google ADK FunctionTool objects.
|
|
32
|
+
|
|
33
|
+
Requires: pip install google-adk
|
|
34
|
+
|
|
35
|
+
Returns:
|
|
36
|
+
List of FunctionTool objects for Google ADK agents.
|
|
37
|
+
|
|
38
|
+
Example:
|
|
39
|
+
```python
|
|
40
|
+
from google.adk.agents import Agent
|
|
41
|
+
from google.adk.runners import Runner
|
|
42
|
+
|
|
43
|
+
async with env:
|
|
44
|
+
agent = Agent(
|
|
45
|
+
name="assistant",
|
|
46
|
+
model="gemini-2.0-flash",
|
|
47
|
+
instruction="You are a helpful assistant.",
|
|
48
|
+
tools=env.as_adk_tools(),
|
|
49
|
+
)
|
|
50
|
+
runner = Runner(agent=agent)
|
|
51
|
+
result = await runner.run("Find information about Python")
|
|
52
|
+
```
|
|
53
|
+
"""
|
|
54
|
+
try:
|
|
55
|
+
from google.adk.tools.function_tool import FunctionTool
|
|
56
|
+
except ImportError as e:
|
|
57
|
+
raise ImportError(
|
|
58
|
+
"Google ADK not installed. Install with: pip install google-adk"
|
|
59
|
+
) from e
|
|
60
|
+
|
|
61
|
+
tools = []
|
|
62
|
+
for t in self.as_tools():
|
|
63
|
+
# ADK only needs async function - it wraps it in FunctionTool
|
|
64
|
+
async_fn = create_async_tool_fn(self, t.name, t.description)
|
|
65
|
+
tool = FunctionTool(async_fn)
|
|
66
|
+
tools.append(tool)
|
|
67
|
+
return tools
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
"""Anthropic/Claude integrations - format conversion and tool runner."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
from typing import TYPE_CHECKING, Any
|
|
7
|
+
|
|
8
|
+
if TYPE_CHECKING:
|
|
9
|
+
import mcp.types as mcp_types
|
|
10
|
+
|
|
11
|
+
__all__ = ["AnthropicMixin"]
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class AnthropicMixin:
|
|
15
|
+
"""Mixin providing Anthropic/Claude format conversion and tool runner.
|
|
16
|
+
|
|
17
|
+
Format methods (no deps):
|
|
18
|
+
as_claude_tools() - Claude API format
|
|
19
|
+
as_claude_programmatic_tools() - Programmatic tool use format
|
|
20
|
+
|
|
21
|
+
Integration methods (requires anthropic):
|
|
22
|
+
as_anthropic_runner() - Tool runner for executing tool_use blocks
|
|
23
|
+
|
|
24
|
+
Requires: as_tools() -> list[mcp_types.Tool], call_tool(name, args)
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
def as_tools(self) -> list[mcp_types.Tool]:
|
|
28
|
+
raise NotImplementedError
|
|
29
|
+
|
|
30
|
+
async def call_tool(self, name: str, arguments: dict[str, Any]) -> Any:
|
|
31
|
+
raise NotImplementedError
|
|
32
|
+
|
|
33
|
+
# =========================================================================
|
|
34
|
+
# Format Conversion (no external deps)
|
|
35
|
+
# =========================================================================
|
|
36
|
+
|
|
37
|
+
def as_claude_tools(self, *, cache_control: bool = False) -> list[dict[str, Any]]:
|
|
38
|
+
"""Convert to Claude/Anthropic tool format.
|
|
39
|
+
|
|
40
|
+
Args:
|
|
41
|
+
cache_control: Add cache_control for prompt caching
|
|
42
|
+
|
|
43
|
+
Returns:
|
|
44
|
+
List of tool definitions for Claude API.
|
|
45
|
+
|
|
46
|
+
Example:
|
|
47
|
+
```python
|
|
48
|
+
from anthropic import Anthropic
|
|
49
|
+
|
|
50
|
+
client = Anthropic()
|
|
51
|
+
async with env:
|
|
52
|
+
response = client.messages.create(
|
|
53
|
+
model="claude-sonnet-4-20250514",
|
|
54
|
+
max_tokens=1024,
|
|
55
|
+
messages=[{"role": "user", "content": "Navigate to google.com"}],
|
|
56
|
+
tools=env.as_claude_tools(),
|
|
57
|
+
)
|
|
58
|
+
# Execute tool calls
|
|
59
|
+
for block in response.content:
|
|
60
|
+
if block.type == "tool_use":
|
|
61
|
+
result = await env.call_tool(block)
|
|
62
|
+
```
|
|
63
|
+
"""
|
|
64
|
+
tools = []
|
|
65
|
+
for t in self.as_tools():
|
|
66
|
+
tool: dict[str, Any] = {
|
|
67
|
+
"name": t.name,
|
|
68
|
+
"description": t.description or "",
|
|
69
|
+
"input_schema": t.inputSchema or {"type": "object", "properties": {}},
|
|
70
|
+
}
|
|
71
|
+
if cache_control:
|
|
72
|
+
tool["cache_control"] = {"type": "ephemeral"}
|
|
73
|
+
tools.append(tool)
|
|
74
|
+
return tools
|
|
75
|
+
|
|
76
|
+
def as_claude_programmatic_tools(self, *, cache_control: bool = False) -> list[dict[str, Any]]:
|
|
77
|
+
"""Convert to Claude programmatic tool use format.
|
|
78
|
+
|
|
79
|
+
Programmatic tool use allows Claude to execute tools via code execution.
|
|
80
|
+
|
|
81
|
+
Example:
|
|
82
|
+
```python
|
|
83
|
+
from anthropic import Anthropic
|
|
84
|
+
|
|
85
|
+
client = Anthropic()
|
|
86
|
+
async with env:
|
|
87
|
+
response = client.messages.create(
|
|
88
|
+
model="claude-sonnet-4-20250514",
|
|
89
|
+
max_tokens=1024,
|
|
90
|
+
messages=[{"role": "user", "content": "Analyze the data"}],
|
|
91
|
+
tools=env.as_claude_programmatic_tools(),
|
|
92
|
+
betas=["code-execution-2025-01-24"],
|
|
93
|
+
)
|
|
94
|
+
```
|
|
95
|
+
"""
|
|
96
|
+
tools = []
|
|
97
|
+
for t in self.as_tools():
|
|
98
|
+
tool: dict[str, Any] = {
|
|
99
|
+
"name": t.name,
|
|
100
|
+
"description": t.description or "",
|
|
101
|
+
"input_schema": t.inputSchema or {"type": "object", "properties": {}},
|
|
102
|
+
"allowed_callers": ["code_execution_20250825"],
|
|
103
|
+
}
|
|
104
|
+
if cache_control:
|
|
105
|
+
tool["cache_control"] = {"type": "ephemeral"}
|
|
106
|
+
tools.append(tool)
|
|
107
|
+
return tools
|
|
108
|
+
|
|
109
|
+
# =========================================================================
|
|
110
|
+
# Tool Runner Integration (requires anthropic)
|
|
111
|
+
# =========================================================================
|
|
112
|
+
|
|
113
|
+
def as_anthropic_runner(self) -> EnvToolRunner:
|
|
114
|
+
"""Create an Anthropic tool runner for this environment.
|
|
115
|
+
|
|
116
|
+
Requires: pip install anthropic
|
|
117
|
+
|
|
118
|
+
Returns:
|
|
119
|
+
EnvToolRunner that can process tool_use blocks from Claude.
|
|
120
|
+
|
|
121
|
+
Example:
|
|
122
|
+
```python
|
|
123
|
+
from anthropic import Anthropic
|
|
124
|
+
|
|
125
|
+
client = Anthropic()
|
|
126
|
+
async with env:
|
|
127
|
+
runner = env.as_anthropic_runner()
|
|
128
|
+
|
|
129
|
+
response = client.messages.create(
|
|
130
|
+
model="claude-sonnet-4-20250514",
|
|
131
|
+
max_tokens=1024,
|
|
132
|
+
messages=[{"role": "user", "content": "Navigate to google.com"}],
|
|
133
|
+
tools=env.as_claude_tools(),
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
# Execute all tool_use blocks
|
|
137
|
+
results = []
|
|
138
|
+
for block in response.content:
|
|
139
|
+
if block.type == "tool_use":
|
|
140
|
+
result = await runner.run(block)
|
|
141
|
+
results.append(result)
|
|
142
|
+
```
|
|
143
|
+
"""
|
|
144
|
+
return EnvToolRunner(self)
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
class EnvToolRunner:
|
|
148
|
+
"""Tool runner that executes tools against an Environment."""
|
|
149
|
+
|
|
150
|
+
def __init__(self, env: AnthropicMixin) -> None:
|
|
151
|
+
self.env = env
|
|
152
|
+
self._tool_names: set[str] | None = None
|
|
153
|
+
|
|
154
|
+
@property
|
|
155
|
+
def tool_names(self) -> set[str]:
|
|
156
|
+
"""Get available tool names."""
|
|
157
|
+
if self._tool_names is None:
|
|
158
|
+
self._tool_names = {t.name for t in self.env.as_tools()}
|
|
159
|
+
return self._tool_names
|
|
160
|
+
|
|
161
|
+
async def run(self, tool_use_block: Any) -> Any:
|
|
162
|
+
"""Execute a tool_use block from Claude.
|
|
163
|
+
|
|
164
|
+
Args:
|
|
165
|
+
tool_use_block: A ToolUseBlock from Claude's response.
|
|
166
|
+
|
|
167
|
+
Returns:
|
|
168
|
+
Tool result dict (or BetaToolResultBlockParam if anthropic installed).
|
|
169
|
+
"""
|
|
170
|
+
name = tool_use_block.name
|
|
171
|
+
tool_use_id = tool_use_block.id
|
|
172
|
+
arguments = tool_use_block.input or {}
|
|
173
|
+
|
|
174
|
+
try:
|
|
175
|
+
result = await self.env.call_tool(name, **arguments)
|
|
176
|
+
content = result if isinstance(result, str) else json.dumps(result) if result else ""
|
|
177
|
+
result_dict: dict[str, Any] = {
|
|
178
|
+
"type": "tool_result",
|
|
179
|
+
"tool_use_id": tool_use_id,
|
|
180
|
+
"content": content,
|
|
181
|
+
}
|
|
182
|
+
except Exception as e:
|
|
183
|
+
result_dict = {
|
|
184
|
+
"type": "tool_result",
|
|
185
|
+
"tool_use_id": tool_use_id,
|
|
186
|
+
"content": f"Error: {e}",
|
|
187
|
+
"is_error": True,
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
# Return typed object if anthropic is available
|
|
191
|
+
try:
|
|
192
|
+
from anthropic.types.beta import BetaToolResultBlockParam
|
|
193
|
+
|
|
194
|
+
return BetaToolResultBlockParam(**result_dict)
|
|
195
|
+
except ImportError:
|
|
196
|
+
return result_dict
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"""Google/Gemini integrations - format conversion."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import TYPE_CHECKING, Any
|
|
6
|
+
|
|
7
|
+
if TYPE_CHECKING:
|
|
8
|
+
import mcp.types as mcp_types
|
|
9
|
+
|
|
10
|
+
__all__ = ["GeminiMixin"]
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class GeminiMixin:
|
|
14
|
+
"""Mixin providing Google/Gemini format conversion.
|
|
15
|
+
|
|
16
|
+
Format methods (no deps):
|
|
17
|
+
as_gemini_tools() - Gemini tool format
|
|
18
|
+
as_gemini_tool_config() - Tool execution config
|
|
19
|
+
|
|
20
|
+
Requires: as_tools() -> list[mcp_types.Tool]
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
def as_tools(self) -> list[mcp_types.Tool]:
|
|
24
|
+
raise NotImplementedError
|
|
25
|
+
|
|
26
|
+
def as_gemini_tools(self) -> list[dict[str, Any]]:
|
|
27
|
+
"""Convert to Gemini/Google AI tool format.
|
|
28
|
+
|
|
29
|
+
Returns:
|
|
30
|
+
List with function_declarations for Gemini API.
|
|
31
|
+
|
|
32
|
+
Example:
|
|
33
|
+
```python
|
|
34
|
+
import google.generativeai as genai
|
|
35
|
+
|
|
36
|
+
model = genai.GenerativeModel("gemini-1.5-pro")
|
|
37
|
+
async with env:
|
|
38
|
+
response = model.generate_content(
|
|
39
|
+
"Navigate to google.com",
|
|
40
|
+
tools=env.as_gemini_tools(),
|
|
41
|
+
)
|
|
42
|
+
# Execute tool calls
|
|
43
|
+
for part in response.candidates[0].content.parts:
|
|
44
|
+
if fn := part.function_call:
|
|
45
|
+
result = await env.call_tool(part)
|
|
46
|
+
```
|
|
47
|
+
"""
|
|
48
|
+
return [
|
|
49
|
+
{
|
|
50
|
+
"function_declarations": [
|
|
51
|
+
{
|
|
52
|
+
"name": t.name,
|
|
53
|
+
"description": t.description or "",
|
|
54
|
+
"parameters": t.inputSchema or {"type": "object", "properties": {}},
|
|
55
|
+
}
|
|
56
|
+
for t in self.as_tools()
|
|
57
|
+
]
|
|
58
|
+
}
|
|
59
|
+
]
|
|
60
|
+
|
|
61
|
+
def as_gemini_tool_config(
|
|
62
|
+
self,
|
|
63
|
+
mode: str = "AUTO",
|
|
64
|
+
allowed_tools: list[str] | None = None,
|
|
65
|
+
) -> dict[str, Any]:
|
|
66
|
+
"""Get Gemini tool_config for controlling tool execution.
|
|
67
|
+
|
|
68
|
+
Args:
|
|
69
|
+
mode: "AUTO", "ANY", or "NONE"
|
|
70
|
+
allowed_tools: If mode is "ANY", list of allowed tool names
|
|
71
|
+
|
|
72
|
+
Returns:
|
|
73
|
+
Tool config dict for Gemini API.
|
|
74
|
+
|
|
75
|
+
Example:
|
|
76
|
+
```python
|
|
77
|
+
import google.generativeai as genai
|
|
78
|
+
|
|
79
|
+
model = genai.GenerativeModel("gemini-1.5-pro")
|
|
80
|
+
async with env:
|
|
81
|
+
# Force specific tool usage
|
|
82
|
+
response = model.generate_content(
|
|
83
|
+
"Search for cats",
|
|
84
|
+
tools=env.as_gemini_tools(),
|
|
85
|
+
tool_config=env.as_gemini_tool_config(mode="ANY", allowed_tools=["search"]),
|
|
86
|
+
)
|
|
87
|
+
```
|
|
88
|
+
"""
|
|
89
|
+
config: dict[str, Any] = {"function_calling_config": {"mode": mode}}
|
|
90
|
+
if mode == "ANY" and allowed_tools:
|
|
91
|
+
config["function_calling_config"]["allowed_function_names"] = allowed_tools
|
|
92
|
+
return config
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"""LangChain integration."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import TYPE_CHECKING, Any
|
|
6
|
+
|
|
7
|
+
from hud.environment.utils.schema import schema_to_pydantic
|
|
8
|
+
from hud.environment.utils.tool_wrappers import create_tool_fns
|
|
9
|
+
|
|
10
|
+
if TYPE_CHECKING:
|
|
11
|
+
import mcp.types as mcp_types
|
|
12
|
+
|
|
13
|
+
__all__ = ["LangChainMixin"]
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class LangChainMixin:
|
|
17
|
+
"""Mixin providing LangChain integration.
|
|
18
|
+
|
|
19
|
+
Integration methods (requires langchain-core):
|
|
20
|
+
as_langchain_tools() - LangChain StructuredTool objects
|
|
21
|
+
|
|
22
|
+
Requires: as_tools() -> list[mcp_types.Tool], call_tool(name, args)
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
def as_tools(self) -> list[mcp_types.Tool]:
|
|
26
|
+
raise NotImplementedError
|
|
27
|
+
|
|
28
|
+
async def call_tool(self, name: str, arguments: dict[str, Any]) -> Any:
|
|
29
|
+
raise NotImplementedError
|
|
30
|
+
|
|
31
|
+
def as_langchain_tools(self) -> list[Any]:
|
|
32
|
+
"""Convert to LangChain StructuredTool objects.
|
|
33
|
+
|
|
34
|
+
Requires: pip install langchain-core
|
|
35
|
+
|
|
36
|
+
Returns:
|
|
37
|
+
List of StructuredTool objects for LangChain agents.
|
|
38
|
+
|
|
39
|
+
Example:
|
|
40
|
+
```python
|
|
41
|
+
from langchain_openai import ChatOpenAI
|
|
42
|
+
from langchain.agents import create_tool_calling_agent, AgentExecutor
|
|
43
|
+
from langchain_core.prompts import ChatPromptTemplate
|
|
44
|
+
|
|
45
|
+
llm = ChatOpenAI(model="gpt-4o")
|
|
46
|
+
async with env:
|
|
47
|
+
tools = env.as_langchain_tools()
|
|
48
|
+
|
|
49
|
+
prompt = ChatPromptTemplate.from_messages(
|
|
50
|
+
[
|
|
51
|
+
("system", "You are a helpful assistant."),
|
|
52
|
+
("human", "{input}"),
|
|
53
|
+
("placeholder", "{agent_scratchpad}"),
|
|
54
|
+
]
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
agent = create_tool_calling_agent(llm, tools, prompt)
|
|
58
|
+
executor = AgentExecutor(agent=agent, tools=tools)
|
|
59
|
+
result = await executor.ainvoke({"input": "Navigate to google.com"})
|
|
60
|
+
```
|
|
61
|
+
"""
|
|
62
|
+
try:
|
|
63
|
+
from langchain_core.tools import StructuredTool
|
|
64
|
+
except ImportError as e:
|
|
65
|
+
raise ImportError(
|
|
66
|
+
"LangChain not installed. Install with: pip install langchain-core"
|
|
67
|
+
) from e
|
|
68
|
+
|
|
69
|
+
tools = []
|
|
70
|
+
for t in self.as_tools():
|
|
71
|
+
schema = t.inputSchema or {"type": "object", "properties": {}}
|
|
72
|
+
sync_fn, async_fn = create_tool_fns(self, t)
|
|
73
|
+
|
|
74
|
+
tool = StructuredTool(
|
|
75
|
+
name=t.name,
|
|
76
|
+
description=t.description or "",
|
|
77
|
+
func=sync_fn,
|
|
78
|
+
coroutine=async_fn,
|
|
79
|
+
args_schema=schema_to_pydantic(t.name, schema),
|
|
80
|
+
)
|
|
81
|
+
tools.append(tool)
|
|
82
|
+
return tools
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"""LlamaIndex integration."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import TYPE_CHECKING, Any
|
|
6
|
+
|
|
7
|
+
from hud.environment.utils.tool_wrappers import create_tool_fns
|
|
8
|
+
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
import mcp.types as mcp_types
|
|
11
|
+
|
|
12
|
+
__all__ = ["LlamaIndexMixin"]
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class LlamaIndexMixin:
|
|
16
|
+
"""Mixin providing LlamaIndex integration.
|
|
17
|
+
|
|
18
|
+
Integration methods (requires llama-index-core):
|
|
19
|
+
as_llamaindex_tools() - LlamaIndex FunctionTool objects
|
|
20
|
+
|
|
21
|
+
Requires: as_tools() -> list[mcp_types.Tool], call_tool(name, args)
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
def as_tools(self) -> list[mcp_types.Tool]:
|
|
25
|
+
raise NotImplementedError
|
|
26
|
+
|
|
27
|
+
async def call_tool(self, name: str, arguments: dict[str, Any]) -> Any:
|
|
28
|
+
raise NotImplementedError
|
|
29
|
+
|
|
30
|
+
def as_llamaindex_tools(self) -> list[Any]:
|
|
31
|
+
"""Convert to LlamaIndex FunctionTool objects.
|
|
32
|
+
|
|
33
|
+
Requires: pip install llama-index-core
|
|
34
|
+
|
|
35
|
+
Returns:
|
|
36
|
+
List of FunctionTool objects for LlamaIndex agents.
|
|
37
|
+
|
|
38
|
+
Example:
|
|
39
|
+
```python
|
|
40
|
+
from llama_index.llms.openai import OpenAI
|
|
41
|
+
from llama_index.core.agent import ReActAgent
|
|
42
|
+
|
|
43
|
+
llm = OpenAI(model="gpt-4o")
|
|
44
|
+
async with env:
|
|
45
|
+
tools = env.as_llamaindex_tools()
|
|
46
|
+
agent = ReActAgent.from_tools(tools, llm=llm, verbose=True)
|
|
47
|
+
response = await agent.achat("Find information about Python")
|
|
48
|
+
```
|
|
49
|
+
"""
|
|
50
|
+
try:
|
|
51
|
+
from llama_index.core.tools import FunctionTool
|
|
52
|
+
except ImportError as e:
|
|
53
|
+
raise ImportError(
|
|
54
|
+
"LlamaIndex not installed. Install with: pip install llama-index-core"
|
|
55
|
+
) from e
|
|
56
|
+
|
|
57
|
+
tools = []
|
|
58
|
+
for t in self.as_tools():
|
|
59
|
+
sync_fn, async_fn = create_tool_fns(self, t)
|
|
60
|
+
|
|
61
|
+
tool = FunctionTool.from_defaults(
|
|
62
|
+
fn=sync_fn,
|
|
63
|
+
async_fn=async_fn,
|
|
64
|
+
name=t.name,
|
|
65
|
+
description=t.description or "",
|
|
66
|
+
)
|
|
67
|
+
tools.append(tool)
|
|
68
|
+
return tools
|