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,101 @@
1
+ """OpenAI Agents SDK connectors - import tools from OpenAI agents."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ from typing import Any
7
+
8
+ __all__ = ["OpenAIConnectorMixin"]
9
+
10
+
11
+ class OpenAIConnectorMixin:
12
+ """Mixin providing OpenAI Agents SDK connector methods."""
13
+
14
+ # These are defined on Environment/MCPServer
15
+ _tool_manager: Any
16
+
17
+ def connect_function_tools(
18
+ self,
19
+ tools: list[Any],
20
+ *,
21
+ prefix: str | None = None,
22
+ ) -> Any:
23
+ """Import FunctionTools from the OpenAI Agents SDK.
24
+
25
+ Wraps each tool so calls go through HUD with telemetry.
26
+
27
+ Example:
28
+ ```python
29
+ from agents import function_tool
30
+
31
+
32
+ @function_tool
33
+ def search(query: str) -> str:
34
+ '''Search for information.'''
35
+ return f"Results for {query}"
36
+
37
+
38
+ @function_tool
39
+ def calculate(expression: str) -> float:
40
+ '''Evaluate a math expression.'''
41
+ return eval(expression)
42
+
43
+
44
+ env = Environment("my-env")
45
+ env.connect_function_tools([search, calculate])
46
+
47
+ async with env:
48
+ result = await env.call_tool("search", query="MCP protocol")
49
+ ```
50
+
51
+ Note:
52
+ Requires `openai-agents`: pip install openai-agents
53
+ """
54
+ try:
55
+ from agents import FunctionTool
56
+ except ImportError as e:
57
+ raise ImportError(
58
+ "openai-agents is required for connect_function_tools. "
59
+ "Install with: pip install openai-agents"
60
+ ) from e
61
+
62
+ for tool in tools:
63
+ if isinstance(tool, FunctionTool):
64
+ self._add_openai_function_tool(tool, prefix)
65
+
66
+ return self
67
+
68
+ def _add_openai_function_tool(self, tool: Any, prefix: str | None) -> None:
69
+ """Convert OpenAI FunctionTool to local MCP tool."""
70
+ name = f"{prefix}_{tool.name}" if prefix else tool.name
71
+
72
+ # Get the original invoke function
73
+ original_invoke = tool.on_invoke_tool
74
+
75
+ # Create wrapper that calls the original
76
+ async def invoke(**arguments: Any) -> Any:
77
+ # OpenAI's on_invoke_tool expects (ToolContext, str_json_args)
78
+ # We need to create a minimal context
79
+ from agents.tool_context import ToolContext
80
+
81
+ ctx = ToolContext(context=None)
82
+ result = await original_invoke(ctx, json.dumps(arguments))
83
+ return result
84
+
85
+ # Set function metadata for FastMCP
86
+ invoke.__name__ = name
87
+ invoke.__doc__ = tool.description
88
+
89
+ # Register using FastMCP's tool decorator mechanism
90
+ # We access the internal _tool_manager from MCPServer
91
+ from fastmcp.tools import Tool as FastMCPTool
92
+
93
+ fastmcp_tool = FastMCPTool.from_function(
94
+ fn=invoke,
95
+ name=name,
96
+ description=tool.description,
97
+ )
98
+ # Override the schema with OpenAI's (more accurate)
99
+ fastmcp_tool.parameters = tool.params_json_schema
100
+
101
+ self._tool_manager.add_tool(fastmcp_tool)
@@ -0,0 +1,172 @@
1
+ """Remote connection connectors - HUD Hub, URL, OpenAPI."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import logging
6
+ from typing import TYPE_CHECKING, Any, cast
7
+
8
+ from hud.environment.connectors.mcp_config import MCPConfigConnectorMixin
9
+
10
+ if TYPE_CHECKING:
11
+ from collections.abc import Callable
12
+
13
+ from fastmcp.tools.tool import Tool
14
+
15
+ __all__ = ["RemoteConnectorMixin"]
16
+
17
+ logger = logging.getLogger(__name__)
18
+
19
+
20
+ class RemoteConnectorMixin(MCPConfigConnectorMixin):
21
+ """Mixin providing remote connection methods.
22
+
23
+ Note: include_router() is inherited from MCPServer (via FastMCP).
24
+ """
25
+
26
+ def connect_hub(
27
+ self,
28
+ slug: str,
29
+ *,
30
+ alias: str | None = None,
31
+ prefix: str | None = None,
32
+ include: list[str] | None = None,
33
+ exclude: list[str] | None = None,
34
+ transform: Callable[[Tool], Tool | None] | None = None,
35
+ ) -> Any:
36
+ """Connect to a HUD Hub environment.
37
+
38
+ Creates an MCP connection to the HUD API with the hub slug in headers.
39
+
40
+ Example:
41
+ ```python
42
+ env = Environment("my-env")
43
+ env.connect_hub("browser")
44
+
45
+ async with env:
46
+ await env.call_tool("navigate", url="https://google.com")
47
+ ```
48
+ """
49
+ from hud.settings import settings
50
+
51
+ logger.info("Connecting to hub environment: %s", slug)
52
+
53
+ # Store hub config for serialization (v5 format)
54
+ # Note: Only first hub is stored for serialization (task configs use single hub)
55
+ if not hasattr(self, "_hub_config") or self._hub_config is None:
56
+ hub_config: dict[str, Any] = {"name": slug}
57
+ if include:
58
+ hub_config["include"] = include
59
+ if exclude:
60
+ hub_config["exclude"] = exclude
61
+ self._hub_config = hub_config
62
+
63
+ # Create mcp_config with standard MCP URL and hub slug in headers
64
+ # Note: Authorization is injected at request time by httpx/aiohttp hooks
65
+ # in hud.eval.instrument (uses contextvar for api_key).
66
+ mcp_config = {
67
+ "hud": {
68
+ "url": settings.hud_mcp_url,
69
+ "headers": {"Environment-Name": slug},
70
+ }
71
+ }
72
+
73
+ self.connect_mcp_config(
74
+ mcp_config, prefix=prefix, include=include, exclude=exclude, transform=transform
75
+ )
76
+ logger.info("Hub connected: %s", slug)
77
+ return self
78
+
79
+ def connect_url(
80
+ self,
81
+ url: str,
82
+ *,
83
+ headers: dict[str, str] | None = None,
84
+ alias: str | None = None,
85
+ prefix: str | None = None,
86
+ include: list[str] | None = None,
87
+ exclude: list[str] | None = None,
88
+ transform: Callable[[Tool], Tool | None] | None = None,
89
+ ) -> Any:
90
+ """Connect to an MCP server via URL.
91
+
92
+ Example:
93
+ ```python
94
+ env = Environment("my-env")
95
+ env.connect_url(
96
+ "https://mcp.example.com",
97
+ headers={"Authorization": "Bearer token"},
98
+ )
99
+
100
+ async with env:
101
+ await env.call_tool("search", query="hello")
102
+ ```
103
+ """
104
+ from hud.environment.connection import ConnectionType
105
+
106
+ auth = headers.get("Authorization") if headers else None
107
+ return self._add_connection(
108
+ alias or url,
109
+ url,
110
+ connection_type=ConnectionType.REMOTE,
111
+ auth=auth,
112
+ prefix=prefix,
113
+ include=include,
114
+ exclude=exclude,
115
+ transform=transform,
116
+ )
117
+
118
+ def connect_openapi(
119
+ self,
120
+ openapi_spec: dict[str, Any] | str,
121
+ *,
122
+ base_url: str | None = None,
123
+ headers: dict[str, str] | None = None,
124
+ name: str | None = None,
125
+ prefix: str | None = None,
126
+ timeout: float = 30.0,
127
+ ) -> Any:
128
+ """Mount an OpenAPI specification as an MCP server.
129
+
130
+ Converts REST API endpoints to MCP tools. Base URL is auto-inferred
131
+ from the spec URL when possible.
132
+
133
+ Example:
134
+ ```python
135
+ env = Environment("my-env")
136
+ env.connect_openapi("https://petstore.swagger.io/v2/swagger.json")
137
+
138
+ async with env:
139
+ result = await env.call_tool("getPetById", petId=1)
140
+ ```
141
+ """
142
+ from urllib.parse import urlparse
143
+
144
+ import httpx
145
+ from fastmcp import FastMCP
146
+
147
+ if isinstance(openapi_spec, str):
148
+ if openapi_spec.startswith(("http://", "https://")):
149
+ if base_url is None:
150
+ parsed = urlparse(openapi_spec)
151
+ base_url = f"{parsed.scheme}://{parsed.netloc}"
152
+
153
+ resp = httpx.get(openapi_spec, headers=headers)
154
+ resp.raise_for_status()
155
+ openapi_spec = resp.json()
156
+ else:
157
+ import json
158
+
159
+ with open(openapi_spec) as f:
160
+ openapi_spec = json.load(f)
161
+
162
+ if base_url is None:
163
+ raise ValueError("base_url is required when openapi_spec is a dict or file")
164
+
165
+ client = httpx.AsyncClient(base_url=base_url, headers=headers or {}, timeout=timeout)
166
+ mcp_server = FastMCP.from_openapi(
167
+ openapi_spec=cast("dict[str, Any]", openapi_spec),
168
+ client=client,
169
+ name=name or "openapi",
170
+ )
171
+ self.include_router(mcp_server, prefix=prefix) # type: ignore
172
+ return self