veomni-new-op
SkillDev toolsGuides your agent through adding a new optimized kernel or operator to the VeOmni codebase, from design to registration, tests, and docs.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the veomni-new-op skill
About this capability
Use this skill when adding a new optimized kernel or operator to veomni/ops/. Covers the full lifecycle: understanding VeOmni's ops architecture (KERNEL_REGISTRY + OpSlot dispatch, with a thin function-pointer shim for a few legacy global ops), implementing the kernel, registering it, adding tests,
What this skill tells your AI
The instructions your AI receives, as published by bytedance-seed/veomni in .agents/skills/veomni-new-op/SKILL.md and read by ahel’s review.
Before You Start
- Read
.agents/knowledge/constraints.md— especially the "Hardware" section (NPU guards, device-agnostic helpers) and "Module-level OpSlots are shared by every model instance" under "Trainer Extensions". - Read
docs/design/kernel_selection.mdanddocs/design/unified_kernel_registry.md— understand the kernel lifecycle, theKERNEL_REGISTRY, andOpSlotdispatch. - Familiarize yourself with the ops architecture below.
VeOmni Ops Architecture
Most VeOmni ops in v5 are registry-driven: a kernel registers itself in
veomni.ops.kernel_registry.KERNEL_REGISTRY and is dispatched at model-build
time through OpSlot instances declared in the patchgen-generated modeling
files (see veomni/ops/dispatch.py and _bind_veomni_ops() in
veomni/models/auto.py).
veomni/ops/
├── __init__.py # apply_ops_patch / apply_ops_config entry points
├── kernel_registry.py # KERNEL_REGISTRY (the single source of truth)
├── dispatch.py # OpSlot + binding helpers
├── config/ # legacy OpSpec/BackendSpec registry: apply_global_ops()
│ # + apply_per_model_patches() for device_patch.py models
├── kernels/ # all registry-driven kernels
│ ├── attention/ # FA2/3/4 + sequence-parallel wrappers
│ ├── cross_entropy/ # eager + liger fused CE
│ ├── deepseek_sparse_attention/
│ ├── deepseek_v4/ # TileLang sparse attention / indexer
│ ├── load_balancing_loss/
│ ├── mhc/ # TileKernels DeepSeek V4 adapters
│ ├── moe/ # fused MoE (group_gemm / quack / npu_group_gemm)
│ ├── rms_norm/ # eager / liger / batch-invariant
│ ├── rotary/ # default / triton-deterministic
│ ├── swiglu/ # eager / liger
│ └── gated_delta_rule/
├── batch_invariant_ops/ # ATen-level interception for bitwise determinism
├── liger/ # Liger kernel adapters
└── platform/ # NPU-specific helpers
Three mechanisms coexist. Pick the first one unless you have a concrete reason not to:
KERNEL_REGISTRY+OpSlot(preferred for new ops). Each kernel registers itself under a(slot_name, variant)pair (e.g.("cross_entropy_loss", "causal"),("moe_experts", "standard")). Patchgen-generated modeling code declares matchingOpSlotinstances; at model-build time_bind_veomni_ops()walks the generated module, finds eachOpSlot, and binds it to the concrete registry entry chosen byOpsImplementationConfig(config/registry.py).- Legacy global function pointer shim (kept for a few global ops that
are dispatched outside generated modeling). Public-API functions like
fused_moe_forwardandload_balancing_lossstill expose a thin pointer that is rebound byapply_ops_config()so call sites in non-patchgen code (DeepSeek MLA inference paths, NPU custom forwards) can keep importing the public name without going through anOpSlot. - Per-model
device_patch.pyviaOpSpec/BackendSpecinops/config/registry.py.apply_per_model_patches(hf_module, model_name, targets={op: attr})setattr-replaces attributes on an HF module. Used by the models that have no patchgen-generated file (wan) or that need a runtime device-specific swap after generation (deepseek_v3,deepseek_v4). Those threedevice_patch.pyfiles are its only callers. Do not extend this for new kernels.
Mechanism 1 covers any kernel living inside a patchgen-generated modeling file.
Use 2 only when the kernel must be callable from unpatched (or
non-Transformers) Python code, and 3 only when touching a model that already
ships a device_patch.py.
Phase 1: Design
-
Determine op category:
- Registry-driven kernel (the common case, used inside patchgen-generated modeling): register under a
(slot_name, variant)inKERNEL_REGISTRYand add a matchingOpSlotin the relevant<model>_patch_gen_config.py. No global mutation; selection is driven byOpsImplementationConfig. - Global op with public API (e.g.
fused_moe_forward,load_balancing_loss): expose a public function inveomni/ops/__init__.pyand rebind it fromapply_ops_config()based on the activeOpsImplementationConfig. Only use this when a non-patchgen call site (NPU MLA forward, manual inference scripts, etc.) needs to import the kernel directly. - Library op (no dispatch — called directly by model code): just create the module, no registry entry needed.
- NPU variant: add alongside the GPU implementation behind an
is_torch_npu_available()guard.
- Registry-driven kernel (the common case, used inside patchgen-generated modeling): register under a
-
Decide selection mechanism: read
docs/design/kernel_selection.mdanddocs/design/unified_kernel_registry.mdto determine if you need:- Config field in
OpsImplementationConfig(veomni/arguments/arguments_types.py) - Environment variable
- Both
- Config field in
-
Determine binding timing:
- Model build time (default): registry entries are resolved by
_bind_veomni_ops()inveomni/models/auto.pywhen a model is constructed. New kernels just need to register themselves at import time. apply_ops_config()time: legacy global ops (rebound function pointers) are wired inveomni/ops/__init__.py::apply_ops_config(ops_config).
- Model build time (default): registry entries are resolved by
Phase 2: Implement
-
Create the op directory under
veomni/ops/kernels/<op_name>/. -
Implement each kernel variant in its own file (e.g.
triton_kernel.py,eager.py,npu_kernel.py). Each variant declares a concrete function with the kernel's canonical signature. -
Register the kernel in
veomni/ops/kernels/<op_name>/__init__.py. OneKERNEL_REGISTRY.register(KernelSpec(...))call per implementation —register()takes a singleKernelSpecand returnsNone, so it is not a decorator:from veomni.ops.kernel_registry import KERNEL_REGISTRY, HardwareRequirement, KernelSpec def _my_op_triton_factory(): from .triton_kernel import my_op_triton # imported only when selected return my_op_triton KERNEL_REGISTRY.register( KernelSpec( name="triton", # impl name the user selects in the config op_name="my_op", # the logical op — matches the OpSlot variant="standard", # op shape, when one op has several factory=_my_op_triton_factory, hardware=HardwareRequirement(device_type="gpu"), description="Triton my_op", ) )factoryis a zero-argument callable returning the kernel, not the kernel itself. Keeping it lazy is what stops an optional dependency (Liger, Triton,torch_npu) from being imported just because the module was loaded.hardwareis enforced atresolve()time, so an unavailable kernel fails with a clear error instead of at first use.Mind the two axes:
(op_name, variant)identifies the slot,nameidentifies the implementation within it. Kernels in different variants never collide.Then declare a matching
OpSlotin the patchgen config of every model that uses it — the arguments are(op_name, variant), not an implementation:from veomni.ops.dispatch import OpSlot veomni_my_op = OpSlot("my_op", "standard")_bind_veomni_ops()callsslot.bind(impl_name)with the implementation selected byOpsImplementationConfig. Seeveomni/ops/kernels/rotary/__init__.pyfor a live example, andveomni/ops/README.mdfor the op/variant/impl table. -
Wire the config field (if the user needs to choose an implementation):
- Add a field to
OpsImplementationConfiginveomni/arguments/arguments_types.py. - Call
register_op(OpSpec(name=..., config_field=..., scope=..., default=..., backends={...}))from the sameveomni/ops/kernels/<op_name>/__init__.py— the mapping lives next to the kernel, not insideveomni/ops/config/registry.py, which only definesOpSpec/BackendSpec/register_op. Seeveomni/ops/kernels/rms_norm/__init__.py, which registers both anOpSpecand itsKernelSpecs.
- Add a field to
-
For legacy global ops (only when needed): add the public function to
veomni/ops/__init__.pyand rebind it fromapply_ops_config(ops_config). -
Async Ulysses split wrappers (only for
rms_normandrotary_pos_emb): compound Functions cannot callOpSlot. They use no-autograd(output, saved)/backwardpairs inveomni/distributed/sequence_parallel/op_wrappers.py. A new backend or variant must either add a matching wrapper there, or be left off_SUPPORTED_IMPLEMENTATIONS/_SUPPORTED_VARIANTSsoget_op_wrapperrejects it.KERNEL_REGISTRYcoverage is not enough. -
NPU support:
- Always guard NPU imports with
is_torch_npu_available(). - Put NPU implementations in a separate file (e.g.,
npu_kernel.py). - Register the NPU variant under the same slot with a distinct variant name.
- Always guard NPU imports with
Phase 3: Test
-
Add unit tests to
tests/ops/. The GPU job runs this directory wholesale, so a new file needs nogpu_unit_tests.ymlchange. The NPU job does not — it enumerates ops files by name, so if the kernel must run on Ascend, add a line tonpu_unit_tests.yml(see.agents/knowledge/testing.md):- Test correctness: compare output against a reference implementation (eager PyTorch)
- Test numerical precision: verify tolerance for bf16/fp16
- Test edge cases: empty inputs, single-element tensors, extreme shapes If the kernel only binds on SM90+, guard it so the SM89 GPU runners skip rather than fail.
-
Add benchmark (optional but recommended for performance-critical ops):
- Use
veomni/ops/kernels/moe/_kernels/utils/benchmark_utils.pyas reference - Compare against baseline implementation
- Use
-
Run:
pytest tests/ops/ -v
Phase 4: Document
-
Update
docs/design/kernel_selection.md:- Add the new op to the Quick Reference table
- Describe the selection mechanism
-
Update
.agents/knowledge/architecture.mdif the op adds a new subdirectory toveomni/ops/.
Phase 5: Finalize
- Run
make quality. - Verify the new variant shows up in
KERNEL_REGISTRY.dump()and that the relevantOpSlotis rebound afterbuild_foundation_model. - Before opening the PR, run
/veomni-reviewover the branch diff — a new kernel touchesveomni/, so the gate applies.
Common Pitfalls
- Forgetting to register in
KERNEL_REGISTRY: the variant is invisible to_bind_veomni_ops()andOpSlotwill fall through to its default — you'll silently exercise the wrong kernel. - Forgetting to add the matching
OpSlotto the patchgen config: registering a kernel alone has no effect — generated modeling code must declare anOpSlotfor it to be picked up. - Unconditional NPU imports: importing NPU modules without an
is_torch_npu_available()guard crashes on GPU-only environments. - Binding at wrong time: registry entries are resolved when
build_foundation_modelruns_bind_veomni_ops(). Kernels that depend on per-model config must be picked at that point — not at module-import time. - New
rms_norm/rotary_pos_embbackend without an async wrapper:OpSlotwill bind, but async Ulysses goes throughop_wrappers.py, not the registry callable. Add a split wrapper or confirmget_op_wrapperrejects the new name; do not derive the supported set fromKERNEL_REGISTRY. - Sequence parallel interaction: ops that touch attention or loss must handle sequence parallel correctly — use
get_parallel_state().sp_enabledto check and dispatch. - Mixed precision: fused kernels often require specific dtypes (bf16/fp16). Add assertions at the public API level to catch dtype mismatches early.
- Not exporting public APIs: if the op provides a public function (legacy global ops), export it from
veomni/ops/__init__.py's__all__.
Signals
- GitHub stars
- 2k
- Forks
- 272
- Last commit
- Sep 2026
Advanced
- Catalog kind
- skill
- Gateway key
veomni-new-op- Source
- github.com/bytedance-seed/veomni