hud-python 0.4.45__py3-none-any.whl → 0.5.13__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.
Files changed (282) hide show
  1. hud/__init__.py +27 -7
  2. hud/agents/__init__.py +70 -5
  3. hud/agents/base.py +238 -500
  4. hud/agents/claude.py +236 -247
  5. hud/agents/gateway.py +42 -0
  6. hud/agents/gemini.py +264 -0
  7. hud/agents/gemini_cua.py +324 -0
  8. hud/agents/grounded_openai.py +98 -100
  9. hud/agents/misc/integration_test_agent.py +51 -20
  10. hud/agents/misc/response_agent.py +48 -36
  11. hud/agents/openai.py +282 -296
  12. hud/agents/{openai_chat_generic.py → openai_chat.py} +63 -33
  13. hud/agents/operator.py +199 -0
  14. hud/agents/resolver.py +70 -0
  15. hud/agents/tests/conftest.py +133 -0
  16. hud/agents/tests/test_base.py +300 -622
  17. hud/agents/tests/test_base_runtime.py +233 -0
  18. hud/agents/tests/test_claude.py +381 -214
  19. hud/agents/tests/test_client.py +9 -10
  20. hud/agents/tests/test_gemini.py +369 -0
  21. hud/agents/tests/test_grounded_openai_agent.py +65 -50
  22. hud/agents/tests/test_openai.py +377 -140
  23. hud/agents/tests/test_operator.py +362 -0
  24. hud/agents/tests/test_resolver.py +192 -0
  25. hud/agents/tests/test_run_eval.py +179 -0
  26. hud/agents/types.py +148 -0
  27. hud/cli/__init__.py +493 -546
  28. hud/cli/analyze.py +43 -5
  29. hud/cli/build.py +699 -113
  30. hud/cli/debug.py +8 -5
  31. hud/cli/dev.py +889 -732
  32. hud/cli/eval.py +793 -667
  33. hud/cli/flows/dev.py +167 -0
  34. hud/cli/flows/init.py +191 -0
  35. hud/cli/flows/tasks.py +153 -56
  36. hud/cli/flows/templates.py +151 -0
  37. hud/cli/flows/tests/__init__.py +1 -0
  38. hud/cli/flows/tests/test_dev.py +126 -0
  39. hud/cli/init.py +60 -58
  40. hud/cli/pull.py +1 -1
  41. hud/cli/push.py +38 -13
  42. hud/cli/rft.py +311 -0
  43. hud/cli/rft_status.py +145 -0
  44. hud/cli/tests/test_analyze.py +5 -5
  45. hud/cli/tests/test_analyze_metadata.py +3 -2
  46. hud/cli/tests/test_analyze_module.py +120 -0
  47. hud/cli/tests/test_build.py +110 -8
  48. hud/cli/tests/test_build_failure.py +41 -0
  49. hud/cli/tests/test_build_module.py +50 -0
  50. hud/cli/tests/test_cli_init.py +6 -1
  51. hud/cli/tests/test_cli_more_wrappers.py +30 -0
  52. hud/cli/tests/test_cli_root.py +140 -0
  53. hud/cli/tests/test_convert.py +361 -0
  54. hud/cli/tests/test_debug.py +12 -10
  55. hud/cli/tests/test_dev.py +197 -0
  56. hud/cli/tests/test_eval.py +251 -0
  57. hud/cli/tests/test_eval_bedrock.py +51 -0
  58. hud/cli/tests/test_init.py +124 -0
  59. hud/cli/tests/test_main_module.py +11 -5
  60. hud/cli/tests/test_mcp_server.py +12 -100
  61. hud/cli/tests/test_push.py +1 -1
  62. hud/cli/tests/test_push_happy.py +74 -0
  63. hud/cli/tests/test_push_wrapper.py +23 -0
  64. hud/cli/tests/test_registry.py +1 -1
  65. hud/cli/tests/test_utils.py +1 -1
  66. hud/cli/{rl → utils}/celebrate.py +14 -12
  67. hud/cli/utils/config.py +18 -1
  68. hud/cli/utils/docker.py +130 -4
  69. hud/cli/utils/env_check.py +9 -9
  70. hud/cli/utils/git.py +136 -0
  71. hud/cli/utils/interactive.py +39 -5
  72. hud/cli/utils/metadata.py +70 -1
  73. hud/cli/utils/runner.py +1 -1
  74. hud/cli/utils/server.py +2 -2
  75. hud/cli/utils/source_hash.py +3 -3
  76. hud/cli/utils/tasks.py +4 -1
  77. hud/cli/utils/tests/__init__.py +0 -0
  78. hud/cli/utils/tests/test_config.py +58 -0
  79. hud/cli/utils/tests/test_docker.py +93 -0
  80. hud/cli/utils/tests/test_docker_hints.py +71 -0
  81. hud/cli/utils/tests/test_env_check.py +74 -0
  82. hud/cli/utils/tests/test_environment.py +42 -0
  83. hud/cli/utils/tests/test_git.py +142 -0
  84. hud/cli/utils/tests/test_interactive_module.py +60 -0
  85. hud/cli/utils/tests/test_local_runner.py +50 -0
  86. hud/cli/utils/tests/test_logging_utils.py +23 -0
  87. hud/cli/utils/tests/test_metadata.py +49 -0
  88. hud/cli/utils/tests/test_package_runner.py +35 -0
  89. hud/cli/utils/tests/test_registry_utils.py +49 -0
  90. hud/cli/utils/tests/test_remote_runner.py +25 -0
  91. hud/cli/utils/tests/test_runner_modules.py +52 -0
  92. hud/cli/utils/tests/test_source_hash.py +36 -0
  93. hud/cli/utils/tests/test_tasks.py +80 -0
  94. hud/cli/utils/version_check.py +258 -0
  95. hud/cli/{rl → utils}/viewer.py +2 -2
  96. hud/clients/README.md +12 -11
  97. hud/clients/__init__.py +4 -3
  98. hud/clients/base.py +166 -26
  99. hud/clients/environment.py +51 -0
  100. hud/clients/fastmcp.py +13 -6
  101. hud/clients/mcp_use.py +45 -15
  102. hud/clients/tests/test_analyze_scenarios.py +206 -0
  103. hud/clients/tests/test_protocol.py +9 -3
  104. hud/datasets/__init__.py +23 -20
  105. hud/datasets/loader.py +326 -0
  106. hud/datasets/runner.py +198 -105
  107. hud/datasets/tests/__init__.py +0 -0
  108. hud/datasets/tests/test_loader.py +221 -0
  109. hud/datasets/tests/test_utils.py +315 -0
  110. hud/datasets/utils.py +270 -90
  111. hud/environment/__init__.py +52 -0
  112. hud/environment/connection.py +258 -0
  113. hud/environment/connectors/__init__.py +33 -0
  114. hud/environment/connectors/base.py +68 -0
  115. hud/environment/connectors/local.py +177 -0
  116. hud/environment/connectors/mcp_config.py +137 -0
  117. hud/environment/connectors/openai.py +101 -0
  118. hud/environment/connectors/remote.py +172 -0
  119. hud/environment/environment.py +835 -0
  120. hud/environment/integrations/__init__.py +45 -0
  121. hud/environment/integrations/adk.py +67 -0
  122. hud/environment/integrations/anthropic.py +196 -0
  123. hud/environment/integrations/gemini.py +92 -0
  124. hud/environment/integrations/langchain.py +82 -0
  125. hud/environment/integrations/llamaindex.py +68 -0
  126. hud/environment/integrations/openai.py +238 -0
  127. hud/environment/mock.py +306 -0
  128. hud/environment/router.py +263 -0
  129. hud/environment/scenarios.py +620 -0
  130. hud/environment/tests/__init__.py +1 -0
  131. hud/environment/tests/test_connection.py +317 -0
  132. hud/environment/tests/test_connectors.py +205 -0
  133. hud/environment/tests/test_environment.py +593 -0
  134. hud/environment/tests/test_integrations.py +257 -0
  135. hud/environment/tests/test_local_connectors.py +242 -0
  136. hud/environment/tests/test_scenarios.py +1086 -0
  137. hud/environment/tests/test_tools.py +208 -0
  138. hud/environment/types.py +23 -0
  139. hud/environment/utils/__init__.py +35 -0
  140. hud/environment/utils/formats.py +215 -0
  141. hud/environment/utils/schema.py +171 -0
  142. hud/environment/utils/tool_wrappers.py +113 -0
  143. hud/eval/__init__.py +67 -0
  144. hud/eval/context.py +727 -0
  145. hud/eval/display.py +299 -0
  146. hud/eval/instrument.py +187 -0
  147. hud/eval/manager.py +533 -0
  148. hud/eval/parallel.py +268 -0
  149. hud/eval/task.py +372 -0
  150. hud/eval/tests/__init__.py +1 -0
  151. hud/eval/tests/test_context.py +178 -0
  152. hud/eval/tests/test_eval.py +210 -0
  153. hud/eval/tests/test_manager.py +152 -0
  154. hud/eval/tests/test_parallel.py +168 -0
  155. hud/eval/tests/test_task.py +291 -0
  156. hud/eval/types.py +65 -0
  157. hud/eval/utils.py +194 -0
  158. hud/patches/__init__.py +19 -0
  159. hud/patches/mcp_patches.py +308 -0
  160. hud/patches/warnings.py +54 -0
  161. hud/samples/browser.py +4 -4
  162. hud/server/__init__.py +2 -1
  163. hud/server/low_level.py +2 -1
  164. hud/server/router.py +164 -0
  165. hud/server/server.py +567 -80
  166. hud/server/tests/test_mcp_server_integration.py +11 -11
  167. hud/server/tests/test_mcp_server_more.py +1 -1
  168. hud/server/tests/test_server_extra.py +2 -0
  169. hud/settings.py +45 -3
  170. hud/shared/exceptions.py +36 -10
  171. hud/shared/hints.py +26 -1
  172. hud/shared/requests.py +15 -3
  173. hud/shared/tests/test_exceptions.py +40 -31
  174. hud/shared/tests/test_hints.py +167 -0
  175. hud/telemetry/__init__.py +20 -19
  176. hud/telemetry/exporter.py +201 -0
  177. hud/telemetry/instrument.py +165 -253
  178. hud/telemetry/tests/test_eval_telemetry.py +356 -0
  179. hud/telemetry/tests/test_exporter.py +258 -0
  180. hud/telemetry/tests/test_instrument.py +401 -0
  181. hud/tools/__init__.py +18 -2
  182. hud/tools/agent.py +223 -0
  183. hud/tools/apply_patch.py +639 -0
  184. hud/tools/base.py +54 -4
  185. hud/tools/bash.py +2 -2
  186. hud/tools/computer/__init__.py +36 -3
  187. hud/tools/computer/anthropic.py +2 -2
  188. hud/tools/computer/gemini.py +385 -0
  189. hud/tools/computer/hud.py +23 -6
  190. hud/tools/computer/openai.py +20 -21
  191. hud/tools/computer/qwen.py +434 -0
  192. hud/tools/computer/settings.py +37 -0
  193. hud/tools/edit.py +3 -7
  194. hud/tools/executors/base.py +4 -2
  195. hud/tools/executors/pyautogui.py +1 -1
  196. hud/tools/grounding/grounded_tool.py +13 -18
  197. hud/tools/grounding/grounder.py +10 -31
  198. hud/tools/grounding/tests/test_grounded_tool.py +26 -44
  199. hud/tools/jupyter.py +330 -0
  200. hud/tools/playwright.py +18 -3
  201. hud/tools/shell.py +308 -0
  202. hud/tools/tests/test_agent_tool.py +355 -0
  203. hud/tools/tests/test_apply_patch.py +718 -0
  204. hud/tools/tests/test_computer.py +4 -9
  205. hud/tools/tests/test_computer_actions.py +24 -2
  206. hud/tools/tests/test_jupyter_tool.py +181 -0
  207. hud/tools/tests/test_shell.py +596 -0
  208. hud/tools/tests/test_submit.py +85 -0
  209. hud/tools/tests/test_types.py +193 -0
  210. hud/tools/types.py +21 -1
  211. hud/types.py +194 -56
  212. hud/utils/__init__.py +2 -0
  213. hud/utils/env.py +67 -0
  214. hud/utils/hud_console.py +89 -18
  215. hud/utils/mcp.py +15 -58
  216. hud/utils/strict_schema.py +162 -0
  217. hud/utils/tests/test_init.py +1 -2
  218. hud/utils/tests/test_mcp.py +1 -28
  219. hud/utils/tests/test_pretty_errors.py +186 -0
  220. hud/utils/tests/test_tool_shorthand.py +154 -0
  221. hud/utils/tests/test_version.py +1 -1
  222. hud/utils/types.py +20 -0
  223. hud/version.py +1 -1
  224. hud_python-0.5.13.dist-info/METADATA +264 -0
  225. hud_python-0.5.13.dist-info/RECORD +305 -0
  226. {hud_python-0.4.45.dist-info → hud_python-0.5.13.dist-info}/WHEEL +1 -1
  227. hud/agents/langchain.py +0 -261
  228. hud/agents/lite_llm.py +0 -72
  229. hud/cli/rl/__init__.py +0 -180
  230. hud/cli/rl/config.py +0 -101
  231. hud/cli/rl/display.py +0 -133
  232. hud/cli/rl/gpu.py +0 -63
  233. hud/cli/rl/gpu_utils.py +0 -321
  234. hud/cli/rl/local_runner.py +0 -595
  235. hud/cli/rl/presets.py +0 -96
  236. hud/cli/rl/remote_runner.py +0 -463
  237. hud/cli/rl/rl_api.py +0 -150
  238. hud/cli/rl/vllm.py +0 -177
  239. hud/cli/rl/wait_utils.py +0 -89
  240. hud/datasets/parallel.py +0 -687
  241. hud/misc/__init__.py +0 -1
  242. hud/misc/claude_plays_pokemon.py +0 -292
  243. hud/otel/__init__.py +0 -35
  244. hud/otel/collector.py +0 -142
  245. hud/otel/config.py +0 -181
  246. hud/otel/context.py +0 -570
  247. hud/otel/exporters.py +0 -369
  248. hud/otel/instrumentation.py +0 -135
  249. hud/otel/processors.py +0 -121
  250. hud/otel/tests/__init__.py +0 -1
  251. hud/otel/tests/test_processors.py +0 -197
  252. hud/rl/README.md +0 -30
  253. hud/rl/__init__.py +0 -1
  254. hud/rl/actor.py +0 -176
  255. hud/rl/buffer.py +0 -405
  256. hud/rl/chat_template.jinja +0 -101
  257. hud/rl/config.py +0 -192
  258. hud/rl/distributed.py +0 -132
  259. hud/rl/learner.py +0 -637
  260. hud/rl/tests/__init__.py +0 -1
  261. hud/rl/tests/test_learner.py +0 -186
  262. hud/rl/train.py +0 -382
  263. hud/rl/types.py +0 -101
  264. hud/rl/utils/start_vllm_server.sh +0 -30
  265. hud/rl/utils.py +0 -524
  266. hud/rl/vllm_adapter.py +0 -143
  267. hud/telemetry/job.py +0 -352
  268. hud/telemetry/replay.py +0 -74
  269. hud/telemetry/tests/test_replay.py +0 -40
  270. hud/telemetry/tests/test_trace.py +0 -63
  271. hud/telemetry/trace.py +0 -158
  272. hud/utils/agent_factories.py +0 -86
  273. hud/utils/async_utils.py +0 -65
  274. hud/utils/group_eval.py +0 -223
  275. hud/utils/progress.py +0 -149
  276. hud/utils/tasks.py +0 -127
  277. hud/utils/tests/test_async_utils.py +0 -173
  278. hud/utils/tests/test_progress.py +0 -261
  279. hud_python-0.4.45.dist-info/METADATA +0 -552
  280. hud_python-0.4.45.dist-info/RECORD +0 -228
  281. {hud_python-0.4.45.dist-info → hud_python-0.5.13.dist-info}/entry_points.txt +0 -0
  282. {hud_python-0.4.45.dist-info → hud_python-0.5.13.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