hud-python 0.5.7__tar.gz → 0.5.9__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.
- {hud_python-0.5.7 → hud_python-0.5.9}/PKG-INFO +1 -1
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/__init__.py +21 -3
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/build.py +11 -2
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/clients/mcp_use.py +6 -1
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/datasets/loader.py +4 -8
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/datasets/tests/test_loader.py +14 -14
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/environment/connection.py +28 -4
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/environment/connectors/mcp_config.py +29 -1
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/environment/environment.py +11 -4
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/environment/tests/test_connectors.py +10 -23
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/environment/tests/test_environment.py +248 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/environment/tests/test_local_connectors.py +81 -40
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/eval/tests/test_task.py +107 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/patches/mcp_patches.py +64 -21
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/tools/agent.py +23 -16
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/utils/tests/test_version.py +1 -1
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/version.py +1 -1
- {hud_python-0.5.7 → hud_python-0.5.9}/pyproject.toml +1 -1
- {hud_python-0.5.7 → hud_python-0.5.9}/.gitignore +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/LICENSE +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/README.md +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/examples/README.md +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/__init__.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/__main__.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/agents/__init__.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/agents/base.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/agents/claude.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/agents/gateway.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/agents/gemini.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/agents/gemini_cua.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/agents/grounded_openai.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/agents/misc/__init__.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/agents/misc/integration_test_agent.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/agents/misc/response_agent.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/agents/openai.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/agents/openai_chat.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/agents/operator.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/agents/resolver.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/agents/tests/__init__.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/agents/tests/conftest.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/agents/tests/test_base.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/agents/tests/test_base_runtime.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/agents/tests/test_claude.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/agents/tests/test_client.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/agents/tests/test_gemini.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/agents/tests/test_grounded_openai_agent.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/agents/tests/test_openai.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/agents/tests/test_operator.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/agents/tests/test_resolver.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/agents/tests/test_run_eval.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/__main__.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/analyze.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/clone.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/debug.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/dev.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/eval.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/flows/__init__.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/flows/dev.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/flows/init.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/flows/tasks.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/flows/templates.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/flows/tests/__init__.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/flows/tests/test_dev.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/get.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/init.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/list_func.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/pull.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/push.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/remove.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/rft.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/rft_status.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/tests/__init__.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/tests/test_analyze.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/tests/test_analyze_metadata.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/tests/test_analyze_module.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/tests/test_build.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/tests/test_build_failure.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/tests/test_build_module.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/tests/test_cli_init.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/tests/test_cli_main.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/tests/test_cli_more_wrappers.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/tests/test_cli_root.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/tests/test_clone.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/tests/test_convert.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/tests/test_cursor.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/tests/test_debug.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/tests/test_dev.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/tests/test_eval.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/tests/test_eval_bedrock.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/tests/test_init.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/tests/test_list_func.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/tests/test_main_module.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/tests/test_mcp_server.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/tests/test_pull.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/tests/test_push.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/tests/test_push_happy.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/tests/test_push_wrapper.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/tests/test_registry.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/tests/test_utils.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/utils/__init__.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/utils/celebrate.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/utils/config.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/utils/cursor.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/utils/docker.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/utils/env_check.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/utils/environment.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/utils/git.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/utils/interactive.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/utils/local_runner.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/utils/logging.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/utils/metadata.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/utils/package_runner.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/utils/registry.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/utils/remote_runner.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/utils/runner.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/utils/server.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/utils/source_hash.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/utils/tasks.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/utils/tests/__init__.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/utils/tests/test_config.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/utils/tests/test_docker.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/utils/tests/test_docker_hints.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/utils/tests/test_env_check.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/utils/tests/test_environment.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/utils/tests/test_git.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/utils/tests/test_interactive_module.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/utils/tests/test_local_runner.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/utils/tests/test_logging_utils.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/utils/tests/test_metadata.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/utils/tests/test_package_runner.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/utils/tests/test_registry_utils.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/utils/tests/test_remote_runner.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/utils/tests/test_runner_modules.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/utils/tests/test_source_hash.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/utils/tests/test_tasks.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/utils/version_check.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/utils/viewer.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/clients/README.md +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/clients/__init__.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/clients/base.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/clients/environment.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/clients/fastmcp.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/clients/tests/__init__.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/clients/tests/test_analyze_scenarios.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/clients/tests/test_client_integration.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/clients/tests/test_fastmcp.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/clients/tests/test_mcp_use_retry.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/clients/tests/test_protocol.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/clients/utils/__init__.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/clients/utils/mcp_use_retry.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/clients/utils/retry.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/clients/utils/retry_transport.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/datasets/__init__.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/datasets/runner.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/datasets/tests/__init__.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/datasets/tests/test_utils.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/datasets/utils.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/environment/__init__.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/environment/connectors/__init__.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/environment/connectors/base.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/environment/connectors/local.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/environment/connectors/openai.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/environment/connectors/remote.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/environment/integrations/__init__.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/environment/integrations/adk.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/environment/integrations/anthropic.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/environment/integrations/gemini.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/environment/integrations/langchain.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/environment/integrations/llamaindex.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/environment/integrations/openai.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/environment/mock.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/environment/router.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/environment/scenarios.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/environment/tests/__init__.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/environment/tests/test_connection.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/environment/tests/test_integrations.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/environment/tests/test_scenarios.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/environment/tests/test_tools.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/environment/types.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/environment/utils/__init__.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/environment/utils/formats.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/environment/utils/schema.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/environment/utils/tool_wrappers.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/eval/__init__.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/eval/context.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/eval/display.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/eval/instrument.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/eval/manager.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/eval/parallel.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/eval/task.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/eval/tests/__init__.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/eval/tests/test_context.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/eval/tests/test_eval.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/eval/tests/test_manager.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/eval/tests/test_parallel.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/eval/types.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/eval/utils.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/native/__init__.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/native/comparator.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/native/tests/__init__.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/native/tests/test_comparator.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/native/tests/test_native_init.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/patches/__init__.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/patches/warnings.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/py.typed +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/samples/__init__.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/samples/browser.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/server/__init__.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/server/context.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/server/helper/__init__.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/server/low_level.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/server/router.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/server/server.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/server/tests/__init__.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/server/tests/test_add_tool.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/server/tests/test_context.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/server/tests/test_mcp_server_handlers.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/server/tests/test_mcp_server_integration.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/server/tests/test_mcp_server_more.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/server/tests/test_run_wrapper.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/server/tests/test_server_extra.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/server/tests/test_sigterm_runner.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/settings.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/shared/__init__.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/shared/exceptions.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/shared/hints.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/shared/requests.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/shared/tests/__init__.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/shared/tests/test_exceptions.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/shared/tests/test_hints.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/shared/tests/test_requests.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/telemetry/__init__.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/telemetry/exporter.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/telemetry/instrument.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/telemetry/tests/__init__.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/telemetry/tests/test_eval_telemetry.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/telemetry/tests/test_exporter.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/telemetry/tests/test_instrument.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/tools/__init__.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/tools/apply_patch.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/tools/base.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/tools/bash.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/tools/computer/__init__.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/tools/computer/anthropic.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/tools/computer/gemini.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/tools/computer/hud.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/tools/computer/openai.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/tools/computer/qwen.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/tools/computer/settings.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/tools/edit.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/tools/executors/__init__.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/tools/executors/base.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/tools/executors/pyautogui.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/tools/executors/tests/__init__.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/tools/executors/tests/test_base_executor.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/tools/executors/tests/test_pyautogui_executor.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/tools/executors/xdo.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/tools/grounding/__init__.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/tools/grounding/config.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/tools/grounding/grounded_tool.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/tools/grounding/grounder.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/tools/grounding/tests/__init__.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/tools/grounding/tests/test_grounded_tool.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/tools/jupyter.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/tools/playwright.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/tools/response.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/tools/shell.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/tools/submit.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/tools/tests/__init__.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/tools/tests/test_agent_tool.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/tools/tests/test_apply_patch.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/tools/tests/test_base.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/tools/tests/test_bash.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/tools/tests/test_bash_extended.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/tools/tests/test_computer.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/tools/tests/test_computer_actions.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/tools/tests/test_edit.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/tools/tests/test_init.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/tools/tests/test_jupyter_tool.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/tools/tests/test_playwright_tool.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/tools/tests/test_response.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/tools/tests/test_shell.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/tools/tests/test_submit.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/tools/tests/test_tools.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/tools/tests/test_tools_init.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/tools/tests/test_types.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/tools/tests/test_utils.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/tools/types.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/tools/utils.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/types.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/utils/__init__.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/utils/env.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/utils/hud_console.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/utils/mcp.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/utils/pretty_errors.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/utils/strict_schema.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/utils/telemetry.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/utils/tests/__init__.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/utils/tests/test_init.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/utils/tests/test_mcp.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/utils/tests/test_pretty_errors.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/utils/tests/test_telemetry.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/utils/tests/test_tool_shorthand.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/utils/tool_shorthand.py +0 -0
- {hud_python-0.5.7 → hud_python-0.5.9}/hud/utils/types.py +0 -0
|
@@ -757,7 +757,8 @@ def build(
|
|
|
757
757
|
hud build environments/text_2048 -e API_KEY=secret
|
|
758
758
|
hud build . --tag my-env:v1.0 -e VAR1=value1 -e VAR2=value2
|
|
759
759
|
hud build . --no-cache # Force rebuild
|
|
760
|
-
hud build . --remote-cache my-cache-repo # Use ECR remote cache (requires AWS_ACCOUNT_ID and AWS_DEFAULT_REGION)
|
|
760
|
+
hud build . --remote-cache my-cache-repo # Use ECR remote cache (requires AWS_ACCOUNT_ID and AWS_DEFAULT_REGION)
|
|
761
|
+
hud build . --build-arg NODE_ENV=production # Pass Docker build args[/not dim]
|
|
761
762
|
""" # noqa: E501
|
|
762
763
|
# Parse directory and extra arguments
|
|
763
764
|
if params:
|
|
@@ -767,8 +768,9 @@ def build(
|
|
|
767
768
|
directory = "."
|
|
768
769
|
extra_args = []
|
|
769
770
|
|
|
770
|
-
# Parse environment variables from extra args
|
|
771
|
+
# Parse environment variables and build args from extra args
|
|
771
772
|
env_vars = {}
|
|
773
|
+
build_args = {}
|
|
772
774
|
i = 0
|
|
773
775
|
while i < len(extra_args):
|
|
774
776
|
if extra_args[i] == "-e" and i + 1 < len(extra_args):
|
|
@@ -792,10 +794,26 @@ def build(
|
|
|
792
794
|
key, value = env_arg.split("=", 1)
|
|
793
795
|
env_vars[key] = value
|
|
794
796
|
i += 2
|
|
797
|
+
elif extra_args[i] == "--build-arg" and i + 1 < len(extra_args):
|
|
798
|
+
# Parse --build-arg KEY=VALUE format
|
|
799
|
+
build_arg = extra_args[i + 1]
|
|
800
|
+
if "=" in build_arg:
|
|
801
|
+
key, value = build_arg.split("=", 1)
|
|
802
|
+
build_args[key] = value
|
|
803
|
+
i += 2
|
|
804
|
+
elif extra_args[i].startswith("--build-arg="):
|
|
805
|
+
# Parse --build-arg=KEY=VALUE format
|
|
806
|
+
build_arg = extra_args[i][12:] # Remove --build-arg=
|
|
807
|
+
if "=" in build_arg:
|
|
808
|
+
key, value = build_arg.split("=", 1)
|
|
809
|
+
build_args[key] = value
|
|
810
|
+
i += 1
|
|
795
811
|
else:
|
|
796
812
|
i += 1
|
|
797
813
|
|
|
798
|
-
build_command(
|
|
814
|
+
build_command(
|
|
815
|
+
directory, tag, no_cache, verbose, env_vars, platform, remote_cache, build_args or None
|
|
816
|
+
)
|
|
799
817
|
|
|
800
818
|
|
|
801
819
|
@app.command()
|
|
@@ -651,10 +651,12 @@ def build_environment(
|
|
|
651
651
|
env_vars: dict[str, str] | None = None,
|
|
652
652
|
platform: str | None = None,
|
|
653
653
|
remote_cache: str | None = None,
|
|
654
|
+
build_args: dict[str, str] | None = None,
|
|
654
655
|
) -> None:
|
|
655
656
|
"""Build a HUD environment and generate lock file."""
|
|
656
657
|
hud_console = HUDConsole()
|
|
657
658
|
env_vars = env_vars or {}
|
|
659
|
+
build_args = build_args or {}
|
|
658
660
|
hud_console.header("HUD Environment Build")
|
|
659
661
|
|
|
660
662
|
# Resolve directory
|
|
@@ -721,7 +723,7 @@ def build_environment(
|
|
|
721
723
|
temp_tag,
|
|
722
724
|
no_cache,
|
|
723
725
|
verbose,
|
|
724
|
-
build_args=None,
|
|
726
|
+
build_args=build_args or None,
|
|
725
727
|
platform=platform,
|
|
726
728
|
remote_cache=remote_cache,
|
|
727
729
|
):
|
|
@@ -1002,6 +1004,10 @@ def build_environment(
|
|
|
1002
1004
|
if image_tag and image_tag not in [version_tag, latest_tag]:
|
|
1003
1005
|
label_cmd.extend(["-t", image_tag])
|
|
1004
1006
|
|
|
1007
|
+
# Add build args to final image build (same as initial build)
|
|
1008
|
+
for key, value in build_args.items():
|
|
1009
|
+
label_cmd.extend(["--build-arg", f"{key}={value}"])
|
|
1010
|
+
|
|
1005
1011
|
label_cmd.append(str(env_dir))
|
|
1006
1012
|
|
|
1007
1013
|
# Run rebuild using Docker's native output formatting
|
|
@@ -1106,6 +1112,9 @@ def build_command(
|
|
|
1106
1112
|
env_vars: dict[str, str] | None = None,
|
|
1107
1113
|
platform: str | None = None,
|
|
1108
1114
|
remote_cache: str | None = None,
|
|
1115
|
+
build_args: dict[str, str] | None = None,
|
|
1109
1116
|
) -> None:
|
|
1110
1117
|
"""Build a HUD environment and generate lock file."""
|
|
1111
|
-
build_environment(
|
|
1118
|
+
build_environment(
|
|
1119
|
+
directory, tag, no_cache, verbose, env_vars, platform, remote_cache, build_args
|
|
1120
|
+
)
|
|
@@ -64,9 +64,14 @@ class MCPUseHUDClient(BaseHUDClient):
|
|
|
64
64
|
return
|
|
65
65
|
|
|
66
66
|
# Use configurable timeout for SSE read operations to support long-running tool calls.
|
|
67
|
+
max_request_timeout = 840
|
|
67
68
|
for server_cfg in mcp_config.values():
|
|
68
69
|
if "sse_read_timeout" not in server_cfg:
|
|
69
|
-
server_cfg["sse_read_timeout"] =
|
|
70
|
+
server_cfg["sse_read_timeout"] = (
|
|
71
|
+
min(settings.client_timeout, max_request_timeout)
|
|
72
|
+
if settings.client_timeout > 0
|
|
73
|
+
else max_request_timeout
|
|
74
|
+
)
|
|
70
75
|
|
|
71
76
|
# If a server target matches HUD's MCP host and no auth is provided,
|
|
72
77
|
# inject the HUD API key as a Bearer token to avoid OAuth browser flow.
|
|
@@ -14,6 +14,10 @@ import warnings
|
|
|
14
14
|
from pathlib import Path
|
|
15
15
|
from typing import TYPE_CHECKING, Any, overload
|
|
16
16
|
|
|
17
|
+
import httpx
|
|
18
|
+
|
|
19
|
+
from hud.settings import settings
|
|
20
|
+
|
|
17
21
|
if TYPE_CHECKING:
|
|
18
22
|
from hud.eval.task import Task
|
|
19
23
|
|
|
@@ -106,10 +110,6 @@ def _load_from_huggingface(dataset_name: str) -> list[Task]:
|
|
|
106
110
|
|
|
107
111
|
def _load_raw_from_api(dataset_name: str) -> list[dict[str, Any]]:
|
|
108
112
|
"""Load raw task dicts from HUD API."""
|
|
109
|
-
import httpx
|
|
110
|
-
|
|
111
|
-
from hud.settings import settings
|
|
112
|
-
|
|
113
113
|
headers = {}
|
|
114
114
|
if settings.api_key:
|
|
115
115
|
headers["Authorization"] = f"Bearer {settings.api_key}"
|
|
@@ -271,10 +271,6 @@ def save_tasks(
|
|
|
271
271
|
TypeError: If any task is not a v5 Task object (must have 'scenario')
|
|
272
272
|
ValueError: If API key is not set or save fails
|
|
273
273
|
"""
|
|
274
|
-
import httpx
|
|
275
|
-
|
|
276
|
-
from hud.settings import settings
|
|
277
|
-
|
|
278
274
|
if not settings.api_key:
|
|
279
275
|
raise ValueError("HUD_API_KEY is required to save tasks")
|
|
280
276
|
|
|
@@ -12,8 +12,8 @@ from hud.datasets.loader import load_tasks
|
|
|
12
12
|
class TestLoadTasks:
|
|
13
13
|
"""Tests for load_tasks() function."""
|
|
14
14
|
|
|
15
|
-
@patch("httpx.Client")
|
|
16
|
-
@patch("hud.
|
|
15
|
+
@patch("hud.datasets.loader.httpx.Client")
|
|
16
|
+
@patch("hud.datasets.loader.settings")
|
|
17
17
|
def test_load_tasks_success(
|
|
18
18
|
self, mock_settings: MagicMock, mock_client_class: MagicMock
|
|
19
19
|
) -> None:
|
|
@@ -62,8 +62,8 @@ class TestLoadTasks:
|
|
|
62
62
|
params={"all": "true"},
|
|
63
63
|
)
|
|
64
64
|
|
|
65
|
-
@patch("httpx.Client")
|
|
66
|
-
@patch("hud.
|
|
65
|
+
@patch("hud.datasets.loader.httpx.Client")
|
|
66
|
+
@patch("hud.datasets.loader.settings")
|
|
67
67
|
def test_load_tasks_single_task(
|
|
68
68
|
self, mock_settings: MagicMock, mock_client_class: MagicMock
|
|
69
69
|
) -> None:
|
|
@@ -97,8 +97,8 @@ class TestLoadTasks:
|
|
|
97
97
|
assert tasks[0].scenario == "checkout"
|
|
98
98
|
assert tasks[0].id == "task-1"
|
|
99
99
|
|
|
100
|
-
@patch("httpx.Client")
|
|
101
|
-
@patch("hud.
|
|
100
|
+
@patch("hud.datasets.loader.httpx.Client")
|
|
101
|
+
@patch("hud.datasets.loader.settings")
|
|
102
102
|
def test_load_tasks_no_api_key(
|
|
103
103
|
self, mock_settings: MagicMock, mock_client_class: MagicMock
|
|
104
104
|
) -> None:
|
|
@@ -129,8 +129,8 @@ class TestLoadTasks:
|
|
|
129
129
|
params={"all": "true"},
|
|
130
130
|
)
|
|
131
131
|
|
|
132
|
-
@patch("httpx.Client")
|
|
133
|
-
@patch("hud.
|
|
132
|
+
@patch("hud.datasets.loader.httpx.Client")
|
|
133
|
+
@patch("hud.datasets.loader.settings")
|
|
134
134
|
def test_load_tasks_http_error(
|
|
135
135
|
self, mock_settings: MagicMock, mock_client_class: MagicMock
|
|
136
136
|
) -> None:
|
|
@@ -149,8 +149,8 @@ class TestLoadTasks:
|
|
|
149
149
|
with pytest.raises(ValueError, match="Failed to load tasks"):
|
|
150
150
|
load_tasks("test-org/test-dataset")
|
|
151
151
|
|
|
152
|
-
@patch("httpx.Client")
|
|
153
|
-
@patch("hud.
|
|
152
|
+
@patch("hud.datasets.loader.httpx.Client")
|
|
153
|
+
@patch("hud.datasets.loader.settings")
|
|
154
154
|
def test_load_tasks_json_error(
|
|
155
155
|
self, mock_settings: MagicMock, mock_client_class: MagicMock
|
|
156
156
|
) -> None:
|
|
@@ -171,8 +171,8 @@ class TestLoadTasks:
|
|
|
171
171
|
with pytest.raises(ValueError, match="Failed to load tasks"):
|
|
172
172
|
load_tasks("test-org/test-dataset")
|
|
173
173
|
|
|
174
|
-
@patch("httpx.Client")
|
|
175
|
-
@patch("hud.
|
|
174
|
+
@patch("hud.datasets.loader.httpx.Client")
|
|
175
|
+
@patch("hud.datasets.loader.settings")
|
|
176
176
|
def test_load_tasks_empty(self, mock_settings: MagicMock, mock_client_class: MagicMock) -> None:
|
|
177
177
|
"""load_tasks() handles empty dataset."""
|
|
178
178
|
mock_settings.hud_api_url = "https://api.hud.ai"
|
|
@@ -192,8 +192,8 @@ class TestLoadTasks:
|
|
|
192
192
|
|
|
193
193
|
assert len(tasks) == 0
|
|
194
194
|
|
|
195
|
-
@patch("httpx.Client")
|
|
196
|
-
@patch("hud.
|
|
195
|
+
@patch("hud.datasets.loader.httpx.Client")
|
|
196
|
+
@patch("hud.datasets.loader.settings")
|
|
197
197
|
def test_load_tasks_missing_fields(
|
|
198
198
|
self, mock_settings: MagicMock, mock_client_class: MagicMock
|
|
199
199
|
) -> None:
|
|
@@ -120,8 +120,10 @@ class Connector:
|
|
|
120
120
|
"""
|
|
121
121
|
from fastmcp.client import Client as FastMCPClient
|
|
122
122
|
|
|
123
|
-
|
|
124
|
-
|
|
123
|
+
self.client = FastMCPClient(
|
|
124
|
+
transport=self._transport,
|
|
125
|
+
auth=self._auth,
|
|
126
|
+
)
|
|
125
127
|
await self.client.__aenter__()
|
|
126
128
|
|
|
127
129
|
async def disconnect(self) -> None:
|
|
@@ -198,10 +200,21 @@ class Connector:
|
|
|
198
200
|
|
|
199
201
|
Always fetches fresh data from the server (no caching check).
|
|
200
202
|
The result is cached for use by router.build_resources() via cached_resources property.
|
|
203
|
+
|
|
204
|
+
Note: resources/list is optional in the MCP spec. If the server doesn't
|
|
205
|
+
implement it, we return an empty list gracefully.
|
|
201
206
|
"""
|
|
202
207
|
if self.client is None:
|
|
203
208
|
raise RuntimeError("Not connected - call connect() first")
|
|
204
|
-
|
|
209
|
+
try:
|
|
210
|
+
self._resources_cache = await self.client.list_resources()
|
|
211
|
+
except Exception as e:
|
|
212
|
+
# Handle servers that don't implement resources/list (optional in MCP spec)
|
|
213
|
+
if "Method not found" in str(e):
|
|
214
|
+
logger.debug("Server %s does not support resources/list", self.name)
|
|
215
|
+
self._resources_cache = []
|
|
216
|
+
else:
|
|
217
|
+
raise
|
|
205
218
|
return self._resources_cache
|
|
206
219
|
|
|
207
220
|
async def list_prompts(self) -> list[mcp_types.Prompt]:
|
|
@@ -209,10 +222,21 @@ class Connector:
|
|
|
209
222
|
|
|
210
223
|
Always fetches fresh data from the server (no caching check).
|
|
211
224
|
The result is cached for use by router.build_prompts() via cached_prompts property.
|
|
225
|
+
|
|
226
|
+
Note: prompts/list is optional in the MCP spec. If the server doesn't
|
|
227
|
+
implement it, we return an empty list gracefully.
|
|
212
228
|
"""
|
|
213
229
|
if self.client is None:
|
|
214
230
|
raise RuntimeError("Not connected - call connect() first")
|
|
215
|
-
|
|
231
|
+
try:
|
|
232
|
+
self._prompts_cache = await self.client.list_prompts()
|
|
233
|
+
except Exception as e:
|
|
234
|
+
# Handle servers that don't implement prompts/list (optional in MCP spec)
|
|
235
|
+
if "Method not found" in str(e):
|
|
236
|
+
logger.debug("Server %s does not support prompts/list", self.name)
|
|
237
|
+
self._prompts_cache = []
|
|
238
|
+
else:
|
|
239
|
+
raise
|
|
216
240
|
return self._prompts_cache
|
|
217
241
|
|
|
218
242
|
async def read_resource(
|
|
@@ -50,6 +50,7 @@ class MCPConfigConnectorMixin(BaseConnectorMixin):
|
|
|
50
50
|
```
|
|
51
51
|
"""
|
|
52
52
|
from hud.environment.connection import ConnectionType
|
|
53
|
+
from hud.settings import settings
|
|
53
54
|
|
|
54
55
|
name = alias or next(iter(config.keys()), "mcp")
|
|
55
56
|
server_config = next(iter(config.values()), {})
|
|
@@ -57,9 +58,20 @@ class MCPConfigConnectorMixin(BaseConnectorMixin):
|
|
|
57
58
|
is_local = "command" in server_config or "args" in server_config
|
|
58
59
|
conn_type = ConnectionType.LOCAL if is_local else ConnectionType.REMOTE
|
|
59
60
|
|
|
61
|
+
transport: Any = config
|
|
62
|
+
if not is_local and "url" in server_config:
|
|
63
|
+
max_request_timeout = 840
|
|
64
|
+
server_config.setdefault(
|
|
65
|
+
"sse_read_timeout",
|
|
66
|
+
min(settings.client_timeout, max_request_timeout)
|
|
67
|
+
if settings.client_timeout > 0
|
|
68
|
+
else max_request_timeout,
|
|
69
|
+
)
|
|
70
|
+
transport = _build_transport(server_config)
|
|
71
|
+
|
|
60
72
|
return self._add_connection(
|
|
61
73
|
name,
|
|
62
|
-
|
|
74
|
+
transport,
|
|
63
75
|
connection_type=conn_type,
|
|
64
76
|
prefix=prefix,
|
|
65
77
|
include=include,
|
|
@@ -107,3 +119,19 @@ class MCPConfigConnectorMixin(BaseConnectorMixin):
|
|
|
107
119
|
for server_name, server_config in mcp_config.items():
|
|
108
120
|
self.connect_mcp({server_name: server_config}, alias=server_name, **kwargs)
|
|
109
121
|
return self
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def _build_transport(server_config: dict[str, Any]) -> Any:
|
|
125
|
+
from fastmcp.client.transports import SSETransport, StreamableHttpTransport
|
|
126
|
+
from fastmcp.mcp_config import infer_transport_type_from_url
|
|
127
|
+
|
|
128
|
+
url = server_config["url"]
|
|
129
|
+
transport_type = server_config.get("transport") or infer_transport_type_from_url(url)
|
|
130
|
+
transport_cls = SSETransport if transport_type == "sse" else StreamableHttpTransport
|
|
131
|
+
|
|
132
|
+
return transport_cls(
|
|
133
|
+
url=url,
|
|
134
|
+
headers=server_config.get("headers"),
|
|
135
|
+
auth=server_config.get("auth"),
|
|
136
|
+
sse_read_timeout=server_config.get("sse_read_timeout"),
|
|
137
|
+
)
|
|
@@ -189,18 +189,25 @@ class Environment(
|
|
|
189
189
|
"""Return tools in MCP format (base format).
|
|
190
190
|
|
|
191
191
|
Applies agent-level include/exclude filtering if set.
|
|
192
|
+
Supports fnmatch-style wildcards (e.g., "*setup*", "browser_*").
|
|
192
193
|
"""
|
|
194
|
+
import fnmatch
|
|
195
|
+
|
|
193
196
|
tools = self._router.tools
|
|
194
197
|
|
|
195
198
|
# Apply agent-level filtering (from v4 allowed_tools/disallowed_tools)
|
|
196
199
|
if self._agent_include is not None or self._agent_exclude is not None:
|
|
197
200
|
filtered = []
|
|
198
201
|
for tool in tools:
|
|
199
|
-
# Include filter: None means include all
|
|
200
|
-
if self._agent_include is not None and
|
|
202
|
+
# Include filter: None means include all, check if matches any pattern
|
|
203
|
+
if self._agent_include is not None and not any(
|
|
204
|
+
fnmatch.fnmatch(tool.name, pattern) for pattern in self._agent_include
|
|
205
|
+
):
|
|
201
206
|
continue
|
|
202
|
-
# Exclude filter
|
|
203
|
-
if self._agent_exclude is not None and
|
|
207
|
+
# Exclude filter: skip if tool matches any exclude pattern
|
|
208
|
+
if self._agent_exclude is not None and any(
|
|
209
|
+
fnmatch.fnmatch(tool.name, pattern) for pattern in self._agent_exclude
|
|
210
|
+
):
|
|
204
211
|
continue
|
|
205
212
|
filtered.append(tool)
|
|
206
213
|
return filtered
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
from typing import Any
|
|
6
|
-
from unittest.mock import
|
|
6
|
+
from unittest.mock import patch
|
|
7
7
|
|
|
8
8
|
from hud.environment.connection import ConnectionType, Connector
|
|
9
9
|
|
|
@@ -180,39 +180,26 @@ class TestRemoteConnectorMixin:
|
|
|
180
180
|
conn = env._connections["example"]
|
|
181
181
|
assert conn._auth == "Bearer my-token"
|
|
182
182
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
"""connect_hub fetches mcp_config from API."""
|
|
183
|
+
def test_connect_hub_creates_connection(self) -> None:
|
|
184
|
+
"""connect_hub creates connection with correct config."""
|
|
186
185
|
from hud.environment.connectors.remote import RemoteConnectorMixin
|
|
187
186
|
|
|
188
187
|
class TestEnv(RemoteConnectorMixin):
|
|
189
188
|
def __init__(self) -> None:
|
|
190
189
|
self._connections: dict[str, Connector] = {}
|
|
190
|
+
self._hub_config: dict[str, Any] | None = None
|
|
191
191
|
|
|
192
192
|
def mount(self, server: Any, *, prefix: str | None = None) -> None:
|
|
193
193
|
pass
|
|
194
194
|
|
|
195
|
-
# Mock httpx response
|
|
196
|
-
mock_response = MagicMock()
|
|
197
|
-
mock_response.json.return_value = {
|
|
198
|
-
"mcp_config": {
|
|
199
|
-
"browser": {"url": "https://mcp.hud.ai/browser"},
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
mock_response.raise_for_status = MagicMock()
|
|
203
|
-
|
|
204
|
-
mock_client = MagicMock()
|
|
205
|
-
mock_client.get.return_value = mock_response
|
|
206
|
-
mock_client.__enter__ = MagicMock(return_value=mock_client)
|
|
207
|
-
mock_client.__exit__ = MagicMock(return_value=None)
|
|
208
|
-
mock_httpx_cls.return_value = mock_client
|
|
209
|
-
|
|
210
195
|
env = TestEnv()
|
|
211
196
|
with patch("hud.settings.settings") as mock_settings:
|
|
212
|
-
mock_settings.
|
|
213
|
-
mock_settings.
|
|
197
|
+
mock_settings.hud_mcp_url = "https://mcp.hud.ai"
|
|
198
|
+
mock_settings.client_timeout = 300 # Used in connect_mcp for sse_read_timeout
|
|
214
199
|
|
|
215
|
-
env.connect_hub("
|
|
200
|
+
env.connect_hub("browser")
|
|
216
201
|
|
|
217
|
-
# connect_hub creates a connection named "hud" (
|
|
202
|
+
# connect_hub creates a connection named "hud" (from mcp_config key)
|
|
218
203
|
assert "hud" in env._connections
|
|
204
|
+
# Verify hub config is stored for serialization
|
|
205
|
+
assert env._hub_config == {"name": "browser"}
|
|
@@ -343,3 +343,251 @@ class TestEnvironmentMCPProtocol:
|
|
|
343
343
|
assert hasattr(env, "_env_call_tool")
|
|
344
344
|
assert callable(env._env_list_tools)
|
|
345
345
|
assert callable(env._env_call_tool)
|
|
346
|
+
|
|
347
|
+
|
|
348
|
+
class TestEnvironmentToolFiltering:
|
|
349
|
+
"""Tests for agent-level tool filtering with wildcard support (v4 backwards compat)."""
|
|
350
|
+
|
|
351
|
+
@pytest.mark.asyncio
|
|
352
|
+
async def test_as_tools_no_filter(self) -> None:
|
|
353
|
+
"""as_tools returns all tools when no filter is set."""
|
|
354
|
+
from hud.environment import Environment
|
|
355
|
+
|
|
356
|
+
env = Environment("test")
|
|
357
|
+
|
|
358
|
+
@env.tool()
|
|
359
|
+
def tool_a() -> str:
|
|
360
|
+
"""Tool A."""
|
|
361
|
+
return "a"
|
|
362
|
+
|
|
363
|
+
@env.tool()
|
|
364
|
+
def tool_b() -> str:
|
|
365
|
+
"""Tool B."""
|
|
366
|
+
return "b"
|
|
367
|
+
|
|
368
|
+
await env._build_routing()
|
|
369
|
+
|
|
370
|
+
tools = env.as_tools()
|
|
371
|
+
tool_names = [t.name for t in tools]
|
|
372
|
+
|
|
373
|
+
assert "tool_a" in tool_names
|
|
374
|
+
assert "tool_b" in tool_names
|
|
375
|
+
|
|
376
|
+
@pytest.mark.asyncio
|
|
377
|
+
async def test_as_tools_exact_include(self) -> None:
|
|
378
|
+
"""as_tools filters with exact include list."""
|
|
379
|
+
from hud.environment import Environment
|
|
380
|
+
|
|
381
|
+
env = Environment("test")
|
|
382
|
+
|
|
383
|
+
@env.tool()
|
|
384
|
+
def tool_a() -> str:
|
|
385
|
+
"""Tool A."""
|
|
386
|
+
return "a"
|
|
387
|
+
|
|
388
|
+
@env.tool()
|
|
389
|
+
def tool_b() -> str:
|
|
390
|
+
"""Tool B."""
|
|
391
|
+
return "b"
|
|
392
|
+
|
|
393
|
+
env._agent_include = ["tool_a"]
|
|
394
|
+
await env._build_routing()
|
|
395
|
+
|
|
396
|
+
tools = env.as_tools()
|
|
397
|
+
tool_names = [t.name for t in tools]
|
|
398
|
+
|
|
399
|
+
assert "tool_a" in tool_names
|
|
400
|
+
assert "tool_b" not in tool_names
|
|
401
|
+
|
|
402
|
+
@pytest.mark.asyncio
|
|
403
|
+
async def test_as_tools_exact_exclude(self) -> None:
|
|
404
|
+
"""as_tools filters with exact exclude list."""
|
|
405
|
+
from hud.environment import Environment
|
|
406
|
+
|
|
407
|
+
env = Environment("test")
|
|
408
|
+
|
|
409
|
+
@env.tool()
|
|
410
|
+
def tool_a() -> str:
|
|
411
|
+
"""Tool A."""
|
|
412
|
+
return "a"
|
|
413
|
+
|
|
414
|
+
@env.tool()
|
|
415
|
+
def tool_b() -> str:
|
|
416
|
+
"""Tool B."""
|
|
417
|
+
return "b"
|
|
418
|
+
|
|
419
|
+
env._agent_exclude = ["tool_a"]
|
|
420
|
+
await env._build_routing()
|
|
421
|
+
|
|
422
|
+
tools = env.as_tools()
|
|
423
|
+
tool_names = [t.name for t in tools]
|
|
424
|
+
|
|
425
|
+
assert "tool_a" not in tool_names
|
|
426
|
+
assert "tool_b" in tool_names
|
|
427
|
+
|
|
428
|
+
@pytest.mark.asyncio
|
|
429
|
+
async def test_as_tools_wildcard_exclude_prefix(self) -> None:
|
|
430
|
+
"""as_tools filters with wildcard prefix pattern (e.g., 'setup_*')."""
|
|
431
|
+
from hud.environment import Environment
|
|
432
|
+
|
|
433
|
+
env = Environment("test")
|
|
434
|
+
|
|
435
|
+
@env.tool()
|
|
436
|
+
def setup_database() -> str:
|
|
437
|
+
"""Setup tool."""
|
|
438
|
+
return "setup"
|
|
439
|
+
|
|
440
|
+
@env.tool()
|
|
441
|
+
def setup_user() -> str:
|
|
442
|
+
"""Another setup tool."""
|
|
443
|
+
return "setup"
|
|
444
|
+
|
|
445
|
+
@env.tool()
|
|
446
|
+
def run_query() -> str:
|
|
447
|
+
"""Regular tool."""
|
|
448
|
+
return "query"
|
|
449
|
+
|
|
450
|
+
env._agent_exclude = ["setup_*"]
|
|
451
|
+
await env._build_routing()
|
|
452
|
+
|
|
453
|
+
tools = env.as_tools()
|
|
454
|
+
tool_names = [t.name for t in tools]
|
|
455
|
+
|
|
456
|
+
assert "setup_database" not in tool_names
|
|
457
|
+
assert "setup_user" not in tool_names
|
|
458
|
+
assert "run_query" in tool_names
|
|
459
|
+
|
|
460
|
+
@pytest.mark.asyncio
|
|
461
|
+
async def test_as_tools_wildcard_exclude_contains(self) -> None:
|
|
462
|
+
"""as_tools filters with wildcard contains pattern (e.g., '*setup*')."""
|
|
463
|
+
from hud.environment import Environment
|
|
464
|
+
|
|
465
|
+
env = Environment("test")
|
|
466
|
+
|
|
467
|
+
@env.tool()
|
|
468
|
+
def hud_setup() -> str:
|
|
469
|
+
"""Contains setup."""
|
|
470
|
+
return "setup"
|
|
471
|
+
|
|
472
|
+
@env.tool()
|
|
473
|
+
def setup_env() -> str:
|
|
474
|
+
"""Starts with setup."""
|
|
475
|
+
return "setup"
|
|
476
|
+
|
|
477
|
+
@env.tool()
|
|
478
|
+
def my_setup_tool() -> str:
|
|
479
|
+
"""Contains setup in middle."""
|
|
480
|
+
return "setup"
|
|
481
|
+
|
|
482
|
+
@env.tool()
|
|
483
|
+
def run_query() -> str:
|
|
484
|
+
"""No setup in name."""
|
|
485
|
+
return "query"
|
|
486
|
+
|
|
487
|
+
env._agent_exclude = ["*setup*"]
|
|
488
|
+
await env._build_routing()
|
|
489
|
+
|
|
490
|
+
tools = env.as_tools()
|
|
491
|
+
tool_names = [t.name for t in tools]
|
|
492
|
+
|
|
493
|
+
assert "hud_setup" not in tool_names
|
|
494
|
+
assert "setup_env" not in tool_names
|
|
495
|
+
assert "my_setup_tool" not in tool_names
|
|
496
|
+
assert "run_query" in tool_names
|
|
497
|
+
|
|
498
|
+
@pytest.mark.asyncio
|
|
499
|
+
async def test_as_tools_multiple_wildcard_patterns(self) -> None:
|
|
500
|
+
"""as_tools filters with multiple wildcard patterns."""
|
|
501
|
+
from hud.environment import Environment
|
|
502
|
+
|
|
503
|
+
env = Environment("test")
|
|
504
|
+
|
|
505
|
+
@env.tool()
|
|
506
|
+
def setup_db() -> str:
|
|
507
|
+
"""Setup tool."""
|
|
508
|
+
return "setup"
|
|
509
|
+
|
|
510
|
+
@env.tool()
|
|
511
|
+
def evaluate_result() -> str:
|
|
512
|
+
"""Evaluate tool."""
|
|
513
|
+
return "evaluate"
|
|
514
|
+
|
|
515
|
+
@env.tool()
|
|
516
|
+
def checkout_branch() -> str:
|
|
517
|
+
"""Checkout tool."""
|
|
518
|
+
return "checkout"
|
|
519
|
+
|
|
520
|
+
@env.tool()
|
|
521
|
+
def run_query() -> str:
|
|
522
|
+
"""Regular tool."""
|
|
523
|
+
return "query"
|
|
524
|
+
|
|
525
|
+
env._agent_exclude = ["*setup*", "*evaluate*", "checkout_branch"]
|
|
526
|
+
await env._build_routing()
|
|
527
|
+
|
|
528
|
+
tools = env.as_tools()
|
|
529
|
+
tool_names = [t.name for t in tools]
|
|
530
|
+
|
|
531
|
+
assert "setup_db" not in tool_names
|
|
532
|
+
assert "evaluate_result" not in tool_names
|
|
533
|
+
assert "checkout_branch" not in tool_names
|
|
534
|
+
assert "run_query" in tool_names
|
|
535
|
+
|
|
536
|
+
@pytest.mark.asyncio
|
|
537
|
+
async def test_as_tools_wildcard_include_all(self) -> None:
|
|
538
|
+
"""as_tools with ['*'] include pattern matches all tools."""
|
|
539
|
+
from hud.environment import Environment
|
|
540
|
+
|
|
541
|
+
env = Environment("test")
|
|
542
|
+
|
|
543
|
+
@env.tool()
|
|
544
|
+
def tool_a() -> str:
|
|
545
|
+
"""Tool A."""
|
|
546
|
+
return "a"
|
|
547
|
+
|
|
548
|
+
@env.tool()
|
|
549
|
+
def tool_b() -> str:
|
|
550
|
+
"""Tool B."""
|
|
551
|
+
return "b"
|
|
552
|
+
|
|
553
|
+
env._agent_include = ["*"]
|
|
554
|
+
await env._build_routing()
|
|
555
|
+
|
|
556
|
+
tools = env.as_tools()
|
|
557
|
+
tool_names = [t.name for t in tools]
|
|
558
|
+
|
|
559
|
+
assert "tool_a" in tool_names
|
|
560
|
+
assert "tool_b" in tool_names
|
|
561
|
+
|
|
562
|
+
@pytest.mark.asyncio
|
|
563
|
+
async def test_as_tools_include_and_exclude_combined(self) -> None:
|
|
564
|
+
"""as_tools applies both include and exclude filters."""
|
|
565
|
+
from hud.environment import Environment
|
|
566
|
+
|
|
567
|
+
env = Environment("test")
|
|
568
|
+
|
|
569
|
+
@env.tool()
|
|
570
|
+
def browser_navigate() -> str:
|
|
571
|
+
"""Browser tool."""
|
|
572
|
+
return "nav"
|
|
573
|
+
|
|
574
|
+
@env.tool()
|
|
575
|
+
def browser_setup() -> str:
|
|
576
|
+
"""Browser setup - should be excluded."""
|
|
577
|
+
return "setup"
|
|
578
|
+
|
|
579
|
+
@env.tool()
|
|
580
|
+
def file_read() -> str:
|
|
581
|
+
"""File tool - not included."""
|
|
582
|
+
return "read"
|
|
583
|
+
|
|
584
|
+
env._agent_include = ["browser_*"]
|
|
585
|
+
env._agent_exclude = ["*setup*"]
|
|
586
|
+
await env._build_routing()
|
|
587
|
+
|
|
588
|
+
tools = env.as_tools()
|
|
589
|
+
tool_names = [t.name for t in tools]
|
|
590
|
+
|
|
591
|
+
assert "browser_navigate" in tool_names
|
|
592
|
+
assert "browser_setup" not in tool_names # Excluded by *setup*
|
|
593
|
+
assert "file_read" not in tool_names # Not included by browser_*
|