vla / workspace /tests /test_openvla_adapter.py
anhtld's picture
auto-sync 2026-07-02T13:37:00Z workspace (part 33)
da08b7d verified
Raw
History Blame Contribute Delete
5.64 kB
from __future__ import annotations
from pathlib import Path
from types import SimpleNamespace
import pytest
torch = pytest.importorskip("torch")
from dovla_cil.data.schema import ActionChunk
from dovla_cil.models.dovla import DoVLAConfig, DoVLAModel
from dovla_cil.models.openvla_adapter import (
ExternalOpenVLAAdapter,
PretrainedCLIPBackbone,
ToyVLABackbone,
VLABackbone,
)
def _config() -> DoVLAConfig:
return DoVLAConfig(
obs_dim=10,
lang_dim=16,
action_dim=8,
hidden_dim=32,
action_horizon=3,
effect_dim=7,
intervention_dim=24,
)
def test_toy_vla_backbone_works() -> None:
config = _config()
backbone = ToyVLABackbone(config)
observation = torch.randn(2, config.obs_dim)
instructions = ["pick the mug", "open the drawer"]
action = torch.randn(2, config.action_horizon, config.action_dim)
context = backbone.encode_observation_language(observation, instructions)
action_z = backbone.encode_action(action)
policy = backbone.forward_policy(observation, instructions)
intervention = backbone.forward_intervention(observation, instructions, action)
decoded = backbone.decode_action(policy)
assert isinstance(backbone, VLABackbone)
assert context.shape == (2, config.hidden_dim)
assert action_z.shape == (2, config.hidden_dim)
assert policy.shape == (2, config.action_horizon, config.action_dim)
assert intervention.shape == (2, config.intervention_dim)
assert isinstance(decoded, ActionChunk)
def test_external_openvla_adapter_requires_configuration() -> None:
with pytest.raises(NotImplementedError) as exc_info:
ExternalOpenVLAAdapter()
assert "checkpoint_path" in str(exc_info.value)
def test_external_openvla_adapter_methods_are_placeholders() -> None:
adapter = ExternalOpenVLAAdapter(checkpoint_path=Path("missing-openvla-checkpoint"))
with pytest.raises(NotImplementedError) as exc_info:
adapter.forward_policy(None, "pick the mug")
assert "extension point only" in str(exc_info.value)
def test_dovla_model_can_use_injected_backbone() -> None:
config = _config()
backbone = ToyVLABackbone(config)
model = DoVLAModel(config, backbone=backbone)
observation = torch.randn(2, config.obs_dim)
instructions = ["pick the mug", "open the drawer"]
action = torch.randn(2, config.action_horizon, config.action_dim)
policy = model.forward_policy(observation, instructions)
effect = model.forward_effect(observation, instructions, action)
reward = model.forward_reward(observation, instructions, action)
z = model.encode_intervention(observation, instructions, action)
assert model.backbone is backbone
assert policy.shape == (2, config.action_horizon, config.action_dim)
assert effect["effect_vector"].shape == (2, config.effect_dim)
assert reward.shape == (2,)
assert z.shape == (2, config.intervention_dim)
class _FakeCLIP(torch.nn.Module):
def __init__(self, projection_dim: int = 12) -> None:
super().__init__()
self.anchor = torch.nn.Parameter(torch.zeros(()))
self.config = SimpleNamespace(
projection_dim=projection_dim,
vision_config=SimpleNamespace(image_size=8),
)
def get_image_features(self, *, pixel_values):
values = pixel_values.mean(dim=(1, 2, 3), keepdim=False).unsqueeze(1)
return values.repeat(1, self.config.projection_dim) + self.anchor
def get_text_features(self, *, input_ids, **_kwargs):
values = input_ids.float().mean(dim=1, keepdim=True)
return values.repeat(1, self.config.projection_dim) + self.anchor
class _FakeProcessor:
tokenizer = None
def __init__(self) -> None:
self.tokenizer = self
def __call__(self, texts, **_kwargs):
return {
"input_ids": torch.tensor(
[[len(word) for word in text.split()][:4] for text in texts],
dtype=torch.long,
)
}
def test_pretrained_clip_backbone_supports_raw_and_cached_features() -> None:
config = DoVLAConfig(
obs_dim=10,
lang_dim=16,
action_dim=8,
hidden_dim=32,
action_horizon=3,
effect_dim=7,
intervention_dim=24,
observation_mode="rgb",
backbone_type="native",
)
backbone = PretrainedCLIPBackbone(
config,
clip_model=_FakeCLIP(),
processor=_FakeProcessor(),
)
images = torch.randint(0, 255, (2, 12, 10, 3), dtype=torch.uint8)
instructions = ["pick red cube", "push blue cube"]
action = torch.randn(2, config.action_horizon, config.action_dim)
cached = backbone.encode_pretrained_features(images, instructions)
raw_context = backbone.encode_observation_language(images, instructions)
cached_context = backbone.encode_observation_language(cached, instructions)
intervention = backbone.forward_intervention(cached, instructions, action)
assert cached.shape == (2, 24)
assert torch.allclose(raw_context, cached_context)
assert raw_context.shape == (2, config.hidden_dim)
assert intervention.shape == (2, config.intervention_dim)
assert not any(parameter.requires_grad for parameter in backbone.clip_model.parameters())
def test_clip_model_config_requires_rgb_and_model_path() -> None:
with pytest.raises(ValueError, match="observation_mode"):
DoVLAConfig(backbone_type="clip", backbone_model="local-clip")
with pytest.raises(ValueError, match="backbone_model"):
DoVLAConfig(observation_mode="rgb", backbone_type="clip")