hud-python 0.5.3__tar.gz → 0.5.5__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.3 → hud_python-0.5.5}/PKG-INFO +1 -1
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/agents/claude.py +12 -4
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/agents/gemini.py +12 -4
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/agents/openai.py +12 -4
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/agents/tests/test_openai.py +2 -1
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/environment/environment.py +17 -5
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/environment/scenarios.py +68 -54
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/utils/hud_console.py +30 -17
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/utils/tests/test_version.py +1 -1
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/version.py +1 -1
- {hud_python-0.5.3 → hud_python-0.5.5}/pyproject.toml +1 -1
- {hud_python-0.5.3 → hud_python-0.5.5}/.gitignore +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/LICENSE +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/README.md +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/examples/README.md +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/__init__.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/__main__.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/agents/__init__.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/agents/base.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/agents/gateway.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/agents/gemini_cua.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/agents/grounded_openai.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/agents/misc/__init__.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/agents/misc/integration_test_agent.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/agents/misc/response_agent.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/agents/openai_chat.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/agents/operator.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/agents/resolver.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/agents/tests/__init__.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/agents/tests/conftest.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/agents/tests/test_base.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/agents/tests/test_base_runtime.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/agents/tests/test_claude.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/agents/tests/test_client.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/agents/tests/test_gemini.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/agents/tests/test_grounded_openai_agent.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/agents/tests/test_operator.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/agents/tests/test_resolver.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/agents/tests/test_run_eval.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/__init__.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/__main__.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/analyze.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/build.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/clone.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/debug.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/dev.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/eval.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/flows/__init__.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/flows/dev.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/flows/init.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/flows/tasks.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/flows/templates.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/flows/tests/__init__.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/flows/tests/test_dev.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/get.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/init.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/list_func.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/pull.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/push.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/remove.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/rft.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/rft_status.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/tests/__init__.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/tests/test_analyze.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/tests/test_analyze_metadata.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/tests/test_analyze_module.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/tests/test_build.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/tests/test_build_failure.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/tests/test_build_module.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/tests/test_cli_init.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/tests/test_cli_main.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/tests/test_cli_more_wrappers.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/tests/test_cli_root.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/tests/test_clone.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/tests/test_convert.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/tests/test_cursor.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/tests/test_debug.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/tests/test_dev.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/tests/test_eval.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/tests/test_eval_bedrock.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/tests/test_init.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/tests/test_list_func.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/tests/test_main_module.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/tests/test_mcp_server.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/tests/test_pull.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/tests/test_push.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/tests/test_push_happy.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/tests/test_push_wrapper.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/tests/test_registry.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/tests/test_utils.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/utils/__init__.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/utils/celebrate.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/utils/config.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/utils/cursor.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/utils/docker.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/utils/env_check.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/utils/environment.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/utils/git.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/utils/interactive.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/utils/local_runner.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/utils/logging.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/utils/metadata.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/utils/package_runner.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/utils/registry.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/utils/remote_runner.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/utils/runner.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/utils/server.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/utils/source_hash.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/utils/tasks.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/utils/tests/__init__.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/utils/tests/test_config.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/utils/tests/test_docker.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/utils/tests/test_docker_hints.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/utils/tests/test_env_check.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/utils/tests/test_environment.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/utils/tests/test_git.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/utils/tests/test_interactive_module.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/utils/tests/test_local_runner.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/utils/tests/test_logging_utils.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/utils/tests/test_metadata.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/utils/tests/test_package_runner.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/utils/tests/test_registry_utils.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/utils/tests/test_remote_runner.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/utils/tests/test_runner_modules.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/utils/tests/test_source_hash.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/utils/tests/test_tasks.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/utils/version_check.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/utils/viewer.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/clients/README.md +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/clients/__init__.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/clients/base.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/clients/environment.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/clients/fastmcp.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/clients/mcp_use.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/clients/tests/__init__.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/clients/tests/test_analyze_scenarios.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/clients/tests/test_client_integration.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/clients/tests/test_fastmcp.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/clients/tests/test_mcp_use_retry.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/clients/tests/test_protocol.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/clients/utils/__init__.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/clients/utils/mcp_use_retry.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/clients/utils/retry.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/clients/utils/retry_transport.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/datasets/__init__.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/datasets/loader.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/datasets/runner.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/datasets/tests/__init__.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/datasets/tests/test_loader.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/datasets/tests/test_utils.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/datasets/utils.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/environment/__init__.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/environment/connection.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/environment/connectors/__init__.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/environment/connectors/base.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/environment/connectors/local.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/environment/connectors/mcp_config.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/environment/connectors/openai.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/environment/connectors/remote.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/environment/integrations/__init__.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/environment/integrations/adk.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/environment/integrations/anthropic.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/environment/integrations/gemini.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/environment/integrations/langchain.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/environment/integrations/llamaindex.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/environment/integrations/openai.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/environment/mock.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/environment/router.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/environment/tests/__init__.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/environment/tests/test_connection.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/environment/tests/test_connectors.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/environment/tests/test_environment.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/environment/tests/test_integrations.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/environment/tests/test_local_connectors.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/environment/tests/test_scenarios.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/environment/tests/test_tools.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/environment/types.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/environment/utils/__init__.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/environment/utils/formats.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/environment/utils/schema.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/environment/utils/tool_wrappers.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/eval/__init__.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/eval/context.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/eval/display.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/eval/instrument.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/eval/manager.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/eval/parallel.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/eval/task.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/eval/tests/__init__.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/eval/tests/test_context.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/eval/tests/test_eval.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/eval/tests/test_manager.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/eval/tests/test_parallel.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/eval/tests/test_task.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/eval/types.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/eval/utils.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/native/__init__.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/native/comparator.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/native/tests/__init__.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/native/tests/test_comparator.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/native/tests/test_native_init.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/patches/__init__.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/patches/mcp_patches.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/patches/warnings.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/py.typed +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/samples/__init__.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/samples/browser.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/server/__init__.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/server/context.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/server/helper/__init__.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/server/low_level.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/server/router.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/server/server.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/server/tests/__init__.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/server/tests/test_add_tool.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/server/tests/test_context.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/server/tests/test_mcp_server_handlers.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/server/tests/test_mcp_server_integration.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/server/tests/test_mcp_server_more.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/server/tests/test_run_wrapper.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/server/tests/test_server_extra.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/server/tests/test_sigterm_runner.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/settings.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/shared/__init__.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/shared/exceptions.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/shared/hints.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/shared/requests.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/shared/tests/__init__.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/shared/tests/test_exceptions.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/shared/tests/test_hints.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/shared/tests/test_requests.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/telemetry/__init__.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/telemetry/exporter.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/telemetry/instrument.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/telemetry/tests/__init__.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/telemetry/tests/test_eval_telemetry.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/telemetry/tests/test_exporter.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/telemetry/tests/test_instrument.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/tools/__init__.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/tools/agent.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/tools/apply_patch.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/tools/base.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/tools/bash.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/tools/computer/__init__.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/tools/computer/anthropic.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/tools/computer/gemini.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/tools/computer/hud.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/tools/computer/openai.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/tools/computer/qwen.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/tools/computer/settings.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/tools/edit.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/tools/executors/__init__.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/tools/executors/base.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/tools/executors/pyautogui.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/tools/executors/tests/__init__.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/tools/executors/tests/test_base_executor.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/tools/executors/tests/test_pyautogui_executor.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/tools/executors/xdo.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/tools/grounding/__init__.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/tools/grounding/config.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/tools/grounding/grounded_tool.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/tools/grounding/grounder.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/tools/grounding/tests/__init__.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/tools/grounding/tests/test_grounded_tool.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/tools/jupyter.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/tools/playwright.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/tools/response.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/tools/shell.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/tools/submit.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/tools/tests/__init__.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/tools/tests/test_agent_tool.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/tools/tests/test_apply_patch.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/tools/tests/test_base.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/tools/tests/test_bash.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/tools/tests/test_bash_extended.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/tools/tests/test_computer.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/tools/tests/test_computer_actions.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/tools/tests/test_edit.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/tools/tests/test_init.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/tools/tests/test_jupyter_tool.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/tools/tests/test_playwright_tool.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/tools/tests/test_response.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/tools/tests/test_shell.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/tools/tests/test_submit.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/tools/tests/test_tools.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/tools/tests/test_tools_init.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/tools/tests/test_types.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/tools/tests/test_utils.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/tools/types.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/tools/utils.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/types.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/utils/__init__.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/utils/env.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/utils/mcp.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/utils/pretty_errors.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/utils/strict_schema.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/utils/telemetry.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/utils/tests/__init__.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/utils/tests/test_init.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/utils/tests/test_mcp.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/utils/tests/test_pretty_errors.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/utils/tests/test_telemetry.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/utils/tests/test_tool_shorthand.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/utils/tool_shorthand.py +0 -0
- {hud_python-0.5.3 → hud_python-0.5.5}/hud/utils/types.py +0 -0
|
@@ -76,10 +76,18 @@ class ClaudeAgent(MCPAgent):
|
|
|
76
76
|
|
|
77
77
|
model_client = self.config.model_client
|
|
78
78
|
if model_client is None:
|
|
79
|
-
|
|
80
|
-
if
|
|
81
|
-
|
|
82
|
-
|
|
79
|
+
# Default to HUD gateway when HUD_API_KEY is available
|
|
80
|
+
if settings.api_key:
|
|
81
|
+
from hud.agents.gateway import build_gateway_client
|
|
82
|
+
|
|
83
|
+
model_client = build_gateway_client("anthropic")
|
|
84
|
+
elif settings.anthropic_api_key:
|
|
85
|
+
model_client = AsyncAnthropic(api_key=settings.anthropic_api_key)
|
|
86
|
+
else:
|
|
87
|
+
raise ValueError(
|
|
88
|
+
"No API key found. Set HUD_API_KEY for HUD gateway, "
|
|
89
|
+
"or ANTHROPIC_API_KEY for direct Anthropic access."
|
|
90
|
+
)
|
|
83
91
|
|
|
84
92
|
self.anthropic_client = model_client
|
|
85
93
|
self.max_tokens = self.config.max_tokens
|
|
@@ -61,10 +61,18 @@ class GeminiAgent(MCPAgent):
|
|
|
61
61
|
|
|
62
62
|
model_client = self.config.model_client
|
|
63
63
|
if model_client is None:
|
|
64
|
-
|
|
65
|
-
if
|
|
66
|
-
|
|
67
|
-
|
|
64
|
+
# Default to HUD gateway when HUD_API_KEY is available
|
|
65
|
+
if settings.api_key:
|
|
66
|
+
from hud.agents.gateway import build_gateway_client
|
|
67
|
+
|
|
68
|
+
model_client = build_gateway_client("gemini")
|
|
69
|
+
elif settings.gemini_api_key:
|
|
70
|
+
model_client = genai.Client(api_key=settings.gemini_api_key)
|
|
71
|
+
else:
|
|
72
|
+
raise ValueError(
|
|
73
|
+
"No API key found. Set HUD_API_KEY for HUD gateway, "
|
|
74
|
+
"or GEMINI_API_KEY for direct Gemini access."
|
|
75
|
+
)
|
|
68
76
|
|
|
69
77
|
if self.config.validate_api_key:
|
|
70
78
|
try:
|
|
@@ -79,10 +79,18 @@ class OpenAIAgent(MCPAgent):
|
|
|
79
79
|
|
|
80
80
|
model_client = self.config.model_client
|
|
81
81
|
if model_client is None:
|
|
82
|
-
|
|
83
|
-
if
|
|
84
|
-
|
|
85
|
-
|
|
82
|
+
# Default to HUD gateway when HUD_API_KEY is available
|
|
83
|
+
if settings.api_key:
|
|
84
|
+
from hud.agents.gateway import build_gateway_client
|
|
85
|
+
|
|
86
|
+
model_client = build_gateway_client("openai")
|
|
87
|
+
elif settings.openai_api_key:
|
|
88
|
+
model_client = AsyncOpenAI(api_key=settings.openai_api_key)
|
|
89
|
+
else:
|
|
90
|
+
raise ValueError(
|
|
91
|
+
"No API key found. Set HUD_API_KEY for HUD gateway, "
|
|
92
|
+
"or OPENAI_API_KEY for direct OpenAI access."
|
|
93
|
+
)
|
|
86
94
|
|
|
87
95
|
if self.config.validate_api_key:
|
|
88
96
|
try:
|
|
@@ -128,8 +128,9 @@ class TestOpenAIAgent:
|
|
|
128
128
|
async def test_init_without_client_no_api_key(self) -> None:
|
|
129
129
|
"""Test agent initialization fails without API key."""
|
|
130
130
|
with patch("hud.agents.openai.settings") as mock_settings:
|
|
131
|
+
mock_settings.api_key = None
|
|
131
132
|
mock_settings.openai_api_key = None
|
|
132
|
-
with pytest.raises(ValueError, match="
|
|
133
|
+
with pytest.raises(ValueError, match="No API key found"):
|
|
133
134
|
OpenAIAgent.create()
|
|
134
135
|
|
|
135
136
|
@pytest.mark.asyncio
|
|
@@ -129,6 +129,7 @@ class Environment(
|
|
|
129
129
|
super().__init__(name=name, instructions=instructions, **fastmcp_kwargs)
|
|
130
130
|
self._connections: dict[str, Connector] = {}
|
|
131
131
|
self._router = ToolRouter(conflict_resolution=conflict_resolution)
|
|
132
|
+
self._routing_built = False # Track if _build_routing has been called
|
|
132
133
|
self._in_context = False
|
|
133
134
|
|
|
134
135
|
# Tool call queues - run after connections established
|
|
@@ -224,6 +225,9 @@ class Environment(
|
|
|
224
225
|
Automatically filters to only connections where the tool exists
|
|
225
226
|
(based on cached_tools from initial discovery).
|
|
226
227
|
|
|
228
|
+
For internal tools (starting with _), tries ALL connections since
|
|
229
|
+
internal tools are hidden from list_tools() and won't be in cached_tools.
|
|
230
|
+
|
|
227
231
|
Args:
|
|
228
232
|
tool_name: Name of the tool to call
|
|
229
233
|
**kwargs: Arguments to pass to the tool
|
|
@@ -233,10 +237,13 @@ class Environment(
|
|
|
233
237
|
"""
|
|
234
238
|
import asyncio
|
|
235
239
|
|
|
236
|
-
#
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
+
# For internal tools (underscore prefix), try ALL connections since
|
|
241
|
+
# they're hidden from list_tools() and won't appear in cached_tools.
|
|
242
|
+
# For regular tools, only try connections that advertise the tool.
|
|
243
|
+
if tool_name.startswith("_"):
|
|
244
|
+
targets = set(self._connections.keys())
|
|
245
|
+
else:
|
|
246
|
+
targets = self._connections_with_tool(tool_name)
|
|
240
247
|
|
|
241
248
|
results: dict[str, Any] = {}
|
|
242
249
|
|
|
@@ -245,7 +252,8 @@ class Environment(
|
|
|
245
252
|
if not connector or not connector.client:
|
|
246
253
|
return
|
|
247
254
|
try:
|
|
248
|
-
|
|
255
|
+
# Use connector.call_tool which expects arguments as a dict
|
|
256
|
+
results[name] = await connector.call_tool(tool_name, kwargs)
|
|
249
257
|
logger.debug("Broadcast '%s' to '%s' succeeded", tool_name, name)
|
|
250
258
|
except Exception as e:
|
|
251
259
|
results[name] = e
|
|
@@ -361,6 +369,7 @@ class Environment(
|
|
|
361
369
|
if self._connections:
|
|
362
370
|
await asyncio.gather(*[c.disconnect() for c in self._connections.values()])
|
|
363
371
|
self._router.clear()
|
|
372
|
+
self._routing_built = False
|
|
364
373
|
|
|
365
374
|
async def run_async(
|
|
366
375
|
self,
|
|
@@ -389,6 +398,7 @@ class Environment(
|
|
|
389
398
|
connections=self._connections,
|
|
390
399
|
connection_order=list(self._connections.keys()),
|
|
391
400
|
)
|
|
401
|
+
self._routing_built = True
|
|
392
402
|
# Populate mock schemas for auto-generated mock values
|
|
393
403
|
self._populate_mock_schemas()
|
|
394
404
|
|
|
@@ -406,6 +416,8 @@ class Environment(
|
|
|
406
416
|
|
|
407
417
|
async def _env_list_tools(self) -> list[mcp_types.Tool]:
|
|
408
418
|
"""Return all tools including those from connectors."""
|
|
419
|
+
if not self._routing_built:
|
|
420
|
+
await self._build_routing()
|
|
409
421
|
return self._router.tools
|
|
410
422
|
|
|
411
423
|
async def _env_call_tool(self, name: str, arguments: dict[str, Any] | None = None) -> list[Any]:
|
|
@@ -70,35 +70,17 @@ class ScenarioMixin:
|
|
|
70
70
|
async def submit(self, scenario: str, answer: str) -> None:
|
|
71
71
|
"""Submit the agent's answer for a scenario's evaluate phase.
|
|
72
72
|
|
|
73
|
-
|
|
74
|
-
that have the _hud_submit tool (auto-detected by Environment).
|
|
73
|
+
Stores locally and broadcasts to connected hubs with _hud_submit tool.
|
|
75
74
|
|
|
76
75
|
Args:
|
|
77
76
|
scenario: Name of the scenario (without env prefix)
|
|
78
77
|
answer: The agent's answer/result to submit
|
|
79
|
-
|
|
80
|
-
Example:
|
|
81
|
-
# Direct call with scenario name
|
|
82
|
-
await env.submit("checkout", "Order completed successfully")
|
|
83
|
-
|
|
84
|
-
# Or via EvalContext (knows its own scenario)
|
|
85
|
-
await ctx.submit("Order completed successfully")
|
|
86
78
|
"""
|
|
87
|
-
# Store locally for our scenarios
|
|
88
79
|
self._scenario_answers[scenario] = answer
|
|
89
|
-
logger.debug(
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
)
|
|
94
|
-
|
|
95
|
-
# Broadcast to connections that have _hud_submit
|
|
96
|
-
# Environment._broadcast_tool auto-filters to connections with the tool
|
|
97
|
-
await self._broadcast_tool( # type: ignore[attr-defined]
|
|
98
|
-
"_hud_submit",
|
|
99
|
-
scenario=scenario,
|
|
100
|
-
answer=answer,
|
|
101
|
-
)
|
|
80
|
+
logger.debug("Stored answer for scenario '%s'", scenario)
|
|
81
|
+
|
|
82
|
+
# Broadcast to all connections (internal tools try all connections)
|
|
83
|
+
await self._broadcast_tool("_hud_submit", scenario=scenario, answer=answer) # type: ignore[attr-defined]
|
|
102
84
|
|
|
103
85
|
def _register_hud_submit_tool(self) -> None:
|
|
104
86
|
"""Register the _hud_submit tool for receiving agent answers.
|
|
@@ -178,13 +160,9 @@ class ScenarioMixin:
|
|
|
178
160
|
prompt_id = f"{safe_env_name}:{scenario_name}"
|
|
179
161
|
logger.debug("Remote scenario (adding namespace): prompt_id=%s", prompt_id)
|
|
180
162
|
# Serialize args for MCP prompt (only supports string values)
|
|
181
|
-
# JSON-encode any non-string values so they can be deserialized on the other side
|
|
182
163
|
serialized_args: dict[str, str] = {}
|
|
183
164
|
for key, value in args.items():
|
|
184
|
-
if isinstance(value, str)
|
|
185
|
-
serialized_args[key] = value
|
|
186
|
-
else:
|
|
187
|
-
serialized_args[key] = json.dumps(value)
|
|
165
|
+
serialized_args[key] = value if isinstance(value, str) else json.dumps(value)
|
|
188
166
|
|
|
189
167
|
try:
|
|
190
168
|
result = await self.get_prompt(prompt_id, serialized_args) # type: ignore[attr-defined]
|
|
@@ -193,14 +171,26 @@ class ScenarioMixin:
|
|
|
193
171
|
try:
|
|
194
172
|
prompts = await self.list_prompts() # type: ignore[attr-defined]
|
|
195
173
|
scenario_prompts = [p.name for p in prompts if ":" in p.name]
|
|
196
|
-
available = (
|
|
197
|
-
"\n ".join(scenario_prompts) if scenario_prompts else "(none found)"
|
|
198
|
-
)
|
|
174
|
+
available = "\n ".join(scenario_prompts) if scenario_prompts else "(none)"
|
|
199
175
|
except Exception:
|
|
200
|
-
available = "(could not fetch
|
|
176
|
+
available = "(could not fetch)"
|
|
177
|
+
scenario_prompts = []
|
|
178
|
+
|
|
179
|
+
original_error = str(e)
|
|
180
|
+
if prompt_id in scenario_prompts:
|
|
181
|
+
raise ValueError(
|
|
182
|
+
f"⚠️ ERROR: Scenario '{prompt_id}' exists but failed to execute.\n\n"
|
|
183
|
+
f"The scenario was found but encountered an error during setup:\n"
|
|
184
|
+
f" {original_error}\n\n"
|
|
185
|
+
f"This could be caused by:\n"
|
|
186
|
+
f" - Missing or invalid scenario arguments\n"
|
|
187
|
+
f" - An error in the scenario's setup function\n"
|
|
188
|
+
f" - Connection or serialization issues\n\n"
|
|
189
|
+
f"Check the scenario definition and required arguments."
|
|
190
|
+
) from e
|
|
201
191
|
|
|
202
192
|
raise ValueError(
|
|
203
|
-
f"Scenario not found.\n\n"
|
|
193
|
+
f"⚠️ ERROR: Scenario not found.\n\n"
|
|
204
194
|
f"Scenario IDs have the format 'environment_name:scenario_name'.\n"
|
|
205
195
|
f"If you only specify 'scenario_name', the SDK uses your task's env name "
|
|
206
196
|
f"as the prefix.\n"
|
|
@@ -212,7 +202,7 @@ class ScenarioMixin:
|
|
|
212
202
|
f"Fix: Use one of the scenario IDs above in your task JSON."
|
|
213
203
|
) from e
|
|
214
204
|
|
|
215
|
-
# Validate the response
|
|
205
|
+
# Validate the response
|
|
216
206
|
if result.messages:
|
|
217
207
|
first_msg = result.messages[0]
|
|
218
208
|
content = first_msg.content
|
|
@@ -275,23 +265,24 @@ class ScenarioMixin:
|
|
|
275
265
|
del self._scenario_latest[scenario_name]
|
|
276
266
|
|
|
277
267
|
# Remote scenario - read via MCP resource
|
|
278
|
-
# If scenario_name already contains ":", it's already namespaced - use directly
|
|
279
268
|
if ":" in scenario_name:
|
|
280
269
|
resource_id = scenario_name
|
|
281
270
|
else:
|
|
282
271
|
env_name = getattr(self, "_source_env_name", None) or self.name
|
|
283
272
|
safe_env_name = env_name.replace("_", "-")
|
|
284
273
|
resource_id = f"{safe_env_name}:{scenario_name}"
|
|
274
|
+
|
|
285
275
|
try:
|
|
286
276
|
contents = await self.read_resource(resource_id) # type: ignore[attr-defined]
|
|
287
277
|
if contents:
|
|
288
|
-
|
|
289
|
-
if hasattr(
|
|
290
|
-
data = json.loads(
|
|
278
|
+
first = contents[0]
|
|
279
|
+
if hasattr(first, "text") and isinstance(first.text, str): # type: ignore[union-attr]
|
|
280
|
+
data = json.loads(first.text) # type: ignore[union-attr]
|
|
291
281
|
if "reward" in data:
|
|
292
282
|
return float(data["reward"])
|
|
293
283
|
except Exception as e:
|
|
294
284
|
logger.warning("Failed to get scenario reward: %s", e)
|
|
285
|
+
|
|
295
286
|
return None
|
|
296
287
|
|
|
297
288
|
def scenario(
|
|
@@ -362,7 +353,7 @@ class ScenarioMixin:
|
|
|
362
353
|
# Only include JSON-serializable defaults
|
|
363
354
|
default_val = p.default
|
|
364
355
|
if default_val is None or isinstance(
|
|
365
|
-
default_val, (str
|
|
356
|
+
default_val, (str | int | float | bool | list | dict)
|
|
366
357
|
):
|
|
367
358
|
arg_info["default"] = default_val
|
|
368
359
|
|
|
@@ -412,27 +403,50 @@ class ScenarioMixin:
|
|
|
412
403
|
from pydantic import TypeAdapter
|
|
413
404
|
|
|
414
405
|
# Deserialize JSON-encoded arguments using Pydantic TypeAdapter
|
|
415
|
-
#
|
|
406
|
+
# MCP prompts only support string arguments, so complex types are
|
|
407
|
+
# JSON-serialized on the sending side and deserialized here
|
|
416
408
|
deserialized_args: dict[str, Any] = {}
|
|
417
409
|
for arg_name, arg_value in handler_args.items():
|
|
418
410
|
annotation = param_annotations.get(arg_name)
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
411
|
+
|
|
412
|
+
# Only attempt deserialization on string values
|
|
413
|
+
if not isinstance(arg_value, str):
|
|
414
|
+
deserialized_args[arg_name] = arg_value
|
|
415
|
+
continue
|
|
416
|
+
|
|
417
|
+
# If annotation is explicitly str, keep as string
|
|
418
|
+
if annotation is str:
|
|
419
|
+
deserialized_args[arg_name] = arg_value
|
|
420
|
+
continue
|
|
421
|
+
|
|
422
|
+
# If we have a non-str type annotation, use TypeAdapter
|
|
423
|
+
if annotation is not None:
|
|
425
424
|
try:
|
|
426
425
|
adapter = TypeAdapter(annotation)
|
|
427
426
|
deserialized_args[arg_name] = adapter.validate_json(arg_value)
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
427
|
+
continue
|
|
428
|
+
except Exception: # noqa: S110
|
|
429
|
+
pass # Fall through to generic JSON decode
|
|
430
|
+
|
|
431
|
+
# Try JSON decode for strings that look like JSON
|
|
432
|
+
stripped = arg_value.strip()
|
|
433
|
+
if (stripped and stripped[0] in "[{") or stripped in ("true", "false", "null"):
|
|
434
|
+
try:
|
|
435
|
+
deserialized_args[arg_name] = json.loads(arg_value)
|
|
436
|
+
continue
|
|
437
|
+
except json.JSONDecodeError:
|
|
438
|
+
pass
|
|
439
|
+
|
|
440
|
+
# Try to decode if it looks like a number
|
|
441
|
+
if stripped.lstrip("-").replace(".", "", 1).isdigit():
|
|
442
|
+
try:
|
|
443
|
+
deserialized_args[arg_name] = json.loads(arg_value)
|
|
444
|
+
continue
|
|
445
|
+
except json.JSONDecodeError:
|
|
446
|
+
pass
|
|
447
|
+
|
|
448
|
+
# Keep as string
|
|
449
|
+
deserialized_args[arg_name] = arg_value
|
|
436
450
|
|
|
437
451
|
# Create generator instance with deserialized args
|
|
438
452
|
gen = scenario_fn(**deserialized_args)
|
|
@@ -21,6 +21,7 @@ import traceback
|
|
|
21
21
|
from typing import TYPE_CHECKING, Any, Literal, Self
|
|
22
22
|
|
|
23
23
|
from rich.console import Console
|
|
24
|
+
from rich.markup import escape
|
|
24
25
|
from rich.panel import Panel
|
|
25
26
|
from rich.table import Table
|
|
26
27
|
|
|
@@ -95,7 +96,7 @@ class HUDConsole:
|
|
|
95
96
|
stderr: If True, output to stderr (default), otherwise stdout
|
|
96
97
|
"""
|
|
97
98
|
console = self._stderr_console if stderr else self._stdout_console
|
|
98
|
-
console.print(f"[{GREEN}]✅ {message}[/{GREEN}]")
|
|
99
|
+
console.print(f"[{GREEN}]✅ {escape(message)}[/{GREEN}]")
|
|
99
100
|
|
|
100
101
|
def error(self, message: str, stderr: bool = True) -> None:
|
|
101
102
|
"""Print an error message.
|
|
@@ -106,10 +107,12 @@ class HUDConsole:
|
|
|
106
107
|
"""
|
|
107
108
|
console = self._stderr_console if stderr else self._stdout_console
|
|
108
109
|
tb = traceback.format_exc()
|
|
110
|
+
escaped_message = escape(message)
|
|
109
111
|
if "NoneType: None" not in tb:
|
|
110
|
-
|
|
112
|
+
escaped_tb = escape(tb)
|
|
113
|
+
console.print(f"[{RED} not bold]❌ {escaped_message}\n{escaped_tb}[/{RED} not bold]")
|
|
111
114
|
else:
|
|
112
|
-
console.print(f"[{RED} not bold]❌ {
|
|
115
|
+
console.print(f"[{RED} not bold]❌ {escaped_message}[/{RED} not bold]")
|
|
113
116
|
|
|
114
117
|
def warning(self, message: str, stderr: bool = True) -> None:
|
|
115
118
|
"""Print a warning message.
|
|
@@ -119,7 +122,7 @@ class HUDConsole:
|
|
|
119
122
|
stderr: If True, output to stderr (default), otherwise stdout
|
|
120
123
|
"""
|
|
121
124
|
console = self._stderr_console if stderr else self._stdout_console
|
|
122
|
-
console.print(f"⚠️ [{YELLOW} not bold]{message}[/{YELLOW} not bold]")
|
|
125
|
+
console.print(f"⚠️ [{YELLOW} not bold]{escape(message)}[/{YELLOW} not bold]")
|
|
123
126
|
|
|
124
127
|
def info(self, message: str, stderr: bool = True) -> None:
|
|
125
128
|
"""Print an info message.
|
|
@@ -129,7 +132,7 @@ class HUDConsole:
|
|
|
129
132
|
stderr: If True, output to stderr (default), otherwise stdout
|
|
130
133
|
"""
|
|
131
134
|
console = self._stderr_console if stderr else self._stdout_console
|
|
132
|
-
console.print(f"[{TEXT} not bold]{message}[/{TEXT} not bold]")
|
|
135
|
+
console.print(f"[{TEXT} not bold]{escape(message)}[/{TEXT} not bold]")
|
|
133
136
|
|
|
134
137
|
def print(self, message: str, stderr: bool = True) -> None:
|
|
135
138
|
"""Print a message.
|
|
@@ -151,7 +154,7 @@ class HUDConsole:
|
|
|
151
154
|
"""
|
|
152
155
|
console = self._stderr_console if stderr else self._stdout_console
|
|
153
156
|
console.print(
|
|
154
|
-
f"[{DIM} not bold][default]{label}[/default][/{DIM} not bold] [default]{value}[/default]" # noqa: E501
|
|
157
|
+
f"[{DIM} not bold][default]{escape(label)}[/default][/{DIM} not bold] [default]{escape(value)}[/default]" # noqa: E501
|
|
155
158
|
)
|
|
156
159
|
|
|
157
160
|
def link(self, url: str, stderr: bool = True) -> None:
|
|
@@ -162,7 +165,7 @@ class HUDConsole:
|
|
|
162
165
|
stderr: If True, output to stderr (default), otherwise stdout
|
|
163
166
|
"""
|
|
164
167
|
console = self._stderr_console if stderr else self._stdout_console
|
|
165
|
-
console.print(f"[{SECONDARY} underline]{url}[/{SECONDARY} underline]")
|
|
168
|
+
console.print(f"[{SECONDARY} underline]{escape(url)}[/{SECONDARY} underline]")
|
|
166
169
|
|
|
167
170
|
def json_config(self, json_str: str, stderr: bool = True) -> None:
|
|
168
171
|
"""Print JSON configuration with neutral theme.
|
|
@@ -173,7 +176,7 @@ class HUDConsole:
|
|
|
173
176
|
"""
|
|
174
177
|
# Print JSON with neutral grey text
|
|
175
178
|
console = self._stderr_console if stderr else self._stdout_console
|
|
176
|
-
console.print(f"[{TEXT}]{json_str}[/{TEXT}]")
|
|
179
|
+
console.print(f"[{TEXT}]{escape(json_str)}[/{TEXT}]")
|
|
177
180
|
|
|
178
181
|
def key_value_table(
|
|
179
182
|
self, data: dict[str, str | int | float], show_header: bool = False, stderr: bool = True
|
|
@@ -203,7 +206,7 @@ class HUDConsole:
|
|
|
203
206
|
stderr: If True, output to stderr (default), otherwise stdout
|
|
204
207
|
"""
|
|
205
208
|
console = self._stderr_console if stderr else self._stdout_console
|
|
206
|
-
console.print(f"[{DIM}]{message}[/{DIM}]")
|
|
209
|
+
console.print(f"[{DIM}]{escape(message)}[/{DIM}]")
|
|
207
210
|
|
|
208
211
|
def phase(self, phase_num: int, title: str, stderr: bool = True) -> None:
|
|
209
212
|
"""Print a phase header (for debug command).
|
|
@@ -236,7 +239,7 @@ class HUDConsole:
|
|
|
236
239
|
stderr: If True, output to stderr (default), otherwise stdout
|
|
237
240
|
"""
|
|
238
241
|
console = self._stderr_console if stderr else self._stdout_console
|
|
239
|
-
console.print(f"[rgb(181,137,0)]💡 Hint: {hint}[/rgb(181,137,0)]")
|
|
242
|
+
console.print(f"[rgb(181,137,0)]💡 Hint: {escape(hint)}[/rgb(181,137,0)]")
|
|
240
243
|
|
|
241
244
|
def status_item(
|
|
242
245
|
self,
|
|
@@ -265,10 +268,14 @@ class HUDConsole:
|
|
|
265
268
|
indicator = indicators.get(status, indicators["info"])
|
|
266
269
|
console = self._stderr_console if stderr else self._stdout_console
|
|
267
270
|
|
|
271
|
+
escaped_label = escape(label)
|
|
272
|
+
escaped_value = escape(value)
|
|
268
273
|
if primary:
|
|
269
|
-
console.print(
|
|
274
|
+
console.print(
|
|
275
|
+
f"{indicator} {escaped_label}: [bold {SECONDARY}]{escaped_value}[/bold {SECONDARY}]"
|
|
276
|
+
)
|
|
270
277
|
else:
|
|
271
|
-
console.print(f"{indicator} {
|
|
278
|
+
console.print(f"{indicator} {escaped_label}: [{TEXT}]{escaped_value}[/{TEXT}]")
|
|
272
279
|
|
|
273
280
|
def command_example(
|
|
274
281
|
self, command: str, description: str | None = None, stderr: bool = True
|
|
@@ -546,7 +553,12 @@ class HUDConsole:
|
|
|
546
553
|
except (TypeError, ValueError):
|
|
547
554
|
args_str = str(arguments)[:60]
|
|
548
555
|
|
|
549
|
-
|
|
556
|
+
escaped_name = escape(name)
|
|
557
|
+
escaped_args = escape(args_str)
|
|
558
|
+
return (
|
|
559
|
+
f"[{GOLD}]→[/{GOLD}] [bold {TEXT}]{escaped_name}[/bold {TEXT}]"
|
|
560
|
+
f"[{DIM}]({escaped_args})[/{DIM}]"
|
|
561
|
+
)
|
|
550
562
|
|
|
551
563
|
def format_tool_result(self, content: str, is_error: bool = False) -> str:
|
|
552
564
|
"""Format a tool result in compact HUD style.
|
|
@@ -562,11 +574,12 @@ class HUDConsole:
|
|
|
562
574
|
if len(content) > 80:
|
|
563
575
|
content = content[:77] + "..."
|
|
564
576
|
|
|
577
|
+
escaped_content = escape(content)
|
|
565
578
|
# Format with status using HUD colors
|
|
566
579
|
if is_error:
|
|
567
|
-
return f" [{RED}]✗[/{RED}] [{DIM}]{
|
|
580
|
+
return f" [{RED}]✗[/{RED}] [{DIM}]{escaped_content}[/{DIM}]"
|
|
568
581
|
else:
|
|
569
|
-
return f" [{GREEN}]✓[/{GREEN}] [{TEXT}]{
|
|
582
|
+
return f" [{GREEN}]✓[/{GREEN}] [{TEXT}]{escaped_content}[/{TEXT}]"
|
|
570
583
|
|
|
571
584
|
def confirm(self, message: str, default: bool = True) -> bool:
|
|
572
585
|
"""Print a confirmation message.
|
|
@@ -590,12 +603,12 @@ class HUDConsole:
|
|
|
590
603
|
stderr: If True, output to stderr
|
|
591
604
|
"""
|
|
592
605
|
console = self._stderr_console if stderr else self._stdout_console
|
|
593
|
-
console.print(f"[{color}]{symbol}[/{color}] {message}")
|
|
606
|
+
console.print(f"[{color}]{symbol}[/{color}] {escape(message)}")
|
|
594
607
|
|
|
595
608
|
def detail(self, message: str, stderr: bool = True) -> None:
|
|
596
609
|
"""Print an indented detail line with gold pointer symbol."""
|
|
597
610
|
console = self._stderr_console if stderr else self._stdout_console
|
|
598
|
-
console.print(f" [{GOLD}]{Symbols.ITEM}[/{GOLD}] {message}")
|
|
611
|
+
console.print(f" [{GOLD}]{Symbols.ITEM}[/{GOLD}] {escape(message)}")
|
|
599
612
|
|
|
600
613
|
def flow(self, message: str, stderr: bool = True) -> None:
|
|
601
614
|
"""Print a flow/transition message with wave symbol."""
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|