Skip to content

Add WALL-OSS-0.5 native decoder and action processor components#38

Draft
Grape203 wants to merge 11 commits into
MEmbodied:mainfrom
Grape203:dev/walloss05-native-pr26-base
Draft

Add WALL-OSS-0.5 native decoder and action processor components#38
Grape203 wants to merge 11 commits into
MEmbodied:mainfrom
Grape203:dev/walloss05-native-pr26-base

Conversation

@Grape203

@Grape203 Grape203 commented Jul 2, 2026

Copy link
Copy Markdown

Summary

This PR adds an experimental native WALL-OSS-0.5 decoder/action-processor implementation path.

The branch is rebased on the latest upstream/main after Cosmos3 support was merged. The goal of this PR is to move beyond the previous wrapper-based path and provide native PhyAI modeling components for the WALL-OSS-0.5 decoder and action processor.

What is included

  • WallOSS05NativeConfig
  • Native ActionProcessor
  • Native Sparse MoE
  • Native expert-wise RMSNorm / Norm-MoE
  • Native M-RoPE
  • Native attention projection, SDPA attention core, and joint attention
  • Native decoder FFN block
  • Native decoder layer
  • 36-layer native decoder model skeleton
  • Parity and shape-check scripts under examples/

Validation

All native checks passed after rebasing onto latest upstream/main.

Validated scripts include:

  • run_walloss05_native_action_processor_check.py
  • run_walloss05_native_action_processor_parity.py
  • run_walloss05_native_sparse_moe_parity.py
  • run_walloss05_native_norm_moe_parity.py
  • run_walloss05_native_decoder_ffn_parity.py
  • run_walloss05_native_attention_projection_parity.py
  • run_walloss05_native_mrope_parity.py
  • run_walloss05_native_attention_core_parity.py
  • run_walloss05_native_joint_attention_parity.py
  • run_walloss05_native_decoder_layer_parity.py
  • run_walloss05_native_decoder_model_skeleton_check.py

The current implementation verifies native decoder/action-processor components, layer-0 no-cache / mot_opt=True paths, and 36-layer decoder checkpoint tensor shape alignment.

Scope

This PR does not claim full end-to-end WALL-OSS-0.5 runtime integration yet.

Not included in this PR:

  • Native vision tower
  • Full Engine / ModelRunner / Scheduler integration
  • Full action generation loop replacement
  • KV-cache path parity
  • Full official adapter end-to-end rollout

Those parts are intended for the next integration stage.

@coderabbitai

coderabbitai Bot commented Jul 2, 2026

Copy link
Copy Markdown

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 6157576a-900c-4b56-b1f8-d2fb15948021

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces experimental native PyTorch modules for the WALL-OSS-0.5 model, including configuration overlays, action processors, attention projections, M-RoPE, attention core, decoder layers, and the decoder model skeleton, along with several parity and smoke test scripts to validate the implementation against official references. The review feedback focuses on performance optimizations, suggesting that position embeddings be sliced and cast once at the model level rather than redundantly in each layer, that .expand() be used instead of .repeat() for memory efficiency, that slice indices in M-RoPE be precomputed to avoid splitting tensors on every forward pass, and that redundant mask creation be avoided when the attention mask is null to enable SDPA optimized fast-paths.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +915 to +921
query_states, key_states = self.mrope(
query_states.contiguous(),
key_states.contiguous(),
cos[..., : (cos.size(3) // 2)].contiguous().float(),
sin[..., : (sin.size(3) // 2)].contiguous().float(),
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

In WallOSS05JointAttentionNative.forward, slicing, casting, and making cos/sin contiguous on every layer's forward pass is highly redundant (done 36 times per forward pass). We can optimize this by checking if the embeddings are already sliced to head_dim // 2, allowing us to perform this operation once at the model level.

        cos, sin = position_embeddings
        if cos.size(-1) == self.head_dim:
            cos = cos[..., : (self.head_dim // 2)].contiguous().float()
            sin = sin[..., : (self.head_dim // 2)].contiguous().float()
        query_states, key_states = self.mrope(
            query_states.contiguous(),
            key_states.contiguous(),
            cos,
            sin,
        )

projection_dtype: torch.dtype | None = None,
apply_final_norm: bool = True,
) -> torch.Tensor:
for layer in self.layers:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

In WallOSS05DecoderModelNative.forward, we can slice, cast, and make cos/sin contiguous once before the layer loop, saving 35 redundant operations per forward pass.

        cos, sin = position_embeddings
        cos = cos[..., : (cos.size(3) // 2)].contiguous().float()
        sin = sin[..., : (sin.size(3) // 2)].contiguous().float()
        position_embeddings = (cos, sin)

        for layer in self.layers:

time_embed = self.time_embed(timestep).to(dtype=noisy_action.dtype)
action_embed = _linear_forward(self.w1, noisy_action)

time_embed = time_embed.unsqueeze(1).repeat(1, action_embed.shape[1], 1)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

In WallOSS05ActionProcessorNative.step, time_embed is repeated using .repeat(). Using .expand() is much more efficient as it creates a view without copying memory, and it is fully compatible with torch.cat.

Suggested change
time_embed = time_embed.unsqueeze(1).repeat(1, action_embed.shape[1], 1)
time_embed = time_embed.unsqueeze(1).expand(-1, action_embed.shape[1], -1)

Comment on lines +608 to +615
def __init__(self, mrope_section: list[int] | tuple[int, ...]):
super().__init__()
section = tuple(int(v) for v in mrope_section)
if len(section) != 3:
raise ValueError(f"mrope_section must have 3 entries, got {section}")
if any(v <= 0 for v in section):
raise ValueError(f"mrope_section entries must be positive, got {section}")
self.mrope_section = section

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

In WallOSS05MRoPENative.__init__, we can precompute the slice indices for cos and sin to avoid splitting the entire tensor along the last dimension on every forward pass.

    def __init__(self, mrope_section: list[int] | tuple[int, ...]):
        super().__init__()
        section = tuple(int(v) for v in mrope_section)
        if len(section) != 3:
            raise ValueError(f"mrope_section must have 3 entries, got {section}")
        if any(v <= 0 for v in section):
            raise ValueError(f"mrope_section entries must be positive, got {section}")
        self.mrope_section = section

        sections = list(section) + list(section)
        offsets = [0]
        for s in sections:
            offsets.append(offsets[-1] + s)
        self.slices = [(i % 3, offsets[i], offsets[i+1]) for i in range(len(sections))]

Comment on lines +627 to +642
cos = cos.float()
sin = sin.float()

cos = torch.cat((cos, cos), dim=-1)
sin = torch.cat((sin, sin), dim=-1)

mrope_section_doubled = list(self.mrope_section) + list(self.mrope_section)

cos_split = torch.cat(
[m[i % 3] for i, m in enumerate(cos.split(mrope_section_doubled, dim=-1))],
dim=-1,
).unsqueeze(2)
sin_split = torch.cat(
[m[i % 3] for i, m in enumerate(sin.split(mrope_section_doubled, dim=-1))],
dim=-1,
).unsqueeze(2)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

In WallOSS05MRoPENative.forward, we can use direct slicing with the precomputed slice indices instead of calling cos.split and list comprehension. This is significantly faster and avoids creating intermediate split tensors.

        cos = torch.cat((cos, cos), dim=-1)
        sin = torch.cat((sin, sin), dim=-1)

        cos_split = torch.cat(
            [cos[idx, :, :, start:end] for idx, start, end in self.slices],
            dim=-1,
        ).unsqueeze(2)
        sin_split = torch.cat(
            [sin[idx, :, :, start:end] for idx, start, end in self.slices],
            dim=-1,
        ).unsqueeze(2)

Comment on lines +726 to +735
causal_mask = torch.ones(
bsz,
1,
1,
key_len,
device=device,
dtype=dtype,
).contiguous()
causal_mask = causal_mask.to(torch.bool)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

In WallOSS05AttentionCoreNative._prepare_causal_mask, if attention_mask is None and q_len == 1, creating a mask of all True is redundant and prevents SDPA from using optimized unmasked fast-paths. We should only create the ones mask if causal_mask is not None.

Suggested change
causal_mask = torch.ones(
bsz,
1,
1,
key_len,
device=device,
dtype=dtype,
).contiguous()
causal_mask = causal_mask.to(torch.bool)
if q_len == 1 and causal_mask is not None:
causal_mask = torch.ones(
bsz,
1,
1,
key_len,
device=device,
dtype=dtype,
).contiguous()
causal_mask = causal_mask.to(torch.bool)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant