diff --git a/src/kitchenbench/embodiment.py b/src/kitchenbench/embodiment.py index a19e7c5..6f168f8 100644 --- a/src/kitchenbench/embodiment.py +++ b/src/kitchenbench/embodiment.py @@ -19,6 +19,8 @@ from __future__ import annotations +from typing import Any + import numpy as np from inspect_robots import ( Action, @@ -149,7 +151,7 @@ def _observe(self) -> Observation: instruction=self._instruction, ) - def _render(self) -> np.ndarray: + def _render(self) -> np.ndarray[Any, Any]: img = np.zeros((_IMG, _IMG, 3), dtype=np.uint8) filled = round(self._progress * _IMG) img[_IMG - filled :, :, 1] = 200 # a green progress bar rising from the bottom diff --git a/src/kitchenbench/instances.py b/src/kitchenbench/instances.py index 05fb15b..90fdcc5 100644 --- a/src/kitchenbench/instances.py +++ b/src/kitchenbench/instances.py @@ -15,7 +15,6 @@ from __future__ import annotations from dataclasses import dataclass, field -from typing import Any import numpy as np @@ -206,7 +205,7 @@ class TaskInstance: setup: dict[str, Distribution] target_kind: str language_vars: tuple[str, ...] = () - static: dict[str, Any] = field(default_factory=dict) + static: dict[str, Scalar] = field(default_factory=dict) # hashable scalars only validation: Validation = field(default_factory=Validation) sim: SimSpec | None = None diff --git a/tests/test_instances.py b/tests/test_instances.py index fa34587..a01e79f 100644 --- a/tests/test_instances.py +++ b/tests/test_instances.py @@ -101,20 +101,28 @@ def test_validation_rejects_too_few_experts() -> None: def test_instances_and_realizations_are_hashable() -> None: from kitchenbench.specs import SPECS - inst = _instance() - # TaskInstance should be hashable - assert hash(inst) is not None - s_inst = {inst} - assert inst in s_inst - - # Realization should be hashable - real = inst.realize(0) - assert hash(real) is not None - s_real = {real} - assert real in s_real - - # TaskSpec should also be hashable - spec = SPECS[0] - assert hash(spec) is not None - s_spec = {spec} - assert spec in s_spec + # TaskInstance: equal objects must hash equal and support dict lookup + inst_a = _instance() + inst_b = _instance() + assert inst_a == inst_b + assert hash(inst_a) == hash(inst_b) + assert {inst_a: "x"}[inst_b] == "x" + + # Realization: equal objects must hash equal and support dict lookup + real_a = inst_a.realize(0) + real_b = inst_b.realize(0) + assert real_a == real_b + assert hash(real_a) == hash(real_b) + assert {real_a: "y"}[real_b] == "y" + + # TaskSpec: equal objects must hash equal and support dict lookup + spec_a = SPECS[0] + spec_b = SPECS[0] + assert spec_a == spec_b + assert hash(spec_a) == hash(spec_b) + assert {spec_a: "z"}[spec_b] == "z" + + # Spec-wide invariant: every instance across SPECS must be hashable + for spec in SPECS: + for instance in spec.instances: + assert hash(instance) is not None