hud 0.6.16.dev2__tar.gz → 0.6.16.dev4__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-0.6.16.dev2 → hud-0.6.16.dev4}/PKG-INFO +2 -1
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/agents/claude/agent.py +114 -9
- hud-0.6.16.dev4/hud/agents/claude/tools/base.py +26 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/agents/claude/tools/coding.py +89 -9
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/agents/claude/tools/computer.py +102 -4
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/agents/claude/tools/tests/test_computer.py +18 -4
- hud-0.6.16.dev4/hud/agents/tests/test_claude_agent.py +303 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/agents/tests/test_provider_native_tools.py +19 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/cli/eval.py +2 -1
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/cli/jobs.py +2 -1
- hud-0.6.16.dev4/hud/cli/qa.py +419 -0
- hud-0.6.16.dev4/hud/cli/qa_analysis.py +207 -0
- hud-0.6.16.dev4/hud/cli/tests/test_jobs.py +38 -0
- hud-0.6.16.dev4/hud/cli/tests/test_qa.py +474 -0
- hud-0.6.16.dev4/hud/cli/tests/test_qa_analysis.py +87 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/cli/trace.py +2 -2
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/environment/egress.py +10 -8
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/environment/namespace.py +3 -1
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/environment/robot/gym.py +1 -1
- hud-0.6.16.dev4/hud/environment/tests/test_platform.py +52 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/environment/tests/test_robot_regressions.py +52 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/environment/tests/test_workspace.py +3 -1
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/eval/job.py +2 -2
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/eval/run.py +8 -7
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/eval/runtime/__init__.py +0 -2
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/eval/runtime/core.py +12 -26
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/eval/runtime/daytona.py +22 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/eval/runtime/docker.py +50 -19
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/eval/runtime/local.py +60 -25
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/eval/runtime/modal.py +52 -21
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/eval/tests/test_docker_provider.py +185 -7
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/eval/tests/test_job.py +19 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/eval/tests/test_local_runtime.py +71 -1
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/eval/tests/test_rollout.py +17 -16
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/telemetry/robot/recorder.py +11 -3
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/utils/platform.py +12 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/utils/process.py +77 -1
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/utils/requests.py +9 -2
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/utils/tests/test_platform.py +18 -2
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/utils/tests/test_process.py +39 -1
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/utils/tests/test_requests.py +16 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/version.py +1 -1
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/pyproject.toml +1 -0
- hud-0.6.16.dev2/hud/agents/claude/tools/base.py +0 -17
- hud-0.6.16.dev2/hud/agents/tests/test_claude_agent.py +0 -144
- hud-0.6.16.dev2/hud/cli/qa.py +0 -193
- hud-0.6.16.dev2/hud/cli/tests/test_qa.py +0 -171
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/.gitignore +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/LICENSE +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/README.md +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/__init__.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/__main__.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/agents/__init__.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/agents/base.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/agents/browser_use/__init__.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/agents/browser_use/agent.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/agents/claude/__init__.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/agents/claude/sdk/__init__.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/agents/claude/sdk/agent.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/agents/claude/sdk/computer_mcp.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/agents/claude/tools/__init__.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/agents/claude/tools/hosted.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/agents/claude/tools/mcp_proxy.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/agents/claude/tools/settings.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/agents/claude/tools/tests/__init__.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/agents/gemini/__init__.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/agents/gemini/agent.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/agents/gemini/settings.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/agents/gemini/tools/__init__.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/agents/gemini/tools/base.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/agents/gemini/tools/coding.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/agents/gemini/tools/computer.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/agents/gemini/tools/filesystem.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/agents/gemini/tools/hosted.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/agents/gemini/tools/mcp_proxy.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/agents/gemini/tools/tests/__init__.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/agents/gemini/tools/tests/test_computer.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/agents/misc/__init__.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/agents/misc/response_automation.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/agents/openai/__init__.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/agents/openai/agent.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/agents/openai/tools/__init__.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/agents/openai/tools/apply_patch.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/agents/openai/tools/base.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/agents/openai/tools/coding.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/agents/openai/tools/computer.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/agents/openai/tools/hosted.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/agents/openai/tools/mcp_proxy.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/agents/openai/tools/strict_schema.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/agents/openai/tools/tests/__init__.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/agents/openai/tools/tests/test_computer.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/agents/openai/tools/tests/test_strict_schema.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/agents/openai_compatible/__init__.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/agents/openai_compatible/agent.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/agents/openai_compatible/tools/__init__.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/agents/openai_compatible/tools/base.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/agents/openai_compatible/tools/filesystem.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/agents/openai_compatible/tools/mcp_proxy.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/agents/robot/__init__.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/agents/robot/adapter.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/agents/robot/agent.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/agents/robot/batching.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/agents/robot/dataset.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/agents/robot/model.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/agents/tests/__init__.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/agents/tests/test_apply_patch.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/agents/tests/test_base.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/agents/tests/test_claude_sdk_agent.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/agents/tests/test_gemini_agent.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/agents/tests/test_openai_agent.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/agents/tests/test_openai_compatible_agent.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/agents/tests/test_robot_dataset_writer.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/agents/tests/test_tool_agent.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/agents/tests/test_trace.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/agents/tool_agent.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/agents/tools/__init__.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/agents/tools/base.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/agents/tools/hosted.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/agents/tools/mcp.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/agents/tools/rfb.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/agents/tools/ssh.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/agents/types.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/capabilities/__init__.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/capabilities/base.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/capabilities/cdp.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/capabilities/mcp.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/capabilities/rfb.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/capabilities/robot.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/capabilities/ssh.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/capabilities/tests/__init__.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/capabilities/tests/test_rfb.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/capabilities/tests/test_ssh.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/cli/__init__.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/cli/__main__.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/cli/cancel.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/cli/client.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/cli/deploy.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/cli/init.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/cli/login.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/cli/models.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/cli/presets.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/cli/serve.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/cli/sync.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/cli/task.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/cli/templates.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/cli/tests/__init__.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/cli/tests/test_cli_init.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/cli/tests/test_cli_main.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/cli/tests/test_cli_more_wrappers.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/cli/tests/test_deploy.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/cli/tests/test_eval_bedrock.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/cli/tests/test_eval_config.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/cli/tests/test_init.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/cli/tests/test_main_module.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/cli/tests/test_sync_export.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/cli/tests/test_trace.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/cli/tests/test_usage.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/cli/utils/__init__.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/cli/utils/api.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/cli/utils/build_display.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/cli/utils/build_logs.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/cli/utils/config.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/cli/utils/context.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/cli/utils/display.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/cli/utils/jobs.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/cli/utils/registry.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/cli/utils/source.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/cli/utils/tasks.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/cli/utils/tests/__init__.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/cli/utils/tests/test_build_display.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/cli/utils/tests/test_config.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/cli/utils/tests/test_context.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/cli/utils/tests/test_registry.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/cli/utils/tests/test_source.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/cli/utils/tests/test_tasks.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/cli/utils/tests/test_version_check.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/cli/utils/usage.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/cli/utils/version_check.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/clients/__init__.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/clients/client.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/clients/tests/__init__.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/clients/tests/test_connect.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/conftest.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/environment/__init__.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/environment/arguments.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/environment/env.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/environment/file_tracker.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/environment/robot/__init__.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/environment/robot/bridge.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/environment/robot/endpoint.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/environment/server.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/environment/tests/__init__.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/environment/tests/conftest.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/environment/tests/test_arguments.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/environment/tests/test_capability_backing.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/environment/tests/test_file_tracker.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/environment/tests/test_file_tracking.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/environment/tests/test_loader.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/environment/tests/test_manifest.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/environment/tests/test_server.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/environment/tests/test_sessions.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/environment/tests/test_tunnel.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/environment/utils.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/environment/workspace.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/eval/__init__.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/eval/chat.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/eval/docker-seccomp.json +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/eval/file_tracking.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/eval/runtime/compose.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/eval/runtime/hosted.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/eval/runtime/hud.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/eval/sync.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/eval/task.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/eval/taskset.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/eval/tests/__init__.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/eval/tests/test_chat.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/eval/tests/test_file_tracking_observer.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/eval/tests/test_hosted.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/eval/tests/test_shared.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/eval/tests/test_sync.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/eval/tests/test_task.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/graders/__init__.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/graders/base.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/graders/bash.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/graders/combine.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/graders/judge.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/graders/results.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/graders/text.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/integrations/__init__.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/integrations/harbor/Dockerfile +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/integrations/harbor/__init__.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/integrations/harbor/adapt.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/integrations/harbor/env.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/integrations/harbor/export.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/integrations/harbor/install.sh +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/integrations/harbor/tests/__init__.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/integrations/harbor/tests/conftest.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/integrations/harbor/tests/tasks/agent-lifecycle/environment/Dockerfile +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/integrations/harbor/tests/tasks/agent-lifecycle/instruction.md +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/integrations/harbor/tests/tasks/agent-lifecycle/solution/solve.sh +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/integrations/harbor/tests/tasks/agent-lifecycle/task.toml +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/integrations/harbor/tests/tasks/agent-lifecycle/tests/test.sh +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/integrations/harbor/tests/tasks/hello-mcp/environment/Dockerfile +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/integrations/harbor/tests/tasks/hello-mcp/environment/docker-compose.yaml +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/integrations/harbor/tests/tasks/hello-mcp/environment/mcp-server/Dockerfile +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/integrations/harbor/tests/tasks/hello-mcp/environment/mcp-server/__init__.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/integrations/harbor/tests/tasks/hello-mcp/environment/mcp-server/server.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/integrations/harbor/tests/tasks/hello-mcp/instruction.md +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/integrations/harbor/tests/tasks/hello-mcp/solution/solve.sh +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/integrations/harbor/tests/tasks/hello-mcp/task.toml +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/integrations/harbor/tests/tasks/hello-mcp/tests/test.sh +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/integrations/harbor/tests/tasks/phase-boundary/environment/Dockerfile +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/integrations/harbor/tests/tasks/phase-boundary/environment/entrypoint.sh +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/integrations/harbor/tests/tasks/phase-boundary/instruction.md +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/integrations/harbor/tests/tasks/phase-boundary/solution/solve.sh +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/integrations/harbor/tests/tasks/phase-boundary/task.toml +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/integrations/harbor/tests/tasks/phase-boundary/tests/test.sh +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/integrations/harbor/tests/tasks/sidecar-reachability/environment/Dockerfile +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/integrations/harbor/tests/tasks/sidecar-reachability/environment/docker-compose.yaml +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/integrations/harbor/tests/tasks/sidecar-reachability/instruction.md +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/integrations/harbor/tests/tasks/sidecar-reachability/solution/solve.sh +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/integrations/harbor/tests/tasks/sidecar-reachability/task.toml +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/integrations/harbor/tests/tasks/sidecar-reachability/tests/Dockerfile +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/integrations/harbor/tests/tasks/sidecar-reachability/tests/test.sh +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/integrations/harbor/tests/tasks/verifier-lifecycle/environment/Dockerfile +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/integrations/harbor/tests/tasks/verifier-lifecycle/environment/entrypoint.sh +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/integrations/harbor/tests/tasks/verifier-lifecycle/instruction.md +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/integrations/harbor/tests/tasks/verifier-lifecycle/solution/solve.sh +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/integrations/harbor/tests/tasks/verifier-lifecycle/task.toml +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/integrations/harbor/tests/tasks/verifier-lifecycle/tests/test.sh +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/integrations/harbor/tests/test_contract.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/integrations/harbor/tests/test_harbor.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/integrations/harbor/tests/test_integration.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/patches/__init__.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/patches/mcp_patches.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/patches/tests/__init__.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/patches/tests/test_warnings.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/patches/warnings.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/py.typed +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/settings.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/telemetry/__init__.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/telemetry/context.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/telemetry/exporter.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/telemetry/instrument.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/telemetry/robot/__init__.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/telemetry/robot/video.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/telemetry/span.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/telemetry/tests/__init__.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/telemetry/tests/test_exporter.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/telemetry/tests/test_instrument.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/train/__init__.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/train/base.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/train/client.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/train/tests/__init__.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/train/tests/test_client.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/train/types.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/types.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/utils/__init__.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/utils/docker.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/utils/exceptions.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/utils/gateway.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/utils/hints.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/utils/hud_console.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/utils/modules.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/utils/naming.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/utils/serialization.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/utils/tests/__init__.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/utils/tests/test_exceptions.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/utils/tests/test_gateway.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/utils/tests/test_hints.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/utils/tests/test_hud_console.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/utils/tests/test_serialization.py +0 -0
- {hud-0.6.16.dev2 → hud-0.6.16.dev4}/hud/utils/time.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: hud
|
|
3
|
-
Version: 0.6.16.
|
|
3
|
+
Version: 0.6.16.dev4
|
|
4
4
|
Summary: SDK for the HUD platform.
|
|
5
5
|
Project-URL: Homepage, https://github.com/hud-evals/hud-python
|
|
6
6
|
Project-URL: Bug Tracker, https://github.com/hud-evals/hud-python/issues
|
|
@@ -39,6 +39,7 @@ Requires-Dist: asyncssh>=2.23.0
|
|
|
39
39
|
Requires-Dist: asyncvnc>=1.3.0
|
|
40
40
|
Requires-Dist: fastmcp==3.2.0
|
|
41
41
|
Requires-Dist: google-genai
|
|
42
|
+
Requires-Dist: httpx2<3,>=2.0.0
|
|
42
43
|
Requires-Dist: httpx<1,>=0.23.0
|
|
43
44
|
Requires-Dist: mcp<2.0,>=1.24.0
|
|
44
45
|
Requires-Dist: openai>=2.45.0
|
|
@@ -2,13 +2,25 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
import asyncio
|
|
5
6
|
import copy
|
|
6
7
|
import json
|
|
7
8
|
import logging
|
|
9
|
+
import math
|
|
10
|
+
from random import SystemRandom
|
|
8
11
|
from typing import TYPE_CHECKING, Literal, cast
|
|
9
12
|
|
|
13
|
+
import httpx
|
|
14
|
+
import httpx2
|
|
10
15
|
import mcp.types as mcp_types
|
|
11
|
-
from anthropic import
|
|
16
|
+
from anthropic import (
|
|
17
|
+
APIConnectionError,
|
|
18
|
+
APIStatusError,
|
|
19
|
+
APITimeoutError,
|
|
20
|
+
AsyncAnthropic,
|
|
21
|
+
AsyncAnthropicBedrock,
|
|
22
|
+
Omit,
|
|
23
|
+
)
|
|
12
24
|
from anthropic.types import CacheControlEphemeralParam
|
|
13
25
|
from anthropic.types.beta import (
|
|
14
26
|
BetaBase64ImageSourceParam,
|
|
@@ -42,6 +54,42 @@ logger = logging.getLogger(__name__)
|
|
|
42
54
|
ClaudeImageMediaType = Literal["image/jpeg", "image/png", "image/gif", "image/webp"]
|
|
43
55
|
ClaudeToolResultContent = BetaTextBlockParam | BetaImageBlockParam | BetaRequestDocumentBlockParam
|
|
44
56
|
|
|
57
|
+
_RETRYABLE_STREAM_ERROR_TYPES = frozenset(
|
|
58
|
+
{
|
|
59
|
+
"api_error",
|
|
60
|
+
"gateway_error",
|
|
61
|
+
"gateway_timeout",
|
|
62
|
+
"overloaded_error",
|
|
63
|
+
"rate_limit_error",
|
|
64
|
+
"timeout_error",
|
|
65
|
+
"upstream_error",
|
|
66
|
+
}
|
|
67
|
+
)
|
|
68
|
+
_STREAM_MAX_ATTEMPTS = 3
|
|
69
|
+
_STREAM_RETRY_BASE_DELAY_SECONDS = 1.0
|
|
70
|
+
_STREAM_RETRY_MAX_DELAY_SECONDS = 5.0
|
|
71
|
+
_STREAM_JITTER = SystemRandom()
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def _stream_retry_delay(attempt: int, error: Exception) -> float:
|
|
75
|
+
if isinstance(error, APIStatusError):
|
|
76
|
+
for header, divisor in (("retry-after-ms", 1000.0), ("retry-after", 1.0)):
|
|
77
|
+
value = error.response.headers.get(header)
|
|
78
|
+
if value is None:
|
|
79
|
+
continue
|
|
80
|
+
try:
|
|
81
|
+
retry_after = float(value) / divisor
|
|
82
|
+
except ValueError:
|
|
83
|
+
continue
|
|
84
|
+
if math.isfinite(retry_after):
|
|
85
|
+
return min(max(retry_after, 0.0), _STREAM_RETRY_MAX_DELAY_SECONDS)
|
|
86
|
+
|
|
87
|
+
backoff_limit = min(
|
|
88
|
+
_STREAM_RETRY_BASE_DELAY_SECONDS * (2 ** (attempt - 1)),
|
|
89
|
+
_STREAM_RETRY_MAX_DELAY_SECONDS,
|
|
90
|
+
)
|
|
91
|
+
return _STREAM_JITTER.uniform(0.0, backoff_limit)
|
|
92
|
+
|
|
45
93
|
|
|
46
94
|
class ClaudeAgent(ToolAgent[BetaMessageParam, ClaudeConfig]):
|
|
47
95
|
"""Anthropic Claude agent. Drives SSH (coding), RFB (computer), and MCP capabilities."""
|
|
@@ -168,6 +216,67 @@ class ClaudeAgent(ToolAgent[BetaMessageParam, ClaudeConfig]):
|
|
|
168
216
|
|
|
169
217
|
# ─── Anthropic call ───────────────────────────────────────────────
|
|
170
218
|
|
|
219
|
+
async def _stream_response(
|
|
220
|
+
self,
|
|
221
|
+
client: AsyncAnthropic,
|
|
222
|
+
*,
|
|
223
|
+
messages: list[BetaMessageParam],
|
|
224
|
+
system: str | Omit,
|
|
225
|
+
tools: list[BetaToolUnionParam],
|
|
226
|
+
tool_choice: BetaToolChoiceAutoParam,
|
|
227
|
+
betas: list[str] | Omit,
|
|
228
|
+
) -> BetaMessage:
|
|
229
|
+
attempt = 1
|
|
230
|
+
while True:
|
|
231
|
+
stream_opened = False
|
|
232
|
+
try:
|
|
233
|
+
async with client.beta.messages.stream(
|
|
234
|
+
model=self.config.model,
|
|
235
|
+
system=system,
|
|
236
|
+
max_tokens=self.config.max_tokens,
|
|
237
|
+
messages=messages,
|
|
238
|
+
tools=tools,
|
|
239
|
+
tool_choice=tool_choice,
|
|
240
|
+
betas=betas,
|
|
241
|
+
) as stream:
|
|
242
|
+
stream_opened = True
|
|
243
|
+
async for _ in stream:
|
|
244
|
+
pass
|
|
245
|
+
return await stream.get_final_message()
|
|
246
|
+
except (
|
|
247
|
+
APIConnectionError,
|
|
248
|
+
APIStatusError,
|
|
249
|
+
APITimeoutError,
|
|
250
|
+
httpx.TransportError,
|
|
251
|
+
httpx2.TransportError,
|
|
252
|
+
) as exc:
|
|
253
|
+
inline_error = (
|
|
254
|
+
isinstance(exc, APIStatusError)
|
|
255
|
+
and exc.status_code == 200
|
|
256
|
+
and exc.type in _RETRYABLE_STREAM_ERROR_TYPES
|
|
257
|
+
)
|
|
258
|
+
interrupted_stream = stream_opened and isinstance(
|
|
259
|
+
exc,
|
|
260
|
+
APIConnectionError
|
|
261
|
+
| APITimeoutError
|
|
262
|
+
| httpx.TransportError
|
|
263
|
+
| httpx2.TransportError,
|
|
264
|
+
)
|
|
265
|
+
if attempt >= _STREAM_MAX_ATTEMPTS or not (inline_error or interrupted_stream):
|
|
266
|
+
raise
|
|
267
|
+
|
|
268
|
+
retry_delay = _stream_retry_delay(attempt, exc)
|
|
269
|
+
error_type = exc.type if isinstance(exc, APIStatusError) else type(exc).__name__
|
|
270
|
+
logger.warning(
|
|
271
|
+
"Claude stream failed with %s; retrying in %.2fs (attempt %d/%d)",
|
|
272
|
+
error_type,
|
|
273
|
+
retry_delay,
|
|
274
|
+
attempt + 1,
|
|
275
|
+
_STREAM_MAX_ATTEMPTS,
|
|
276
|
+
)
|
|
277
|
+
attempt += 1
|
|
278
|
+
await asyncio.sleep(retry_delay)
|
|
279
|
+
|
|
171
280
|
async def get_response(
|
|
172
281
|
self,
|
|
173
282
|
state: RunState[BetaMessageParam],
|
|
@@ -202,18 +311,14 @@ class ClaudeAgent(ToolAgent[BetaMessageParam, ClaudeConfig]):
|
|
|
202
311
|
)
|
|
203
312
|
else:
|
|
204
313
|
client = cast("AsyncAnthropic", self.anthropic_client)
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
system=system,
|
|
208
|
-
max_tokens=self.config.max_tokens,
|
|
314
|
+
response = await self._stream_response(
|
|
315
|
+
client,
|
|
209
316
|
messages=messages_cached,
|
|
317
|
+
system=system,
|
|
210
318
|
tools=tools,
|
|
211
319
|
tool_choice=tool_choice,
|
|
212
320
|
betas=betas,
|
|
213
|
-
)
|
|
214
|
-
async for _ in stream:
|
|
215
|
-
pass
|
|
216
|
-
response = await stream.get_final_message()
|
|
321
|
+
)
|
|
217
322
|
|
|
218
323
|
state.messages.append(
|
|
219
324
|
BetaMessageParam(role="assistant", content=response.content),
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"""Claude-specific tool spec."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
|
|
7
|
+
from hud.agents.tools.base import AgentToolSpec
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@dataclass(frozen=True)
|
|
11
|
+
class ClaudeToolSpec(AgentToolSpec):
|
|
12
|
+
"""Claude tool spec — adds the optional Anthropic beta flag."""
|
|
13
|
+
|
|
14
|
+
beta: str | None = None
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def is_anthropic_model(model: str | None) -> bool:
|
|
18
|
+
"""Whether the model accepts Anthropic server-tool shorthands (``bash_20250124`` etc.).
|
|
19
|
+
|
|
20
|
+
Non-Anthropic models served over Anthropic-compatible endpoints (e.g. OpenRouter)
|
|
21
|
+
reject those shorthands, so tools fall back to plain ``input_schema`` definitions.
|
|
22
|
+
"""
|
|
23
|
+
return bool(model) and "claude" in model.lower()
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
__all__ = ["ClaudeToolSpec", "is_anthropic_model"]
|
|
@@ -10,12 +10,13 @@ from hud.agents.tools import SSHTool
|
|
|
10
10
|
from hud.agents.tools.base import result_text, tool_err
|
|
11
11
|
from hud.types import MCPToolResult
|
|
12
12
|
|
|
13
|
-
from .base import ClaudeToolSpec
|
|
13
|
+
from .base import ClaudeToolSpec, is_anthropic_model
|
|
14
14
|
|
|
15
15
|
if TYPE_CHECKING:
|
|
16
16
|
from anthropic.types.beta import (
|
|
17
17
|
BetaToolBash20250124Param,
|
|
18
18
|
BetaToolTextEditor20250728Param,
|
|
19
|
+
BetaToolUnionParam,
|
|
19
20
|
)
|
|
20
21
|
|
|
21
22
|
|
|
@@ -24,11 +25,64 @@ CLAUDE_BASH_SPEC = ClaudeToolSpec(
|
|
|
24
25
|
api_name="bash",
|
|
25
26
|
)
|
|
26
27
|
|
|
28
|
+
GENERIC_BASH_SPEC = ClaudeToolSpec(
|
|
29
|
+
api_type="function",
|
|
30
|
+
api_name="bash",
|
|
31
|
+
)
|
|
32
|
+
|
|
27
33
|
CLAUDE_TEXT_EDITOR_SPEC = ClaudeToolSpec(
|
|
28
34
|
api_type="text_editor_20250728",
|
|
29
35
|
api_name="str_replace_based_edit_tool",
|
|
30
36
|
)
|
|
31
37
|
|
|
38
|
+
GENERIC_TEXT_EDITOR_SPEC = ClaudeToolSpec(
|
|
39
|
+
api_type="function",
|
|
40
|
+
api_name="str_replace_based_edit_tool",
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
_BASH_INPUT_SCHEMA: dict[str, Any] = {
|
|
44
|
+
"type": "object",
|
|
45
|
+
"properties": {
|
|
46
|
+
"command": {
|
|
47
|
+
"type": "string",
|
|
48
|
+
"description": "The bash command to run.",
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
"required": ["command"],
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
_TEXT_EDITOR_INPUT_SCHEMA: dict[str, Any] = {
|
|
55
|
+
"type": "object",
|
|
56
|
+
"properties": {
|
|
57
|
+
"command": {
|
|
58
|
+
"type": "string",
|
|
59
|
+
"enum": ["view", "create", "str_replace", "insert"],
|
|
60
|
+
"description": "The editor operation to run.",
|
|
61
|
+
},
|
|
62
|
+
"path": {
|
|
63
|
+
"type": "string",
|
|
64
|
+
"description": "Absolute path to the file.",
|
|
65
|
+
},
|
|
66
|
+
"file_text": {
|
|
67
|
+
"type": "string",
|
|
68
|
+
"description": "Full file contents for `create`.",
|
|
69
|
+
},
|
|
70
|
+
"old_str": {
|
|
71
|
+
"type": "string",
|
|
72
|
+
"description": "Exact existing text to replace for `str_replace`; must be unique.",
|
|
73
|
+
},
|
|
74
|
+
"new_str": {
|
|
75
|
+
"type": "string",
|
|
76
|
+
"description": "Replacement text for `str_replace`, or text to insert for `insert`.",
|
|
77
|
+
},
|
|
78
|
+
"insert_line": {
|
|
79
|
+
"type": "integer",
|
|
80
|
+
"description": "Line number after which to insert for `insert` (0 = top of file).",
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
"required": ["command", "path"],
|
|
84
|
+
}
|
|
85
|
+
|
|
32
86
|
|
|
33
87
|
class ClaudeBashTool(SSHTool):
|
|
34
88
|
"""Claude's native ``bash_20250124`` schema, executed over SSH."""
|
|
@@ -37,10 +91,18 @@ class ClaudeBashTool(SSHTool):
|
|
|
37
91
|
|
|
38
92
|
@classmethod
|
|
39
93
|
def default_spec(cls, model: str) -> ClaudeToolSpec:
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
94
|
+
return CLAUDE_BASH_SPEC if is_anthropic_model(model) else GENERIC_BASH_SPEC
|
|
95
|
+
|
|
96
|
+
def to_params(self) -> BetaToolUnionParam:
|
|
97
|
+
if self.spec.api_type == "function":
|
|
98
|
+
return cast(
|
|
99
|
+
"BetaToolUnionParam",
|
|
100
|
+
{
|
|
101
|
+
"name": self.name,
|
|
102
|
+
"description": "Run a command in a bash shell on the remote machine.",
|
|
103
|
+
"input_schema": _BASH_INPUT_SCHEMA,
|
|
104
|
+
},
|
|
105
|
+
)
|
|
44
106
|
return cast(
|
|
45
107
|
"BetaToolBash20250124Param",
|
|
46
108
|
{"type": self.spec.api_type, "name": self.name},
|
|
@@ -77,14 +139,25 @@ class ClaudeTextEditorTool(SSHTool):
|
|
|
77
139
|
|
|
78
140
|
@classmethod
|
|
79
141
|
def default_spec(cls, model: str) -> ClaudeToolSpec:
|
|
80
|
-
|
|
81
|
-
return CLAUDE_TEXT_EDITOR_SPEC
|
|
142
|
+
return CLAUDE_TEXT_EDITOR_SPEC if is_anthropic_model(model) else GENERIC_TEXT_EDITOR_SPEC
|
|
82
143
|
|
|
83
144
|
@property
|
|
84
145
|
def provider_name(self) -> str:
|
|
85
146
|
return self.spec.api_name
|
|
86
147
|
|
|
87
|
-
def to_params(self) ->
|
|
148
|
+
def to_params(self) -> BetaToolUnionParam:
|
|
149
|
+
if self.spec.api_type == "function":
|
|
150
|
+
return cast(
|
|
151
|
+
"BetaToolUnionParam",
|
|
152
|
+
{
|
|
153
|
+
"name": self.provider_name,
|
|
154
|
+
"description": (
|
|
155
|
+
"View, create, and edit files on the remote machine "
|
|
156
|
+
"(view/create/str_replace/insert)."
|
|
157
|
+
),
|
|
158
|
+
"input_schema": _TEXT_EDITOR_INPUT_SCHEMA,
|
|
159
|
+
},
|
|
160
|
+
)
|
|
88
161
|
return cast(
|
|
89
162
|
"BetaToolTextEditor20250728Param",
|
|
90
163
|
{"type": self.spec.api_type, "name": self.provider_name},
|
|
@@ -142,4 +215,11 @@ class ClaudeTextEditorTool(SSHTool):
|
|
|
142
215
|
return await self.file_write(path, "".join(lines))
|
|
143
216
|
|
|
144
217
|
|
|
145
|
-
__all__ = [
|
|
218
|
+
__all__ = [
|
|
219
|
+
"CLAUDE_BASH_SPEC",
|
|
220
|
+
"CLAUDE_TEXT_EDITOR_SPEC",
|
|
221
|
+
"GENERIC_BASH_SPEC",
|
|
222
|
+
"GENERIC_TEXT_EDITOR_SPEC",
|
|
223
|
+
"ClaudeBashTool",
|
|
224
|
+
"ClaudeTextEditorTool",
|
|
225
|
+
]
|
|
@@ -19,12 +19,13 @@ from hud.agents.tools.base import tool_err, tool_ok
|
|
|
19
19
|
from hud.capabilities.rfb import ScreenshotEncoding, WebPScreenshotEncoding
|
|
20
20
|
from hud.types import MCPToolResult
|
|
21
21
|
|
|
22
|
-
from .base import ClaudeToolSpec
|
|
22
|
+
from .base import ClaudeToolSpec, is_anthropic_model
|
|
23
23
|
|
|
24
24
|
if TYPE_CHECKING:
|
|
25
25
|
from anthropic.types.beta import (
|
|
26
26
|
BetaToolComputerUse20250124Param,
|
|
27
27
|
BetaToolComputerUse20251124Param,
|
|
28
|
+
BetaToolUnionParam,
|
|
28
29
|
)
|
|
29
30
|
|
|
30
31
|
from hud.agents.tools.rfb import Button
|
|
@@ -110,9 +111,89 @@ CLAUDE_COMPUTER_SPECS: tuple[ClaudeToolSpec, ...] = (
|
|
|
110
111
|
),
|
|
111
112
|
)
|
|
112
113
|
|
|
113
|
-
# Fallback for unknown models — use the latest version.
|
|
114
|
+
# Fallback for unknown Claude models — use the latest version.
|
|
114
115
|
_DEFAULT_COMPUTER_SPEC = CLAUDE_COMPUTER_SPECS[0]
|
|
115
116
|
|
|
117
|
+
GENERIC_COMPUTER_SPEC = ClaudeToolSpec(
|
|
118
|
+
api_type="function",
|
|
119
|
+
api_name="computer",
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
_COMPUTER_INPUT_SCHEMA: dict[str, Any] = {
|
|
123
|
+
"type": "object",
|
|
124
|
+
"properties": {
|
|
125
|
+
"action": {
|
|
126
|
+
"type": "string",
|
|
127
|
+
"enum": [
|
|
128
|
+
"screenshot",
|
|
129
|
+
"zoom",
|
|
130
|
+
"left_click",
|
|
131
|
+
"right_click",
|
|
132
|
+
"middle_click",
|
|
133
|
+
"double_click",
|
|
134
|
+
"triple_click",
|
|
135
|
+
"mouse_move",
|
|
136
|
+
"left_mouse_down",
|
|
137
|
+
"left_mouse_up",
|
|
138
|
+
"type",
|
|
139
|
+
"key",
|
|
140
|
+
"hold_key",
|
|
141
|
+
"scroll",
|
|
142
|
+
"left_click_drag",
|
|
143
|
+
"wait",
|
|
144
|
+
"cursor_position",
|
|
145
|
+
],
|
|
146
|
+
"description": "The action to perform on the screen.",
|
|
147
|
+
},
|
|
148
|
+
"coordinate": {
|
|
149
|
+
"type": "array",
|
|
150
|
+
"items": {"type": "integer"},
|
|
151
|
+
"minItems": 2,
|
|
152
|
+
"maxItems": 2,
|
|
153
|
+
"description": "[x, y] pixel coordinate for click/move/scroll, or drag end point.",
|
|
154
|
+
},
|
|
155
|
+
"start_coordinate": {
|
|
156
|
+
"type": "array",
|
|
157
|
+
"items": {"type": "integer"},
|
|
158
|
+
"minItems": 2,
|
|
159
|
+
"maxItems": 2,
|
|
160
|
+
"description": "[x, y] drag start point for `left_click_drag`.",
|
|
161
|
+
},
|
|
162
|
+
"text": {
|
|
163
|
+
"type": "string",
|
|
164
|
+
"description": (
|
|
165
|
+
"Text to type for `type`; key chord (xdotool syntax, e.g. `ctrl+s`) for "
|
|
166
|
+
"`key`/`hold_key`; optional modifier keys held during clicks and scrolls."
|
|
167
|
+
),
|
|
168
|
+
},
|
|
169
|
+
"duration": {
|
|
170
|
+
"type": "number",
|
|
171
|
+
"description": "Seconds to hold for `hold_key` or pause for `wait`.",
|
|
172
|
+
},
|
|
173
|
+
"scroll_direction": {
|
|
174
|
+
"type": "string",
|
|
175
|
+
"enum": ["up", "down", "left", "right"],
|
|
176
|
+
"description": "Direction for `scroll`.",
|
|
177
|
+
},
|
|
178
|
+
"scroll_amount": {
|
|
179
|
+
"type": "integer",
|
|
180
|
+
"description": "Number of scroll clicks for `scroll`.",
|
|
181
|
+
},
|
|
182
|
+
"repeat": {
|
|
183
|
+
"type": "integer",
|
|
184
|
+
"description": "Times to repeat the key chord for `key`.",
|
|
185
|
+
},
|
|
186
|
+
"region": {
|
|
187
|
+
"type": "array",
|
|
188
|
+
"items": {"type": "integer"},
|
|
189
|
+
"minItems": 4,
|
|
190
|
+
"maxItems": 4,
|
|
191
|
+
"description": "[x0, y0, x1, y1] region to magnify for `zoom`.",
|
|
192
|
+
},
|
|
193
|
+
},
|
|
194
|
+
"required": ["action"],
|
|
195
|
+
}
|
|
196
|
+
|
|
116
197
|
|
|
117
198
|
class ClaudeComputerTool(RFBTool):
|
|
118
199
|
"""Claude's native ``computer_use`` schema, executed over an RFB capability."""
|
|
@@ -121,12 +202,29 @@ class ClaudeComputerTool(RFBTool):
|
|
|
121
202
|
|
|
122
203
|
@classmethod
|
|
123
204
|
def default_spec(cls, model: str) -> ClaudeToolSpec | None:
|
|
205
|
+
if not is_anthropic_model(model):
|
|
206
|
+
return GENERIC_COMPUTER_SPEC
|
|
124
207
|
for candidate in CLAUDE_COMPUTER_SPECS:
|
|
125
208
|
if candidate.supports_model(model):
|
|
126
209
|
return candidate
|
|
127
210
|
return _DEFAULT_COMPUTER_SPEC
|
|
128
211
|
|
|
129
|
-
def to_params(
|
|
212
|
+
def to_params(
|
|
213
|
+
self,
|
|
214
|
+
) -> BetaToolComputerUse20250124Param | BetaToolComputerUse20251124Param | BetaToolUnionParam:
|
|
215
|
+
if self.spec.api_type == "function":
|
|
216
|
+
return cast(
|
|
217
|
+
"BetaToolUnionParam",
|
|
218
|
+
{
|
|
219
|
+
"name": self.name,
|
|
220
|
+
"description": (
|
|
221
|
+
"Control the remote computer's screen, mouse, and keyboard. "
|
|
222
|
+
f"The display is {self.display_width}x{self.display_height} pixels. "
|
|
223
|
+
"Most actions return a screenshot of the result."
|
|
224
|
+
),
|
|
225
|
+
"input_schema": _COMPUTER_INPUT_SCHEMA,
|
|
226
|
+
},
|
|
227
|
+
)
|
|
130
228
|
if self.spec.api_type == "computer_20251124":
|
|
131
229
|
return cast(
|
|
132
230
|
"BetaToolComputerUse20251124Param",
|
|
@@ -375,4 +473,4 @@ def _crop_png(
|
|
|
375
473
|
return buf.getvalue(), encoding.mime_type
|
|
376
474
|
|
|
377
475
|
|
|
378
|
-
__all__ = ["CLAUDE_COMPUTER_SPECS", "ClaudeComputerTool"]
|
|
476
|
+
__all__ = ["CLAUDE_COMPUTER_SPECS", "GENERIC_COMPUTER_SPEC", "ClaudeComputerTool"]
|
|
@@ -14,6 +14,7 @@ from PIL import Image
|
|
|
14
14
|
|
|
15
15
|
from hud.agents.claude.tools.computer import (
|
|
16
16
|
CLAUDE_COMPUTER_SPECS,
|
|
17
|
+
GENERIC_COMPUTER_SPEC,
|
|
17
18
|
ClaudeComputerTool,
|
|
18
19
|
_crop_png,
|
|
19
20
|
_hold_keys,
|
|
@@ -120,18 +121,31 @@ def test_default_spec_per_model() -> None:
|
|
|
120
121
|
spec_45 = ClaudeComputerTool.default_spec("claude-sonnet-4-5-20250101")
|
|
121
122
|
assert spec_45 is not None
|
|
122
123
|
assert spec_45.api_type == "computer_20250124"
|
|
123
|
-
# Unknown model falls back to the latest spec.
|
|
124
|
-
spec_unknown = ClaudeComputerTool.default_spec("totally-unknown")
|
|
124
|
+
# Unknown Claude model falls back to the latest spec.
|
|
125
|
+
spec_unknown = ClaudeComputerTool.default_spec("claude-totally-unknown")
|
|
125
126
|
assert spec_unknown is not None
|
|
126
127
|
assert spec_unknown.api_type == "computer_20251124"
|
|
128
|
+
# Non-Anthropic models get the generic input_schema spec.
|
|
129
|
+
assert ClaudeComputerTool.default_spec("qwen/qwen3.8-max") is GENERIC_COMPUTER_SPEC
|
|
127
130
|
|
|
128
131
|
|
|
129
132
|
def test_to_params_reflects_spec_version() -> None:
|
|
130
133
|
tool = RecordingComputer()
|
|
131
134
|
tool.spec = CLAUDE_COMPUTER_SPECS[0]
|
|
132
|
-
assert tool.to_params()
|
|
135
|
+
assert tool.to_params().get("type") == "computer_20251124"
|
|
133
136
|
tool.spec = CLAUDE_COMPUTER_SPECS[1]
|
|
134
|
-
assert tool.to_params()
|
|
137
|
+
assert tool.to_params().get("type") == "computer_20250124"
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def test_to_params_generic_spec_uses_input_schema() -> None:
|
|
141
|
+
tool = RecordingComputer()
|
|
142
|
+
tool.spec = GENERIC_COMPUTER_SPEC
|
|
143
|
+
params: Any = tool.to_params()
|
|
144
|
+
assert "type" not in params
|
|
145
|
+
assert params["name"] == "computer"
|
|
146
|
+
assert "200x100" in params["description"]
|
|
147
|
+
assert params["input_schema"]["required"] == ["action"]
|
|
148
|
+
assert "left_click" in params["input_schema"]["properties"]["action"]["enum"]
|
|
135
149
|
|
|
136
150
|
|
|
137
151
|
# ─── action dispatch ──────────────────────────────────────────────────
|