hud-python 0.4.32__tar.gz → 0.4.33__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.
Potentially problematic release.
This version of hud-python might be problematic. Click here for more details.
- {hud_python-0.4.32 → hud_python-0.4.33}/PKG-INFO +1 -1
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/cli/flows/tasks.py +2 -1
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/cli/rl/remote_runner.py +2 -4
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/rl/buffer.py +108 -77
- hud_python-0.4.33/hud/samples/__init__.py +7 -0
- hud_python-0.4.33/hud/samples/browser.py +33 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/types.py +19 -6
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/utils/mcp.py +6 -1
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/utils/tests/test_version.py +1 -1
- hud_python-0.4.33/hud/utils/tool_shorthand.py +59 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/version.py +1 -1
- {hud_python-0.4.32 → hud_python-0.4.33}/pyproject.toml +1 -1
- {hud_python-0.4.32 → hud_python-0.4.33}/.gitignore +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/LICENSE +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/README.md +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/environments/README.md +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/environments/browser/README.md +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/environments/browser/apps/2048/README.md +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/environments/browser/apps/2048/backend/pyproject.toml +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/environments/browser/apps/README.md +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/environments/browser/apps/todo/README.md +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/environments/browser/apps/todo/backend/pyproject.toml +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/environments/browser/pyproject.toml +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/environments/remote_browser/README.md +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/environments/remote_browser/pyproject.toml +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/environments/remote_browser/src/hud_controller/providers/README.md +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/environments/text_2048/README.md +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/environments/text_2048/pyproject.toml +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/examples/README.md +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/__init__.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/__main__.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/agents/__init__.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/agents/base.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/agents/claude.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/agents/grounded_openai.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/agents/langchain.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/agents/misc/__init__.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/agents/misc/response_agent.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/agents/openai.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/agents/openai_chat_generic.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/agents/tests/__init__.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/agents/tests/test_base.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/agents/tests/test_claude.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/agents/tests/test_client.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/agents/tests/test_grounded_openai_agent.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/agents/tests/test_openai.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/cli/__init__.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/cli/__main__.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/cli/analyze.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/cli/build.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/cli/clone.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/cli/debug.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/cli/dev.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/cli/eval.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/cli/flows/__init__.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/cli/get.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/cli/init.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/cli/list_func.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/cli/pull.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/cli/push.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/cli/remove.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/cli/rl/__init__.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/cli/rl/config.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/cli/rl/display.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/cli/rl/gpu.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/cli/rl/gpu_utils.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/cli/rl/local_runner.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/cli/rl/presets.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/cli/rl/rl_api.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/cli/rl/vllm.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/cli/tests/__init__.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/cli/tests/test_analyze.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/cli/tests/test_analyze_metadata.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/cli/tests/test_build.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/cli/tests/test_cli_init.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/cli/tests/test_cli_main.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/cli/tests/test_clone.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/cli/tests/test_cursor.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/cli/tests/test_debug.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/cli/tests/test_list_func.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/cli/tests/test_main_module.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/cli/tests/test_mcp_server.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/cli/tests/test_pull.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/cli/tests/test_push.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/cli/tests/test_registry.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/cli/tests/test_utils.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/cli/utils/__init__.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/cli/utils/cursor.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/cli/utils/docker.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/cli/utils/environment.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/cli/utils/interactive.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/cli/utils/logging.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/cli/utils/metadata.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/cli/utils/registry.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/cli/utils/remote_runner.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/cli/utils/runner.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/cli/utils/server.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/cli/utils/tasks.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/clients/README.md +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/clients/__init__.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/clients/base.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/clients/fastmcp.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/clients/mcp_use.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/clients/tests/__init__.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/clients/tests/test_client_integration.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/clients/tests/test_fastmcp.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/clients/tests/test_mcp_use_retry.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/clients/tests/test_protocol.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/clients/utils/__init__.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/clients/utils/mcp_use_retry.py +3 -3
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/clients/utils/retry.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/clients/utils/retry_transport.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/datasets/__init__.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/datasets/parallel.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/datasets/runner.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/datasets/utils.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/misc/__init__.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/misc/claude_plays_pokemon.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/native/__init__.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/native/comparator.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/native/tests/__init__.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/native/tests/test_comparator.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/native/tests/test_native_init.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/otel/__init__.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/otel/collector.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/otel/config.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/otel/context.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/otel/exporters.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/otel/instrumentation.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/otel/processors.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/otel/tests/__init__.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/otel/tests/test_processors.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/py.typed +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/rl/README.md +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/rl/__init__.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/rl/actor.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/rl/chat_template.jinja +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/rl/config.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/rl/distributed.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/rl/learner.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/rl/tests/__init__.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/rl/tests/test_learner.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/rl/train.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/rl/types.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/rl/utils/start_vllm_server.sh +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/rl/utils.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/rl/vllm_adapter.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/server/__init__.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/server/context.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/server/helper/__init__.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/server/low_level.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/server/server.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/server/tests/__init__.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/settings.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/shared/__init__.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/shared/exceptions.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/shared/hints.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/shared/requests.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/shared/tests/__init__.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/shared/tests/test_exceptions.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/shared/tests/test_requests.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/telemetry/__init__.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/telemetry/instrument.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/telemetry/job.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/telemetry/replay.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/telemetry/tests/__init__.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/telemetry/tests/test_replay.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/telemetry/tests/test_trace.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/telemetry/trace.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/tools/__init__.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/tools/base.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/tools/bash.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/tools/computer/__init__.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/tools/computer/anthropic.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/tools/computer/hud.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/tools/computer/openai.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/tools/computer/settings.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/tools/edit.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/tools/executors/__init__.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/tools/executors/base.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/tools/executors/pyautogui.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/tools/executors/tests/__init__.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/tools/executors/tests/test_base_executor.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/tools/executors/tests/test_pyautogui_executor.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/tools/executors/xdo.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/tools/grounding/__init__.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/tools/grounding/config.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/tools/grounding/grounded_tool.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/tools/grounding/grounder.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/tools/grounding/tests/__init__.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/tools/grounding/tests/test_grounded_tool.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/tools/playwright.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/tools/response.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/tools/submit.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/tools/tests/__init__.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/tools/tests/test_base.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/tools/tests/test_bash.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/tools/tests/test_bash_extended.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/tools/tests/test_computer.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/tools/tests/test_computer_actions.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/tools/tests/test_edit.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/tools/tests/test_init.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/tools/tests/test_playwright_tool.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/tools/tests/test_response.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/tools/tests/test_tools.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/tools/tests/test_tools_init.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/tools/tests/test_utils.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/tools/types.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/tools/utils.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/utils/__init__.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/utils/agent_factories.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/utils/async_utils.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/utils/group_eval.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/utils/hud_console.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/utils/pretty_errors.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/utils/progress.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/utils/tasks.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/utils/telemetry.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/utils/tests/__init__.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/utils/tests/test_async_utils.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/utils/tests/test_init.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/utils/tests/test_mcp.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/utils/tests/test_progress.py +0 -0
- {hud_python-0.4.32 → hud_python-0.4.33}/hud/utils/tests/test_telemetry.py +0 -0
|
@@ -32,6 +32,7 @@ def _validate_tasks(tasks: list[Task]) -> bool:
|
|
|
32
32
|
A task is considered remote if any "url" field anywhere inside mcp_config
|
|
33
33
|
is a valid remote URL (e.g., https://mcp.hud.so/v3/mcp).
|
|
34
34
|
"""
|
|
35
|
+
|
|
35
36
|
def _has_remote_url(obj: Any) -> bool:
|
|
36
37
|
if isinstance(obj, dict):
|
|
37
38
|
for k, v in obj.items():
|
|
@@ -146,7 +147,7 @@ def _derive_remote_image(lock_data: dict[str, Any]) -> str:
|
|
|
146
147
|
# Base name always comes from lock_data.image to preserve org/repo
|
|
147
148
|
image_ref = str(lock_data.get("image", "")).strip()
|
|
148
149
|
if not image_ref:
|
|
149
|
-
raise typer.Exit(
|
|
150
|
+
raise typer.Exit(1)
|
|
150
151
|
name, tag = extract_name_and_tag(image_ref)
|
|
151
152
|
return f"{name}:{tag}"
|
|
152
153
|
|
|
@@ -9,8 +9,8 @@ from __future__ import annotations
|
|
|
9
9
|
import os
|
|
10
10
|
import subprocess
|
|
11
11
|
import time
|
|
12
|
-
from pathlib import Path
|
|
13
12
|
import uuid
|
|
13
|
+
from pathlib import Path
|
|
14
14
|
|
|
15
15
|
from rich.console import Console
|
|
16
16
|
|
|
@@ -51,9 +51,7 @@ def ensure_vllm_deployed(model_name: str, gpu_type: str = "A100", timeout: int =
|
|
|
51
51
|
hud_console.info("Waiting for vLLM server to be ready...")
|
|
52
52
|
start_time = time.time()
|
|
53
53
|
with hud_console.progress() as progress:
|
|
54
|
-
progress.update(
|
|
55
|
-
"Checking deployment status (see live status on https://app.hud.so/models)"
|
|
56
|
-
)
|
|
54
|
+
progress.update("Checking deployment status (see live status on https://app.hud.so/models)")
|
|
57
55
|
while True:
|
|
58
56
|
if time.time() - start_time > timeout:
|
|
59
57
|
hud_console.error("Timeout waiting for vLLM deployment")
|
|
@@ -219,12 +219,93 @@ class ReplayBuffer(Buffer[Trace]):
|
|
|
219
219
|
else:
|
|
220
220
|
raise ValueError(f"Invalid select strategy: {self.select_strategy}")
|
|
221
221
|
|
|
222
|
+
def _extract_group_key(self, trace: Trace) -> tuple[str, str]:
|
|
223
|
+
"""Return a stable grouping key for a trace.
|
|
224
|
+
|
|
225
|
+
Preference order:
|
|
226
|
+
1) task.id when present (kind='id')
|
|
227
|
+
2) task.prompt exact string (kind='prompt') when id is None
|
|
228
|
+
3) 'NA' for missing/errored entries (kind='NA')
|
|
229
|
+
"""
|
|
230
|
+
if getattr(trace, "isError", False):
|
|
231
|
+
return ("NA", "NA")
|
|
232
|
+
|
|
233
|
+
task = getattr(trace, "task", None)
|
|
234
|
+
if task is None:
|
|
235
|
+
return ("NA", "NA")
|
|
236
|
+
|
|
237
|
+
tid = getattr(task, "id", None)
|
|
238
|
+
if tid is not None:
|
|
239
|
+
return ("id", str(tid))
|
|
240
|
+
|
|
241
|
+
prompt = getattr(task, "prompt", None)
|
|
242
|
+
if prompt:
|
|
243
|
+
return ("prompt", str(prompt))
|
|
244
|
+
|
|
245
|
+
return ("NA", "NA")
|
|
246
|
+
|
|
247
|
+
def _validate_and_split_groups(
|
|
248
|
+
self, recent_traces: list[Trace]
|
|
249
|
+
) -> tuple[list[list[Trace]], list[tuple[str, str]]]:
|
|
250
|
+
"""Validate and split recent traces into homogeneous groups by id or prompt.
|
|
251
|
+
|
|
252
|
+
- Uses id when present; otherwise falls back to prompt equality.
|
|
253
|
+
- Any NA/error traces are excluded and the group is filled by duplicating
|
|
254
|
+
existing valid members in that group.
|
|
255
|
+
- Always returns len == groups_per_batch groups of size == group_size.
|
|
256
|
+
"""
|
|
257
|
+
from collections import Counter
|
|
258
|
+
|
|
259
|
+
groups_per_batch = self.batch_size // self.group_size
|
|
260
|
+
|
|
261
|
+
window_keys = [self._extract_group_key(t) for t in recent_traces]
|
|
262
|
+
window_counter = Counter(k for k in window_keys if k[0] != "NA")
|
|
263
|
+
|
|
264
|
+
validated_groups: list[list[Trace]] = []
|
|
265
|
+
selected_keys: list[tuple[str, str]] = []
|
|
266
|
+
|
|
267
|
+
for g_idx in range(groups_per_batch):
|
|
268
|
+
start = g_idx * self.group_size
|
|
269
|
+
end = start + self.group_size
|
|
270
|
+
chunk = recent_traces[start:end]
|
|
271
|
+
|
|
272
|
+
key_counts = Counter()
|
|
273
|
+
per_item_keys: list[tuple[str, str]] = []
|
|
274
|
+
for tr in chunk:
|
|
275
|
+
k = self._extract_group_key(tr)
|
|
276
|
+
per_item_keys.append(k)
|
|
277
|
+
if k[0] != "NA":
|
|
278
|
+
key_counts[k] += 1
|
|
279
|
+
|
|
280
|
+
if key_counts:
|
|
281
|
+
best_key = key_counts.most_common(1)[0][0]
|
|
282
|
+
elif window_counter:
|
|
283
|
+
best_key = window_counter.most_common(1)[0][0]
|
|
284
|
+
else:
|
|
285
|
+
best_key = ("NA", "NA")
|
|
286
|
+
|
|
287
|
+
homogeneous = [tr for tr, k in zip(chunk, per_item_keys, strict=False) if k == best_key]
|
|
288
|
+
|
|
289
|
+
while len(homogeneous) < self.group_size:
|
|
290
|
+
if homogeneous:
|
|
291
|
+
homogeneous.append(homogeneous[-1])
|
|
292
|
+
else:
|
|
293
|
+
idx = next((i for i, wk in enumerate(window_keys) if wk[0] != "NA"), None)
|
|
294
|
+
if idx is not None:
|
|
295
|
+
homogeneous.append(recent_traces[idx])
|
|
296
|
+
elif chunk:
|
|
297
|
+
homogeneous.append(chunk[0])
|
|
298
|
+
else:
|
|
299
|
+
homogeneous.append(recent_traces[0])
|
|
300
|
+
|
|
301
|
+
validated_groups.append(homogeneous)
|
|
302
|
+
selected_keys.append(best_key)
|
|
303
|
+
|
|
304
|
+
return validated_groups, selected_keys
|
|
305
|
+
|
|
222
306
|
def _sample_high_variance_traces(self) -> list[Trace]:
|
|
223
307
|
from collections import Counter, defaultdict, deque
|
|
224
308
|
|
|
225
|
-
# Expect recent window to already be grouped by task id
|
|
226
|
-
|
|
227
|
-
# Build recent window and earlier lookup (short form)
|
|
228
309
|
buf_list = list(self.buffer)
|
|
229
310
|
if len(buf_list) < self.batch_size:
|
|
230
311
|
hud_console.warning(
|
|
@@ -234,81 +315,32 @@ class ReplayBuffer(Buffer[Trace]):
|
|
|
234
315
|
take = min(len(buf_list) or 1, self.batch_size - len(buf_list))
|
|
235
316
|
buf_list.extend(buf_list[:take])
|
|
236
317
|
recent_traces = buf_list[-self.batch_size :]
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
)
|
|
318
|
+
|
|
319
|
+
recent_keys = [self._extract_group_key(t) for t in recent_traces]
|
|
320
|
+
hud_console.info(f"[group-sampler] recent-window histogram: {Counter(recent_keys)}")
|
|
240
321
|
|
|
241
322
|
hud_console.info(
|
|
242
323
|
f"[group-sampler] Building earlier traces lookup, buffer size: {len(buf_list)}"
|
|
243
324
|
)
|
|
244
|
-
|
|
325
|
+
earlier_traces_by_key: dict[tuple[str, str], deque[Trace]] = defaultdict(deque)
|
|
245
326
|
for tr in buf_list[: -self.batch_size]:
|
|
246
|
-
|
|
327
|
+
k = self._extract_group_key(tr)
|
|
328
|
+
if k[0] != "NA":
|
|
329
|
+
earlier_traces_by_key[k].append(tr)
|
|
330
|
+
|
|
331
|
+
groups, group_keys = self._validate_and_split_groups(recent_traces)
|
|
247
332
|
|
|
248
|
-
# Chunk from the most-recent end
|
|
249
333
|
final_traces: list[Trace] = []
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
end = start + self.group_size
|
|
255
|
-
group = recent_traces[start:end]
|
|
256
|
-
|
|
257
|
-
# Assert homogeneity: every trace in a group must share the same task id
|
|
258
|
-
cnt = Counter(getattr(t.task, "id", "NA") for t in group)
|
|
259
|
-
if len(cnt) != 1:
|
|
260
|
-
raise RuntimeError(f"Group {g_idx} is not homogeneous: {dict(cnt)}")
|
|
261
|
-
target_tid = next(iter(cnt.keys()))
|
|
262
|
-
|
|
263
|
-
# Build homogeneous group of target_tid, filling from earlier traces to increase spread
|
|
264
|
-
homogeneous: list[Trace] = [
|
|
265
|
-
t for t in group if getattr(t.task, "id", "NA") == target_tid
|
|
266
|
-
]
|
|
267
|
-
needed = self.group_size - len(homogeneous)
|
|
268
|
-
|
|
269
|
-
# Greedy fill: choose earlier traces (same task-id) farthest from current mean reward
|
|
270
|
-
def current_mean(homogeneous: list[Trace]) -> float:
|
|
271
|
-
if not homogeneous:
|
|
334
|
+
for g_idx, (homogeneous, target_key) in enumerate(zip(groups, group_keys, strict=False)):
|
|
335
|
+
|
|
336
|
+
def current_mean(h: list[Trace]) -> float:
|
|
337
|
+
if not h:
|
|
272
338
|
return 0.0
|
|
273
|
-
vals = [float(getattr(t, "reward", 0.0) or 0.0) for t in
|
|
339
|
+
vals = [float(getattr(t, "reward", 0.0) or 0.0) for t in h]
|
|
274
340
|
return sum(vals) / len(vals)
|
|
275
341
|
|
|
276
|
-
|
|
277
|
-
pool = earlier_traces_by_task.get(target_tid, deque())
|
|
278
|
-
if pool:
|
|
279
|
-
mu = current_mean(homogeneous)
|
|
280
|
-
# pick element farthest from current mean
|
|
281
|
-
best_i = None
|
|
282
|
-
best_dist = -1.0
|
|
283
|
-
for i, tr in enumerate(list(pool)):
|
|
284
|
-
r = float(getattr(tr, "reward", 0.0) or 0.0)
|
|
285
|
-
dist = abs(r - mu)
|
|
286
|
-
if dist > best_dist:
|
|
287
|
-
best_dist = dist
|
|
288
|
-
best_i = i
|
|
289
|
-
# pop selected
|
|
290
|
-
chosen = list(pool)[best_i] # type: ignore[index]
|
|
291
|
-
# remove from deque efficiently by rotating
|
|
292
|
-
left = list(pool)
|
|
293
|
-
if best_i is not None:
|
|
294
|
-
left.pop(best_i) # O(n) but pool is small in practice
|
|
295
|
-
earlier_traces_by_task[target_tid] = deque(left)
|
|
296
|
-
homogeneous.append(chosen)
|
|
297
|
-
else:
|
|
298
|
-
# duplicate extreme within current homogeneous set
|
|
299
|
-
if not homogeneous:
|
|
300
|
-
raise RuntimeError(f"Group {g_idx} has no traces for target {target_tid}")
|
|
301
|
-
mu = current_mean(homogeneous)
|
|
302
|
-
extreme = max(
|
|
303
|
-
homogeneous, key=lambda t: abs(float(getattr(t, "reward", 0.0) or 0.0) - mu)
|
|
304
|
-
)
|
|
305
|
-
homogeneous.append(extreme)
|
|
306
|
-
needed -= 1
|
|
307
|
-
|
|
308
|
-
# Replacement step: swap in earlier traces to increase reward spread
|
|
309
|
-
pool = earlier_traces_by_task.get(target_tid, deque())
|
|
342
|
+
pool = earlier_traces_by_key.get(target_key, deque())
|
|
310
343
|
if pool:
|
|
311
|
-
# Log pool stats
|
|
312
344
|
pool_vals = [float(getattr(tr, "reward", 0.0) or 0.0) for tr in list(pool)]
|
|
313
345
|
if pool_vals:
|
|
314
346
|
pool_mean = sum(pool_vals) / len(pool_vals)
|
|
@@ -316,16 +348,15 @@ class ReplayBuffer(Buffer[Trace]):
|
|
|
316
348
|
pool_vals
|
|
317
349
|
)
|
|
318
350
|
hud_console.info(
|
|
319
|
-
f"[group-sampler] Group {g_idx}: earlier-pool size={len(pool_vals)}
|
|
351
|
+
f"[group-sampler] Group {g_idx}: earlier-pool size={len(pool_vals)} "
|
|
352
|
+
f"mean={pool_mean:.4f} std={(pool_var**0.5):.4f}"
|
|
320
353
|
)
|
|
321
354
|
|
|
322
|
-
# Decide how many to replace (up to 1/4 of group, at least 1)
|
|
323
355
|
replace_k = max(1, self.group_size // 4)
|
|
324
356
|
replace_k = min(replace_k, len(pool), self.group_size)
|
|
325
357
|
|
|
326
358
|
if replace_k > 0:
|
|
327
359
|
mu = current_mean(homogeneous)
|
|
328
|
-
# Select replacement candidates from pool farthest from current mean
|
|
329
360
|
pool_list = list(pool)
|
|
330
361
|
pool_indices = list(range(len(pool_list)))
|
|
331
362
|
pool_indices.sort(
|
|
@@ -337,12 +368,11 @@ class ReplayBuffer(Buffer[Trace]):
|
|
|
337
368
|
chosen_pool_idx = set(pool_indices[:replace_k])
|
|
338
369
|
replacements = [pool_list[i] for i in pool_indices[:replace_k]]
|
|
339
370
|
|
|
340
|
-
# Remove chosen from pool deque
|
|
341
371
|
remaining = [tr for i, tr in enumerate(pool_list) if i not in chosen_pool_idx]
|
|
342
|
-
|
|
372
|
+
earlier_traces_by_key[target_key] = deque(remaining)
|
|
343
373
|
|
|
344
|
-
# Select current group positions closest to mean to replace
|
|
345
374
|
group_indices = list(range(len(homogeneous)))
|
|
375
|
+
mu = current_mean(homogeneous)
|
|
346
376
|
group_indices.sort(
|
|
347
377
|
key=lambda i: abs(
|
|
348
378
|
(float(getattr(homogeneous[i], "reward", 0.0) or 0.0)) - mu
|
|
@@ -353,18 +383,19 @@ class ReplayBuffer(Buffer[Trace]):
|
|
|
353
383
|
for pos, new_tr in zip(target_positions, replacements, strict=False):
|
|
354
384
|
homogeneous[pos] = new_tr
|
|
355
385
|
|
|
356
|
-
|
|
357
|
-
if any(getattr(t.task, "id", "NA") != target_tid for t in homogeneous):
|
|
386
|
+
if any(self._extract_group_key(t) != target_key for t in homogeneous):
|
|
358
387
|
raise RuntimeError(f"Group {g_idx} is not homogeneous after sampling")
|
|
359
388
|
final_traces.extend(homogeneous)
|
|
360
389
|
|
|
361
390
|
for i in range(0, len(final_traces), self.group_size):
|
|
362
391
|
block = final_traces[i : i + self.group_size]
|
|
363
|
-
|
|
392
|
+
keys = {self._extract_group_key(t) for t in block}
|
|
393
|
+
if len(keys) != 1:
|
|
364
394
|
raise RuntimeError(f"Homogeneity validation failed for block starting at index {i}")
|
|
365
395
|
|
|
366
396
|
hud_console.info(
|
|
367
|
-
f"[group-sampler] final histogram:
|
|
397
|
+
f"[group-sampler] final histogram: "
|
|
398
|
+
f"{Counter(self._extract_group_key(t) for t in final_traces)}"
|
|
368
399
|
)
|
|
369
400
|
return final_traces
|
|
370
401
|
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"""Sample browser task factory."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from pydantic import Field
|
|
8
|
+
|
|
9
|
+
from hud.settings import settings
|
|
10
|
+
from hud.types import MCPToolCall, Task
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class BrowserTask(Task):
|
|
14
|
+
"""Task subclass with browser defaults for BrowserTask(prompt=...)."""
|
|
15
|
+
|
|
16
|
+
prompt: str = "Open Google and be ready to search."
|
|
17
|
+
mcp_config: dict[str, Any] = Field(
|
|
18
|
+
default_factory=lambda: {
|
|
19
|
+
"browser": {
|
|
20
|
+
"url": "https://mcp.hud.so/v3/mcp",
|
|
21
|
+
"headers": {
|
|
22
|
+
"Authorization": f"Bearer {settings.api_key}",
|
|
23
|
+
"Mcp-Image": "hudevals/hud-remote-browser:0.1.1",
|
|
24
|
+
},
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
)
|
|
28
|
+
setup_tool: MCPToolCall | list[MCPToolCall] | None = Field(
|
|
29
|
+
default_factory=lambda: MCPToolCall(
|
|
30
|
+
name="setup",
|
|
31
|
+
arguments={"name": "navigate_to_url", "arguments": {"url": "https://www.google.com"}},
|
|
32
|
+
)
|
|
33
|
+
)
|
|
@@ -12,6 +12,7 @@ from mcp.types import CallToolRequestParams, CallToolResult
|
|
|
12
12
|
from pydantic import BaseModel, ConfigDict, Field, field_validator
|
|
13
13
|
|
|
14
14
|
from hud.settings import settings
|
|
15
|
+
from hud.utils.tool_shorthand import normalize_to_tool_call_dict
|
|
15
16
|
|
|
16
17
|
logger = logging.getLogger(__name__)
|
|
17
18
|
|
|
@@ -59,8 +60,18 @@ class Task(BaseModel):
|
|
|
59
60
|
|
|
60
61
|
@field_validator("setup_tool", "evaluate_tool", mode="before")
|
|
61
62
|
@classmethod
|
|
62
|
-
def convert_dict_to_tool_call(cls, v: Any) -> Any:
|
|
63
|
-
"""Convert dict to MCPToolCall instance
|
|
63
|
+
def convert_dict_to_tool_call(cls, v: Any, info: Any) -> Any:
|
|
64
|
+
"""Convert dict (with shorthands) to MCPToolCall instance.
|
|
65
|
+
|
|
66
|
+
Supports nested forms by walking to the deepest tool name and its arguments.
|
|
67
|
+
Examples:
|
|
68
|
+
- {"name": "navigate", "arguments": {...}} -> name=navigate
|
|
69
|
+
- {"navigate": {...}} -> name=navigate
|
|
70
|
+
- {"setup": {"navigate": {...}}} -> name=navigate
|
|
71
|
+
- {"name": "setup", "arguments": {"name": "navigate", "arguments": {...}}}
|
|
72
|
+
-> name=navigate
|
|
73
|
+
- Lists are normalized element-wise
|
|
74
|
+
"""
|
|
64
75
|
if v is None:
|
|
65
76
|
return None
|
|
66
77
|
|
|
@@ -73,10 +84,12 @@ class Task(BaseModel):
|
|
|
73
84
|
|
|
74
85
|
raise HudConfigError(f"Invalid JSON string: {e}") from e
|
|
75
86
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
if isinstance(
|
|
79
|
-
return
|
|
87
|
+
normalized = normalize_to_tool_call_dict(v)
|
|
88
|
+
|
|
89
|
+
if isinstance(normalized, dict):
|
|
90
|
+
return MCPToolCall(**normalized)
|
|
91
|
+
if isinstance(normalized, list):
|
|
92
|
+
return [MCPToolCall(**item) if isinstance(item, dict) else item for item in normalized]
|
|
80
93
|
return v
|
|
81
94
|
|
|
82
95
|
@field_validator("mcp_config", mode="before")
|
|
@@ -66,8 +66,13 @@ def setup_hud_telemetry(
|
|
|
66
66
|
auto_trace_cm = None
|
|
67
67
|
|
|
68
68
|
if not run_id and auto_trace:
|
|
69
|
+
# Start an auto trace and capture its ID for headers/metadata
|
|
69
70
|
auto_trace_cm = trace("My Trace")
|
|
70
|
-
|
|
71
|
+
_trace_obj = auto_trace_cm.__enter__()
|
|
72
|
+
try:
|
|
73
|
+
run_id = getattr(_trace_obj, "id", None) or str(_trace_obj)
|
|
74
|
+
except Exception: # pragma: no cover - fallback shouldn't fail lint
|
|
75
|
+
run_id = None
|
|
71
76
|
|
|
72
77
|
# Patch HUD servers with run-id (works whether auto or user trace)
|
|
73
78
|
if run_id:
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def _is_call_like(obj: Any) -> bool:
|
|
7
|
+
if not isinstance(obj, dict):
|
|
8
|
+
return False
|
|
9
|
+
if "name" in obj and "arguments" in obj:
|
|
10
|
+
return True
|
|
11
|
+
if len(obj) == 1:
|
|
12
|
+
_, v = next(iter(obj.items()))
|
|
13
|
+
return isinstance(v, dict)
|
|
14
|
+
return False
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _to_call_dict(obj: Any) -> Any:
|
|
18
|
+
"""Recursively convert shorthand/wrapped dicts into name/arguments templates.
|
|
19
|
+
|
|
20
|
+
Rules:
|
|
21
|
+
- If obj is a dict with {name, arguments}: return {name, arguments: recurse(arguments)}
|
|
22
|
+
- Else if obj is a single-key dict {k: v}: return {name: k, arguments: recurse(v)}
|
|
23
|
+
- Else: return obj unchanged (leaf arguments/value)
|
|
24
|
+
"""
|
|
25
|
+
if isinstance(obj, dict):
|
|
26
|
+
if "name" in obj and "arguments" in obj:
|
|
27
|
+
args = obj.get("arguments")
|
|
28
|
+
# Only recurse into arguments if it looks like another call
|
|
29
|
+
if _is_call_like(args):
|
|
30
|
+
return {"name": obj.get("name"), "arguments": _to_call_dict(args)}
|
|
31
|
+
return {"name": obj.get("name"), "arguments": args}
|
|
32
|
+
if len(obj) == 1:
|
|
33
|
+
k, v = next(iter(obj.items()))
|
|
34
|
+
if isinstance(v, dict):
|
|
35
|
+
return {"name": k, "arguments": _to_call_dict(v)}
|
|
36
|
+
return obj
|
|
37
|
+
return obj
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def normalize_to_tool_call_dict(value: Any) -> Any:
|
|
41
|
+
"""
|
|
42
|
+
Convert shorthand or nested forms into a direct tool call dict:
|
|
43
|
+
{"name": final_name, "arguments": final_arguments}
|
|
44
|
+
Lists are normalized element-wise.
|
|
45
|
+
"""
|
|
46
|
+
if value is None:
|
|
47
|
+
return value
|
|
48
|
+
|
|
49
|
+
def _normalize_one(item: Any) -> Any:
|
|
50
|
+
call = _to_call_dict(item)
|
|
51
|
+
return call
|
|
52
|
+
|
|
53
|
+
if isinstance(value, list):
|
|
54
|
+
return [_normalize_one(x) for x in value]
|
|
55
|
+
|
|
56
|
+
if isinstance(value, dict):
|
|
57
|
+
return _normalize_one(value)
|
|
58
|
+
|
|
59
|
+
return value
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{hud_python-0.4.32 → hud_python-0.4.33}/environments/browser/apps/2048/backend/pyproject.toml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{hud_python-0.4.32 → hud_python-0.4.33}/environments/browser/apps/todo/backend/pyproject.toml
RENAMED
|
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
|
|
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
|