diff --git a/.file_mapping.json b/.file_mapping.json index caec1ac6..1606f284 100644 --- a/.file_mapping.json +++ b/.file_mapping.json @@ -1,7 +1,7 @@ { - "_source_commit": "aa88627921856c9c83ab8edf42c46e506255d3a6-dirty", - "_dest_commit": "fcd1d7eb07ad76dd7f15c61185a2377f15918ac7", - "_generated_at": "2026-09-07T05:52:05Z", + "_source_commit": "6cc9d886c99205e0b3d34f1b96b7eaa893122764-dirty", + "_dest_commit": "4e0fe142e2aeb74b456fbf9e0c0748d7028bdd7c", + "_generated_at": "2026-09-08T05:52:20Z", "files": { "imaginaire/__init__.py": "cosmos_framework/__init__.py", "imaginaire/attention/__init__.py": "cosmos_framework/model/attention/__init__.py", diff --git a/cosmos_framework/data/generator/action/utils/domain_utils.py b/cosmos_framework/data/generator/action/utils/domain_utils.py index bf57841f..ad56547f 100644 --- a/cosmos_framework/data/generator/action/utils/domain_utils.py +++ b/cosmos_framework/data/generator/action/utils/domain_utils.py @@ -8,10 +8,9 @@ "av": 1, "camera_pose": 2, "hand_pose": 3, - # Alias for the WebHumanAction (Action100M) Lance hand adapter. Same domain - # as "hand_pose" (shared with embodiment_a) so it reuses the same action2llm/llm2action - # DomainAwareLinear weights rather than training a fresh encoder/decoder. - "webhumanaction_hand": 3, + # WebHumanAction omits the camera prefix from its 48D native hand layout, + # so it must not reuse hand_pose/embodiment_a's camera-inclusive domain 3 projector. + "webhumanaction_hand": 31, "pusht": 4, "libero": 5, "umi": 6, @@ -36,7 +35,7 @@ "behavior1k_lerobot": 22, # BEHAVIOR-1K R1Pro mobile bimanual (23D joint action) "maniparena": 23, # ManipArena x2robot/ex001_6r dual-arm; own 20D EE-direct action projection # New dedicated slot (not reusing agibot's domain 15): WebHumanAction body - # (camera+head+wrists, yesCam 36D) trains its own action2llm/llm2action + # (ego/head+wrists+fingertips, no camera action, 57D) trains its own action2llm/llm2action # DomainAwareLinear weights from scratch instead of continuing agibot's. "webhumanaction_body": 24, "so101-molmo-midtrain-15hz": 25, @@ -72,7 +71,7 @@ "agibotworld": 29, "embodiment_c_gripper": 29, "embodiment_c_gripper_ext": 29, - "webhumanaction_body": 36, # camera(9) + head(9) + R_wrist(9) + L_wrist(9) + "webhumanaction_body": 57, # ego/head(9) + [R_wrist(9)+R_fingertips(15)] + [L_wrist(9)+L_fingertips(15)] "xdof_yam": 20, "molmoact2_yam": 20, "abc_yam": 20, diff --git a/cosmos_framework/model/generator/omni_mot_causal_model.py b/cosmos_framework/model/generator/omni_mot_causal_model.py index 309f3346..6ec6845e 100644 --- a/cosmos_framework/model/generator/omni_mot_causal_model.py +++ b/cosmos_framework/model/generator/omni_mot_causal_model.py @@ -429,7 +429,12 @@ def _resolve_teacher_forcing_replay_policy(value: Any) -> TeacherForcingReplayPo if OmegaConf.is_config(value): value = OmegaConf.to_object(value) if isinstance(value, dict): - value = TeacherForcingReplayPolicyConfig(**value) + # Drop lazy-config serializer metadata before construction. A config that has been + # round-tripped through an exported checkpoint carries a "_type" marker alongside + # the real fields, and attrs rejects it as an unexpected keyword. In-process + # construction never sees the marker, so this only failed when loading from an + # export -- which is every public run of a causal model. + value = TeacherForcingReplayPolicyConfig(**{k: v for k, v in value.items() if not k.startswith("_")}) if not isinstance(value, TeacherForcingReplayPolicyConfig): raise TypeError( "teacher_forcing_replay_policy must resolve to a TeacherForcingReplayPolicyConfig, " diff --git a/cosmos_framework/model/generator/omni_mot_causal_model_test.py b/cosmos_framework/model/generator/omni_mot_causal_model_test.py index 51c01c18..79d00e03 100644 --- a/cosmos_framework/model/generator/omni_mot_causal_model_test.py +++ b/cosmos_framework/model/generator/omni_mot_causal_model_test.py @@ -2683,3 +2683,47 @@ def test_training_step_resets_flag_on_error(self) -> None: model.training_step({}, iteration=0) assert model._bidirectional_step_active is False + + +@pytest.mark.L0 +@pytest.mark.CPU +def test_teacher_forcing_replay_policy_resolves_a_serialized_config_dict() -> None: + """A config round-tripped through an exported checkpoint must still resolve. + + Serializing a config adds a "_type" marker next to the real fields. Passing that + dict straight to attrs raised TypeError("unexpected keyword argument '_type'"), so + every causal model failed to load from an exported artifact while in-process + construction -- which never sees the marker -- kept working. + """ + from cosmos_framework.configs.base.defaults.replay_attention import ( + TeacherForcingReplayPolicyConfig, + ) + from cosmos_framework.model.generator.omni_mot_causal_model import ( + _resolve_teacher_forcing_replay_policy, + ) + + serialized = { + "_type": "cosmos_framework.configs.base.defaults.replay_attention.TeacherForcingReplayPolicyConfig", + "control_visibility": "current", + "controls_read_strict_past_clean_rgb": True, + "clean_pass_causality": "chunk", + } + + resolved = _resolve_teacher_forcing_replay_policy(serialized) + + assert isinstance(resolved, TeacherForcingReplayPolicyConfig) + assert resolved.control_visibility == "current" + assert resolved.controls_read_strict_past_clean_rgb is True + assert resolved.clean_pass_causality == "chunk" + + +@pytest.mark.L0 +@pytest.mark.CPU +def test_teacher_forcing_replay_policy_still_rejects_unknown_real_fields() -> None: + """Stripping the marker must not turn typos into silently ignored fields.""" + from cosmos_framework.model.generator.omni_mot_causal_model import ( + _resolve_teacher_forcing_replay_policy, + ) + + with pytest.raises(TypeError, match="control_visibilty"): + _resolve_teacher_forcing_replay_policy({"_type": "x", "control_visibilty": "current"})